Add angle TZ angle bracket support to tzset_r

- from Brian Inglis <Brian.Inglis@systematicsw.ab.ca>

  - add support to _tzset_unlocked_r() to support quoting std and dst
    names with angle brackets <> as per Posix
  - modify documentation of tzset.c
This commit is contained in:
Jeff Johnston 2022-04-08 15:06:37 -04:00
parent 2c157e7e23
commit 539ac66ffa
2 changed files with 128 additions and 39 deletions

View File

@ -1,6 +1,6 @@
/*
FUNCTION
<<tzset>>---set timezone characteristics from TZ environment variable
<<tzset>>---set timezone characteristics from <[TZ]> environment variable
INDEX
tzset
@ -13,36 +13,84 @@ SYNOPSIS
void _tzset_r (struct _reent *<[reent_ptr]>);
DESCRIPTION
<<tzset>> examines the TZ environment variable and sets up the three
external variables: <<_timezone>>, <<_daylight>>, and <<tzname>>. The
value of <<_timezone>> shall be the offset from the current time zone
to GMT. The value of <<_daylight>> shall be 0 if there is no daylight
savings time for the current time zone, otherwise it will be non-zero.
The <<tzname>> array has two entries: the first is the name of the
standard time zone, the second is the name of the daylight-savings time
zone.
<<tzset>> examines the <[TZ]> environment variable and sets up the three
external variables: <<_timezone>>, <<_daylight>>, and <<tzname>>.
The value of <<_timezone>> shall be the offset from the current time
to Universal Time.
The value of <<_daylight>> shall be 0 if there is no daylight savings
time for the current time zone, otherwise it will be non-zero.
The <<tzname>> array has two entries: the first is the designation of the
standard time period, the second is the designation of the alternate time
period.
The TZ environment variable is expected to be in the following POSIX
format:
The <[TZ]> environment variable is expected to be in the following POSIX
format (spaces inserted for clarity):
stdoffset1[dst[offset2][,start[/time1],end[/time2]]]
<[std]> <[offset1]> [<[dst]> [<[offset2]>] [,<[start]> [/<[time1]>], <[end]> [/<[time2]>]]]
where: std is the name of the standard time-zone (minimum 3 chars)
offset1 is the value to add to local time to arrive at Universal time
it has the form: hh[:mm[:ss]]
dst is the name of the alternate (daylight-savings) time-zone (min 3 chars)
offset2 is the value to add to local time to arrive at Universal time
it has the same format as the std offset
start is the day that the alternate time-zone starts
time1 is the optional time that the alternate time-zone starts
(this is in local time and defaults to 02:00:00 if not specified)
end is the day that the alternate time-zone ends
time2 is the time that the alternate time-zone ends
(it is in local time and defaults to 02:00:00 if not specified)
where:
Note that there is no white-space padding between fields. Also note that
if TZ is null, the default is Universal GMT which has no daylight-savings
time. If TZ is empty, the default EST5EDT is used.
<[std]> is the designation for the standard time period (minimum 3,
maximum <<TZNAME_MAX>> bytes) in one of two forms:
*- quoted within angle bracket '<' '>' characters: portable numeric
sign or alphanumeric characters in the current locale; the
quoting characters are not included in the designation
*- unquoted: portable alphabetic characters in the current locale
<[offset1]> is the value to add to local standard time to get Universal Time;
it has the format:
[<[S]>]<[hh]>[:<[mm]>[:<[ss]>]]
where:
<[S]> is an optional numeric sign character; if negative '-', the
time zone is East of the International Reference
Meridian; else it is positive and West, and '+' may be used
<[hh]> is the required numeric hour between 0 and 24
<[mm]> is the optional numeric minute between 0 and 59
<[ss]> is the optional numeric second between 0 and 59
<[dst]> is the designation of the alternate (daylight saving or
summer) time period, with the same limits and forms as
the standard time period designation
<[offset2]> is the value to add to local alternate time to get
Universal Time; it has the same format as <[offset1]>
<[start]> is the date in the year that alternate time starts;
the form may be one of:
(quotes "'" around characters below are used only to distinguish literals)
<[n]> zero based Julian day (0-365), counting February 29 Leap days
'J'<[n]> one based Julian day (1-365), not counting February 29 Leap
days; in all years day 59 is February 28 and day 60 is March 1
'M'<[m]>'.'<[w]>'.'<[d]>
month <[m]> (1-12) week <[w]> (1-5) day <[d]> (0-6) where week 1 is
the first week in month <[m]> with day <[d]>; week 5 is the last
week in the month; day 0 is Sunday
<[time1]> is the optional local time that alternate time starts, in
the same format as <[offset1]> without any sign, and defaults
to 02:00:00
<[end]> is the date in the year that alternate time ends, in the same
forms as <[start]>
<[time2]> is the optional local time that alternate time ends, in
the same format as <[offset1]> without any sign, and
defaults to 02:00:00
Note that there is no white-space padding between fields. Also note that
if <[TZ]> is null, the default is Universal Time which has no daylight saving
time. If <[TZ]> is empty, the default EST5EDT is used.
The function <<_tzset_r>> is identical to <<tzset>> only it is reentrant
and is used for applications that use multiple threads.

View File

@ -9,8 +9,11 @@
#define sscanf siscanf /* avoid to pull in FP functions. */
static char __tzname_std[11];
static char __tzname_dst[11];
#define TZNAME_MIN 3 /* POSIX min TZ abbr size local def */
#define TZNAME_MAX 10 /* POSIX max TZ abbr size local def */
static char __tzname_std[TZNAME_MAX + 1];
static char __tzname_dst[TZNAME_MAX + 1];
static char *prev_tzenv = NULL;
void
@ -45,8 +48,25 @@ _tzset_unlocked_r (struct _reent *reent_ptr)
if (*tzenv == ':')
++tzenv;
if (sscanf (tzenv, "%10[^0-9,+-]%n", __tzname_std, &n) <= 0)
return;
/* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. <MESZ+0330> */
if (*tzenv == '<')
{
++tzenv;
/* quit if no items, too few or too many chars, or no close quote '>' */
if (sscanf (tzenv, "%10[-+0-9A-Za-z]%n", __tzname_std, &n) <= 0
|| n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n])
return;
++tzenv; /* bump for close quote '>' */
}
else
{
/* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */
if (sscanf (tzenv, "%10[A-Za-z]%n", __tzname_std, &n) <= 0
|| n < TZNAME_MIN || TZNAME_MAX < n)
return;
}
tzenv += n;
@ -69,16 +89,37 @@ _tzset_unlocked_r (struct _reent *reent_ptr)
_tzname[0] = __tzname_std;
tzenv += n;
if (sscanf (tzenv, "%10[^0-9,+-]%n", __tzname_dst, &n) <= 0)
{ /* No dst */
_tzname[1] = _tzname[0];
_timezone = tz->__tzrule[0].offset;
_daylight = 0;
return;
/* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. <MESZ+0330> */
if (*tzenv == '<')
{
++tzenv;
/* quit if no items, too few or too many chars, or no close quote '>' */
if (sscanf (tzenv, "%10[-+0-9A-Za-z]%n", __tzname_dst, &n) <= 0
|| n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n])
{ /* No dst */
_tzname[1] = _tzname[0];
_timezone = tz->__tzrule[0].offset;
_daylight = 0;
return;
}
++tzenv; /* bump for close quote '>' */
}
else
_tzname[1] = __tzname_dst;
{
/* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */
if (sscanf (tzenv, "%10[A-Za-z]%n", __tzname_dst, &n) <= 0
|| n < TZNAME_MIN || TZNAME_MAX < n)
{ /* No dst */
_tzname[1] = _tzname[0];
_timezone = tz->__tzrule[0].offset;
_daylight = 0;
return;
}
}
_tzname[1] = __tzname_dst;
tzenv += n;
/* otherwise we have a dst name, look for the offset */