2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
5 * The Original Code is Mozilla Communicator client code, released
8 * The Initial Developer of the Original Code is
9 * Netscape Communications Corporation.
10 * Portions created by the Initial Developer are Copyright (C) 1998
11 * the Initial Developer. All Rights Reserved.
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 * Alternatively, the contents of this file may be used under the terms
28 * of either the Mozilla Public License Version 1.1, found at
29 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
30 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
31 * (the "GPL"), in which case the provisions of the MPL or the GPL are
32 * applicable instead of those above. If you wish to allow use of your
33 * version of this file only under the terms of one of those two
34 * licenses (the MPL or the GPL) and not to allow others to use your
35 * version of this file under the LGPL, indicate your decision by
36 * deletingthe provisions above and replace them with the notice and
37 * other provisions required by the MPL or the GPL, as the case may be.
38 * If you do not delete the provisions above, a recipient may use your
39 * version of this file under any of the LGPL, the MPL or the GPL.
49 #include <wtf/Assertions.h>
60 #include <sys/timeb.h>
67 static const double minutesPerDay
= 24.0 * 60.0;
68 static const double secondsPerDay
= 24.0 * 60.0 * 60.0;
69 static const double secondsPerYear
= 24.0 * 60.0 * 60.0 * 365.0;
71 static const double usecPerSec
= 1000000.0;
73 static const double maxUnixTime
= 2145859200.0; // 12/31/2037
75 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
76 // First for non-leap years, then for leap years.
77 static const int firstDayOfMonth
[2][12] = {
78 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
79 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
82 static inline bool isLeapYear(int year
)
93 static inline int daysInYear(int year
)
95 return 365 + isLeapYear(year
);
98 static inline double daysFrom1970ToYear(int year
)
100 // The Gregorian Calendar rules for leap years:
101 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
102 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
103 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
105 static const int leapDaysBefore1971By4Rule
= 1970 / 4;
106 static const int excludedLeapDaysBefore1971By100Rule
= 1970 / 100;
107 static const int leapDaysBefore1971By400Rule
= 1970 / 400;
109 const double yearMinusOne
= year
- 1;
110 const double yearsToAddBy4Rule
= floor(yearMinusOne
/ 4.0) - leapDaysBefore1971By4Rule
;
111 const double yearsToExcludeBy100Rule
= floor(yearMinusOne
/ 100.0) - excludedLeapDaysBefore1971By100Rule
;
112 const double yearsToAddBy400Rule
= floor(yearMinusOne
/ 400.0) - leapDaysBefore1971By400Rule
;
114 return 365.0 * (year
- 1970) + yearsToAddBy4Rule
- yearsToExcludeBy100Rule
+ yearsToAddBy400Rule
;
117 static inline double msToDays(double ms
)
119 return floor(ms
/ msPerDay
);
122 static inline int msToYear(double ms
)
124 int approxYear
= static_cast<int>(floor(ms
/ (msPerDay
* 365.2425)) + 1970);
125 double msFromApproxYearTo1970
= msPerDay
* daysFrom1970ToYear(approxYear
);
126 if (msFromApproxYearTo1970
> ms
)
127 return approxYear
- 1;
128 if (msFromApproxYearTo1970
+ msPerDay
* daysInYear(approxYear
) <= ms
)
129 return approxYear
+ 1;
133 static inline int dayInYear(double ms
, int year
)
135 return static_cast<int>(msToDays(ms
) - daysFrom1970ToYear(year
));
138 static inline double msToMilliseconds(double ms
)
140 double result
= fmod(ms
, msPerDay
);
146 // 0: Sunday, 1: Monday, etc.
147 static inline int msToWeekDay(double ms
)
149 int wd
= (static_cast<int>(msToDays(ms
)) + 4) % 7;
155 static inline int msToSeconds(double ms
)
157 double result
= fmod(floor(ms
/ msPerSecond
), secondsPerMinute
);
159 result
+= secondsPerMinute
;
160 return static_cast<int>(result
);
163 static inline int msToMinutes(double ms
)
165 double result
= fmod(floor(ms
/ msPerMinute
), minutesPerHour
);
167 result
+= minutesPerHour
;
168 return static_cast<int>(result
);
171 static inline int msToHours(double ms
)
173 double result
= fmod(floor(ms
/msPerHour
), hoursPerDay
);
175 result
+= hoursPerDay
;
176 return static_cast<int>(result
);
179 static inline int monthFromDayInYear(int dayInYear
, bool leapYear
)
181 const int d
= dayInYear
;
186 step
+= (leapYear
? 29 : 28);
189 if (d
< (step
+= 31))
191 if (d
< (step
+= 30))
193 if (d
< (step
+= 31))
195 if (d
< (step
+= 30))
197 if (d
< (step
+= 31))
199 if (d
< (step
+= 31))
201 if (d
< (step
+= 30))
203 if (d
< (step
+= 31))
205 if (d
< (step
+= 30))
210 static inline bool checkMonth(int dayInYear
, int& startDayOfThisMonth
, int& startDayOfNextMonth
, int daysInThisMonth
)
212 startDayOfThisMonth
= startDayOfNextMonth
;
213 startDayOfNextMonth
+= daysInThisMonth
;
214 return (dayInYear
<= startDayOfNextMonth
);
217 static inline int dayInMonthFromDayInYear(int dayInYear
, bool leapYear
)
219 const int d
= dayInYear
;
225 const int daysInFeb
= (leapYear
? 29 : 28);
226 if (checkMonth(d
, step
, next
, daysInFeb
))
228 if (checkMonth(d
, step
, next
, 31))
230 if (checkMonth(d
, step
, next
, 30))
232 if (checkMonth(d
, step
, next
, 31))
234 if (checkMonth(d
, step
, next
, 30))
236 if (checkMonth(d
, step
, next
, 31))
238 if (checkMonth(d
, step
, next
, 31))
240 if (checkMonth(d
, step
, next
, 30))
242 if (checkMonth(d
, step
, next
, 31))
244 if (checkMonth(d
, step
, next
, 30))
250 static inline int monthToDayInYear(int month
, bool isLeapYear
)
252 return firstDayOfMonth
[isLeapYear
][month
];
255 static inline double timeToMS(double hour
, double min
, double sec
, double ms
)
257 return (((hour
* minutesPerHour
+ min
) * secondsPerMinute
+ sec
) * msPerSecond
+ ms
);
260 static int dateToDayInYear(int year
, int month
, int day
)
270 int yearday
= static_cast<int>(floor(daysFrom1970ToYear(year
)));
271 int monthday
= monthToDayInYear(month
, isLeapYear(year
));
273 return yearday
+ monthday
+ day
- 1;
276 double getCurrentUTCTime()
279 #if COMPILER(BORLAND)
280 struct timeb timebuffer
;
283 struct _timeb timebuffer
;
286 double utc
= timebuffer
.time
* msPerSecond
+ timebuffer
.millitm
;
289 gettimeofday(&tv
, 0);
290 double utc
= floor(tv
.tv_sec
* msPerSecond
+ tv
.tv_usec
/ 1000);
295 // There is a hard limit at 2038 that we currently do not have a workaround
296 // for (rdar://problem/5052975).
297 static inline int maximumYearForDST()
302 // It is ok if the cached year is not the current year (e.g. Dec 31st)
303 // so long as the rules for DST did not change between the two years, if it does
304 // the app would need to be restarted.
305 static int mimimumYearForDST()
307 // Because of the 2038 issue (see maximumYearForDST) if the current year is
308 // greater than the max year minus 27 (2010), we want to use the max year
309 // minus 27 instead, to ensure there is a range of 28 years that all years
311 static int minYear
= std::min(msToYear(getCurrentUTCTime()), maximumYearForDST() - 27) ;
316 * Find an equivalent year for the one given, where equivalence is deterined by
317 * the two years having the same leapness and the first day of the year, falling
318 * on the same day of the week.
320 * This function returns a year between this current year and 2037, however this
321 * function will potentially return incorrect results if the current year is after
322 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
323 * 2100, (rdar://problem/5055038).
325 int equivalentYearForDST(int year
)
327 static int minYear
= mimimumYearForDST();
328 static int maxYear
= maximumYearForDST();
332 difference
= minYear
- year
;
333 else if (year
< minYear
)
334 difference
= maxYear
- year
;
338 int quotient
= difference
/ 28;
339 int product
= (quotient
) * 28;
342 ASSERT((year
>= minYear
&& year
<= maxYear
) || (product
- year
== static_cast<int>(NaN
)));
347 * Get the difference in milliseconds between this time zone and UTC (GMT)
350 double getUTCOffset()
353 // Register for a notification whenever the time zone changes.
354 static bool triedToRegister
= false;
355 static bool haveNotificationToken
= false;
356 static int notificationToken
;
357 if (!triedToRegister
) {
358 triedToRegister
= true;
359 uint32_t status
= notify_register_check("com.apple.system.timezone", ¬ificationToken
);
360 if (status
== NOTIFY_STATUS_OK
)
361 haveNotificationToken
= true;
364 // If we can verify that we have not received a time zone notification,
365 // then use the cached offset from the last time this function was called.
366 static bool haveCachedOffset
= false;
367 static double cachedOffset
;
368 if (haveNotificationToken
&& haveCachedOffset
) {
370 uint32_t status
= notify_check(notificationToken
, ¬ified
);
371 if (status
== NOTIFY_STATUS_OK
&& !notified
)
378 memset(&localt
, 0, sizeof(localt
));
380 // get the difference between this time zone and UTC on Jan 01, 2000 12:00:00 AM
382 localt
.tm_year
= 100;
383 double utcOffset
= 946684800.0 - mktime(&localt
);
385 utcOffset
*= msPerSecond
;
388 haveCachedOffset
= true;
389 cachedOffset
= utcOffset
;
396 * Get the DST offset for the time passed in. Takes
397 * seconds (not milliseconds) and cannot handle dates before 1970
400 static double getDSTOffsetSimple(double localTimeSeconds
, double utcOffset
)
402 if (localTimeSeconds
> maxUnixTime
)
403 localTimeSeconds
= maxUnixTime
;
404 else if (localTimeSeconds
< 0) // Go ahead a day to make localtime work (does not work with 0)
405 localTimeSeconds
+= secondsPerDay
;
407 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
408 double offsetTime
= (localTimeSeconds
* msPerSecond
) + utcOffset
;
410 // Offset from UTC but doesn't include DST obviously
411 int offsetHour
= msToHours(offsetTime
);
412 int offsetMinute
= msToMinutes(offsetTime
);
414 // FIXME: time_t has a potential problem in 2038
415 time_t localTime
= static_cast<time_t>(localTimeSeconds
);
419 // ### this is not threadsafe but we don't use multiple threads anyway
421 #if USE(MULTIPLE_THREADS)
422 #error Mulitple threads are currently not supported in the Qt/mingw build
424 localTM
= *localtime(&localTime
);
425 #elif PLATFORM(WIN_OS)
427 localTM
= *localtime(&localTime
);
429 localtime_s(&localTM
, &localTime
);
432 localtime_r(&localTime
, &localTM
);
435 double diff
= ((localTM
.tm_hour
- offsetHour
) * secondsPerHour
) + ((localTM
.tm_min
- offsetMinute
) * 60);
438 diff
+= secondsPerDay
;
440 return (diff
* msPerSecond
);
443 // Get the DST offset, given a time in UTC
444 static double getDSTOffset(double ms
, double utcOffset
)
446 // On Mac OS X, the call to localtime (see getDSTOffsetSimple) will return historically accurate
447 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
448 // standard explicitly dictates that historical information should not be considered when
449 // determining DST. For this reason we shift away from years that localtime can handle but would
450 // return historically accurate information.
451 int year
= msToYear(ms
);
452 int equivalentYear
= equivalentYearForDST(year
);
453 if (year
!= equivalentYear
) {
454 bool leapYear
= isLeapYear(year
);
455 int dayInYearLocal
= dayInYear(ms
, year
);
456 int dayInMonth
= dayInMonthFromDayInYear(dayInYearLocal
, leapYear
);
457 int month
= monthFromDayInYear(dayInYearLocal
, leapYear
);
458 int day
= dateToDayInYear(equivalentYear
, month
, dayInMonth
);
459 ms
= (day
* msPerDay
) + msToMilliseconds(ms
);
462 return getDSTOffsetSimple(ms
/ msPerSecond
, utcOffset
);
465 double gregorianDateTimeToMS(const GregorianDateTime
& t
, double milliSeconds
, bool inputIsUTC
)
467 int day
= dateToDayInYear(t
.year
+ 1900, t
.month
, t
.monthDay
);
468 double ms
= timeToMS(t
.hour
, t
.minute
, t
.second
, milliSeconds
);
469 double result
= (day
* msPerDay
) + ms
;
471 if (!inputIsUTC
) { // convert to UTC
472 double utcOffset
= getUTCOffset();
474 result
-= getDSTOffset(result
, utcOffset
);
480 void msToGregorianDateTime(double ms
, bool outputIsUTC
, GregorianDateTime
& tm
)
484 const double utcOff
= getUTCOffset();
486 if (!outputIsUTC
) { // convert to local time
487 dstOff
= getDSTOffset(ms
, utcOff
);
488 ms
+= dstOff
+ utcOff
;
491 const int year
= msToYear(ms
);
492 tm
.second
= msToSeconds(ms
);
493 tm
.minute
= msToMinutes(ms
);
494 tm
.hour
= msToHours(ms
);
495 tm
.weekDay
= msToWeekDay(ms
);
496 tm
.yearDay
= dayInYear(ms
, year
);
497 tm
.monthDay
= dayInMonthFromDayInYear(tm
.yearDay
, isLeapYear(year
));
498 tm
.month
= monthFromDayInYear(tm
.yearDay
, isLeapYear(year
));
499 tm
.year
= year
- 1900;
500 tm
.isDST
= dstOff
!= 0.0;
502 tm
.utcOffset
= static_cast<long>((dstOff
+ utcOff
) / msPerSecond
);