1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
22 # include <crtdbg.h> /* for _CrtSetReportMode */
23 # include <stdlib.h> /* for _set_invalid_parameter_handler */
28 # ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
29 extern int gettimeofday(struct timeval
* tv
);
32 # include <sys/time.h>
40 # ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
44 # endif /* _SVID_GETTOD */
46 return int64_t(tv
.tv_sec
) * PRMJ_USEC_PER_SEC
+ int64_t(tv
.tv_usec
);
51 # if _WIN32_WINNT < _WIN32_WINNT_WIN8
52 extern "C" WINBASEAPI
void WINAPI
GetSystemTimePreciseAsFileTime(LPFILETIME
);
55 // Returns the number of microseconds since the Unix epoch.
56 static int64_t FileTimeToUnixMicroseconds(const FILETIME
& ft
) {
57 // Get the time in 100ns intervals.
58 int64_t t
= (int64_t(ft
.dwHighDateTime
) << 32) | int64_t(ft
.dwLowDateTime
);
60 // The Windows epoch is around 1600. The Unix epoch is around 1970.
61 // Subtract the difference.
62 static const int64_t TimeToEpochIn100ns
= 0x19DB1DED53E8000;
63 t
-= TimeToEpochIn100ns
;
65 // Divide by 10 to convert to microseconds.
71 GetSystemTimePreciseAsFileTime(&ft
);
72 return FileTimeToUnixMicroseconds(ft
);
78 static void PRMJ_InvalidParameterHandler(const wchar_t* expression
,
79 const wchar_t* function
,
80 const wchar_t* file
, unsigned int line
,
81 uintptr_t pReserved
) {
86 /* Format a time value into a buffer. Same semantics as strftime() */
87 size_t PRMJ_FormatTime(char* buf
, size_t buflen
, const char* fmt
,
88 const PRMJTime
* prtm
, int timeZoneYear
,
89 int offsetInSeconds
) {
91 # if defined(XP_UNIX) || defined(XP_WIN)
94 _invalid_parameter_handler oldHandler
;
97 # endif // __MINGW32__
100 memset(&a
, 0, sizeof(struct tm
));
102 a
.tm_sec
= prtm
->tm_sec
;
103 a
.tm_min
= prtm
->tm_min
;
104 a
.tm_hour
= prtm
->tm_hour
;
105 a
.tm_mday
= prtm
->tm_mday
;
106 a
.tm_mon
= prtm
->tm_mon
;
107 a
.tm_wday
= prtm
->tm_wday
;
110 * On systems where |struct tm| has members tm_gmtoff and tm_zone, we
111 * must fill in those values, or else strftime will return wrong results
112 * (e.g., bug 511726, bug 554338).
114 # if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF)
115 char emptyTimeZoneId
[] = "";
118 * Fill out |td| to the time represented by |prtm|, leaving the
119 * timezone fields zeroed out. localtime_r will then fill in the
120 * timezone fields for that local time according to the system's
121 * timezone parameters. Use |timeZoneYear| for the year to ensure the
122 * time zone name matches the time zone offset used by the caller.
125 memset(&td
, 0, sizeof(td
));
126 td
.tm_sec
= prtm
->tm_sec
;
127 td
.tm_min
= prtm
->tm_min
;
128 td
.tm_hour
= prtm
->tm_hour
;
129 td
.tm_mday
= prtm
->tm_mday
;
130 td
.tm_mon
= prtm
->tm_mon
;
131 td
.tm_wday
= prtm
->tm_wday
;
132 td
.tm_year
= timeZoneYear
- 1900;
133 td
.tm_yday
= prtm
->tm_yday
;
134 td
.tm_isdst
= prtm
->tm_isdst
;
136 time_t t
= mktime(&td
);
138 // If either mktime or localtime_r failed, fill in the fallback time
139 // zone offset |offsetInSeconds| and set the time zone identifier to
141 if (t
!= static_cast<time_t>(-1) && localtime_r(&t
, &td
)) {
142 a
.tm_gmtoff
= td
.tm_gmtoff
;
143 a
.tm_zone
= td
.tm_zone
;
145 a
.tm_gmtoff
= offsetInSeconds
;
146 a
.tm_zone
= emptyTimeZoneId
;
152 * Years before 1900 and after 9999 cause strftime() to abort on Windows.
153 * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then
154 * replace matching substrings in the strftime() result with the real year.
155 * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit
156 * year formats (%y) work correctly (since we won't find the fake year
159 constexpr int FAKE_YEAR_BASE
= 9900;
160 int fake_tm_year
= 0;
161 if (prtm
->tm_year
< 1900 || prtm
->tm_year
> 9999) {
162 fake_tm_year
= FAKE_YEAR_BASE
+ prtm
->tm_year
% 100;
163 a
.tm_year
= fake_tm_year
- 1900;
165 a
.tm_year
= prtm
->tm_year
- 1900;
167 a
.tm_yday
= prtm
->tm_yday
;
168 a
.tm_isdst
= prtm
->tm_isdst
;
171 * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
172 * are null. This doesn't quite work, though - the timezone is off by
173 * tzoff + dst. (And mktime seems to return -1 for the exact dst
178 oldHandler
= _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler
);
181 * MinGW doesn't have _CrtSetReportMode and defines it to be a no-op.
182 * We ifdef it off to avoid warnings about unused variables
184 oldReportMode
= _CrtSetReportMode(_CRT_ASSERT
, 0);
185 # endif // __MINGW32__
188 result
= strftime(buf
, buflen
, fmt
, &a
);
191 _set_invalid_parameter_handler(oldHandler
);
193 _CrtSetReportMode(_CRT_ASSERT
, oldReportMode
);
194 # endif // __MINGW32__
197 if (fake_tm_year
&& result
) {
200 size_t real_year_len
;
201 size_t fake_year_len
;
204 sprintf(real_year
, "%d", prtm
->tm_year
);
205 real_year_len
= strlen(real_year
);
206 sprintf(fake_year
, "%d", fake_tm_year
);
207 fake_year_len
= strlen(fake_year
);
209 /* Replace the fake year in the result with the real year. */
210 for (p
= buf
; (p
= strstr(p
, fake_year
)); p
+= real_year_len
) {
211 size_t new_result
= result
+ real_year_len
- fake_year_len
;
212 if (new_result
>= buflen
) {
215 memmove(p
+ real_year_len
, p
+ fake_year_len
, strlen(p
+ fake_year_len
));
216 memcpy(p
, real_year
, real_year_len
);
218 *(buf
+ result
) = '\0';
224 #endif /* !JS_HAS_INTL_API */