Time.cpp (6656B)
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/. */ 6 7 /* PR time code. */ 8 9 #include "vm/Time.h" 10 11 #ifdef SOLARIS 12 # define _REENTRANT 1 13 #endif 14 #include <string.h> 15 #include <time.h> 16 17 #include "jstypes.h" 18 19 #ifdef XP_WIN 20 # include "util/WindowsWrapper.h" 21 # include <crtdbg.h> /* for _CrtSetReportMode */ 22 # include <stdlib.h> /* for _set_invalid_parameter_handler */ 23 #endif 24 25 #ifdef XP_UNIX 26 27 # ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ 28 extern int gettimeofday(struct timeval* tv); 29 # endif 30 31 # include <sys/time.h> 32 33 #endif /* XP_UNIX */ 34 35 #if defined(XP_UNIX) 36 int64_t PRMJ_Now() { 37 struct timeval tv; 38 39 # ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ 40 gettimeofday(&tv); 41 # else 42 gettimeofday(&tv, 0); 43 # endif /* _SVID_GETTOD */ 44 45 return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec); 46 } 47 48 #else 49 50 // Returns the number of microseconds since the Unix epoch. 51 static int64_t FileTimeToUnixMicroseconds(const FILETIME& ft) { 52 // Get the time in 100ns intervals. 53 int64_t t = (int64_t(ft.dwHighDateTime) << 32) | int64_t(ft.dwLowDateTime); 54 55 // The Windows epoch is around 1600. The Unix epoch is around 1970. 56 // Subtract the difference. 57 static const int64_t TimeToEpochIn100ns = 0x19DB1DED53E8000; 58 t -= TimeToEpochIn100ns; 59 60 // Divide by 10 to convert to microseconds. 61 return t / 10; 62 } 63 64 int64_t PRMJ_Now() { 65 FILETIME ft; 66 GetSystemTimePreciseAsFileTime(&ft); 67 return FileTimeToUnixMicroseconds(ft); 68 } 69 #endif 70 71 #if !JS_HAS_INTL_API 72 # ifdef XP_WIN 73 static void PRMJ_InvalidParameterHandler(const wchar_t* expression, 74 const wchar_t* function, 75 const wchar_t* file, unsigned int line, 76 uintptr_t pReserved) { 77 /* empty */ 78 } 79 # endif 80 81 /* Format a time value into a buffer. Same semantics as strftime() */ 82 size_t PRMJ_FormatTime(char* buf, size_t buflen, const char* fmt, 83 const PRMJTime* prtm, int timeZoneYear, 84 int offsetInSeconds) { 85 size_t result = 0; 86 # if defined(XP_UNIX) || defined(XP_WIN) 87 struct tm a; 88 # ifdef XP_WIN 89 _invalid_parameter_handler oldHandler; 90 # ifndef __MINGW32__ 91 int oldReportMode; 92 # endif // __MINGW32__ 93 # endif // XP_WIN 94 95 memset(&a, 0, sizeof(struct tm)); 96 97 a.tm_sec = prtm->tm_sec; 98 a.tm_min = prtm->tm_min; 99 a.tm_hour = prtm->tm_hour; 100 a.tm_mday = prtm->tm_mday; 101 a.tm_mon = prtm->tm_mon; 102 a.tm_wday = prtm->tm_wday; 103 104 /* 105 * On systems where |struct tm| has members tm_gmtoff and tm_zone, we 106 * must fill in those values, or else strftime will return wrong results 107 * (e.g., bug 511726, bug 554338). 108 */ 109 # if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF) 110 char emptyTimeZoneId[] = ""; 111 { 112 /* 113 * Fill out |td| to the time represented by |prtm|, leaving the 114 * timezone fields zeroed out. localtime_r will then fill in the 115 * timezone fields for that local time according to the system's 116 * timezone parameters. Use |timeZoneYear| for the year to ensure the 117 * time zone name matches the time zone offset used by the caller. 118 */ 119 struct tm td; 120 memset(&td, 0, sizeof(td)); 121 td.tm_sec = prtm->tm_sec; 122 td.tm_min = prtm->tm_min; 123 td.tm_hour = prtm->tm_hour; 124 td.tm_mday = prtm->tm_mday; 125 td.tm_mon = prtm->tm_mon; 126 td.tm_wday = prtm->tm_wday; 127 td.tm_year = timeZoneYear - 1900; 128 td.tm_yday = prtm->tm_yday; 129 td.tm_isdst = prtm->tm_isdst; 130 131 time_t t = mktime(&td); 132 133 // If either mktime or localtime_r failed, fill in the fallback time 134 // zone offset |offsetInSeconds| and set the time zone identifier to 135 // the empty string. 136 if (t != static_cast<time_t>(-1) && localtime_r(&t, &td)) { 137 a.tm_gmtoff = td.tm_gmtoff; 138 a.tm_zone = td.tm_zone; 139 } else { 140 a.tm_gmtoff = offsetInSeconds; 141 a.tm_zone = emptyTimeZoneId; 142 } 143 } 144 # endif 145 146 /* 147 * Years before 1900 and after 9999 cause strftime() to abort on Windows. 148 * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then 149 * replace matching substrings in the strftime() result with the real year. 150 * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit 151 * year formats (%y) work correctly (since we won't find the fake year 152 * in that case). 153 */ 154 constexpr int FAKE_YEAR_BASE = 9900; 155 int fake_tm_year = 0; 156 if (prtm->tm_year < 1900 || prtm->tm_year > 9999) { 157 fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100; 158 a.tm_year = fake_tm_year - 1900; 159 } else { 160 a.tm_year = prtm->tm_year - 1900; 161 } 162 a.tm_yday = prtm->tm_yday; 163 a.tm_isdst = prtm->tm_isdst; 164 165 /* 166 * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff 167 * are null. This doesn't quite work, though - the timezone is off by 168 * tzoff + dst. (And mktime seems to return -1 for the exact dst 169 * changeover time.) 170 */ 171 172 # ifdef XP_WIN 173 oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler); 174 # ifndef __MINGW32__ 175 /* 176 * MinGW doesn't have _CrtSetReportMode and defines it to be a no-op. 177 * We ifdef it off to avoid warnings about unused variables 178 */ 179 oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0); 180 # endif // __MINGW32__ 181 # endif // XP_WIN 182 183 result = strftime(buf, buflen, fmt, &a); 184 185 # ifdef XP_WIN 186 _set_invalid_parameter_handler(oldHandler); 187 # ifndef __MINGW32__ 188 _CrtSetReportMode(_CRT_ASSERT, oldReportMode); 189 # endif // __MINGW32__ 190 # endif // XP_WIN 191 192 if (fake_tm_year && result) { 193 char real_year[16]; 194 char fake_year[16]; 195 size_t real_year_len; 196 size_t fake_year_len; 197 char* p; 198 199 sprintf(real_year, "%d", prtm->tm_year); 200 real_year_len = strlen(real_year); 201 sprintf(fake_year, "%d", fake_tm_year); 202 fake_year_len = strlen(fake_year); 203 204 /* Replace the fake year in the result with the real year. */ 205 for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) { 206 size_t new_result = result + real_year_len - fake_year_len; 207 if (new_result >= buflen) { 208 return 0; 209 } 210 memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len)); 211 memcpy(p, real_year, real_year_len); 212 result = new_result; 213 *(buf + result) = '\0'; 214 } 215 } 216 # endif 217 return result; 218 } 219 #endif /* !JS_HAS_INTL_API */