Merge ~anj/epics-base/+git/base-3.15:vxworks-tz-support into ~epics-core/epics-base/+git/epics-base:3.15
- Git
- lp:~anj/epics-base/+git/base-3.15
- vxworks-tz-support
- Merge into 3.15
Proposed by
Andrew Johnson
Status: | Merged |
---|---|
Approved by: | Andrew Johnson |
Approved revision: | d3a8a49552ca7930aa9a5ab34ceb1de9cd25ccf4 |
Merged at revision: | 3881328f2f7a0ad9853ed14c3d0fd5e97d02fafe |
Proposed branch: | ~anj/epics-base/+git/base-3.15:vxworks-tz-support |
Merge into: | ~epics-core/epics-base/+git/epics-base:3.15 |
Diff against target: |
618 lines (+397/-68) 10 files modified
configure/CONFIG_SITE_ENV (+35/-34) documentation/RELEASE_NOTES.html (+32/-0) src/libCom/RTEMS/rtems_init.c (+5/-19) src/libCom/env/envDefs.h (+1/-1) src/libCom/osi/Makefile (+1/-1) src/libCom/osi/epicsTime.cpp (+4/-2) src/libCom/osi/os/vxWorks/osdTime.cpp (+24/-8) src/libCom/osi/os/vxWorks/tz2timezone.c (+278/-0) src/libCom/osi/osiClockTime.c (+11/-3) src/libCom/osi/osiClockTime.h (+6/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
Dirk Zimoch | Pending | ||
Review via email:
|
Commit message
Description of the change
Integrate some code Larry Hoff sent me 10 years ago that avoids the need to adjust the value of TIMEZONE every year...
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
mdavidsaver (mdavidsaver) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andrew Johnson (anj) wrote : | # |
MAD: The RTEMS code will need adjusting when merging up to 7.0, try to add the tzset() as suggested.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andrew Johnson (anj) wrote : | # |
Group 10/4: No major issues.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV | |||
2 | index 49331f9..0084679 100644 | |||
3 | --- a/configure/CONFIG_SITE_ENV | |||
4 | +++ b/configure/CONFIG_SITE_ENV | |||
5 | @@ -24,40 +24,41 @@ | |||
6 | 24 | 24 | ||
7 | 25 | # Site-specific environment settings | 25 | # Site-specific environment settings |
8 | 26 | 26 | ||
43 | 27 | # Time service: | 27 | ## Time service: |
44 | 28 | # EPICS_TIMEZONE | 28 | # EPICS_TZ |
45 | 29 | # Local timezone info for vxWorks and RTEMS. The format is | 29 | # Local timezone rules for vxWorks and RTEMS. The value follows the Posix |
46 | 30 | # <name>::<minutesWest>:<startDST>:<endDST> | 30 | # TZ environment variable's Mm.n.d/h format (see the IBM link below for |
47 | 31 | # where <name> is only used by strftime() for %Z conversions, | 31 | # details). If TZ hasn't already been set when the osdTime timeRegister() |
48 | 32 | # and <startDST> and <endDST> are mmddhh - that is month,day,hour | 32 | # C++ static constructor runs, this parameter will be copied into the TZ |
49 | 33 | # e.g. for ANL in 2018: EPICS_TIMEZONE=CUS::360:031102:110402 | 33 | # environment variable. Once the OS clock has been synchronized to NTP the |
50 | 34 | # The future dates below assume the rules don't get changed; | 34 | # routine tz2timezone() will be run to convert TZ into the TIMEZONE |
51 | 35 | # see http://www.timeanddate.com/time/dst/2018.html to check. | 35 | # variable format that VxWorks needs. |
52 | 36 | # | 36 | # https://developer.ibm.com/articles/au-aix-posix/ |
53 | 37 | # DST for 2018 US: Mar 11 - Nov 04 | 37 | |
54 | 38 | # EU: Mar 25 - Oct 28 | 38 | # Japan Standard Time, no DST: |
55 | 39 | EPICS_TIMEZONE = CUS::360:031102:110402 | 39 | #EPICS_TZ = "JST-9" |
56 | 40 | #EPICS_TIMEZONE = MET::-60:032502:102803 | 40 | |
57 | 41 | # | 41 | # Central European (Summer) Time: |
58 | 42 | # DST for 2019 US: Mar 10 - Nov 03 | 42 | #EPICS_TZ = "CET-1CEST,M3.5.0/2,M10.5.0/3" |
59 | 43 | # EU: Mar 31 - Oct 27 | 43 | |
60 | 44 | #EPICS_TIMEZONE = CUS::360:031002:110302 | 44 | # Greenwich Mean/British Summer Time: |
61 | 45 | #EPICS_TIMEZONE = MET::-60:033102:102703 | 45 | #EPICS_TZ = "GMT0BST,M3.5.0/1,M10.5.0/2" |
62 | 46 | # | 46 | |
63 | 47 | # DST for 2020 US: Mar 08 - Nov 01 | 47 | # US Eastern Standard/Daylight Time: |
64 | 48 | # EU: Mar 29 - Oct 25 | 48 | #EPICS_TZ = "EST5EDT,M3.2.0/2,M11.1.0/2" |
65 | 49 | #EPICS_TIMEZONE = CUS::360:030802:110102 | 49 | |
66 | 50 | #EPICS_TIMEZONE = MET::-60:032902:102503 | 50 | # US Central Standard/Daylight Time: |
67 | 51 | # | 51 | EPICS_TZ = "CST6CDT,M3.2.0/2,M11.1.0/2" |
68 | 52 | # DST for 2021 US: Mar 14 - Nov 07 | 52 | |
69 | 53 | # EU: Mar 28 - Oct 31 | 53 | # US Mountain Standard/Daylight Time: |
70 | 54 | #EPICS_TIMEZONE = CUS::360:031402:110702 | 54 | #EPICS_TZ = "MST7MDT,M3.2.0/2,M11.1.0/2" |
71 | 55 | #EPICS_TIMEZONE = MET::-60:032802:103103 | 55 | |
72 | 56 | # | 56 | # US Pacific Standard/Daylight Time: |
73 | 57 | # DST for 2022 US: Mar 13 - Nov 06 | 57 | #EPICS_TZ = "PST8PDT,M3.2.0/2,M11.1.0/2" |
74 | 58 | # EU: Mar 27 - Oct 30 | 58 | |
75 | 59 | #EPICS_TIMEZONE = CUS::360:031302:110602 | 59 | # US Hawaiian Standard Time, no DST: |
76 | 60 | #EPICS_TIMEZONE = MET::-60:032702:103003 | 60 | #EPICS_TZ = "HST10" |
77 | 61 | |||
78 | 61 | 62 | ||
79 | 62 | # EPICS_TS_NTP_INET | 63 | # EPICS_TS_NTP_INET |
80 | 63 | # NTP time server ip address for VxWorks and RTEMS. | 64 | # NTP time server ip address for VxWorks and RTEMS. |
81 | diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html | |||
82 | index 8ed4e04..f6daa49 100644 | |||
83 | --- a/documentation/RELEASE_NOTES.html | |||
84 | +++ b/documentation/RELEASE_NOTES.html | |||
85 | @@ -16,6 +16,38 @@ | |||
86 | 16 | 16 | ||
87 | 17 | <!-- Insert new items immediately below here ... --> | 17 | <!-- Insert new items immediately below here ... --> |
88 | 18 | 18 | ||
89 | 19 | <h3>Replace EPICS_TIMEZONE with EPICS_TZ</h3> | ||
90 | 20 | |||
91 | 21 | <p>The <tt>EPICS_TIMEZONE</tt> environment parameter provided time-zone | ||
92 | 22 | information for the IOC's locale in the old ANSI format expected by VxWorks for | ||
93 | 23 | its <tt>TIMEZONE</tt> environment variable, and can also used by RTEMS to set | ||
94 | 24 | its <tt>TZ</tt> environment variable. However the <tt>TIMEZONE</tt> value has to | ||
95 | 25 | be updated every year since it contains the exact dates of the daylight-savings | ||
96 | 26 | time changes. The Posix TZ format that RTEMS uses contains rules that for | ||
97 | 27 | calculating those dates, thus its value would only need updating if the rules | ||
98 | 28 | (or the locale) are changed.</p> | ||
99 | 29 | |||
100 | 30 | <p>This release contains changes that replace the <tt>EPICS_TIMEZONE</tt> | ||
101 | 31 | environment parameter with one called <tt>EPICS_TZ</tt> and a routine for | ||
102 | 32 | VxWorks that calculates the <tt>TIMEZONE</tt> environment variable from the | ||
103 | 33 | current <tt>TZ</tt> value. This routine will be run once at start-up, when the | ||
104 | 34 | EPICS clock has synchronized to its NTP server. The calculations it contains | ||
105 | 35 | were worked out and donated to EPICS by Larry Hoff in 2009; it is unforunate | ||
106 | 36 | that it has taken 10 years for them to be integrated into Base.</p> | ||
107 | 37 | |||
108 | 38 | <p>The default value for the <tt>EPICS_TZ</tt> environment parameter is set in | ||
109 | 39 | the Base configure/CONFIG_SITE_ENV file, which contains example settings for | ||
110 | 40 | most EPICS sites that use VxWorks, and a link to a page describing the Posix TZ | ||
111 | 41 | format for any locations that I missed.</p> | ||
112 | 42 | |||
113 | 43 | <p>If a VxWorks IOC runs continuously without being rebooted from December 31st | ||
114 | 44 | to the start of daylight savings time the following year, its <tt>TIMEZONE</tt> | ||
115 | 45 | value will be wrong as it was calculated for the previous year. This only | ||
116 | 46 | affects times that are converted to a string on the IOC however and is easily | ||
117 | 47 | fixed; just run the command <tt>tz2timezone()</tt> on the VxWorks shell and the | ||
118 | 48 | calculation will be redone for the current year. IOCs that get rebooted at least | ||
119 | 49 | once before the start of summer time will not need this to be done.</p> | ||
120 | 50 | |||
121 | 19 | <h3>Cleaning up with Multiple CA contexts in a Process</h3> | 51 | <h3>Cleaning up with Multiple CA contexts in a Process</h3> |
122 | 20 | 52 | ||
123 | 21 | <p>Bruno Martins reported a problem with the CA client library at shutdown in a | 53 | <p>Bruno Martins reported a problem with the CA client library at shutdown in a |
124 | diff --git a/src/libCom/RTEMS/rtems_init.c b/src/libCom/RTEMS/rtems_init.c | |||
125 | index 2b909ab..dcb6daa 100644 | |||
126 | --- a/src/libCom/RTEMS/rtems_init.c | |||
127 | +++ b/src/libCom/RTEMS/rtems_init.c | |||
128 | @@ -581,25 +581,11 @@ Init (rtems_task_argument ignored) | |||
129 | 581 | printf ("***** Can't set time: %s\n", rtems_status_text (sc)); | 581 | printf ("***** Can't set time: %s\n", rtems_status_text (sc)); |
130 | 582 | } | 582 | } |
131 | 583 | if (getenv("TZ") == NULL) { | 583 | if (getenv("TZ") == NULL) { |
151 | 584 | const char *tzp = envGetConfigParamPtr(&EPICS_TIMEZONE); | 584 | const char *tzp = envGetConfigParamPtr(&EPICS_TZ); |
152 | 585 | if (tzp == NULL) { | 585 | if (!tzp || *tzp) |
153 | 586 | printf("Warning -- no timezone information available -- times will be displayed as GMT.\n"); | 586 | printf("Warning: No timezone information, times will be displayed in UTC.\n"); |
154 | 587 | } | 587 | else |
155 | 588 | else { | 588 | epicsEnvSet("TZ", tzp); |
137 | 589 | char tz[10]; | ||
138 | 590 | int minWest, toDst = 0, fromDst = 0; | ||
139 | 591 | if(sscanf(tzp, "%9[^:]::%d:%d:%d", tz, &minWest, &toDst, &fromDst) < 2) { | ||
140 | 592 | printf("Warning: EPICS_TIMEZONE (%s) unrecognizable -- times will be displayed as GMT.\n", tzp); | ||
141 | 593 | } | ||
142 | 594 | else { | ||
143 | 595 | char posixTzBuf[40]; | ||
144 | 596 | char *p = posixTzBuf; | ||
145 | 597 | p += sprintf(p, "%cST%d:%.2d", tz[0], minWest/60, minWest%60); | ||
146 | 598 | if (toDst != fromDst) | ||
147 | 599 | p += sprintf(p, "%cDT", tz[0]); | ||
148 | 600 | epicsEnvSet("TZ", posixTzBuf); | ||
149 | 601 | } | ||
150 | 602 | } | ||
156 | 603 | } | 589 | } |
157 | 604 | tzset(); | 590 | tzset(); |
158 | 605 | osdTimeRegister(); | 591 | osdTimeRegister(); |
159 | diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h | |||
160 | index 20f0eb2..588cecd 100644 | |||
161 | --- a/src/libCom/env/envDefs.h | |||
162 | +++ b/src/libCom/env/envDefs.h | |||
163 | @@ -61,7 +61,7 @@ epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PORT; | |||
164 | 61 | epicsShareExtern const ENV_PARAM EPICS_BUILD_COMPILER_CLASS; | 61 | epicsShareExtern const ENV_PARAM EPICS_BUILD_COMPILER_CLASS; |
165 | 62 | epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS; | 62 | epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS; |
166 | 63 | epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH; | 63 | epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH; |
168 | 64 | epicsShareExtern const ENV_PARAM EPICS_TIMEZONE; | 64 | epicsShareExtern const ENV_PARAM EPICS_TZ; |
169 | 65 | epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; | 65 | epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; |
170 | 66 | epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; | 66 | epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; |
171 | 67 | epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; | 67 | epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; |
172 | diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile | |||
173 | index c06a862..e05aec3 100644 | |||
174 | --- a/src/libCom/osi/Makefile | |||
175 | +++ b/src/libCom/osi/Makefile | |||
176 | @@ -77,7 +77,7 @@ Com_SRCS += epicsGeneralTime.c | |||
177 | 77 | 77 | ||
178 | 78 | # Time providers | 78 | # Time providers |
179 | 79 | Com_SRCS += osiClockTime.c | 79 | Com_SRCS += osiClockTime.c |
181 | 80 | Com_SRCS_vxWorks += osiNTPTime.c | 80 | Com_SRCS_vxWorks += osiNTPTime.c tz2timezone.c |
182 | 81 | Com_SRCS_RTEMS += osiNTPTime.c | 81 | Com_SRCS_RTEMS += osiNTPTime.c |
183 | 82 | 82 | ||
184 | 83 | ifeq ($(OS_CLASS),vxWorks) | 83 | ifeq ($(OS_CLASS),vxWorks) |
185 | diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp | |||
186 | index af4fae2..9f54427 100644 | |||
187 | --- a/src/libCom/osi/epicsTime.cpp | |||
188 | +++ b/src/libCom/osi/epicsTime.cpp | |||
189 | @@ -955,7 +955,8 @@ extern "C" { | |||
190 | 955 | try { | 955 | try { |
191 | 956 | local_tm_nano_sec tmns = epicsTime (*pSrc); | 956 | local_tm_nano_sec tmns = epicsTime (*pSrc); |
192 | 957 | *pDest = tmns.ansi_tm; | 957 | *pDest = tmns.ansi_tm; |
194 | 958 | *pNSecDest = tmns.nSec; | 958 | if (pNSecDest) |
195 | 959 | *pNSecDest = tmns.nSec; | ||
196 | 959 | } | 960 | } |
197 | 960 | catch (...) { | 961 | catch (...) { |
198 | 961 | return epicsTimeERROR; | 962 | return epicsTimeERROR; |
199 | @@ -967,7 +968,8 @@ extern "C" { | |||
200 | 967 | try { | 968 | try { |
201 | 968 | gm_tm_nano_sec gmtmns = epicsTime (*pSrc); | 969 | gm_tm_nano_sec gmtmns = epicsTime (*pSrc); |
202 | 969 | *pDest = gmtmns.ansi_tm; | 970 | *pDest = gmtmns.ansi_tm; |
204 | 970 | *pNSecDest = gmtmns.nSec; | 971 | if (pNSecDest) |
205 | 972 | *pNSecDest = gmtmns.nSec; | ||
206 | 971 | } | 973 | } |
207 | 972 | catch (...) { | 974 | catch (...) { |
208 | 973 | return epicsTimeERROR; | 975 | return epicsTimeERROR; |
209 | diff --git a/src/libCom/osi/os/vxWorks/osdTime.cpp b/src/libCom/osi/os/vxWorks/osdTime.cpp | |||
210 | index 4db375f..eb144c1 100644 | |||
211 | --- a/src/libCom/osi/os/vxWorks/osdTime.cpp | |||
212 | +++ b/src/libCom/osi/os/vxWorks/osdTime.cpp | |||
213 | @@ -24,22 +24,38 @@ | |||
214 | 24 | 24 | ||
215 | 25 | #define NTP_REQUEST_TIMEOUT 4 /* seconds */ | 25 | #define NTP_REQUEST_TIMEOUT 4 /* seconds */ |
216 | 26 | 26 | ||
217 | 27 | extern "C" { | ||
218 | 28 | int tz2timezone(void); | ||
219 | 29 | } | ||
220 | 30 | |||
221 | 27 | static char sntp_sync_task[] = "ipsntps"; | 31 | static char sntp_sync_task[] = "ipsntps"; |
222 | 28 | static char ntp_daemon[] = "ipntpd"; | 32 | static char ntp_daemon[] = "ipntpd"; |
223 | 29 | 33 | ||
224 | 30 | static const char *pserverAddr = NULL; | 34 | static const char *pserverAddr = NULL; |
225 | 35 | static CLOCKTIME_SYNCHOOK prevHook; | ||
226 | 36 | |||
227 | 31 | extern char* sysBootLine; | 37 | extern char* sysBootLine; |
228 | 32 | 38 | ||
229 | 39 | static void timeSync(int synchronized) { | ||
230 | 40 | if (!tz2timezone()) | ||
231 | 41 | ClockTime_syncHook = prevHook; /* Don't call me again */ | ||
232 | 42 | } | ||
233 | 43 | |||
234 | 33 | static int timeRegister(void) | 44 | static int timeRegister(void) |
235 | 34 | { | 45 | { |
243 | 35 | /* If TIMEZONE not defined, set it from EPICS_TIMEZONE */ | 46 | /* If TZ not defined, set it from EPICS_TZ */ |
244 | 36 | if (getenv("TIMEZONE") == NULL) { | 47 | if (getenv("TZ") == NULL) { |
245 | 37 | const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE); | 48 | const char *tz = envGetConfigParamPtr(&EPICS_TZ); |
246 | 38 | if (timezone == NULL) { | 49 | |
247 | 39 | printf("timeRegister: No Time Zone Information\n"); | 50 | if (tz && *tz) { |
248 | 40 | } else { | 51 | epicsEnvSet("TZ", tz); |
249 | 41 | epicsEnvSet("TIMEZONE", timezone); | 52 | |
250 | 53 | /* Call tz2timezone() once we know what year it is */ | ||
251 | 54 | prevHook = ClockTime_syncHook; | ||
252 | 55 | ClockTime_syncHook = timeSync; | ||
253 | 42 | } | 56 | } |
254 | 57 | else if (getenv("TIMEZONE") == NULL) | ||
255 | 58 | printf("timeRegister: No Time Zone Information available\n"); | ||
256 | 43 | } | 59 | } |
257 | 44 | 60 | ||
258 | 45 | // Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider | 61 | // Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider |
259 | @@ -57,7 +73,7 @@ static int timeRegister(void) | |||
260 | 57 | } | 73 | } |
261 | 58 | 74 | ||
262 | 59 | if (useNTP) { | 75 | if (useNTP) { |
264 | 60 | // Start NTP first so it can be used to sync SysTime | 76 | // Start NTP first so it can be used to sync ClockTime |
265 | 61 | NTPTime_Init(100); | 77 | NTPTime_Init(100); |
266 | 62 | ClockTime_Init(CLOCKTIME_SYNC); | 78 | ClockTime_Init(CLOCKTIME_SYNC); |
267 | 63 | } else { | 79 | } else { |
268 | diff --git a/src/libCom/osi/os/vxWorks/tz2timezone.c b/src/libCom/osi/os/vxWorks/tz2timezone.c | |||
269 | 64 | new file mode 100644 | 80 | new file mode 100644 |
270 | index 0000000..df8db85 | |||
271 | --- /dev/null | |||
272 | +++ b/src/libCom/osi/os/vxWorks/tz2timezone.c | |||
273 | @@ -0,0 +1,278 @@ | |||
274 | 1 | /*************************************************************************\ | ||
275 | 2 | * Copyright (c) 2009 Brookhaven Science Associates, as Operator of | ||
276 | 3 | * Brookhaven National Laboratory. | ||
277 | 4 | * Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne | ||
278 | 5 | * National Laboratory. | ||
279 | 6 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
280 | 7 | * in file LICENSE that is included with this distribution. | ||
281 | 8 | \*************************************************************************/ | ||
282 | 9 | /* | ||
283 | 10 | * Authors: Larry Hoff, Andrew Johnson <anj@anl.gov> | ||
284 | 11 | */ | ||
285 | 12 | |||
286 | 13 | /* | ||
287 | 14 | * This file exports a single function "int tz2timezone(void)" which reads | ||
288 | 15 | * the TZ environment variable (defined by POSIX) and converts it to the | ||
289 | 16 | * TIMEZONE environment variable defined by ANSI. The latter is used by | ||
290 | 17 | * VxWorks "time" functions, is largely deprecated in other computing | ||
291 | 18 | * environments, has limitations, and is difficult to maintain. This holds | ||
292 | 19 | * out the possibility of "pretending" that VxWorks supports "TZ" - until | ||
293 | 20 | * such time as it actually does. | ||
294 | 21 | * | ||
295 | 22 | * For simplicity, only the "POSIX standard form" of TZ will be supported. | ||
296 | 23 | * Even that is complicated enough (see following spec). Furthermore, | ||
297 | 24 | * only the "M" form of DST start and stop dates are supported. | ||
298 | 25 | * | ||
299 | 26 | * TZ = zone[-]offset[dst[offset],start[/time],end[/time]] | ||
300 | 27 | * | ||
301 | 28 | * zone | ||
302 | 29 | * A three or more letter name for the timezone in normal (winter) time. | ||
303 | 30 | * | ||
304 | 31 | * [-]offset | ||
305 | 32 | * A signed time giving the offset of the time zone westwards from | ||
306 | 33 | * Greenwich. The time has the form hh[:mm[:ss]] with a one of two | ||
307 | 34 | * digit hour, and optional two digit minutes and seconds. | ||
308 | 35 | * | ||
309 | 36 | * dst | ||
310 | 37 | * The name of the time zone when daylight saving is in effect. It may | ||
311 | 38 | * be followed by an offset giving how big the adjustment is, required | ||
312 | 39 | * if different than the default of 1 hour. | ||
313 | 40 | * | ||
314 | 41 | * start/time,end/time | ||
315 | 42 | * Specify the start and end of the daylight saving period. The start | ||
316 | 43 | * and end fields indicate on what day the changeover occurs, and must | ||
317 | 44 | * be in this format: | ||
318 | 45 | * | ||
319 | 46 | * Mm.n.d | ||
320 | 47 | * This indicates month m, the n-th occurrence of day d, where | ||
321 | 48 | * 1 <= m <= 12, 1 <= n <= 5, 0 <= d <= 6, 0=Sunday | ||
322 | 49 | * The 5th occurrence means the last occurrence of that day in a | ||
323 | 50 | * month. So M4.1.0 is the first Sunday in April, M9.5.0 is the | ||
324 | 51 | * last Sunday in September. | ||
325 | 52 | * | ||
326 | 53 | * The time field indicates what hour the changeover occurs on the given | ||
327 | 54 | * day (TIMEZONE only supports switching on the hour). | ||
328 | 55 | * | ||
329 | 56 | */ | ||
330 | 57 | |||
331 | 58 | #include <vxWorks.h> | ||
332 | 59 | #include <envLib.h> /* getenv() */ | ||
333 | 60 | #include <stdio.h> /* printf() */ | ||
334 | 61 | #include <string.h> /* strchr() */ | ||
335 | 62 | #include <ctype.h> /* isalpha() */ | ||
336 | 63 | |||
337 | 64 | #include <epicsTime.h> | ||
338 | 65 | |||
339 | 66 | /* for reference: TZ syntax, example, and TIMEZONE example | ||
340 | 67 | * std offset dst [offset],start[/time],end[/time] | ||
341 | 68 | * CST6CDT5,M3.2.0,M11.1.0 | ||
342 | 69 | * EST+5EDT,M4.1.0/2,M10.5.0/2 | ||
343 | 70 | * | ||
344 | 71 | * std <unused> offset start stop | ||
345 | 72 | * TIMEZONE=EST::300:030802:110102 | ||
346 | 73 | */ | ||
347 | 74 | |||
348 | 75 | static int extractDate(const char *tz, struct tm *current, char *s) | ||
349 | 76 | { | ||
350 | 77 | static const int startdays[] = { | ||
351 | 78 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 | ||
352 | 79 | }; | ||
353 | 80 | static const int molengths[] = { | ||
354 | 81 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
355 | 82 | }; | ||
356 | 83 | |||
357 | 84 | int month, week, weekday, hour=2; /* default=2AM */ | ||
358 | 85 | int jan1wday, wday, mday; | ||
359 | 86 | |||
360 | 87 | /* Require 'M' format */ | ||
361 | 88 | if (*++tz != 'M') { | ||
362 | 89 | printf("tz2timezone: Unsupported date type, need 'M' format\n"); | ||
363 | 90 | return ERROR; | ||
364 | 91 | } | ||
365 | 92 | tz++; | ||
366 | 93 | |||
367 | 94 | if (sscanf(tz, "%d.%d.%d/%d", &month, &week, &weekday, &hour) < 3) | ||
368 | 95 | return ERROR; /* something important missing */ | ||
369 | 96 | |||
370 | 97 | if (month == 0 || month>12 || | ||
371 | 98 | week < 1 || week > 5 || | ||
372 | 99 | weekday < 0 || weekday > 6 || | ||
373 | 100 | hour < 0 || hour > 23) | ||
374 | 101 | return ERROR; | ||
375 | 102 | |||
376 | 103 | /* Now for some brute-force calendar calculations... */ | ||
377 | 104 | /* start month is in "month", and the day is "weekday", but | ||
378 | 105 | we need to know when that weekday first occurs in that month */ | ||
379 | 106 | /* Let's start with weekday on Jan. 1 */ | ||
380 | 107 | jan1wday = (7 + current->tm_wday - (current->tm_yday % 7)) % 7; | ||
381 | 108 | |||
382 | 109 | /* We need to know if it is a leap year (and if it matters) */ | ||
383 | 110 | /* Let's assume that we're working with a date between 1901 and 2099, | ||
384 | 111 | that way we don't have to think about the "century exception". | ||
385 | 112 | If this code is still running (unchanged) in 2100, I'll be stunned | ||
386 | 113 | (and 139 years old) */ | ||
387 | 114 | wday = (jan1wday + startdays[month-1] + | ||
388 | 115 | ((month > 2 && (current->tm_year % 4 == 0)) ? 1 : 0)) % 7; | ||
389 | 116 | |||
390 | 117 | /* Let's see on what day-of-the-month the first target weekday occurs | ||
391 | 118 | (counting from 1). The result is a number between 1 and 7, inclusive. */ | ||
392 | 119 | mday = 1 + ((7 + weekday - wday) % 7); | ||
393 | 120 | |||
394 | 121 | /* Next, we add the week offset. If we overflow the month, we subtract | ||
395 | 122 | one week */ | ||
396 | 123 | mday += 7 * (week - 1); | ||
397 | 124 | if (mday > molengths[month-1]) | ||
398 | 125 | mday -= 7; | ||
399 | 126 | |||
400 | 127 | /* Should I handle Feb 29? I'm willing to gamble that no one in their right | ||
401 | 128 | mind would schedule a time change to occur on Feb. 29. If so, we'll be a | ||
402 | 129 | week early */ | ||
403 | 130 | sprintf(s, "%02d%02d%02d", month, mday, hour); | ||
404 | 131 | |||
405 | 132 | return OK; | ||
406 | 133 | } | ||
407 | 134 | |||
408 | 135 | |||
409 | 136 | static const char *getTime(const char *s, int *time) | ||
410 | 137 | { | ||
411 | 138 | /* Time format is [+/-]hh[:mm][:ss] followed by the next zone name */ | ||
412 | 139 | |||
413 | 140 | *time = 0; | ||
414 | 141 | |||
415 | 142 | if (!isdigit((int) s[0])) | ||
416 | 143 | return s; /* no number here... */ | ||
417 | 144 | |||
418 | 145 | if (!isdigit((int) s[1])) { /* single digit form */ | ||
419 | 146 | *time = s[0] - '0'; | ||
420 | 147 | return s + 1; | ||
421 | 148 | } | ||
422 | 149 | |||
423 | 150 | if (isdigit((int) s[1])) { /* two digit form */ | ||
424 | 151 | *time = 10 * (s[0] - '0') + (s[1] - '0'); | ||
425 | 152 | return s + 2; | ||
426 | 153 | } | ||
427 | 154 | |||
428 | 155 | return s; /* does not follow supported form */ | ||
429 | 156 | } | ||
430 | 157 | |||
431 | 158 | int tz2timezone(void) | ||
432 | 159 | { | ||
433 | 160 | const char *tz = getenv("TZ"); | ||
434 | 161 | /* Spec. says that zone names must be at least 3 chars. | ||
435 | 162 | * I've never seen a longer zone name, but I'll allocate | ||
436 | 163 | * 40 chars. If you live in a zone with a longer name, | ||
437 | 164 | * you may want to think about the benefits of relocation. | ||
438 | 165 | */ | ||
439 | 166 | char zone[40]; | ||
440 | 167 | char start[10], stop[10]; /* only really need 7 bytes now */ | ||
441 | 168 | int hours = 0, minutes = 0, sign = 1; | ||
442 | 169 | /* This is more than enough, even with a 40-char zone | ||
443 | 170 | * name, and 4-char offset. | ||
444 | 171 | */ | ||
445 | 172 | char timezone[100]; | ||
446 | 173 | int i = 0; /* You *always need an "i" :-) */ | ||
447 | 174 | epicsTimeStamp now; | ||
448 | 175 | struct tm current; | ||
449 | 176 | |||
450 | 177 | /* First let's get the current time. We need the year to | ||
451 | 178 | * compute the start/stop dates for DST. | ||
452 | 179 | */ | ||
453 | 180 | if (epicsTimeGetCurrent(&now) || | ||
454 | 181 | epicsTimeToTM(¤t, NULL, &now)) | ||
455 | 182 | return ERROR; | ||
456 | 183 | |||
457 | 184 | /* Make sure TZ exists. | ||
458 | 185 | * Spec. says that ZONE must be at least 3 chars. | ||
459 | 186 | */ | ||
460 | 187 | if ((!tz) || (strlen(tz) < 3)) | ||
461 | 188 | return ERROR; | ||
462 | 189 | |||
463 | 190 | /* OK, now a bunch of brute-force parsing. My brain hurts if | ||
464 | 191 | * I try to think of an elegant regular expression for the | ||
465 | 192 | * string. | ||
466 | 193 | */ | ||
467 | 194 | |||
468 | 195 | /* Start extracting zone name, must be alpha */ | ||
469 | 196 | while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) { | ||
470 | 197 | zone[i++] = *tz++; | ||
471 | 198 | } | ||
472 | 199 | if (i < 3) | ||
473 | 200 | return ERROR; /* Too short, not a real zone name? */ | ||
474 | 201 | |||
475 | 202 | zone[i] = 0; /* Nil-terminate (for now) */ | ||
476 | 203 | |||
477 | 204 | /* Now extract offset time. The format is [+/-]hh[:mm[:ss]] | ||
478 | 205 | * Recall that TIMEZONE doesn't support seconds.... | ||
479 | 206 | */ | ||
480 | 207 | if (*tz == '-') { | ||
481 | 208 | sign = -1; | ||
482 | 209 | tz++; | ||
483 | 210 | } | ||
484 | 211 | else if (*tz == '+') { | ||
485 | 212 | tz++; | ||
486 | 213 | } | ||
487 | 214 | |||
488 | 215 | /* Need a digit now */ | ||
489 | 216 | if (!isdigit((int) *tz)) | ||
490 | 217 | return ERROR; | ||
491 | 218 | |||
492 | 219 | /* First get the hours */ | ||
493 | 220 | tz = getTime(tz, &hours); | ||
494 | 221 | if (hours > 24) | ||
495 | 222 | return ERROR; | ||
496 | 223 | |||
497 | 224 | if (*tz == ':') { /* There is a minutes part */ | ||
498 | 225 | /* Need another digit now */ | ||
499 | 226 | if (!isdigit((int) *++tz)) | ||
500 | 227 | return ERROR; | ||
501 | 228 | |||
502 | 229 | /* Extract the minutes */ | ||
503 | 230 | tz = getTime(tz, &minutes); | ||
504 | 231 | if (minutes > 60) | ||
505 | 232 | return ERROR; | ||
506 | 233 | |||
507 | 234 | /* Skip any seconds part */ | ||
508 | 235 | if (*tz == ':') { | ||
509 | 236 | int seconds; | ||
510 | 237 | tz = getTime(tz + 1, &seconds); | ||
511 | 238 | } | ||
512 | 239 | } | ||
513 | 240 | |||
514 | 241 | /* Extract any DST zone name, must be alpha */ | ||
515 | 242 | if (isalpha((int) *tz)) { | ||
516 | 243 | zone[i++] = '/'; /* Separate the names */ | ||
517 | 244 | |||
518 | 245 | while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) { | ||
519 | 246 | zone[i++] = *tz++; | ||
520 | 247 | } | ||
521 | 248 | zone[i] = 0; /* Nil-terminate */ | ||
522 | 249 | } | ||
523 | 250 | |||
524 | 251 | minutes += hours * 60; | ||
525 | 252 | minutes *= sign; | ||
526 | 253 | |||
527 | 254 | /* Look for start/stop dates - require neither or both */ | ||
528 | 255 | tz = strchr(tz, ','); | ||
529 | 256 | if (!tz) { /* No daylight savings time here */ | ||
530 | 257 | /* Format the env. variable */ | ||
531 | 258 | sprintf(timezone, "TIMEZONE=%s::%d", zone, minutes); | ||
532 | 259 | } | ||
533 | 260 | else { | ||
534 | 261 | if (extractDate(tz, ¤t, start) != OK) | ||
535 | 262 | return ERROR; | ||
536 | 263 | |||
537 | 264 | tz = strchr(tz + 1, ','); | ||
538 | 265 | if (!tz) | ||
539 | 266 | return ERROR; | ||
540 | 267 | if (extractDate(tz, ¤t, stop) != OK) | ||
541 | 268 | return ERROR; | ||
542 | 269 | |||
543 | 270 | /* Format the env. variable */ | ||
544 | 271 | sprintf(timezone, "TIMEZONE=%s::%d:%s:%s", zone, minutes, start, stop); | ||
545 | 272 | } | ||
546 | 273 | |||
547 | 274 | /* Make it live! */ | ||
548 | 275 | putenv(timezone); | ||
549 | 276 | |||
550 | 277 | return OK; | ||
551 | 278 | } | ||
552 | diff --git a/src/libCom/osi/osiClockTime.c b/src/libCom/osi/osiClockTime.c | |||
553 | index fb9d153..01958b2 100644 | |||
554 | --- a/src/libCom/osi/osiClockTime.c | |||
555 | +++ b/src/libCom/osi/osiClockTime.c | |||
556 | @@ -23,7 +23,8 @@ | |||
557 | 23 | #include "taskwd.h" | 23 | #include "taskwd.h" |
558 | 24 | 24 | ||
559 | 25 | #define NSEC_PER_SEC 1000000000 | 25 | #define NSEC_PER_SEC 1000000000 |
561 | 26 | #define ClockTimeSyncInterval_value 60.0 | 26 | #define ClockTimeSyncInterval_initial 1.0 |
562 | 27 | #define ClockTimeSyncInterval_normal 60.0 | ||
563 | 27 | 28 | ||
564 | 28 | 29 | ||
565 | 29 | static struct { | 30 | static struct { |
566 | @@ -79,7 +80,7 @@ static void ClockTime_InitOnce(void *pfirst) | |||
567 | 79 | 80 | ||
568 | 80 | ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); | 81 | ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); |
569 | 81 | ClockTimePvt.lock = epicsMutexCreate(); | 82 | ClockTimePvt.lock = epicsMutexCreate(); |
571 | 82 | ClockTimePvt.ClockTimeSyncInterval = 1.0; /* First sync */ | 83 | ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_initial; |
572 | 83 | 84 | ||
573 | 84 | epicsAtExit(ClockTime_Shutdown, NULL); | 85 | epicsAtExit(ClockTime_Shutdown, NULL); |
574 | 85 | 86 | ||
575 | @@ -148,6 +149,8 @@ void ClockTime_GetProgramStart(epicsTimeStamp *pDest) | |||
576 | 148 | /* Synchronization thread */ | 149 | /* Synchronization thread */ |
577 | 149 | 150 | ||
578 | 150 | #if defined(vxWorks) || defined(__rtems__) | 151 | #if defined(vxWorks) || defined(__rtems__) |
579 | 152 | CLOCKTIME_SYNCHOOK ClockTime_syncHook = NULL; | ||
580 | 153 | |||
581 | 151 | static void ClockTimeSync(void *dummy) | 154 | static void ClockTimeSync(void *dummy) |
582 | 152 | { | 155 | { |
583 | 153 | taskwdInsert(0, NULL, NULL); | 156 | taskwdInsert(0, NULL, NULL); |
584 | @@ -179,11 +182,16 @@ static void ClockTimeSync(void *dummy) | |||
585 | 179 | ClockTimePvt.syncTime = timeNow; | 182 | ClockTimePvt.syncTime = timeNow; |
586 | 180 | epicsMutexUnlock(ClockTimePvt.lock); | 183 | epicsMutexUnlock(ClockTimePvt.lock); |
587 | 181 | 184 | ||
589 | 182 | ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_value; | 185 | if (ClockTime_syncHook) |
590 | 186 | ClockTime_syncHook(1); | ||
591 | 187 | |||
592 | 188 | ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_normal; | ||
593 | 183 | } | 189 | } |
594 | 184 | } | 190 | } |
595 | 185 | 191 | ||
596 | 186 | ClockTimePvt.synchronized = 0; | 192 | ClockTimePvt.synchronized = 0; |
597 | 193 | if (ClockTime_syncHook) | ||
598 | 194 | ClockTime_syncHook(0); | ||
599 | 187 | taskwdRemove(0); | 195 | taskwdRemove(0); |
600 | 188 | } | 196 | } |
601 | 189 | #endif | 197 | #endif |
602 | diff --git a/src/libCom/osi/osiClockTime.h b/src/libCom/osi/osiClockTime.h | |||
603 | index 17eacab..2359888 100644 | |||
604 | --- a/src/libCom/osi/osiClockTime.h | |||
605 | +++ b/src/libCom/osi/osiClockTime.h | |||
606 | @@ -19,6 +19,12 @@ void ClockTime_Init(int synchronize); | |||
607 | 19 | void ClockTime_Shutdown(void *dummy); | 19 | void ClockTime_Shutdown(void *dummy); |
608 | 20 | int ClockTime_Report(int level); | 20 | int ClockTime_Report(int level); |
609 | 21 | 21 | ||
610 | 22 | #if defined(vxWorks) || defined(__rtems__) | ||
611 | 23 | typedef void (* CLOCKTIME_SYNCHOOK)(int synchronized); | ||
612 | 24 | |||
613 | 25 | extern CLOCKTIME_SYNCHOOK ClockTime_syncHook; | ||
614 | 26 | #endif | ||
615 | 27 | |||
616 | 22 | #ifdef __cplusplus | 28 | #ifdef __cplusplus |
617 | 23 | } | 29 | } |
618 | 24 | #endif | 30 | #endif |
This looks like almost a no-op for RTEMS, so I see no problem. Are the changes to epicsTime.cpp a bug fix?