]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add functions to get gmt offset and daylight savings flag
authorAlan T. DeKok <aland@freeradius.org>
Sun, 22 Jan 2023 13:51:30 +0000 (08:51 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 22 Jan 2023 16:33:33 +0000 (11:33 -0500)
we should note that the server tracks times in UTC, so

(date) foo % (time_delta) 1d

will return the offset from the UTC day, not from the local time.
This could perhaps be more prominent.  But tracking times internally
in UTC is the only way to make sure we're not screwed up by time
zone and DST changes.

doc/antora/modules/reference/pages/xlat/builtin.adoc
src/lib/unlang/xlat_builtin.c
src/lib/util/time.c
src/lib/util/time.h

index a48786dceed4fd092f59da5f51fb5c6279390b83..5a353b1fe305a39a98ab7877aa6e1117bc155bf0 100644 (file)
@@ -1141,19 +1141,83 @@ If no argument is passed, it returns the current time.  Otherwise if the argumen
 
 * `now` - returns the current time
 * `request` - returns the time at which the request packet was received (always less than `now`!)
+* `offset` - returns a `time_delta` of the current time zone offset.  This value may be negative.
+* `dst` - returns a `bool` indicating whether or not the system is running in daylight savings time.
 * any other string is parsed as type `date`, using the normal date parsing routines.
 
+.Example
+
+[source,unlang]
+----
+&Acct-Start-Time := %(time:now)
+----
+
 [NOTE]
 ====
 This expansion should be used in preference to the xref:xlat/character.adoc[single letter expansions] `%l`.  That expansion returns integer seconds, and is not suitable for millisecond or microsecond resolution.
 ====
 
-.Example
+Due to limitations in the underlying time funtions (both system and
+FreeRADIUS), previous versions of FreeRADIUS did not always handle
+dates correctly.  When print dates, the time zone information would
+sometimes not be printed, or the time zone would sometimes be ignored
+when parsed a date from a string.
+
+Even if the time zone was used, the nature of time zones means that
+there may be duplicate time zone names!  For example, the time zone
+`CST` has three separate (and different) definitions.
+
+The server now tracks all times internally as UTC, and by default
+prints times as UTC, or prints the time zone as a decimal offset from
+UTC, instead of printing an ambiguous name.
+
+This handling of time zones has some minor side effects.  When
+calculating values like "tomorrow", the default is to return the UTC
+version of "tomorrow".  This value may not be what you want.
+
+In order to correctly calculate the local value of "tomorrow", it is
+necessary to add the local time zone offset to the UTC time.
+
+Note that the server will automatically determine (and use) any
+daylight savings time differences.  So the value of `%(time:offset)`
+may change while the server is running!
+
+The following example calculates the correct value of "tomorrow" in
+UTC by using the following steps:
 
+* taking the current time of the request
+* calculating how long it has been since the start of the day as a `time_delta`
+* subtracting that `time_delta` from the current time
+
+.Example Calculating the UTC value of "tomorrow"
 [source,unlang]
 ----
-&Acct-Start-Time := %(time:now)
+&Tmp-Date-0 := %(time:request)
+
+&Tmp-Time-Delta-0 := &Tmp-Date-0 % (time_delta) 1d
+
+&Tmp-Date-1 := &Tmp-Date-0 - &Tmp-Time-Delta-0
 ----
 
+The following example calculates the correct value of "tomorrow" in
+local time by using the preceding example, but then adding the local
+time zone offset to the final value.
+
+.Example Calculating the local value of "tomorrow"
+[source,unlang]
+----
+&Tmp-Date-0 := %(time:request)
+
+&Tmp-Time-Delta-0 := &Tmp-Date-0 % (time_delta) 1d
+
+&Tmp-Date-1 := &Tmp-Date-0 - &Tmp-Time-Delta-0 + (time_delta) 1d
+
+&Tmp-Date-1 += %(time:offset)
+----
+
+This kind of math works well for "tomorrow", but it is less useful for
+"next week Monday", or "start of next month".  The `%{nexttime:..}`
+expansion above should be used for those time operations.
+
 // Copyright (C) 2023 Network RADIUS SAS.  Licenced under CC-by-NC 4.0.
 // Development of this documentation was sponsored by Network RADIUS SAS.
index 226cda34480ecc4978bee62a29c7ef4dcd902a36..f8c79dfd564d1ec63740445a0cbaa59907eb9843 100644 (file)
@@ -3510,6 +3510,16 @@ static xlat_action_t xlat_func_time(TALLOC_CTX *ctx, fr_dcursor_t *out,
        } else if (strcmp(arg->vb_strvalue, "request") == 0) {
                value = fr_time_to_unix_time(request->packet->timestamp);
 
+       } else if (strcmp(arg->vb_strvalue, "offset") == 0) {
+               MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL, false));
+               vb->vb_time_delta = fr_time_gmtoff();
+               goto append;
+
+       } else if (strcmp(arg->vb_strvalue, "dst") == 0) {
+               MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL, false));
+               vb->vb_bool = fr_time_is_dst();
+               goto append;
+
        } else if (fr_unix_time_from_str(&value, arg->vb_strvalue, FR_TIME_RES_SEC) < 0) {
                REDEBUG("Invalid time specification '%s'", arg->vb_strvalue);
                return XLAT_ACTION_FAIL;
@@ -3517,6 +3527,8 @@ static xlat_action_t xlat_func_time(TALLOC_CTX *ctx, fr_dcursor_t *out,
 
        MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL, false));
        vb->vb_date = value;
+
+append:
        fr_dcursor_append(out, vb);
 
        return XLAT_ACTION_DONE;
@@ -4057,7 +4069,7 @@ do { \
        XLAT_REGISTER_ARGS("nexttime", xlat_func_next_time, FR_TYPE_UINT64, xlat_func_next_time_args);
        XLAT_REGISTER_ARGS("pairs", xlat_func_pairs, FR_TYPE_STRING, xlat_func_pairs_args);
        XLAT_REGISTER_ARGS("subst", xlat_func_subst, FR_TYPE_STRING, xlat_func_subst_args);
-       XLAT_REGISTER_ARGS("time", xlat_func_time, FR_TYPE_DATE, xlat_func_time_args);
+       XLAT_REGISTER_ARGS("time", xlat_func_time, FR_TYPE_VOID, xlat_func_time_args);
        XLAT_REGISTER_ARGS("trigger", trigger_xlat, FR_TYPE_STRING, trigger_xlat_args);
 
        /*
index 0003b44939685f3b628a3e8984b74763497f43b2..a9aacf7e3588c65e3f7befd481d363d9d367f84a 100644 (file)
@@ -105,7 +105,7 @@ size_t fr_time_precision_table_len = NUM_ELEMENTS(fr_time_precision_table);
 _Atomic int64_t                        our_realtime;   //!< realtime at the start of the epoch in nanoseconds.
 static char const              *tz_names[2] = { NULL, NULL };  //!< normal, DST, from localtime_r(), tm_zone
 static long                    gmtoff[2] = {0, 0};             //!< from localtime_r(), tm_gmtoff
-static int                     isdst = 0;                      //!< from localtime_r(), tm_is_dst
+static bool                    isdst = false;                  //!< from localtime_r(), tm_is_dst
 
 #ifdef HAVE_CLOCK_GETTIME
 int64_t                                our_epoch;
@@ -1282,3 +1282,19 @@ fr_slen_t fr_unix_time_to_str(fr_sbuff_t *out, fr_unix_time_t time, fr_time_res_
 
        FR_SBUFF_SET_RETURN(out, &our_out);
 }
+
+/** Get the offset to gmt.
+ *
+ */
+fr_time_delta_t fr_time_gmtoff(void)
+{
+       return fr_time_delta_wrap(gmtoff[isdst]);
+}
+
+/** Whether or not we're daylight savings.
+ *
+ */
+bool fr_time_is_dst(void)
+{
+       return isdst;
+}
index b2630a16407cf0d0187948a5113c7797dd61c702..b52f0fab88f2854f4752c3cb1442db2d45386b12 100644 (file)
@@ -1013,6 +1013,10 @@ int              fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_r
 fr_slen_t      fr_unix_time_to_str(fr_sbuff_t *out, fr_unix_time_t time, fr_time_res_t res)
                CC_HINT(nonnull);
 
+fr_time_delta_t        fr_time_gmtoff(void);
+
+bool           fr_time_is_dst(void);
+
 #ifdef __cplusplus
 }
 #endif