acs-datetime-procs.tcl

Does not contain a contract.

Location:
/packages/acs-datetime/tcl/acs-datetime-procs.tcl

Related Files

[ hide source ] | [ make this the default ]

File Contents

# /packages/acs-datetime/tcl/acs-datetime-procs.tcl

ad_library {

    Tcl library for the ACS Date-Time service package

    @author  ron@arsdigita.com
    @creation-date 2000-11-21
    @cvs-id  $Id: acs-datetime-procs.tcl,v 1.19.2.8 2023/03/23 16:14:43 antoniop Exp $
}

d_proc -public dt_systime {
    {-format "%Y-%m-%d %H:%M:%S"}
    {-gmt f}
} {
    @param gmt returns the time in GMT.
    @return current server time in the standard format "yyyy-mm-dd hh:mi:ss".
} {
    return [clock format [clock seconds] -format $format -gmt $gmt]
}

d_proc -public dt_sysdate {
    {-format "%Y-%m-%d"}
} {
    @return current server date in the standard format "yyyy-mm-dd"
} {
    return [clock format [clock seconds] -format $format]
}

d_proc -public dt_valid_time_p {
    time
} {
    @return 1 if "time" is a valid time specification, 0 otherwise.
} {
    if {[catch { clock scan $time }]} {
        return 0
    } else {
        return 1
    }
}

d_proc -deprecated dt_format {
    {-format "%Y-%m-%d %H:%M:%S"}
    {-gmt f}
    time
} {
    This proc should not be used, because it does not take internationalization into account. Use lc_time_fmt instead.

    @see lc_time_fmt
} {
    return [clock format [clock scan $time] -format $format -gmt $gmt]
}

ad_proc -public dt_month_names {} {
    @return the calendar month names as a Tcl list (January, February, ...)

    @see lc_get
} {
    return [lc_get mon]
}

ad_proc -public dt_month_abbrev {} {
    @return the calendar month names as a Tcl list (Jan, Feb, ...)

    @see lc_get
} {
    return [lc_get abmon]
}

d_proc -public dt_ansi_to_julian_single_arg {
    ansi
    {era ""}
} {
    Splits the ANSI date into year, month and day, and calls dt_ansi_to_julian
    to transform it to Julian.

    @return the ANSI date as Julian

    @see dt_ansi_to_julian
} {
    set date_list [dt_ansi_to_list $ansi]

    set year [util::trim_leading_zeros [lindex $date_list 0]]
    set month [util::trim_leading_zeros [lindex $date_list 1]]
    set day [util::trim_leading_zeros [lindex $date_list 2]]

    return [dt_ansi_to_julian $year $month $day $era]
}

d_proc -public dt_ansi_to_julian {
    year
    month
    day
    {era ""}
} {
    @param era this argument is obsolete and passing it to the proc
               will generate a warning.

    @return the ANSI date as Julian or -1 in the case
            of an invalid ANSI date argument (year less than
            4713 BCE, greater than 9999 CE, or equal to 0)
} {
    if {$era ne ""} {
        ad_log warning "'era' argument is obsolete"
    }

    try {
        return [clock format [clock scan ${year}-${month}-${day} -format %Y-%m-%d] -format %J]
    } on error {errmsg} {
        ad_log warning "Cannot convert ${year}-${month}-${day} to Julian date: $errmsg"
        return -1
    }
}

d_proc -public dt_julian_to_ansi {
    julian_date
} {
    @return julian_date formatted as "yyyy-mm-dd"
} {
    return [clock format [clock scan $julian_date -format %J] -format %Y-%m-%d]
}

d_proc -public dt_ansi_to_pretty {
    {ansi_date ""}
} {
    Converts an ANSI date into a localzed one.

    With no argument, it returns the current date based on server time.

    Works for both date and date-time strings.

    @param ansi_date    Date in ANSI format (for example, 1998-09-05)
    @return             Localized date (for example, on 'en_US', 05/09/98)
} {
    if {$ansi_date eq ""} {
        set ansi_date [dt_sysdate]
    }

    return [lc_time_fmt $ansi_date "%x"]
}

d_proc -public dt_ansi_to_list {
    {ansi_date ""}
} {
    Parses the given ansi_date string into a list of year, month, day,
    hour, minute, and second. Works for any date than can be parsed
    by clock scan.
} {
    if {$ansi_date eq ""} {
        set ansi_date [dt_systime]
    }

    foreach item [split [clock format [clock scan $ansi_date] -format "%Y %m %d %H %M %S"" "] {
        lappend date_info [util::trim_leading_zeros $item]
    }

    return $date_info
}

d_proc -public dt_num_days_in_month {
    year
    month
} {
    @return the numbers of days for the given month/year
} {
    if {$month == 0} {
        set month 01
    } elseif {$month == 12} {
        incr year
        set month 01
    } elseif {$month == 13} {
        incr year
        set month 02
    } else {
        incr month
    }

    return [clock format [clock scan "last day" -base [clock scan $year-$month-01]] -format %d]
}

d_proc -public dt_first_day_of_month {
    year
    month
} {
    @return the weekday number of the first day for the given month/year
} {
    # calendar widgets are expecting integers 1-7, so we must adjust
    return [expr {[clock format [clock scan $year-$month-01] -format %w] + 1}]
}

d_proc -public dt_next_month {
    year
    month
} {
    @return the ANSI date for the next month
} {
    try {
        return [clock format [clock add [clock scan $year-$month-01] 1 month] -format %Y-%m-%d]
    } on error {errmsg} {
        ad_log warning "Cannot get next month date for $year-$month"
        return ""
    }
}

d_proc -public dt_prev_month {
    year
    month
} {
    @return the ANSI date for the previous month
} {
    try {
        return [clock format [clock add [clock scan $year-$month-01] -1 month] -format %Y-%m-%d]
    } on error {errmsg} {
        ad_log warning "Cannot get previous month date for $year-$month"
        return ""
    }
}

d_proc -public dt_next_month_name {
    year
    month
} {
    @return Localized name of the next month
} {
    try {
        return [lc_time_fmt [lc_clock_to_ansi [clock add [clock scan $year-$month-01] 1 month]] "%B"]
    } on error {errmsg} {
        ad_log warning "Cannot get name of previous month for $year-$month"
        return ""
    }
}

d_proc -public dt_prev_month_name {
    year
    month
} {
    @return Localized name of the previous month
} {
    try {
        return [lc_time_fmt [lc_clock_to_ansi [clock add [clock scan $year-$month-01] -1 month]] "%B"]
    } on error {errmsg} {
        ad_log warning "Cannot get name of previous month for $year-$month"
        return ""
    }
}

d_proc -deprecated dt_widget_datetime {
    {-show_date 1}
    {-date_time_sep " "}
    {-use_am_pm 0}
    {-default none}
    name
    {granularity days}
} {

    @return an HTML form fragment for collecting date-time
    information with names "$name.year""$name.month""$name.day",
    "$name.hours""$name.minutes""$name.seconds", and "$name.ampm".
    These will be numeric ("ampm" is 0 for am, 1 for pm)

    Default specifies what should be set as the current time in the
    form. Valid defaults are "none""now", or any valid date string
    that can be converted with clock scan.

    Granularity can be "months" "days" "hours" "halves" "quarters"
    "fives" "minutes" or "seconds".

    Use -show_date 0 for a time entry widget (no dates).

    All HTML widgets will be output *unless* show_date is 0; they will
    be hidden if not needed to satisfy the current granularity
    level. Values default to 1 for MM/DD and 0 for HH/MI/SS/AM if not
    found in the input string or if below the granularity threshold.

    DEPRECATED: modern HTML5 feature make this widget less
    relevant. It is also cumbersome to style and localize.

    @see template::widget::h5time
    @see template::widget::h5date
} {
    set to_precision [dt_precision $granularity]

    set show_day     [expr {$to_precision < 1441}]
    set show_hours   [expr {$to_precision < 61}]
    set show_minutes [expr {$to_precision < 60}]
    set show_seconds [expr {$to_precision < 1}]

    if {$to_precision == 0} {
        set to_precision 1
    }

    switch $default {
        none    { set value [dt_systime] }
        now     { set value [dt_systime] }
        default { set value [lc_time_fmt $default "%Y-%m-%d %H:%M:%S"] }
    }

    set parsed_date [dt_ansi_to_list $value]
    set year        [lindex $parsed_date 0]
    set month       [lindex $parsed_date 1]
    set day         [lindex $parsed_date 2]
    set hours       [lindex $parsed_date 3]
    set minutes     [lindex $parsed_date 4]
    set seconds     [lindex $parsed_date 5]

    # Kludge to get minutes rounded.  Should make general-purpose for
    # the other values too...

    if {$to_precision < 60} {
        set minutes [expr {[dt_round_to_precision $minutes $to_precision] % 60}]
    }

    if {$default eq "none"} {
        set year    ""
        set month   ""
        set day     ""
        set hours   ""
        set minutes ""
        set seconds ""
    }

    if {$show_date} {
        append input [dt_widget_month_names "$name.month" $month]
        append input [dt_widget_maybe_range $show_day "$name.day" 1 31 $day 1 0 1]
        append input "<input name=\"$name.year\" size=5 maxlength=4 value=\"$year\"> $date_time_sep "
    }

    if {$use_am_pm} {
        if { $hours > 12 } {
            append input [dt_widget_maybe_range \
                $show_hours "$name.hours" 1 12 [expr {$hours - 12}] 1 0]
        } elseif {$hours == 0} {
            append input [dt_widget_maybe_range \
                $show_hours "$name.hours" 1 12 12 1 0]
        } else {
            append input [dt_widget_maybe_range \
                $show_hours "$name.hours" 1 12 $hours 1 0]
        }
    } else {
        append input [dt_widget_maybe_range \
            $show_hours "$name.hours" 0 23 $hours 1 0]
    }

    if {$show_minutes} {
        append input ":"
    }

    append input [dt_widget_maybe_range \
            $show_minutes "$name.minutes" 0 59 $minutes $to_precision 1]

    if {$show_seconds} {
        append input ":"
    }

    append input [dt_widget_maybe_range \
            $show_seconds "$name.seconds" 0 59 $seconds 1 1]

    if {$use_am_pm} {
        if {$hours < 12 || ! $show_hours} {
            set am_selected " selected"
            set pm_selected ""
        } else {
            set am_selected ""
            set pm_selected " selected"
        }

        append input "
        <select name=\"${name}.ampm\">
        <option value=0${am_selected}>AM
        <option value=1${pm_selected}>PM
        </select>"
    } else {
        append input [dt_export_value "${name}.ampm" "AM"]
    }

    return $input
}

d_proc -deprecated dt_widget_month_names {
    name
    {selected_month 0}
} {
    DEPRECATED: modern HTML5 feature make this widget less
    relevant. It is also cumbersome to style and localize.

    @see template::widget::h5time
    @see template::widget::h5date

    @return a select widget for months of the year.
} {
    if {$selected_month eq ""} {set selected_month 0}
    if {![string is integer $selected_month]} {error "selected_month must be integer"}
    set month_names [dt_month_names]
    set input       "<option value=_undef>---------"
    incr selected_month -1

    for {set i 0} {$i < 12} {incr i} {
        append input "<option [expr {$i == $selected_month ? "selected" : ""}] value=[expr {$i+1}]>[lindex $month_names $i]\n"
    }

    return "<select name=\"$name\">\n $input \n </select>\n"
}

d_proc -deprecated dt_widget_numeric_range {
    name
    begin
    end
    {default ""}
    {interval 1}
    {with_leading_zeros 0}
} {
    DEPRECATED: this widget would be difficult to style and is
    actually simpler to inline such an idiom in one's tcl or adp
    code. The templating system also provides select widgets that take
    care of validating the selection.

    @see template::widget::select
    @see template::widget::multiselect

    @return an HTML select widget for a numeric range
} {
    if {$with_leading_zeros} {
        set format "%02d"
    } else {
        set format "%d"
    }

    if {$default ne ""} {
        set default [util::trim_leading_zeros $default]
    }

    set input "<option value=_undef>--\n"

    for { set i $begin } { $i <= $end } { incr i $interval} {
        append input "[expr {$i == $default ? "<option selected>" : "<option>"}][format $format $i]\n"
    }

    return "<select name=\"$name\">\n$input</select>"
}

d_proc -deprecated dt_widget_maybe_range {
    {-hide t}
    {-hidden_value "00"}
    {-default ""}
    {-format "%02d"}
    ask_for_value
    name
    start
    end
    default_value
    {interval 1 }
    {with_leading_zeros 0}
    {hidden_value "00"}
} {
    DEPRECATED: this proc was only used inside of now deprecated
    dt_widget_datetime.

    @see dt_widget_datetime

    @return form numeric range, or hidden_value if ask_for_value is false.
} {
    if {!$ask_for_value} {
        # Note that this flattens to hidden_value for hidden fields
        if {$with_leading_zeros} {
            return [dt_export_value $name $hidden_value]
        } else {
            return [dt_export_value $name [util::trim_leading_zeros $hidden_value]]
        }
    }

    return [dt_widget_numeric_range \
        "$name" $start $end $default_value $interval $with_leading_zeros]
}

ad_proc -public dt_interval_check { start end } {

    Checks the values of start and end to see if they form a valid
    time interval.  Returns:

    > 0  if end > start
      0  if end = start
    < 0  if end < start

    Input variables can be any strings that can be converted to times
    using clock scan.
} {
    return [expr {[clock scan $end] - [clock scan $start]}]
}

d_proc -private -deprecated dt_trim_leading_zeros {
    string
} {
    @return a string w/ leading zeros trimmed.

    Used to get around Tcl interpreter problems w/ thinking leading
    zeros are octal. We could just use validate_integer, but it runs
    one extra regexp that we don't need to run.

    @see util::trim_leading_zeros
} {
    return [util::trim_leading_zeros $string]
}

d_proc -private dt_export_value {
    name
    value
} {
    Makes a hidden form item w/ given name and value
} {
    return "<input name=\"$name\" type=hidden value=\"$value\">"
}

d_proc -private dt_round_to_precision {
    number
    precision
} {

    Rounds the given number to the given precision,
    i.e. <tt>calendar_round_to_precision 44 5</tt> will round to the
    nearest 5 and return 45, while <tt>calendar_round_to_precision
    32.678 .1</tt> will round to 32.7.

} {
    return [expr {$precision * round(double($number)/$precision)}]
}

d_proc -private dt_precision {
    granularity
} {
    @return the precision in minutes corresponding to a named granularity.
} {
    switch -exact $granularity {
        months   { set precision 40000}
        days     { set precision 1440}
        hours    { set precision 60}
        halves   { set precision 30}
        quarters { set precision 15}
        fives    { set precision 5}
        minutes  { set precision 1}
        seconds  { set precision 0}
        default  { set precision 15}
    }

    return $precision
}

# Local variables:
#    mode: tcl
#    tcl-indent-level: 4
#    indent-tabs-mode: nil
# End: