EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: [Merge] ~anj/epics-base/+git/base-3.15:vxworks-tz-support into epics-base:3.15
From: mdavidsaver via Core-talk <[email protected]>
To: Andrew Johnson <[email protected]>
Date: Tue, 25 Jun 2019 01:10:33 -0000
This looks like almost a no-op for RTEMS, so I see no problem.  Are the changes to epicsTime.cpp a bug fix?

Diff comments:

> diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
> index 8ed4e04..f6daa49 100644
> --- a/documentation/RELEASE_NOTES.html
> +++ b/documentation/RELEASE_NOTES.html
> @@ -16,6 +16,38 @@
>  
>  <!-- Insert new items immediately below here ... -->
>  
> +<h3>Replace EPICS_TIMEZONE with EPICS_TZ</h3>
> +
> +<p>The <tt>EPICS_TIMEZONE</tt> environment parameter provided time-zone
> +information for the IOC's locale in the old ANSI format expected by VxWorks for
> +its <tt>TIMEZONE</tt> environment variable, and can also used by RTEMS to set
> +its <tt>TZ</tt> environment variable. However the <tt>TIMEZONE</tt> value has to
> +be updated every year since it contains the exact dates of the daylight-savings
> +time changes. The Posix TZ format that RTEMS uses contains rules that for
> +calculating those dates, thus its value would only need updating if the rules
> +(or the locale) are changed.</p>
> +
> +<p>This release contains changes that replace the <tt>EPICS_TIMEZONE</tt>
> +environment parameter with one called <tt>EPICS_TZ</tt> and a routine for
> +VxWorks that calculates the <tt>TIMEZONE</tt> environment variable from the
> +current <tt>TZ</tt> value. This routine will be run once at start-up, when the
> +EPICS clock has synchronized to its NTP server. The calculations it contains
> +were worked out and donated to EPICS by Larry Hoff in 2009; it is unforunate

unforunate -> unfortunate

> +that it has taken 10 years for them to be integrated into Base.</p>
> +
> +<p>The default value for the <tt>EPICS_TZ</tt> environment parameter is set in
> +the Base configure/CONFIG_SITE_ENV file, which contains example settings for
> +most EPICS sites that use VxWorks, and a link to a page describing the Posix TZ
> +format for any locations that I missed.</p>
> +
> +<p>If a VxWorks IOC runs continuously without being rebooted from December 31st
> +to the start of daylight savings time the following year, its <tt>TIMEZONE</tt>
> +value will be wrong as it was calculated for the previous year. This only
> +affects times that are converted to a string on the IOC however and is easily
> +fixed; just run the command <tt>tz2timezone()</tt> on the VxWorks shell and the
> +calculation will be redone for the current year. IOCs that get rebooted at least
> +once before the start of summer time will not need this to be done.</p>
> +
>  <h3>Cleaning up with Multiple CA contexts in a Process</h3>
>  
>  <p>Bruno Martins reported a problem with the CA client library at shutdown in a
> diff --git a/src/libCom/RTEMS/rtems_init.c b/src/libCom/RTEMS/rtems_init.c
> index 2b909ab..dcb6daa 100644
> --- a/src/libCom/RTEMS/rtems_init.c
> +++ b/src/libCom/RTEMS/rtems_init.c
> @@ -581,25 +581,11 @@ Init (rtems_task_argument ignored)
>              printf ("***** Can't set time: %s\n", rtems_status_text (sc));
>      }
>      if (getenv("TZ") == NULL) {
> -        const char *tzp = envGetConfigParamPtr(&EPICS_TIMEZONE);
> -        if (tzp == NULL) {
> -            printf("Warning -- no timezone information available -- times will be displayed as GMT.\n");
> -        }
> -        else {
> -            char tz[10];
> -            int minWest, toDst = 0, fromDst = 0;
> -            if(sscanf(tzp, "%9[^:]::%d:%d:%d", tz, &minWest, &toDst, &fromDst) < 2) {
> -                printf("Warning: EPICS_TIMEZONE (%s) unrecognizable -- times will be displayed as GMT.\n", tzp);
> -            }
> -            else {
> -                char posixTzBuf[40];
> -                char *p = posixTzBuf;
> -                p += sprintf(p, "%cST%d:%.2d", tz[0], minWest/60, minWest%60);
> -                if (toDst != fromDst)
> -                    p += sprintf(p, "%cDT", tz[0]);
> -                epicsEnvSet("TZ", posixTzBuf);
> -            }
> -        }
> +        const char *tzp = envGetConfigParamPtr(&EPICS_TZ);
> +        if (!tzp || *tzp)
> +            printf("Warning: No timezone information, times will be displayed in UTC.\n");
> +        else
> +            epicsEnvSet("TZ", tzp);

Should call tzset() after changing $TZ.  cf. zoneset() iocsh function added on 7.0 branch (d70bfed75b469e6c671d3d0617f8bfd0c212b664).

>      }
>      tzset();
>      osdTimeRegister();
> diff --git a/src/libCom/osi/os/vxWorks/tz2timezone.c b/src/libCom/osi/os/vxWorks/tz2timezone.c
> new file mode 100644
> index 0000000..df8db85
> --- /dev/null
> +++ b/src/libCom/osi/os/vxWorks/tz2timezone.c
> @@ -0,0 +1,278 @@
> +/*************************************************************************\
> +* Copyright (c) 2009 Brookhaven Science Associates, as Operator of
> +*     Brookhaven National Laboratory.
> +* Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne
> +*     National Laboratory.
> +* EPICS BASE is distributed subject to a Software License Agreement found
> +* in file LICENSE that is included with this distribution.
> +\*************************************************************************/
> +/*
> + * Authors: Larry Hoff, Andrew Johnson <[email protected]>
> + */
> +
> +/*
> + * This file exports a single function "int tz2timezone(void)" which reads
> + * the TZ environment variable (defined by POSIX) and converts it to the
> + * TIMEZONE environment variable defined by ANSI. The latter is used by
> + * VxWorks "time" functions, is largely deprecated in other computing
> + * environments, has limitations, and is difficult to maintain. This holds
> + * out the possibility of "pretending" that VxWorks supports "TZ" - until
> + * such time as it actually does.
> + *
> + * For simplicity, only the "POSIX standard form" of TZ will be supported.
> + * Even that is complicated enough (see following spec). Furthermore,
> + * only the "M" form of DST start and stop dates are supported.
> + *
> + * TZ = zone[-]offset[dst[offset],start[/time],end[/time]]
> + *
> + * zone
> + *     A three or more letter name for the timezone in normal (winter) time.
> + *
> + * [-]offset
> + *     A signed time giving the offset of the time zone westwards from
> + *     Greenwich. The time has the form hh[:mm[:ss]] with a one of two
> + *     digit hour, and optional two digit minutes and seconds.
> + *
> + * dst
> + *     The name of the time zone when daylight saving is in effect. It may
> + *     be followed by an offset giving how big the adjustment is, required
> + *     if different than the default of 1 hour.
> + *
> + * start/time,end/time
> + *     Specify the start and end of the daylight saving period. The start
> + *     and end fields indicate on what day the changeover occurs, and must
> + *     be in this format:
> + *
> + *     Mm.n.d
> + *         This indicates month m, the n-th occurrence of day d, where
> + *             1 <= m <= 12,  1 <= n <= 5,  0 <= d <= 6, 0=Sunday
> + *         The 5th occurrence means the last occurrence of that day in a
> + *         month. So M4.1.0 is the first Sunday in April, M9.5.0 is the
> + *         last Sunday in September.
> + *
> + *     The time field indicates what hour the changeover occurs on the given
> + *     day (TIMEZONE only supports switching on the hour).
> + *
> + */
> +
> +#include <vxWorks.h>
> +#include <envLib.h>   /* getenv() */
> +#include <stdio.h>    /* printf() */
> +#include <string.h>   /* strchr() */
> +#include <ctype.h>    /* isalpha() */
> +
> +#include <epicsTime.h>
> +
> +/* for reference: TZ syntax, example, and TIMEZONE example
> + * std offset dst [offset],start[/time],end[/time]
> + *    CST6CDT5,M3.2.0,M11.1.0
> + *    EST+5EDT,M4.1.0/2,M10.5.0/2
> + *
> + * std <unused> offset start stop
> + *    TIMEZONE=EST::300:030802:110102
> + */
> +
> +static int extractDate(const char *tz, struct tm *current, char *s)
> +{
> +    static const int startdays[] = {
> +        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
> +    };
> +    static const int molengths[] = {
> +        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
> +    };
> +
> +    int month, week, weekday, hour=2; /* default=2AM */
> +    int jan1wday, wday, mday;
> +
> +    /* Require 'M' format */
> +    if (*++tz != 'M') {
> +        printf("tz2timezone: Unsupported date type, need 'M' format\n");
> +        return ERROR;
> +    }
> +    tz++;
> +
> +    if (sscanf(tz, "%d.%d.%d/%d", &month, &week, &weekday, &hour) < 3)
> +        return ERROR; /* something important missing */
> +
> +    if (month == 0 || month>12 ||
> +        week < 1 || week > 5 ||
> +        weekday < 0 || weekday > 6 ||
> +        hour < 0 || hour > 23)
> +        return ERROR;
> +
> +    /* Now for some brute-force calendar calculations... */
> +    /* start month is in "month", and the day is "weekday", but
> +       we need to know when that weekday first occurs in that month */
> +    /* Let's start with weekday on Jan. 1 */
> +    jan1wday = (7 + current->tm_wday - (current->tm_yday % 7)) % 7;
> +
> +    /* We need to know if it is a leap year (and if it matters) */
> +    /* Let's assume that we're working with a date between 1901 and 2099,
> +       that way we don't have to think about the "century exception".
> +       If this code is still running (unchanged) in 2100, I'll be stunned
> +       (and 139 years old) */
> +    wday = (jan1wday + startdays[month-1] +
> +        ((month > 2 && (current->tm_year % 4 == 0)) ? 1 : 0)) % 7;
> +
> +    /* Let's see on what day-of-the-month the first target weekday occurs
> +       (counting from 1). The result is a number between 1 and 7, inclusive. */
> +    mday = 1 + ((7 + weekday - wday) % 7);
> +
> +    /* Next, we add the week offset. If we overflow the month, we subtract
> +       one week */
> +    mday += 7 * (week - 1);
> +    if (mday > molengths[month-1])
> +        mday -= 7;
> +
> +    /* Should I handle Feb 29? I'm willing to gamble that no one in their right
> +       mind would schedule a time change to occur on Feb. 29. If so, we'll be a
> +       week early */
> +    sprintf(s, "%02d%02d%02d", month, mday, hour);
> +
> +    return OK;
> +}
> +
> +
> +static const char *getTime(const char *s, int *time)
> +{
> +    /* Time format is [+/-]hh[:mm][:ss] followed by the next zone name */
> +
> +    *time = 0;
> +
> +    if (!isdigit((int) s[0]))
> +        return s; /* no number here... */
> +
> +    if (!isdigit((int) s[1])) { /* single digit form */
> +        *time = s[0] - '0';
> +        return s + 1;
> +    }
> +
> +    if (isdigit((int) s[1])) { /* two digit form */
> +        *time = 10 * (s[0] - '0') + (s[1] - '0');
> +        return s + 2;
> +    }
> +
> +    return s; /* does not follow supported form */
> +}
> +
> +int tz2timezone(void)
> +{
> +    const char *tz = getenv("TZ");
> +    /* Spec. says that zone names must be at least 3 chars.
> +     * I've never seen a longer zone name, but I'll allocate
> +     * 40 chars. If you live in a zone with a longer name,
> +     * you may want to think about the benefits of relocation.
> +     */
> +    char zone[40];
> +    char start[10], stop[10]; /* only really need 7 bytes now */
> +    int hours = 0, minutes = 0, sign = 1;
> +    /* This is more than enough, even with a 40-char zone
> +     * name, and 4-char offset.
> +     */
> +    char timezone[100];
> +    int i = 0; /* You *always need an "i" :-) */
> +    epicsTimeStamp now;
> +    struct tm current;
> +
> +    /* First let's get the current time. We need the year to
> +     * compute the start/stop dates for DST.
> +     */
> +    if (epicsTimeGetCurrent(&now) ||
> +        epicsTimeToTM(&current, NULL, &now))
> +        return ERROR;
> +
> +    /* Make sure TZ exists.
> +     * Spec. says that ZONE must be at least 3 chars.
> +     */
> +    if ((!tz) || (strlen(tz) < 3))
> +        return ERROR;
> +
> +    /* OK, now a bunch of brute-force parsing. My brain hurts if
> +     * I try to think of an elegant regular expression for the
> +     * string.
> +     */

Could sscanf() avoid some of this pain?

> +
> +    /* Start extracting zone name, must be alpha */
> +    while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) {
> +        zone[i++] = *tz++;
> +    }
> +    if (i < 3)
> +        return ERROR; /* Too short, not a real zone name? */
> +
> +    zone[i] = 0; /* Nil-terminate (for now) */
> +
> +    /* Now extract offset time. The format is [+/-]hh[:mm[:ss]]
> +     * Recall that TIMEZONE doesn't support seconds....
> +     */
> +    if (*tz == '-') {
> +        sign = -1;
> +        tz++;
> +    }
> +    else if (*tz == '+') {
> +        tz++;
> +    }
> +
> +    /* Need a digit now */
> +    if (!isdigit((int) *tz))
> +        return ERROR;
> +
> +    /* First get the hours */
> +    tz = getTime(tz, &hours);
> +    if (hours > 24)
> +        return ERROR;
> +
> +    if (*tz == ':') { /* There is a minutes part */
> +        /* Need another digit now */
> +        if (!isdigit((int) *++tz))
> +            return ERROR;
> +
> +        /* Extract the minutes */
> +        tz = getTime(tz, &minutes);
> +        if (minutes > 60)
> +            return ERROR;
> +
> +        /* Skip any seconds part */
> +        if (*tz == ':') {
> +            int seconds;
> +            tz = getTime(tz + 1, &seconds);
> +        }
> +    }
> +
> +    /* Extract any DST zone name, must be alpha */
> +    if (isalpha((int) *tz)) {
> +        zone[i++] = '/'; /* Separate the names */
> +
> +        while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) {
> +            zone[i++] = *tz++;
> +        }
> +        zone[i] = 0; /* Nil-terminate */
> +    }
> +
> +    minutes += hours * 60;
> +    minutes *= sign;
> +
> +    /* Look for start/stop dates - require neither or both */
> +    tz = strchr(tz, ',');
> +    if (!tz) {  /* No daylight savings time here */
> +        /* Format the env. variable */
> +        sprintf(timezone, "TIMEZONE=%s::%d", zone, minutes);
> +    }
> +    else {
> +        if (extractDate(tz, &current, start) != OK)
> +            return ERROR;
> +
> +        tz = strchr(tz + 1, ',');
> +        if (!tz)
> +            return ERROR;
> +        if (extractDate(tz, &current, stop) != OK)
> +            return ERROR;
> +
> +        /* Format the env. variable */
> +        sprintf(timezone, "TIMEZONE=%s::%d:%s:%s", zone, minutes, start, stop);
> +    }
> +
> +    /* Make it live! */
> +    putenv(timezone);
> +
> +    return OK;
> +}


-- 
https://code.launchpad.net/~anj/epics-base/+git/base-3.15/+merge/368570
Your team EPICS Core Developers is subscribed to branch epics-base:3.15.

References:
[Merge] ~anj/epics-base/+git/base-3.15:vxworks-tz-support into epics-base:3.15 Andrew Johnson via Core-talk

Navigate by Date:
Prev: Re: [Merge] ~epics-core/epics-base/+git/make:rpath-origin into epics-base:7.0 mdavidsaver via Core-talk
Next: [Merge] ~info-martin-konrad/epics-base:dont-nuke-global-cac-thread-id-in-exit-handler into epics-base:3.15 noreply--- via Core-talk
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
Navigate by Thread:
Prev: [Merge] ~anj/epics-base/+git/base-3.15:vxworks-tz-support into epics-base:3.15 Andrew Johnson via Core-talk
Next: Re: [Merge] ~anj/epics-base/+git/base-3.15:vxworks-tz-support into epics-base:3.15 Andrew Johnson via Core-talk
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
ANJ, 25 Jun 2019 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·