Have VxWorks call tz2timezone() once clock is sync'd
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
278
src/libCom/osi/os/vxWorks/tz2timezone.c
Normal file
278
src/libCom/osi/os/vxWorks/tz2timezone.c
Normal file
@@ -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 <anj@anl.gov>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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(¤t, 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, ¤t, start) != OK)
|
||||
return ERROR;
|
||||
|
||||
tz = strchr(tz + 1, ',');
|
||||
if (!tz)
|
||||
return ERROR;
|
||||
if (extractDate(tz, ¤t, 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;
|
||||
}
|
||||
Reference in New Issue
Block a user