1 |
/* $Header$ */ |
2 |
/* |
3 |
* tclWinTime.c -- |
4 |
* |
5 |
* Contains Windows specific versions of Tcl functions that |
6 |
* obtain time values from the operating system. |
7 |
* |
8 |
* Copyright 1995-1998 by Sun Microsystems, Inc. |
9 |
* |
10 |
* See the file "license.terms" for information on usage and redistribution |
11 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
12 |
* |
13 |
* RCS: @(#) $Id: tclwintime.c,v 1.1.1.1 2001/06/13 04:50:48 dtashley Exp $ |
14 |
*/ |
15 |
|
16 |
#include "tclWinInt.h" |
17 |
|
18 |
#define SECSPERDAY (60L * 60L * 24L) |
19 |
#define SECSPERYEAR (SECSPERDAY * 365L) |
20 |
#define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY) |
21 |
|
22 |
/* |
23 |
* The following arrays contain the day of year for the last day of |
24 |
* each month, where index 1 is January. |
25 |
*/ |
26 |
|
27 |
static int normalDays[] = { |
28 |
-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 |
29 |
}; |
30 |
|
31 |
static int leapDays[] = { |
32 |
-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 |
33 |
}; |
34 |
|
35 |
typedef struct ThreadSpecificData { |
36 |
char tzName[64]; /* Time zone name */ |
37 |
struct tm tm; /* time information */ |
38 |
} ThreadSpecificData; |
39 |
static Tcl_ThreadDataKey dataKey; |
40 |
|
41 |
/* |
42 |
* Declarations for functions defined later in this file. |
43 |
*/ |
44 |
|
45 |
static struct tm * ComputeGMT _ANSI_ARGS_((const time_t *tp)); |
46 |
|
47 |
/* |
48 |
*---------------------------------------------------------------------- |
49 |
* |
50 |
* TclpGetSeconds -- |
51 |
* |
52 |
* This procedure returns the number of seconds from the epoch. |
53 |
* On most Unix systems the epoch is Midnight Jan 1, 1970 GMT. |
54 |
* |
55 |
* Results: |
56 |
* Number of seconds from the epoch. |
57 |
* |
58 |
* Side effects: |
59 |
* None. |
60 |
* |
61 |
*---------------------------------------------------------------------- |
62 |
*/ |
63 |
|
64 |
unsigned long |
65 |
TclpGetSeconds() |
66 |
{ |
67 |
return (unsigned long) time((time_t *) NULL); |
68 |
} |
69 |
|
70 |
/* |
71 |
*---------------------------------------------------------------------- |
72 |
* |
73 |
* TclpGetClicks -- |
74 |
* |
75 |
* This procedure returns a value that represents the highest |
76 |
* resolution clock available on the system. There are no |
77 |
* guarantees on what the resolution will be. In Tcl we will |
78 |
* call this value a "click". The start time is also system |
79 |
* dependant. |
80 |
* |
81 |
* Results: |
82 |
* Number of clicks from some start time. |
83 |
* |
84 |
* Side effects: |
85 |
* None. |
86 |
* |
87 |
*---------------------------------------------------------------------- |
88 |
*/ |
89 |
|
90 |
unsigned long |
91 |
TclpGetClicks() |
92 |
{ |
93 |
return GetTickCount(); |
94 |
} |
95 |
|
96 |
/* |
97 |
*---------------------------------------------------------------------- |
98 |
* |
99 |
* TclpGetTimeZone -- |
100 |
* |
101 |
* Determines the current timezone. The method varies wildly |
102 |
* between different Platform implementations, so its hidden in |
103 |
* this function. |
104 |
* |
105 |
* Results: |
106 |
* Minutes west of GMT. |
107 |
* |
108 |
* Side effects: |
109 |
* None. |
110 |
* |
111 |
*---------------------------------------------------------------------- |
112 |
*/ |
113 |
|
114 |
int |
115 |
TclpGetTimeZone (currentTime) |
116 |
unsigned long currentTime; |
117 |
{ |
118 |
int timeZone; |
119 |
|
120 |
tzset(); |
121 |
timeZone = _timezone / 60; |
122 |
|
123 |
return timeZone; |
124 |
} |
125 |
|
126 |
/* |
127 |
*---------------------------------------------------------------------- |
128 |
* |
129 |
* TclpGetTime -- |
130 |
* |
131 |
* Gets the current system time in seconds and microseconds |
132 |
* since the beginning of the epoch: 00:00 UCT, January 1, 1970. |
133 |
* |
134 |
* Results: |
135 |
* Returns the current time in timePtr. |
136 |
* |
137 |
* Side effects: |
138 |
* None. |
139 |
* |
140 |
*---------------------------------------------------------------------- |
141 |
*/ |
142 |
|
143 |
void |
144 |
TclpGetTime(timePtr) |
145 |
Tcl_Time *timePtr; /* Location to store time information. */ |
146 |
{ |
147 |
struct timeb t; |
148 |
|
149 |
ftime(&t); |
150 |
timePtr->sec = t.time; |
151 |
timePtr->usec = t.millitm * 1000; |
152 |
} |
153 |
|
154 |
/* |
155 |
*---------------------------------------------------------------------- |
156 |
* |
157 |
* TclpGetTZName -- |
158 |
* |
159 |
* Gets the current timezone string. |
160 |
* |
161 |
* Results: |
162 |
* Returns a pointer to a static string, or NULL on failure. |
163 |
* |
164 |
* Side effects: |
165 |
* None. |
166 |
* |
167 |
*---------------------------------------------------------------------- |
168 |
*/ |
169 |
|
170 |
char * |
171 |
TclpGetTZName(int dst) |
172 |
{ |
173 |
int len; |
174 |
char *zone, *p; |
175 |
TIME_ZONE_INFORMATION tz; |
176 |
Tcl_Encoding encoding; |
177 |
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
178 |
char *name = tsdPtr->tzName; |
179 |
|
180 |
/* |
181 |
* tzset() under Borland doesn't seem to set up tzname[] at all. |
182 |
* tzset() under MSVC has the following weird observed behavior: |
183 |
* First time we call "clock format [clock seconds] -format %Z -gmt 1" |
184 |
* we get "GMT", but on all subsequent calls we get the current time |
185 |
* zone string, even though env(TZ) is GMT and the variable _timezone |
186 |
* is 0. |
187 |
*/ |
188 |
|
189 |
name[0] = '\0'; |
190 |
|
191 |
zone = getenv("TZ"); |
192 |
if (zone != NULL) { |
193 |
/* |
194 |
* TZ is of form "NST-4:30NDT", where "NST" would be the |
195 |
* name of the standard time zone for this area, "-4:30" is |
196 |
* the offset from GMT in hours, and "NDT is the name of |
197 |
* the daylight savings time zone in this area. The offset |
198 |
* and DST strings are optional. |
199 |
*/ |
200 |
|
201 |
len = strlen(zone); |
202 |
if (len > 3) { |
203 |
len = 3; |
204 |
} |
205 |
if (dst != 0) { |
206 |
/* |
207 |
* Skip the offset string and get the DST string. |
208 |
*/ |
209 |
|
210 |
p = zone + len; |
211 |
p += strspn(p, "+-:0123456789"); |
212 |
if (*p != '\0') { |
213 |
zone = p; |
214 |
len = strlen(zone); |
215 |
if (len > 3) { |
216 |
len = 3; |
217 |
} |
218 |
} |
219 |
} |
220 |
Tcl_ExternalToUtf(NULL, NULL, zone, len, 0, NULL, name, |
221 |
sizeof(tsdPtr->tzName), NULL, NULL, NULL); |
222 |
} |
223 |
if (name[0] == '\0') { |
224 |
if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) { |
225 |
/* |
226 |
* MSDN: On NT this is returned if DST is not used in |
227 |
* the current TZ |
228 |
*/ |
229 |
dst = 0; |
230 |
} |
231 |
encoding = Tcl_GetEncoding(NULL, "unicode"); |
232 |
Tcl_ExternalToUtf(NULL, encoding, |
233 |
(char *) ((dst) ? tz.DaylightName : tz.StandardName), -1, |
234 |
0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL); |
235 |
Tcl_FreeEncoding(encoding); |
236 |
} |
237 |
return name; |
238 |
} |
239 |
|
240 |
/* |
241 |
*---------------------------------------------------------------------- |
242 |
* |
243 |
* TclpGetDate -- |
244 |
* |
245 |
* This function converts between seconds and struct tm. If |
246 |
* useGMT is true, then the returned date will be in Greenwich |
247 |
* Mean Time (GMT). Otherwise, it will be in the local time zone. |
248 |
* |
249 |
* Results: |
250 |
* Returns a static tm structure. |
251 |
* |
252 |
* Side effects: |
253 |
* None. |
254 |
* |
255 |
*---------------------------------------------------------------------- |
256 |
*/ |
257 |
|
258 |
struct tm * |
259 |
TclpGetDate(t, useGMT) |
260 |
TclpTime_t t; |
261 |
int useGMT; |
262 |
{ |
263 |
const time_t *tp = (const time_t *) t; |
264 |
struct tm *tmPtr; |
265 |
long time; |
266 |
|
267 |
if (!useGMT) { |
268 |
tzset(); |
269 |
|
270 |
/* |
271 |
* If we are in the valid range, let the C run-time library |
272 |
* handle it. Otherwise we need to fake it. Note that this |
273 |
* algorithm ignores daylight savings time before the epoch. |
274 |
*/ |
275 |
|
276 |
if (*tp >= 0) { |
277 |
return localtime(tp); |
278 |
} |
279 |
|
280 |
time = *tp - _timezone; |
281 |
|
282 |
/* |
283 |
* If we aren't near to overflowing the long, just add the bias and |
284 |
* use the normal calculation. Otherwise we will need to adjust |
285 |
* the result at the end. |
286 |
*/ |
287 |
|
288 |
if (*tp < (LONG_MAX - 2 * SECSPERDAY) |
289 |
&& *tp > (LONG_MIN + 2 * SECSPERDAY)) { |
290 |
tmPtr = ComputeGMT(&time); |
291 |
} else { |
292 |
tmPtr = ComputeGMT(tp); |
293 |
|
294 |
tzset(); |
295 |
|
296 |
/* |
297 |
* Add the bias directly to the tm structure to avoid overflow. |
298 |
* Propagate seconds overflow into minutes, hours and days. |
299 |
*/ |
300 |
|
301 |
time = tmPtr->tm_sec - _timezone; |
302 |
tmPtr->tm_sec = (int)(time % 60); |
303 |
if (tmPtr->tm_sec < 0) { |
304 |
tmPtr->tm_sec += 60; |
305 |
time -= 60; |
306 |
} |
307 |
|
308 |
time = tmPtr->tm_min + time/60; |
309 |
tmPtr->tm_min = (int)(time % 60); |
310 |
if (tmPtr->tm_min < 0) { |
311 |
tmPtr->tm_min += 60; |
312 |
time -= 60; |
313 |
} |
314 |
|
315 |
time = tmPtr->tm_hour + time/60; |
316 |
tmPtr->tm_hour = (int)(time % 24); |
317 |
if (tmPtr->tm_hour < 0) { |
318 |
tmPtr->tm_hour += 24; |
319 |
time -= 24; |
320 |
} |
321 |
|
322 |
time /= 24; |
323 |
tmPtr->tm_mday += time; |
324 |
tmPtr->tm_yday += time; |
325 |
tmPtr->tm_wday = (tmPtr->tm_wday + time) % 7; |
326 |
} |
327 |
} else { |
328 |
tmPtr = ComputeGMT(tp); |
329 |
} |
330 |
return tmPtr; |
331 |
} |
332 |
|
333 |
/* |
334 |
*---------------------------------------------------------------------- |
335 |
* |
336 |
* ComputeGMT -- |
337 |
* |
338 |
* This function computes GMT given the number of seconds since |
339 |
* the epoch (midnight Jan 1 1970). |
340 |
* |
341 |
* Results: |
342 |
* Returns a (per thread) statically allocated struct tm. |
343 |
* |
344 |
* Side effects: |
345 |
* Updates the values of the static struct tm. |
346 |
* |
347 |
*---------------------------------------------------------------------- |
348 |
*/ |
349 |
|
350 |
static struct tm * |
351 |
ComputeGMT(tp) |
352 |
const time_t *tp; |
353 |
{ |
354 |
struct tm *tmPtr; |
355 |
long tmp, rem; |
356 |
int isLeap; |
357 |
int *days; |
358 |
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
359 |
|
360 |
tmPtr = &tsdPtr->tm; |
361 |
|
362 |
/* |
363 |
* Compute the 4 year span containing the specified time. |
364 |
*/ |
365 |
|
366 |
tmp = *tp / SECSPER4YEAR; |
367 |
rem = *tp % SECSPER4YEAR; |
368 |
|
369 |
/* |
370 |
* Correct for weird mod semantics so the remainder is always positive. |
371 |
*/ |
372 |
|
373 |
if (rem < 0) { |
374 |
tmp--; |
375 |
rem += SECSPER4YEAR; |
376 |
} |
377 |
|
378 |
/* |
379 |
* Compute the year after 1900 by taking the 4 year span and adjusting |
380 |
* for the remainder. This works because 2000 is a leap year, and |
381 |
* 1900/2100 are out of the range. |
382 |
*/ |
383 |
|
384 |
tmp = (tmp * 4) + 70; |
385 |
isLeap = 0; |
386 |
if (rem >= SECSPERYEAR) { /* 1971, etc. */ |
387 |
tmp++; |
388 |
rem -= SECSPERYEAR; |
389 |
if (rem >= SECSPERYEAR) { /* 1972, etc. */ |
390 |
tmp++; |
391 |
rem -= SECSPERYEAR; |
392 |
if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */ |
393 |
tmp++; |
394 |
rem -= SECSPERYEAR + SECSPERDAY; |
395 |
} else { |
396 |
isLeap = 1; |
397 |
} |
398 |
} |
399 |
} |
400 |
tmPtr->tm_year = tmp; |
401 |
|
402 |
/* |
403 |
* Compute the day of year and leave the seconds in the current day in |
404 |
* the remainder. |
405 |
*/ |
406 |
|
407 |
tmPtr->tm_yday = rem / SECSPERDAY; |
408 |
rem %= SECSPERDAY; |
409 |
|
410 |
/* |
411 |
* Compute the time of day. |
412 |
*/ |
413 |
|
414 |
tmPtr->tm_hour = rem / 3600; |
415 |
rem %= 3600; |
416 |
tmPtr->tm_min = rem / 60; |
417 |
tmPtr->tm_sec = rem % 60; |
418 |
|
419 |
/* |
420 |
* Compute the month and day of month. |
421 |
*/ |
422 |
|
423 |
days = (isLeap) ? leapDays : normalDays; |
424 |
for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) { |
425 |
} |
426 |
tmPtr->tm_mon = --tmp; |
427 |
tmPtr->tm_mday = tmPtr->tm_yday - days[tmp]; |
428 |
|
429 |
/* |
430 |
* Compute day of week. Epoch started on a Thursday. |
431 |
*/ |
432 |
|
433 |
tmPtr->tm_wday = (*tp / SECSPERDAY) + 4; |
434 |
if ((*tp % SECSPERDAY) < 0) { |
435 |
tmPtr->tm_wday--; |
436 |
} |
437 |
tmPtr->tm_wday %= 7; |
438 |
if (tmPtr->tm_wday < 0) { |
439 |
tmPtr->tm_wday += 7; |
440 |
} |
441 |
|
442 |
return tmPtr; |
443 |
} |
444 |
|
445 |
/* End of tclwintime.c */ |