xref: /aosp_15_r20/external/lzma/CPP/Windows/TimeUtils.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // Windows/TimeUtils.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifndef _WIN32
6 #include <sys/time.h>
7 #include <time.h>
8 #endif
9 
10 #include "Defs.h"
11 #include "TimeUtils.h"
12 
13 namespace NWindows {
14 namespace NTime {
15 
16 static const UInt32 kNumTimeQuantumsInSecond = 10000000;
17 static const unsigned kFileTimeStartYear = 1601;
18 #if !defined(_WIN32) || defined(UNDER_CE)
19 static const unsigned kDosTimeStartYear = 1980;
20 #endif
21 static const unsigned kUnixTimeStartYear = 1970;
22 static const UInt64 kUnixTimeOffset =
23     (UInt64)60 * 60 * 24 * (89 + 365 * (UInt32)(kUnixTimeStartYear - kFileTimeStartYear));
24 static const UInt64 kNumSecondsInFileTime = (UInt64)(Int64)-1 / kNumTimeQuantumsInSecond;
25 
DosTime_To_FileTime(UInt32 dosTime,FILETIME & ft)26 bool DosTime_To_FileTime(UInt32 dosTime, FILETIME &ft) throw()
27 {
28   #if defined(_WIN32) && !defined(UNDER_CE)
29   return BOOLToBool(::DosDateTimeToFileTime((UInt16)(dosTime >> 16), (UInt16)(dosTime & 0xFFFF), &ft));
30   #else
31   ft.dwLowDateTime = 0;
32   ft.dwHighDateTime = 0;
33   UInt64 res;
34   if (!GetSecondsSince1601(
35       kDosTimeStartYear + (unsigned)(dosTime >> 25),
36       (unsigned)((dosTime >> 21) & 0xF),
37       (unsigned)((dosTime >> 16) & 0x1F),
38       (unsigned)((dosTime >> 11) & 0x1F),
39       (unsigned)((dosTime >>  5) & 0x3F),
40       (unsigned)((dosTime & 0x1F)) * 2,
41       res))
42     return false;
43   res *= kNumTimeQuantumsInSecond;
44   ft.dwLowDateTime = (UInt32)res;
45   ft.dwHighDateTime = (UInt32)(res >> 32);
46   return true;
47   #endif
48 }
49 
50 static const UInt32 kHighDosTime = 0xFF9FBF7D;
51 static const UInt32 kLowDosTime = 0x210000;
52 
FileTime_To_DosTime(const FILETIME & ft,UInt32 & dosTime)53 bool FileTime_To_DosTime(const FILETIME &ft, UInt32 &dosTime) throw()
54 {
55   #if defined(_WIN32) && !defined(UNDER_CE)
56 
57   WORD datePart, timePart;
58   if (!::FileTimeToDosDateTime(&ft, &datePart, &timePart))
59   {
60     dosTime = (ft.dwHighDateTime >= 0x01C00000) ? kHighDosTime : kLowDosTime;
61     return false;
62   }
63   dosTime = (((UInt32)datePart) << 16) + timePart;
64 
65   #else
66 
67 #define PERIOD_4 (4 * 365 + 1)
68 #define PERIOD_100 (PERIOD_4 * 25 - 1)
69 #define PERIOD_400 (PERIOD_100 * 4 + 1)
70 
71   unsigned year, mon, day, hour, min, sec;
72   UInt64 v64 = ft.dwLowDateTime | ((UInt64)ft.dwHighDateTime << 32);
73   Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
74   unsigned temp;
75   UInt32 v;
76   v64 += (kNumTimeQuantumsInSecond * 2 - 1);
77   v64 /= kNumTimeQuantumsInSecond;
78   sec = (unsigned)(v64 % 60);
79   v64 /= 60;
80   min = (unsigned)(v64 % 60);
81   v64 /= 60;
82   hour = (unsigned)(v64 % 24);
83   v64 /= 24;
84 
85   v = (UInt32)v64;
86 
87   year = kFileTimeStartYear + (unsigned)(v / PERIOD_400 * 400);
88   v %= PERIOD_400;
89 
90   temp = (unsigned)(v / PERIOD_100);
91   if (temp == 4)
92     temp = 3;
93   year += temp * 100;
94   v -= temp * PERIOD_100;
95 
96   temp = v / PERIOD_4;
97   if (temp == 25)
98     temp = 24;
99   year += temp * 4;
100   v -= temp * PERIOD_4;
101 
102   temp = v / 365;
103   if (temp == 4)
104     temp = 3;
105   year += temp;
106   v -= temp * 365;
107 
108   if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
109     ms[1] = 29;
110   for (mon = 1; mon <= 12; mon++)
111   {
112     unsigned s = ms[mon - 1];
113     if (v < s)
114       break;
115     v -= s;
116   }
117   day = (unsigned)v + 1;
118 
119   dosTime = kLowDosTime;
120   if (year < kDosTimeStartYear)
121     return false;
122   year -= kDosTimeStartYear;
123   dosTime = kHighDosTime;
124   if (year >= 128)
125     return false;
126   dosTime =
127       ((UInt32)year << 25)
128     | ((UInt32)mon  << 21)
129     | ((UInt32)day  << 16)
130     | ((UInt32)hour << 11)
131     | ((UInt32)min  << 5)
132     | ((UInt32)sec  >> 1);
133   #endif
134   return true;
135 }
136 
137 
UtcFileTime_To_LocalDosTime(const FILETIME & utc,UInt32 & dosTime)138 bool UtcFileTime_To_LocalDosTime(const FILETIME &utc, UInt32 &dosTime) throw()
139 {
140   FILETIME loc = { 0, 0 };
141   const UInt64 u1 = FILETIME_To_UInt64(utc);
142   const UInt64 kDelta = ((UInt64)1 << 41); // it's larger than quantums in 1 sec.
143   if (u1 >= kDelta)
144   {
145     if (!FileTimeToLocalFileTime(&utc, &loc))
146       loc = utc;
147     else
148     {
149       const UInt64 u2 = FILETIME_To_UInt64(loc);
150       const UInt64 delta = u1 < u2 ? (u2 - u1) : (u1 - u2);
151       if (delta > kDelta) // if FileTimeToLocalFileTime() overflow, we use UTC time
152         loc = utc;
153     }
154   }
155   return FileTime_To_DosTime(loc, dosTime);
156 }
157 
UnixTime_To_FileTime64(UInt32 unixTime)158 UInt64 UnixTime_To_FileTime64(UInt32 unixTime) throw()
159 {
160   return (kUnixTimeOffset + (UInt64)unixTime) * kNumTimeQuantumsInSecond;
161 }
162 
UnixTime_To_FileTime(UInt32 unixTime,FILETIME & ft)163 void UnixTime_To_FileTime(UInt32 unixTime, FILETIME &ft) throw()
164 {
165   const UInt64 v = UnixTime_To_FileTime64(unixTime);
166   ft.dwLowDateTime = (DWORD)v;
167   ft.dwHighDateTime = (DWORD)(v >> 32);
168 }
169 
UnixTime64_To_FileTime64(Int64 unixTime)170 UInt64 UnixTime64_To_FileTime64(Int64 unixTime) throw()
171 {
172   return (UInt64)((Int64)kUnixTimeOffset + unixTime) * kNumTimeQuantumsInSecond;
173 }
174 
175 
UnixTime64_To_FileTime64(Int64 unixTime,UInt64 & fileTime)176 bool UnixTime64_To_FileTime64(Int64 unixTime, UInt64 &fileTime) throw()
177 {
178   if (unixTime > (Int64)(kNumSecondsInFileTime - kUnixTimeOffset))
179   {
180     fileTime = (UInt64)(Int64)-1;
181     return false;
182   }
183   if (unixTime < -(Int64)kUnixTimeOffset)
184   {
185     fileTime = 0;
186     return false;
187   }
188   fileTime = UnixTime64_To_FileTime64(unixTime);
189   return true;
190 }
191 
192 
UnixTime64_To_FileTime(Int64 unixTime,FILETIME & ft)193 bool UnixTime64_To_FileTime(Int64 unixTime, FILETIME &ft) throw()
194 {
195   UInt64 v;
196   const bool res = UnixTime64_To_FileTime64(unixTime, v);
197   ft.dwLowDateTime = (DWORD)v;
198   ft.dwHighDateTime = (DWORD)(v >> 32);
199   return res;
200 }
201 
202 
FileTime_To_UnixTime64(const FILETIME & ft)203 Int64 FileTime_To_UnixTime64(const FILETIME &ft) throw()
204 {
205   const UInt64 winTime = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
206   return (Int64)(winTime / kNumTimeQuantumsInSecond) - (Int64)kUnixTimeOffset;
207 }
208 
FileTime_To_UnixTime64_and_Quantums(const FILETIME & ft,UInt32 & quantums)209 Int64 FileTime_To_UnixTime64_and_Quantums(const FILETIME &ft, UInt32 &quantums) throw()
210 {
211   const UInt64 winTime = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
212   quantums = (UInt32)(winTime % kNumTimeQuantumsInSecond);
213   return (Int64)(winTime / kNumTimeQuantumsInSecond) - (Int64)kUnixTimeOffset;
214 }
215 
FileTime_To_UnixTime(const FILETIME & ft,UInt32 & unixTime)216 bool FileTime_To_UnixTime(const FILETIME &ft, UInt32 &unixTime) throw()
217 {
218   UInt64 winTime = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
219   winTime /= kNumTimeQuantumsInSecond;
220   if (winTime < kUnixTimeOffset)
221   {
222     unixTime = 0;
223     return false;
224   }
225   winTime -= kUnixTimeOffset;
226   if (winTime > (UInt32)0xFFFFFFFF)
227   {
228     unixTime = (UInt32)0xFFFFFFFF;
229     return false;
230   }
231   unixTime = (UInt32)winTime;
232   return true;
233 }
234 
GetSecondsSince1601(unsigned year,unsigned month,unsigned day,unsigned hour,unsigned min,unsigned sec,UInt64 & resSeconds)235 bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day,
236   unsigned hour, unsigned min, unsigned sec, UInt64 &resSeconds) throw()
237 {
238   resSeconds = 0;
239   if (year < kFileTimeStartYear || year >= 10000 || month < 1 || month > 12 ||
240       day < 1 || day > 31 || hour > 23 || min > 59 || sec > 59)
241     return false;
242   const unsigned numYears = year - kFileTimeStartYear;
243   UInt32 numDays = (UInt32)((UInt32)numYears * 365 + numYears / 4 - numYears / 100 + numYears / 400);
244   Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
245   if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
246     ms[1] = 29;
247   month--;
248   for (unsigned i = 0; i < month; i++)
249     numDays += ms[i];
250   numDays += (UInt32)(day - 1);
251   resSeconds = ((UInt64)(numDays * 24 + hour) * 60 + min) * 60 + sec;
252   return true;
253 }
254 
255 
256 /* docs: TIME_UTC is not defined on many platforms:
257       glibc 2.15, macOS 10.13
258       FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0,
259       Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, Solaris 11.3,
260       Cygwin 2.9, mingw, MSVC 14, Android 9.0.
261 */
262 #if defined(TIME_UTC)
263 #define ZIP7_USE_timespec_get
264 // #pragma message("ZIP7_USE_timespec_get")
265 #elif defined(CLOCK_REALTIME)
266 #define ZIP7_USE_clock_gettime
267 // #pragma message("ZIP7_USE_clock_gettime")
268 #endif
269 
GetCurUtc_FiTime(CFiTime & ft)270 void GetCurUtc_FiTime(CFiTime &ft) throw()
271 {
272  #ifdef _WIN32
273 
274   // Both variants provide same low resolution on WinXP: about 15 ms.
275   // But GetSystemTimeAsFileTime is much faster.
276   #ifdef UNDER_CE
277   SYSTEMTIME st;
278   GetSystemTime(&st);
279   SystemTimeToFileTime(&st, &ft);
280   #else
281   GetSystemTimeAsFileTime(&ft);
282   #endif
283 
284  #else
285 
286   FiTime_Clear(ft);
287 #ifdef ZIP7_USE_timespec_get
288   timespec_get(&ft, TIME_UTC);
289 #elif defined ZIP7_USE_clock_gettime
290 
291 #if defined(_AIX)
292   {
293     timespec ts;
294     clock_gettime(CLOCK_REALTIME, &ts);
295     ft.tv_sec = ts.tv_sec;
296     ft.tv_nsec = ts.tv_nsec;
297   }
298 #else
299   clock_gettime(CLOCK_REALTIME, &ft);
300 #endif
301 
302 #else
303   struct timeval now;
304   if (gettimeofday(&now, NULL) == 0)
305   {
306     ft.tv_sec = now.tv_sec;
307     // timeval::tv_usec   can be 64-bit signed in some cases
308     // timespec::tv_nsec  can be 32-bit signed in some cases
309     ft.tv_nsec =
310       (Int32) // to eliminate compiler conversion error
311       (now.tv_usec * 1000);
312   }
313 #endif
314 
315  #endif
316 }
317 
318 #ifndef _WIN32
GetCurUtcFileTime(FILETIME & ft)319 void GetCurUtcFileTime(FILETIME &ft) throw()
320 {
321   UInt64 v = 0;
322 #if defined(ZIP7_USE_timespec_get) || \
323     defined(ZIP7_USE_clock_gettime)
324   timespec ts;
325 #if defined(ZIP7_USE_timespec_get)
326   if (timespec_get(&ts, TIME_UTC))
327 #else
328   if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
329 #endif
330   {
331     v = ((UInt64)ts.tv_sec + kUnixTimeOffset) *
332       kNumTimeQuantumsInSecond + (UInt64)ts.tv_nsec / 100;
333   }
334 #else
335   struct timeval now;
336   if (gettimeofday(&now, NULL) == 0)
337   {
338     v = ((UInt64)now.tv_sec + kUnixTimeOffset) *
339       kNumTimeQuantumsInSecond + (UInt64)now.tv_usec * 10;
340   }
341 #endif
342   ft.dwLowDateTime  = (DWORD)v;
343   ft.dwHighDateTime = (DWORD)(v >> 32);
344 }
345 #endif
346 
347 
348 }}
349 
350 
351 #ifdef _WIN32
352 
353 /*
354 void FiTime_Normalize_With_Prec(CFiTime &ft, unsigned prec)
355 {
356   if (prec == k_PropVar_TimePrec_0
357       || prec == k_PropVar_TimePrec_HighPrec
358       || prec >= k_PropVar_TimePrec_100ns)
359     return;
360   UInt64 v = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
361 
362   int numDigits = (int)prec - (int)k_PropVar_TimePrec_Base;
363   UInt32 d;
364   if (prec == k_PropVar_TimePrec_DOS)
365   {
366     // we round up as windows DosDateTimeToFileTime()
367     v += NWindows::NTime::kNumTimeQuantumsInSecond * 2 - 1;
368     d = NWindows::NTime::kNumTimeQuantumsInSecond * 2;
369   }
370   else
371   {
372     if (prec == k_PropVar_TimePrec_Unix)
373       numDigits = 0;
374     else if (numDigits < 0)
375       return;
376     d = 1;
377     for (unsigned k = numDigits; k < 7; k++)
378       d *= 10;
379   }
380   v /= d;
381   v *= d;
382   ft.dwLowDateTime = (DWORD)v;
383   ft.dwHighDateTime = (DWORD)(v >> 32);
384 }
385 */
386 
387 #else
388 
389 /*
390 void FiTime_Normalize_With_Prec(CFiTime &ft, unsigned prec)
391 {
392   if (prec >= k_PropVar_TimePrec_1ns
393       || prec == k_PropVar_TimePrec_HighPrec)
394     return;
395 
396   int numDigits = (int)prec - (int)k_PropVar_TimePrec_Base;
397   UInt32 d;
398   if (prec == k_PropVar_TimePrec_Unix ||
399       prec == (int)k_PropVar_TimePrec_Base)
400   {
401     ft.tv_nsec = 0;
402     return;
403   }
404   if (prec == k_PropVar_TimePrec_DOS)
405   {
406     // we round up as windows DosDateTimeToFileTime()
407     const unsigned sec1 = (ft.tv_sec & 1);
408     if (ft.tv_nsec == 0 && sec1 == 0)
409       return;
410     ft.tv_nsec = 0;
411     ft.tv_sec += 2 - sec1;
412     return;
413   }
414   {
415     if (prec == k_PropVar_TimePrec_0
416         || numDigits < 0)
417       numDigits = 7;
418     d = 1;
419     for (unsigned k = numDigits; k < 9; k++)
420       d *= 10;
421     ft.tv_nsec /= d;
422     ft.tv_nsec *= d;
423   }
424 }
425 */
426 
Compare_FiTime(const CFiTime * a1,const CFiTime * a2)427 int Compare_FiTime(const CFiTime *a1, const CFiTime *a2)
428 {
429   if (a1->tv_sec < a2->tv_sec) return -1;
430   if (a1->tv_sec > a2->tv_sec) return 1;
431   if (a1->tv_nsec < a2->tv_nsec) return -1;
432   if (a1->tv_nsec > a2->tv_nsec) return 1;
433   return 0;
434 }
435 
FILETIME_To_timespec(const FILETIME & ft,CFiTime & ts)436 bool FILETIME_To_timespec(const FILETIME &ft, CFiTime &ts)
437 {
438   UInt32 quantums;
439   const Int64 sec = NWindows::NTime::FileTime_To_UnixTime64_and_Quantums(ft, quantums);
440   // time_t is long
441   const time_t sec2 = (time_t)sec;
442   if (sec2 == sec)
443   {
444     ts.tv_sec = sec2;
445     ts.tv_nsec = (Int32)(quantums * 100);
446     return true;
447   }
448   return false;
449 }
450 
FiTime_To_FILETIME_ns100(const CFiTime & ts,FILETIME & ft,unsigned & ns100)451 void FiTime_To_FILETIME_ns100(const CFiTime &ts, FILETIME &ft, unsigned &ns100)
452 {
453   const UInt64 v = NWindows::NTime::UnixTime64_To_FileTime64(ts.tv_sec) + ((UInt64)ts.tv_nsec / 100);
454   ns100 = (unsigned)((UInt64)ts.tv_nsec % 100);
455   ft.dwLowDateTime = (DWORD)v;
456   ft.dwHighDateTime = (DWORD)(v >> 32);
457 }
458 
FiTime_To_FILETIME(const CFiTime & ts,FILETIME & ft)459 void FiTime_To_FILETIME(const CFiTime &ts, FILETIME &ft)
460 {
461   const UInt64 v = NWindows::NTime::UnixTime64_To_FileTime64(ts.tv_sec) + ((UInt64)ts.tv_nsec / 100);
462   ft.dwLowDateTime = (DWORD)v;
463   ft.dwHighDateTime = (DWORD)(v >> 32);
464 }
465 
466 #endif
467