1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1997-2018. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20 /*
21 * Purpose: System-dependent time functions.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #include "sys.h"
28 #include "assert.h"
29 #include "erl_os_monotonic_time_extender.h"
30 #include "erl_time.h"
31
32 /* Need to look more closely at qpc before use... */
33 #define ERTS_DISABLE_USE_OF_QPC_FOR_MONOTONIC_TIME 1
34
35 #define LL_LITERAL(X) ERTS_I64_LITERAL(X)
36
37 /******************* Routines for time measurement *********************/
38
39 #define EPOCH_JULIAN_DIFF LL_LITERAL(11644473600)
40 #define TICKS_PER_SECOND LL_LITERAL(10000000)
41 #define SECONDS_PER_DAY LL_LITERAL(86400)
42
43 #define ULI_TO_FILETIME(ft,ull) \
44 do { \
45 (ft).dwLowDateTime = (ull).LowPart; \
46 (ft).dwHighDateTime = (ull).HighPart; \
47 } while (0)
48
49 #define FILETIME_TO_ULI(ull,ft) \
50 do { \
51 (ull).LowPart = (ft).dwLowDateTime; \
52 (ull).HighPart = (ft).dwHighDateTime; \
53 } while (0)
54
55
56 #define EPOCH_TO_FILETIME(ft, epoch) \
57 do { \
58 ULARGE_INTEGER ull; \
59 ull.QuadPart = (((epoch) + EPOCH_JULIAN_DIFF) * TICKS_PER_SECOND); \
60 ULI_TO_FILETIME(ft,ull); \
61 } while(0)
62
63 #define FILETIME_TO_EPOCH(epoch, ft) \
64 do { \
65 ULARGE_INTEGER ull; \
66 FILETIME_TO_ULI(ull,ft); \
67 (epoch) = ((ull.QuadPart / TICKS_PER_SECOND) - EPOCH_JULIAN_DIFF); \
68 } while(0)
69
70 /* Getting timezone information is a heavy operation, so we want to do this
71 only once */
72
73 static TIME_ZONE_INFORMATION static_tzi;
74 static int have_static_tzi = 0;
75
76 static int days_in_month[2][13] = {
77 {0,31,28,31,30,31,30,31,31,30,31,30,31},
78 {0,31,29,31,30,31,30,31,31,30,31,30,31}};
79
80 #define ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT 10
81
82 /*
83 * erts_os_monotonic_time()
84 */
85
86 struct sys_time_internal_state_read_only__ {
87 ULONGLONG (WINAPI *pGetTickCount64)(void);
88 BOOL (WINAPI *pQueryPerformanceCounter)(LARGE_INTEGER *);
89 Sint32 pcf;
90 int using_get_tick_count_time_unit;
91 };
92
93 struct sys_time_internal_state_read_mostly__ {
94 ErtsOsMonotonicTimeExtendState os_mtime_xtnd;
95 };
96
97 struct sys_time_internal_state_write_freq__ {
98 erts_mtx_t mtime_mtx;
99 ULONGLONG wrap;
100 ULONGLONG last_tick_count;
101 };
102
103 __declspec(align(ASSUMED_CACHE_LINE_SIZE)) struct {
104 union {
105 struct sys_time_internal_state_read_only__ o;
106 char align__[(((sizeof(struct sys_time_internal_state_read_only__) - 1)
107 / ASSUMED_CACHE_LINE_SIZE) + 1)
108 * ASSUMED_CACHE_LINE_SIZE];
109 } r;
110 union {
111 struct sys_time_internal_state_read_mostly__ m;
112 char align__[(((sizeof(struct sys_time_internal_state_read_mostly__) - 1)
113 / ASSUMED_CACHE_LINE_SIZE) + 1)
114 * ASSUMED_CACHE_LINE_SIZE];
115 } wr;
116 union {
117 struct sys_time_internal_state_write_freq__ f;
118 char align__[(((sizeof(struct sys_time_internal_state_write_freq__) - 1)
119 / ASSUMED_CACHE_LINE_SIZE) + 1)
120 * ASSUMED_CACHE_LINE_SIZE];
121 } w;
122 } internal_state;
123
124 __declspec(align(ASSUMED_CACHE_LINE_SIZE)) ErtsSysTimeData__ erts_sys_time_data__;
125
126
127 static ERTS_INLINE ErtsSystemTime
SystemTime2MilliSec(SYSTEMTIME * stp)128 SystemTime2MilliSec(SYSTEMTIME *stp)
129 {
130 ErtsSystemTime stime;
131 FILETIME ft;
132 ULARGE_INTEGER ull;
133
134 SystemTimeToFileTime(stp, &ft);
135 FILETIME_TO_ULI(ull,ft);
136 /* now in 100 ns units */
137 stime = (ErtsSystemTime) ull.QuadPart;
138 stime -= (((ErtsSystemTime) EPOCH_JULIAN_DIFF)
139 * ((ErtsSystemTime) (10*1000*1000)));
140 stime /= (ErtsSystemTime) (10*1000); /* ms */
141 return stime;
142 }
143
144 static ErtsMonotonicTime
os_monotonic_time_qpc(void)145 os_monotonic_time_qpc(void)
146 {
147 LARGE_INTEGER pc;
148
149 if (!(*internal_state.r.o.pQueryPerformanceCounter)(&pc))
150 erts_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n");
151
152 return (ErtsMonotonicTime) pc.QuadPart;
153 }
154
155 static void
os_times_qpc(ErtsMonotonicTime * mtimep,ErtsSystemTime * stimep)156 os_times_qpc(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep)
157 {
158 LARGE_INTEGER pc;
159 SYSTEMTIME st;
160 ErtsSystemTime stime;
161 BOOL qpcr;
162
163 qpcr = (*internal_state.r.o.pQueryPerformanceCounter)(&pc);
164 GetSystemTime(&st);
165
166 if (!qpcr)
167 erts_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n");
168
169 *mtimep = (ErtsMonotonicTime) pc.QuadPart;
170
171 stime = SystemTime2MilliSec(&st);
172
173 *stimep = ((ErtsSystemTime)
174 erts_time_unit_conversion((Uint64) stime,
175 (Uint32) 1000,
176 internal_state.r.o.pcf));
177 }
178
179 static Uint32
get_tick_count(void)180 get_tick_count(void)
181 {
182 return (Uint32) GetTickCount();
183 }
184
185 static ErtsMonotonicTime
os_monotonic_time_gtc32(void)186 os_monotonic_time_gtc32(void)
187 {
188 ErtsMonotonicTime mtime;
189 Uint32 ticks = (Uint32) GetTickCount();
190 mtime = ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd,
191 ticks);
192 mtime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
193 return mtime;
194 }
195
196 static void
os_times_gtc32(ErtsMonotonicTime * mtimep,ErtsSystemTime * stimep)197 os_times_gtc32(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep)
198 {
199 SYSTEMTIME st;
200 ErtsSystemTime stime, mtime;
201 Uint32 ticks;
202
203 ticks = (Uint32) GetTickCount();
204 GetSystemTime(&st);
205
206 mtime = ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd,
207 ticks);
208 mtime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
209 *mtimep = mtime;
210
211 stime = SystemTime2MilliSec(&st);
212 stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
213 *stimep = stime;
214
215 }
216
217 static ErtsMonotonicTime
os_monotonic_time_gtc64(void)218 os_monotonic_time_gtc64(void)
219 {
220 ULONGLONG ticks = (*internal_state.r.o.pGetTickCount64)();
221 ErtsMonotonicTime mtime = (ErtsMonotonicTime) ticks;
222 return mtime << ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
223 }
224
225 static void
os_times_gtc64(ErtsMonotonicTime * mtimep,ErtsSystemTime * stimep)226 os_times_gtc64(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep)
227 {
228 SYSTEMTIME st;
229 ErtsSystemTime stime, mtime;
230 ULONGLONG ticks;
231
232 ticks = (*internal_state.r.o.pGetTickCount64)();
233 GetSystemTime(&st);
234
235 mtime = (ErtsMonotonicTime) ticks;
236 mtime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
237 *mtimep = mtime;
238
239 stime = SystemTime2MilliSec(&st);
240 stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
241 *stimep = stime;
242 }
243
244 static ErtsSysHrTime
sys_hrtime_qpc(void)245 sys_hrtime_qpc(void)
246 {
247 LARGE_INTEGER pc;
248
249 if (!(*internal_state.r.o.pQueryPerformanceCounter)(&pc))
250 erts_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n");
251
252 ASSERT(pc.QuadPart > 0);
253
254 return (ErtsSysHrTime) erts_time_unit_conversion((Uint64) pc.QuadPart,
255 internal_state.r.o.pcf,
256 (Uint32) 1000*1000*1000);
257 }
258
259 static ErtsSysHrTime
sys_hrtime_gtc32(void)260 sys_hrtime_gtc32(void)
261 {
262 ErtsSysHrTime time;
263 Uint32 ticks = (Uint32) GetTickCount();
264 time = (ErtsSysHrTime) ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd,
265 ticks);
266 time *= (ErtsSysHrTime) (1000 * 1000);
267 return time;
268 }
269
270 static ErtsSysHrTime
sys_hrtime_gtc64(void)271 sys_hrtime_gtc64(void)
272 {
273 ErtsSysHrTime time = (*internal_state.r.o.pGetTickCount64)();
274 time *= (ErtsSysHrTime) (1000*1000);
275 return time;
276 }
277
278 /*
279 * Init
280 */
281
282 void
sys_init_time(ErtsSysInitTimeResult * init_resp)283 sys_init_time(ErtsSysInitTimeResult *init_resp)
284 {
285 ErtsMonotonicTime (*os_mtime_func)(void);
286 void (*os_times_func)(ErtsMonotonicTime *, ErtsSystemTime *);
287 ErtsSysHrTime (*sys_hrtime_func)(void) = NULL;
288 ErtsMonotonicTime time_unit;
289 char kernel_dll_name[] = "kernel32";
290 HMODULE module;
291
292 init_resp->os_monotonic_time_info.clock_id = NULL;
293
294 module = GetModuleHandle(kernel_dll_name);
295 if (!module) {
296 get_tick_count:
297 erts_mtx_init(&internal_state.w.f.mtime_mtx, "os_monotonic_time", NIL,
298 ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
299 internal_state.w.f.wrap = 0;
300 internal_state.w.f.last_tick_count = 0;
301
302 init_resp->os_monotonic_time_info.func = "GetTickCount";
303 init_resp->os_monotonic_time_info.locked_use = 0;
304 /* 10-16 ms resolution according to MicroSoft documentation */
305 init_resp->os_monotonic_time_info.resolution = 100; /* 10 ms */
306 time_unit = (ErtsMonotonicTime) 1000;
307 time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
308 internal_state.r.o.using_get_tick_count_time_unit = 1;
309 os_mtime_func = os_monotonic_time_gtc32;
310 os_times_func = os_times_gtc32;
311 init_resp->os_monotonic_time_info.extended = 1;
312 erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd,
313 get_tick_count,
314 60*60*24*7); /* Check once a week */
315 if (!sys_hrtime_func)
316 sys_hrtime_func = sys_hrtime_gtc32;
317 }
318 else {
319 int major, minor, build;
320
321 os_version(&major, &minor, &build);
322
323 if (major < 6) {
324
325 get_tick_count64:
326
327 internal_state.r.o.pGetTickCount64
328 = ((ULONGLONG (WINAPI *)(void))
329 GetProcAddress(module, "GetTickCount64"));
330 if (!internal_state.r.o.pGetTickCount64)
331 goto get_tick_count;
332
333 init_resp->os_monotonic_time_info.func = "GetTickCount64";
334 init_resp->os_monotonic_time_info.locked_use = 0;
335 /* 10-16 ms resolution according to MicroSoft documentation */
336 init_resp->os_monotonic_time_info.resolution = 100; /* 10 ms */
337 time_unit = (ErtsMonotonicTime) 1000;
338 time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
339 internal_state.r.o.using_get_tick_count_time_unit = 1;
340 os_mtime_func = os_monotonic_time_gtc64;
341 os_times_func = os_times_gtc64;
342 if (!sys_hrtime_func)
343 sys_hrtime_func = sys_hrtime_gtc64;
344 }
345 else { /* Vista or newer... */
346
347 LARGE_INTEGER pf;
348 BOOL (WINAPI *QPF)(LARGE_INTEGER *);
349
350 QPF = ((BOOL (WINAPI *)(LARGE_INTEGER *))
351 GetProcAddress(module, "QueryPerformanceFrequency"));
352 if (!QPF)
353 goto get_tick_count64;
354 if (!(*QPF)(&pf))
355 goto get_tick_count64;
356
357 internal_state.r.o.pQueryPerformanceCounter
358 = ((BOOL (WINAPI *)(LARGE_INTEGER *))
359 GetProcAddress(module, "QueryPerformanceCounter"));
360 if (!internal_state.r.o.pQueryPerformanceCounter)
361 goto get_tick_count64;
362
363 if (pf.QuadPart > (((LONGLONG) 1) << 32))
364 goto get_tick_count64;
365
366 internal_state.r.o.pcf = (Uint32) pf.QuadPart;
367 sys_hrtime_func = sys_hrtime_qpc;
368
369 /*
370 * We only use QueryPerformanceCounter() for
371 * os-monotonic-time if its frequency is equal
372 * to, or larger than GHz in order to ensure
373 * that the user wont be able to observe faulty
374 * order between values retrieved on different threads.
375 */
376 if (pf.QuadPart < (LONGLONG) 1000*1000*1000)
377 goto get_tick_count64;
378
379 if (ERTS_DISABLE_USE_OF_QPC_FOR_MONOTONIC_TIME)
380 goto get_tick_count64;
381
382 init_resp->os_monotonic_time_info.func = "QueryPerformanceCounter";
383 init_resp->os_monotonic_time_info.locked_use = 0;
384 time_unit = (ErtsMonotonicTime) pf.QuadPart;
385 internal_state.r.o.using_get_tick_count_time_unit = 0;
386 init_resp->os_monotonic_time_info.resolution = time_unit;
387 os_mtime_func = os_monotonic_time_qpc;
388 os_times_func = os_times_qpc;
389 }
390 }
391
392 erts_sys_time_data__.r.o.os_monotonic_time = os_mtime_func;
393 erts_sys_time_data__.r.o.os_times = os_times_func;
394 erts_sys_time_data__.r.o.sys_hrtime = sys_hrtime_func;
395 init_resp->os_monotonic_time_unit = time_unit;
396 init_resp->have_os_monotonic_time = 1;
397 init_resp->have_corrected_os_monotonic_time = 0;
398 init_resp->sys_clock_resolution = 1;
399
400 init_resp->os_system_time_info.func = "GetSystemTime";
401 init_resp->os_system_time_info.clock_id = NULL;
402 init_resp->os_system_time_info.resolution = 100;
403 init_resp->os_system_time_info.locked_use = 0;
404
405 if(GetTimeZoneInformation(&static_tzi) &&
406 static_tzi.StandardDate.wMonth != 0 &&
407 static_tzi.DaylightDate.wMonth != 0) {
408 have_static_tzi = 1;
409 }
410 }
411
412 void
erts_late_sys_init_time(void)413 erts_late_sys_init_time(void)
414 {
415 if (erts_sys_time_data__.r.o.os_monotonic_time == os_monotonic_time_gtc32)
416 erts_late_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd);
417 }
418
419 /* Returns a switchtimes for DST as UTC filetimes given data from a
420 TIME_ZONE_INFORMATION, see sys_localtime_r for usage. */
421 static void
get_dst_switchtime(DWORD year,SYSTEMTIME dstinfo,LONG bias,FILETIME * utc_switchtime)422 get_dst_switchtime(DWORD year,
423 SYSTEMTIME dstinfo, LONG bias,
424 FILETIME *utc_switchtime)
425 {
426 DWORD occu;
427 DWORD weekday,wday_1st;
428 DWORD day, days_in;
429 FILETIME tmp,tmp2;
430 ULARGE_INTEGER ull;
431 int leap_year = 0;
432 if (dstinfo.wYear != 0) {
433 /* A year specific transition, in which case the data in the structure
434 is already properly set for a specific year. Compare year
435 with parameter and see if they correspond, in that case generate a
436 filetime directly, otherwise set the filetime to 0 */
437 if (year != dstinfo.wYear) {
438 utc_switchtime->dwLowDateTime = utc_switchtime->dwHighDateTime = 0;
439 return;
440 }
441 } else {
442 occu = dstinfo.wDay;
443 weekday = dstinfo.wDayOfWeek;
444
445 dstinfo.wDayOfWeek = 0;
446 dstinfo.wDay = 1;
447 dstinfo.wYear = year;
448
449 SystemTimeToFileTime(&dstinfo,&tmp);
450 ull.LowPart = tmp.dwLowDateTime;
451 ull.HighPart = tmp.dwHighDateTime;
452
453 ull.QuadPart /= (TICKS_PER_SECOND*SECONDS_PER_DAY); /* Julian Day */
454 wday_1st = (DWORD) ((ull.QuadPart + LL_LITERAL(1)) % LL_LITERAL(7));
455 day = (weekday >= wday_1st) ?
456 weekday - wday_1st + 1 :
457 weekday - wday_1st + 8;
458 --occu;
459 if (((dstinfo.wYear % 4) == 0 && (dstinfo.wYear % 100) > 0) ||
460 ((dstinfo.wYear % 400) == 0)) {
461 leap_year = 1;
462 }
463 days_in = days_in_month[leap_year][dstinfo.wMonth];
464 while (occu > 0 && (day + 7 <= days_in)) {
465 --occu;
466 day += 7;
467 }
468 dstinfo.wDay = day;
469 }
470 SystemTimeToFileTime(&dstinfo,&tmp);
471 /* correct for bias */
472 ull.LowPart = tmp.dwLowDateTime;
473 ull.HighPart = tmp.dwHighDateTime;
474 ull.QuadPart += (((LONGLONG) bias) * LL_LITERAL(60) * TICKS_PER_SECOND);
475 utc_switchtime->dwLowDateTime = ull.LowPart;
476 utc_switchtime->dwHighDateTime = ull.HighPart;
477 return;
478 }
479
480 /* This function gives approximately the correct year from a FILETIME
481 Around the actual new year, it may return the wrong value, but that's OK
482 as DST never switches around new year. */
483 static DWORD
approx_year(FILETIME ft)484 approx_year(FILETIME ft)
485 {
486 ULARGE_INTEGER ull;
487 FILETIME_TO_ULI(ull,ft);
488 ull.QuadPart /= LL_LITERAL(1000);
489 ull.QuadPart /= SECONDS_PER_DAY;
490 ull.QuadPart /= LL_LITERAL(3652425);
491 ull.QuadPart += 1601;
492 return (DWORD) ull.QuadPart;
493 }
494
495 struct tm *
sys_localtime_r(time_t * epochs,struct tm * ptm)496 sys_localtime_r(time_t *epochs, struct tm *ptm)
497 {
498 FILETIME ft,lft;
499 SYSTEMTIME st;
500
501 if ((((*epochs) + EPOCH_JULIAN_DIFF) * TICKS_PER_SECOND) < 0LL) {
502 fprintf(stderr,"1\r\n"); fflush(stderr);
503 return NULL;
504 }
505
506 EPOCH_TO_FILETIME(ft,*epochs);
507 ptm->tm_isdst = 0;
508 if (have_static_tzi) {
509 FILETIME dst_start, dst_stop;
510 ULARGE_INTEGER ull;
511 DWORD year = approx_year(ft);
512 get_dst_switchtime(year,static_tzi.DaylightDate,
513 static_tzi.Bias+static_tzi.StandardBias,&dst_start);
514 get_dst_switchtime(year,static_tzi.StandardDate,
515 static_tzi.Bias+static_tzi.StandardBias+
516 static_tzi.DaylightBias,
517 &dst_stop);
518 FILETIME_TO_ULI(ull,ft);
519
520 if (CompareFileTime(&ft,&dst_start) >= 0 &&
521 CompareFileTime(&ft,&dst_stop) < 0) {
522 ull.QuadPart -=
523 ((LONGLONG) static_tzi.Bias+static_tzi.StandardBias+
524 static_tzi.DaylightBias) *
525 LL_LITERAL(60) * TICKS_PER_SECOND;
526 ptm->tm_isdst = 1;
527 } else {
528 ull.QuadPart -=
529 ((LONGLONG) static_tzi.Bias+static_tzi.StandardBias)
530 * LL_LITERAL(60) * TICKS_PER_SECOND;
531 }
532 ULI_TO_FILETIME(ft,ull);
533 } else {
534 if (!FileTimeToLocalFileTime(&ft,&lft)) {
535 return NULL;
536 }
537 ft = lft;
538 }
539
540 if (!FileTimeToSystemTime(&ft,&st)) {
541 return NULL;
542 }
543
544 ptm->tm_year = (int) st.wYear - 1900;
545 ptm->tm_mon = (int) st.wMonth - 1;
546 ptm->tm_mday = (int) st.wDay;
547 ptm->tm_hour = (int) st.wHour;
548 ptm->tm_min = (int) st.wMinute;
549 ptm->tm_sec = (int) st.wSecond;
550 ptm->tm_wday = (int) st.wDayOfWeek;
551 {
552 int yday = ptm->tm_mday - 1;
553 int m = ptm->tm_mon;
554 int leap_year = 0;
555 if (((st.wYear % 4) == 0 && (st.wYear % 100) > 0) ||
556 ((st.wYear % 400) == 0)) {
557 leap_year = 1;
558 }
559 while (m > 0) {
560 yday +=days_in_month[leap_year][m];
561 --m;
562 }
563 ptm->tm_yday = yday;
564 }
565 return ptm;
566 }
567
568 struct tm *
sys_gmtime_r(time_t * epochs,struct tm * ptm)569 sys_gmtime_r(time_t *epochs, struct tm *ptm)
570 {
571 FILETIME ft;
572 SYSTEMTIME st;
573
574 if ((((*epochs) + EPOCH_JULIAN_DIFF) * TICKS_PER_SECOND) < 0LL) {
575 return NULL;
576 }
577
578 EPOCH_TO_FILETIME(ft,*epochs);
579
580 if (!FileTimeToSystemTime(&ft,&st)) {
581 return NULL;
582 }
583
584 ptm->tm_year = (int) st.wYear - 1900;
585 ptm->tm_mon = (int) st.wMonth - 1;
586 ptm->tm_mday = (int) st.wDay;
587 ptm->tm_hour = (int) st.wHour;
588 ptm->tm_min = (int) st.wMinute;
589 ptm->tm_sec = (int) st.wSecond;
590 ptm->tm_wday = (int) st.wDayOfWeek;
591 ptm->tm_isdst = 0;
592 {
593 int yday = ptm->tm_mday - 1;
594 int m = ptm->tm_mon;
595 int leap_year = 0;
596 if (((st.wYear % 4) == 0 && (st.wYear % 100) > 0) ||
597 ((st.wYear % 400) == 0)) {
598 leap_year = 1;
599 }
600 while (m > 0) {
601 yday +=days_in_month[leap_year][m];
602 --m;
603 }
604 ptm->tm_yday = yday;
605 }
606
607 return ptm;
608 }
609
610 time_t
sys_mktime(struct tm * ptm)611 sys_mktime(struct tm *ptm)
612 {
613 FILETIME ft;
614 SYSTEMTIME st;
615 int dst = 0;
616 time_t epochs;
617
618 memset(&st,0,sizeof(st));
619 /* Convert relevant parts of truct tm to SYSTEMTIME */
620 st.wYear = (USHORT) (ptm->tm_year + 1900);
621 st.wMonth = (USHORT) (ptm->tm_mon + 1);
622 st.wDay = (USHORT) ptm->tm_mday;
623 st.wHour = (USHORT) ptm->tm_hour;
624 st.wMinute = (USHORT) ptm->tm_min;
625 st.wSecond = (USHORT) ptm->tm_sec;
626
627 SystemTimeToFileTime(&st,&ft);
628
629 /* ft is now some kind of local file time, but it may be wrong depending
630 on what is in the tm_dst field. We need to manually convert it to
631 UTC before turning it into epochs */
632
633 if (have_static_tzi) {
634 FILETIME dst_start, dst_stop;
635 ULARGE_INTEGER ull_start,ull_stop,ull_ft;
636
637 FILETIME_TO_ULI(ull_ft,ft);
638
639 /* Correct everything except DST */
640 ull_ft.QuadPart += (static_tzi.Bias+static_tzi.StandardBias)
641 * LL_LITERAL(60) * TICKS_PER_SECOND;
642
643 /* Determine if DST is active */
644 if (ptm->tm_isdst >= 0) {
645 dst = ptm->tm_isdst;
646 } else if (static_tzi.DaylightDate.wMonth != 0){
647 /* This is how windows mktime does it, meaning it does not
648 take nonexisting local times into account */
649 get_dst_switchtime(st.wYear,static_tzi.DaylightDate,
650 static_tzi.Bias+static_tzi.StandardBias,
651 &dst_start);
652 get_dst_switchtime(st.wYear,static_tzi.StandardDate,
653 static_tzi.Bias+static_tzi.StandardBias+
654 static_tzi.DaylightBias,
655 &dst_stop);
656 FILETIME_TO_ULI(ull_start,dst_start);
657 FILETIME_TO_ULI(ull_stop,dst_stop);
658 if ((ull_ft.QuadPart >= ull_start.QuadPart) &&
659 (ull_ft.QuadPart < ull_stop.QuadPart)) {
660 /* We are in DST */
661 dst = 1;
662 }
663 }
664 /* Correct for DST */
665 if (dst) {
666 ull_ft.QuadPart += static_tzi.DaylightBias *
667 LL_LITERAL(60) * TICKS_PER_SECOND;
668 }
669 epochs = ((ull_ft.QuadPart / TICKS_PER_SECOND) - EPOCH_JULIAN_DIFF);
670 } else {
671 /* No DST, life is easy... */
672 FILETIME lft;
673 LocalFileTimeToFileTime(&ft,&lft);
674 FILETIME_TO_EPOCH(epochs,lft);
675 }
676 /* Normalize the struct tm */
677 sys_localtime_r(&epochs,ptm);
678 return epochs;
679 }
680
681 void
sys_gettimeofday(SysTimeval * tv)682 sys_gettimeofday(SysTimeval *tv)
683 {
684 SYSTEMTIME t;
685 FILETIME ft;
686 ULARGE_INTEGER ull;
687
688 GetSystemTime(&t);
689 SystemTimeToFileTime(&t, &ft);
690 FILETIME_TO_ULI(ull,ft);
691 tv->tv_usec = (long) ((ull.QuadPart / LL_LITERAL(10)) %
692 LL_LITERAL(1000000));
693 tv->tv_sec = (long) ((ull.QuadPart / LL_LITERAL(10000000)) -
694 EPOCH_JULIAN_DIFF);
695 }
696
697 ErtsSystemTime
erts_os_system_time(void)698 erts_os_system_time(void)
699 {
700 SYSTEMTIME st;
701 ErtsSystemTime stime;
702
703 GetSystemTime(&st);
704 stime = SystemTime2MilliSec(&st);
705
706 if (internal_state.r.o.using_get_tick_count_time_unit) {
707 stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
708 return stime;
709 }
710
711 return ((ErtsSystemTime)
712 erts_time_unit_conversion((Uint64) stime,
713 (Uint32) 1000,
714 internal_state.r.o.pcf));
715 }
716
717
718 clock_t
sys_times(SysTimes * buffer)719 sys_times(SysTimes *buffer) {
720 clock_t kernel_ticks = (GetTickCount() /
721 (1000 / SYS_CLK_TCK)) & 0x7FFFFFFF;
722 FILETIME dummy;
723 LONGLONG user;
724 LONGLONG system;
725
726 buffer->tms_utime = buffer->tms_stime = buffer->tms_cutime =
727 buffer->tms_cstime = 0;
728
729 if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
730 (FILETIME *) &system, (FILETIME *) &user) == 0)
731 return kernel_ticks;
732 system /= (LONGLONG)(10000000 / SYS_CLK_TCK);
733 user /= (LONGLONG)(10000000 / SYS_CLK_TCK);
734
735 buffer->tms_utime = (clock_t) (user & LL_LITERAL(0x7FFFFFFF));
736 buffer->tms_stime = (clock_t) (system & LL_LITERAL(0x7FFFFFFF));
737 return kernel_ticks;
738 }
739