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