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: [Merge] ~anj/epics-base/+git/base-3.15:vxworks-tz-support into epics-base:3.15
From: Andrew Johnson via Core-talk <[email protected]>
To: [email protected]
Date: Fri, 07 Jun 2019 20:43:22 -0000
Andrew Johnson has proposed merging ~anj/epics-base/+git/base-3.15:vxworks-tz-support into epics-base:3.15.

Requested reviews:
  Dirk Zimoch (dirk.zimoch)

For more details, see:
https://code.launchpad.net/~anj/epics-base/+git/base-3.15/+merge/368570

Integrate some code Larry Hoff sent me 10 years ago that avoids the need to adjust the value of TIMEZONE every year...
-- 
Your team EPICS Core Developers is subscribed to branch epics-base:3.15.
diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV
index 49331f9..0084679 100644
--- a/configure/CONFIG_SITE_ENV
+++ b/configure/CONFIG_SITE_ENV
@@ -24,40 +24,41 @@
 
 #  Site-specific environment settings
 
-# Time service:
-# EPICS_TIMEZONE
-#	Local timezone info for vxWorks and RTEMS.  The format is
-#	    <name>::<minutesWest>:<startDST>:<endDST>
-#	where <name> is only used by strftime() for %Z conversions,
-#	and <startDST> and <endDST> are mmddhh - that is month,day,hour
-#	e.g. for ANL in 2018: EPICS_TIMEZONE=CUS::360:031102:110402
-#	The future dates below assume the rules don't get changed;
-#	see http://www.timeanddate.com/time/dst/2018.html to check.
-#
-#	DST for 2018  US: Mar 11 - Nov 04
-#	              EU: Mar 25 - Oct 28
-EPICS_TIMEZONE = CUS::360:031102:110402
-#EPICS_TIMEZONE = MET::-60:032502:102803
-#
-#	DST for 2019  US: Mar 10 - Nov 03
-#	              EU: Mar 31 - Oct 27
-#EPICS_TIMEZONE = CUS::360:031002:110302
-#EPICS_TIMEZONE = MET::-60:033102:102703
-#
-#	DST for 2020  US: Mar 08 - Nov 01
-#	              EU: Mar 29 - Oct 25
-#EPICS_TIMEZONE = CUS::360:030802:110102
-#EPICS_TIMEZONE = MET::-60:032902:102503
-#
-#	DST for 2021  US: Mar 14 - Nov 07
-#	              EU: Mar 28 - Oct 31
-#EPICS_TIMEZONE = CUS::360:031402:110702
-#EPICS_TIMEZONE = MET::-60:032802:103103
-#
-#	DST for 2022  US: Mar 13 - Nov 06
-#	              EU: Mar 27 - Oct 30
-#EPICS_TIMEZONE = CUS::360:031302:110602
-#EPICS_TIMEZONE = MET::-60:032702:103003
+## Time service:
+# EPICS_TZ
+#       Local timezone rules for vxWorks and RTEMS. The value follows the Posix
+#       TZ environment variable's Mm.n.d/h format (see the IBM link below for
+#       details). If TZ hasn't already been set when the osdTime timeRegister()
+#       C++ static constructor runs, this parameter will be copied into the TZ
+#       environment variable. Once the OS clock has been synchronized to NTP the
+#       routine tz2timezone() will be run to convert TZ into the TIMEZONE
+#       variable format that VxWorks needs.
+#               https://developer.ibm.com/articles/au-aix-posix/
+
+# Japan Standard Time, no DST:
+#EPICS_TZ = "JST-9"
+
+# Central European (Summer) Time:
+#EPICS_TZ = "CET-1CEST,M3.5.0/2,M10.5.0/3"
+
+# Greenwich Mean/British Summer Time:
+#EPICS_TZ = "GMT0BST,M3.5.0/1,M10.5.0/2"
+
+# US Eastern Standard/Daylight Time:
+#EPICS_TZ = "EST5EDT,M3.2.0/2,M11.1.0/2"
+
+# US Central Standard/Daylight Time:
+EPICS_TZ = "CST6CDT,M3.2.0/2,M11.1.0/2"
+
+# US Mountain Standard/Daylight Time:
+#EPICS_TZ = "MST7MDT,M3.2.0/2,M11.1.0/2"
+
+# US Pacific Standard/Daylight Time:
+#EPICS_TZ = "PST8PDT,M3.2.0/2,M11.1.0/2"
+
+# US Hawaiian Standard Time, no DST:
+#EPICS_TZ = "HST10"
+
 
 # EPICS_TS_NTP_INET
 #	NTP time server ip address for VxWorks and RTEMS.
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
+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);
     }
     tzset();
     osdTimeRegister();
diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h
index 20f0eb2..588cecd 100644
--- a/src/libCom/env/envDefs.h
+++ b/src/libCom/env/envDefs.h
@@ -61,7 +61,7 @@ epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PORT;
 epicsShareExtern const ENV_PARAM EPICS_BUILD_COMPILER_CLASS;
 epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS;
 epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH;
-epicsShareExtern const ENV_PARAM EPICS_TIMEZONE;
+epicsShareExtern const ENV_PARAM EPICS_TZ;
 epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET;
 epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT;
 epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET;
diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile
index c06a862..e05aec3 100644
--- a/src/libCom/osi/Makefile
+++ b/src/libCom/osi/Makefile
@@ -77,7 +77,7 @@ Com_SRCS += epicsGeneralTime.c
 
 # Time providers
 Com_SRCS += osiClockTime.c
-Com_SRCS_vxWorks += osiNTPTime.c
+Com_SRCS_vxWorks += osiNTPTime.c tz2timezone.c
 Com_SRCS_RTEMS += osiNTPTime.c
 
 ifeq ($(OS_CLASS),vxWorks)
diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp
index af4fae2..9f54427 100644
--- a/src/libCom/osi/epicsTime.cpp
+++ b/src/libCom/osi/epicsTime.cpp
@@ -955,7 +955,8 @@ extern "C" {
         try {
             local_tm_nano_sec tmns = epicsTime (*pSrc);
             *pDest = tmns.ansi_tm;
-            *pNSecDest = tmns.nSec;
+            if (pNSecDest)
+                *pNSecDest = tmns.nSec;
         }
         catch (...) {
             return epicsTimeERROR;
@@ -967,7 +968,8 @@ extern "C" {
         try {
             gm_tm_nano_sec gmtmns = epicsTime (*pSrc);
             *pDest = gmtmns.ansi_tm;
-            *pNSecDest = gmtmns.nSec;
+            if (pNSecDest)
+                *pNSecDest = gmtmns.nSec;
         }
         catch (...) {
             return epicsTimeERROR;
diff --git a/src/libCom/osi/os/vxWorks/osdTime.cpp b/src/libCom/osi/os/vxWorks/osdTime.cpp
index 4db375f..eb144c1 100644
--- a/src/libCom/osi/os/vxWorks/osdTime.cpp
+++ b/src/libCom/osi/os/vxWorks/osdTime.cpp
@@ -24,22 +24,38 @@
 
 #define NTP_REQUEST_TIMEOUT 4 /* seconds */
 
+extern "C" {
+    int tz2timezone(void);
+}
+
 static char sntp_sync_task[] = "ipsntps";
 static char ntp_daemon[] = "ipntpd";
 
 static const char *pserverAddr = NULL;
+static CLOCKTIME_SYNCHOOK prevHook;
+
 extern char* sysBootLine;
 
+static void timeSync(int synchronized) {
+    if (!tz2timezone())
+        ClockTime_syncHook = prevHook; /* Don't call me again */
+}
+
 static int timeRegister(void)
 {
-    /* If TIMEZONE not defined, set it from EPICS_TIMEZONE */
-    if (getenv("TIMEZONE") == NULL) {
-        const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE);
-        if (timezone == NULL) {
-            printf("timeRegister: No Time Zone Information\n");
-        } else {
-            epicsEnvSet("TIMEZONE", timezone);
+    /* If TZ not defined, set it from EPICS_TZ */
+    if (getenv("TZ") == NULL) {
+        const char *tz = envGetConfigParamPtr(&EPICS_TZ);
+
+        if (tz && *tz) {
+            epicsEnvSet("TZ", tz);
+
+            /* Call tz2timezone() once we know what year it is */
+            prevHook = ClockTime_syncHook;
+            ClockTime_syncHook = timeSync;
         }
+        else if (getenv("TIMEZONE") == NULL)
+            printf("timeRegister: No Time Zone Information available\n");
     }
 
     // Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider
@@ -57,7 +73,7 @@ static int timeRegister(void)
     }
 
     if (useNTP) {
-        // Start NTP first so it can be used to sync SysTime
+        // Start NTP first so it can be used to sync ClockTime
         NTPTime_Init(100);
         ClockTime_Init(CLOCKTIME_SYNC);
     } else {
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.
+     */
+
+    /* 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;
+}
diff --git a/src/libCom/osi/osiClockTime.c b/src/libCom/osi/osiClockTime.c
index fb9d153..01958b2 100644
--- a/src/libCom/osi/osiClockTime.c
+++ b/src/libCom/osi/osiClockTime.c
@@ -23,7 +23,8 @@
 #include "taskwd.h"
 
 #define NSEC_PER_SEC 1000000000
-#define ClockTimeSyncInterval_value 60.0
+#define ClockTimeSyncInterval_initial 1.0
+#define ClockTimeSyncInterval_normal 60.0
 
 
 static struct {
@@ -79,7 +80,7 @@ static void ClockTime_InitOnce(void *pfirst)
 
     ClockTimePvt.loopEvent   = epicsEventMustCreate(epicsEventEmpty);
     ClockTimePvt.lock        = epicsMutexCreate();
-    ClockTimePvt.ClockTimeSyncInterval = 1.0;   /* First sync */
+    ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_initial;
 
     epicsAtExit(ClockTime_Shutdown, NULL);
 
@@ -148,6 +149,8 @@ void ClockTime_GetProgramStart(epicsTimeStamp *pDest)
 /* Synchronization thread */
 
 #if defined(vxWorks) || defined(__rtems__)
+CLOCKTIME_SYNCHOOK ClockTime_syncHook = NULL;
+
 static void ClockTimeSync(void *dummy)
 {
     taskwdInsert(0, NULL, NULL);
@@ -179,11 +182,16 @@ static void ClockTimeSync(void *dummy)
             ClockTimePvt.syncTime         = timeNow;
             epicsMutexUnlock(ClockTimePvt.lock);
 
-            ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_value;
+            if (ClockTime_syncHook)
+                ClockTime_syncHook(1);
+
+            ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_normal;
         }
     }
 
     ClockTimePvt.synchronized = 0;
+    if (ClockTime_syncHook)
+        ClockTime_syncHook(0);
     taskwdRemove(0);
 }
 #endif
diff --git a/src/libCom/osi/osiClockTime.h b/src/libCom/osi/osiClockTime.h
index 17eacab..2359888 100644
--- a/src/libCom/osi/osiClockTime.h
+++ b/src/libCom/osi/osiClockTime.h
@@ -19,6 +19,12 @@ void ClockTime_Init(int synchronize);
 void ClockTime_Shutdown(void *dummy);
 int  ClockTime_Report(int level);
 
+#if defined(vxWorks) || defined(__rtems__)
+typedef void (* CLOCKTIME_SYNCHOOK)(int synchronized);
+
+extern CLOCKTIME_SYNCHOOK ClockTime_syncHook;
+#endif
+
 #ifdef __cplusplus
 }
 #endif

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

Navigate by Date:
Prev: Re: [Merge] ~epics-core/epics-base/+git/asLib:as-hostname into epics-base:7.0 mdavidsaver via Core-talk
Next: Re: [Merge] ~info-martin-konrad/epics-base:fix-compiler-warnings 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 
Navigate by Thread:
Prev: Build completed: EPICS Base base-3.15-400 AppVeyor via Core-talk
Next: Re: [Merge] ~anj/epics-base/+git/base-3.15:vxworks-tz-support into epics-base:3.15 mdavidsaver 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, 22 Oct 2019 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·