2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
4 * Copyright (C) 2009 Google Inc. All rights reserved.
5 * Copyright (C) 2007-2009 Torch Mobile, Inc.
6 * Copyright (C) 2010 &yet, LLC. (nate@andyet.net)
8 * The Original Code is Mozilla Communicator client code, released
11 * The Initial Developer of the Original Code is
12 * Netscape Communications Corporation.
13 * Portions created by the Initial Developer are Copyright (C) 1998
14 * the Initial Developer. All Rights Reserved.
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 * Alternatively, the contents of this file may be used under the terms
31 * of either the Mozilla Public License Version 1.1, found at
32 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
33 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
34 * (the "GPL"), in which case the provisions of the MPL or the GPL are
35 * applicable instead of those above. If you wish to allow use of your
36 * version of this file only under the terms of one of those two
37 * licenses (the MPL or the GPL) and not to allow others to use your
38 * version of this file under the LGPL, indicate your decision by
39 * deletingthe provisions above and replace them with the notice and
40 * other provisions required by the MPL or the GPL, as the case may be.
41 * If you do not delete the provisions above, a recipient may use your
42 * version of this file under any of the LGPL, the MPL or the GPL.
44 * Copyright 2006-2008 the V8 project authors. All rights reserved.
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions are
49 * * Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * * Redistributions in binary form must reproduce the above
52 * copyright notice, this list of conditions and the following
53 * disclaimer in the documentation and/or other materials provided
54 * with the distribution.
55 * * Neither the name of Google Inc. nor the names of its
56 * contributors may be used to endorse or promote products derived
57 * from this software without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
60 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
61 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
62 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
63 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
64 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
65 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
66 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
67 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
68 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
69 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75 #include "Assertions.h"
76 #include "ASCIICType.h"
77 #include "CurrentTime.h"
78 #include "MathExtras.h"
79 #include "StdLibExtras.h"
80 #include "StringExtras.h"
88 #include "wtf/text/StringBuilder.h"
104 static const double hoursPerDay
= 24.0;
105 static const double secondsPerDay
= 24.0 * 60.0 * 60.0;
107 static const double maxUnixTime
= 2145859200.0; // 12/31/2037
109 // Day of year for the first day of each month, where index 0 is January, and day 0 is January 1.
110 // First for non-leap years, then for leap years.
111 static const int firstDayOfMonth
[2][12] = {
112 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
113 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
116 static inline void getLocalTime(const time_t* localTime
, struct tm
* localTM
)
119 localtime_s(localTM
, localTime
);
121 localtime_r(localTime
, localTM
);
125 bool isLeapYear(int year
)
136 static inline int daysInYear(int year
)
138 return 365 + isLeapYear(year
);
141 static inline double daysFrom1970ToYear(int year
)
143 // The Gregorian Calendar rules for leap years:
144 // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
145 // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
146 // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
148 static const int leapDaysBefore1971By4Rule
= 1970 / 4;
149 static const int excludedLeapDaysBefore1971By100Rule
= 1970 / 100;
150 static const int leapDaysBefore1971By400Rule
= 1970 / 400;
152 const double yearMinusOne
= year
- 1;
153 const double yearsToAddBy4Rule
= floor(yearMinusOne
/ 4.0) - leapDaysBefore1971By4Rule
;
154 const double yearsToExcludeBy100Rule
= floor(yearMinusOne
/ 100.0) - excludedLeapDaysBefore1971By100Rule
;
155 const double yearsToAddBy400Rule
= floor(yearMinusOne
/ 400.0) - leapDaysBefore1971By400Rule
;
157 return 365.0 * (year
- 1970) + yearsToAddBy4Rule
- yearsToExcludeBy100Rule
+ yearsToAddBy400Rule
;
160 static double msToDays(double ms
)
162 return floor(ms
/ msPerDay
);
165 static void appendTwoDigitNumber(StringBuilder
& builder
, int number
)
167 ASSERT(number
>= 0 && number
< 100);
170 builder
.appendNumber(number
);
173 int msToYear(double ms
)
175 int approxYear
= static_cast<int>(floor(ms
/ (msPerDay
* 365.2425)) + 1970);
176 double msFromApproxYearTo1970
= msPerDay
* daysFrom1970ToYear(approxYear
);
177 if (msFromApproxYearTo1970
> ms
)
178 return approxYear
- 1;
179 if (msFromApproxYearTo1970
+ msPerDay
* daysInYear(approxYear
) <= ms
)
180 return approxYear
+ 1;
184 int dayInYear(double ms
, int year
)
186 return static_cast<int>(msToDays(ms
) - daysFrom1970ToYear(year
));
189 static inline double msToMilliseconds(double ms
)
191 double result
= fmod(ms
, msPerDay
);
197 int monthFromDayInYear(int dayInYear
, bool leapYear
)
199 const int d
= dayInYear
;
204 step
+= (leapYear
? 29 : 28);
207 if (d
< (step
+= 31))
209 if (d
< (step
+= 30))
211 if (d
< (step
+= 31))
213 if (d
< (step
+= 30))
215 if (d
< (step
+= 31))
217 if (d
< (step
+= 31))
219 if (d
< (step
+= 30))
221 if (d
< (step
+= 31))
223 if (d
< (step
+= 30))
228 static inline bool checkMonth(int dayInYear
, int& startDayOfThisMonth
, int& startDayOfNextMonth
, int daysInThisMonth
)
230 startDayOfThisMonth
= startDayOfNextMonth
;
231 startDayOfNextMonth
+= daysInThisMonth
;
232 return (dayInYear
<= startDayOfNextMonth
);
235 int dayInMonthFromDayInYear(int dayInYear
, bool leapYear
)
237 const int d
= dayInYear
;
243 const int daysInFeb
= (leapYear
? 29 : 28);
244 if (checkMonth(d
, step
, next
, daysInFeb
))
246 if (checkMonth(d
, step
, next
, 31))
248 if (checkMonth(d
, step
, next
, 30))
250 if (checkMonth(d
, step
, next
, 31))
252 if (checkMonth(d
, step
, next
, 30))
254 if (checkMonth(d
, step
, next
, 31))
256 if (checkMonth(d
, step
, next
, 31))
258 if (checkMonth(d
, step
, next
, 30))
260 if (checkMonth(d
, step
, next
, 31))
262 if (checkMonth(d
, step
, next
, 30))
268 int dayInYear(int year
, int month
, int day
)
270 return firstDayOfMonth
[isLeapYear(year
)][month
] + day
- 1;
273 double dateToDaysFrom1970(int year
, int month
, int day
)
283 double yearday
= floor(daysFrom1970ToYear(year
));
284 ASSERT((year
>= 1970 && yearday
>= 0) || (year
< 1970 && yearday
< 0));
285 return yearday
+ dayInYear(year
, month
, day
);
288 // There is a hard limit at 2038 that we currently do not have a workaround
289 // for (rdar://problem/5052975).
290 static inline int maximumYearForDST()
295 static inline double jsCurrentTime()
297 // JavaScript doesn't recognize fractions of a millisecond.
298 return floor(WTF::currentTimeMS());
301 static inline int minimumYearForDST()
303 // Because of the 2038 issue (see maximumYearForDST) if the current year is
304 // greater than the max year minus 27 (2010), we want to use the max year
305 // minus 27 instead, to ensure there is a range of 28 years that all years
307 return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ;
311 * Find an equivalent year for the one given, where equivalence is deterined by
312 * the two years having the same leapness and the first day of the year, falling
313 * on the same day of the week.
315 * This function returns a year between this current year and 2037, however this
316 * function will potentially return incorrect results if the current year is after
317 * 2010, (rdar://problem/5052975), if the year passed in is before 1900 or after
318 * 2100, (rdar://problem/5055038).
320 static int equivalentYearForDST(int year
)
322 // It is ok if the cached year is not the current year as long as the rules
323 // for DST did not change between the two years; if they did the app would need
325 static int minYear
= minimumYearForDST();
326 int maxYear
= maximumYearForDST();
330 difference
= minYear
- year
;
331 else if (year
< minYear
)
332 difference
= maxYear
- year
;
336 int quotient
= difference
/ 28;
337 int product
= (quotient
) * 28;
340 ASSERT((year
>= minYear
&& year
<= maxYear
) || (product
- year
== static_cast<int>(std::numeric_limits
<double>::quiet_NaN())));
344 static double calculateUTCOffset()
347 TIME_ZONE_INFORMATION timeZoneInformation
;
348 GetTimeZoneInformation(&timeZoneInformation
);
349 int32_t bias
= timeZoneInformation
.Bias
+ timeZoneInformation
.StandardBias
;
350 return -bias
* 60 * 1000;
352 time_t localTime
= time(0);
354 getLocalTime(&localTime
, &localt
);
356 // tm_gmtoff includes any daylight savings offset, so subtract it.
357 return static_cast<double>(localt
.tm_gmtoff
* msPerSecond
- (localt
.tm_isdst
> 0 ? msPerHour
: 0));
362 * Get the DST offset for the time passed in.
364 static double calculateDSTOffsetSimple(double localTimeSeconds
, double utcOffset
)
366 if (localTimeSeconds
> maxUnixTime
)
367 localTimeSeconds
= maxUnixTime
;
368 else if (localTimeSeconds
< 0) // Go ahead a day to make localtime work (does not work with 0)
369 localTimeSeconds
+= secondsPerDay
;
371 // FIXME: time_t has a potential problem in 2038
372 time_t localTime
= static_cast<time_t>(localTimeSeconds
);
375 getLocalTime(&localTime
, &localTM
);
377 return localTM
.tm_isdst
> 0 ? msPerHour
: 0;
380 // Get the DST offset, given a time in UTC
381 static double calculateDSTOffset(double ms
, double utcOffset
)
383 // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate
384 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
385 // standard explicitly dictates that historical information should not be considered when
386 // determining DST. For this reason we shift away from years that localtime can handle but would
387 // return historically accurate information.
388 int year
= msToYear(ms
);
389 int equivalentYear
= equivalentYearForDST(year
);
390 if (year
!= equivalentYear
) {
391 bool leapYear
= isLeapYear(year
);
392 int dayInYearLocal
= dayInYear(ms
, year
);
393 int dayInMonth
= dayInMonthFromDayInYear(dayInYearLocal
, leapYear
);
394 int month
= monthFromDayInYear(dayInYearLocal
, leapYear
);
395 double day
= dateToDaysFrom1970(equivalentYear
, month
, dayInMonth
);
396 ms
= (day
* msPerDay
) + msToMilliseconds(ms
);
399 return calculateDSTOffsetSimple(ms
/ msPerSecond
, utcOffset
);
402 void initializeDates()
405 static bool alreadyInitialized
;
406 ASSERT(!alreadyInitialized
);
407 alreadyInitialized
= true;
410 equivalentYearForDST(2000); // Need to call once to initialize a static used in this function.
413 static inline double ymdhmsToSeconds(int year
, long mon
, long day
, long hour
, long minute
, double second
)
415 double days
= (day
- 32075)
416 + floor(1461 * (year
+ 4800.0 + (mon
- 14) / 12) / 4)
417 + 367 * (mon
- 2 - (mon
- 14) / 12 * 12) / 12
418 - floor(3 * ((year
+ 4900.0 + (mon
- 14) / 12) / 100) / 4)
420 return ((days
* hoursPerDay
+ hour
) * minutesPerHour
+ minute
) * secondsPerMinute
+ second
;
423 // We follow the recommendation of RFC 2822 to consider all
424 // obsolete time zones not listed here equivalent to "-0000".
425 static const struct KnownZone
{
444 inline static void skipSpacesAndComments(const char*& s
)
449 if (!isASCIISpace(ch
)) {
452 else if (ch
== ')' && nesting
> 0)
454 else if (nesting
== 0)
461 // returns 0-11 (Jan-Dec); -1 on failure
462 static int findMonth(const char* monthStr
)
466 for (int i
= 0; i
< 3; ++i
) {
469 needle
[i
] = static_cast<char>(toASCIILower(*monthStr
++));
472 const char *haystack
= "janfebmaraprmayjunjulaugsepoctnovdec";
473 const char *str
= strstr(haystack
, needle
);
475 int position
= static_cast<int>(str
- haystack
);
476 if (position
% 3 == 0)
482 static bool parseInt(const char* string
, char** stopPosition
, int base
, int* result
)
484 long longResult
= strtol(string
, stopPosition
, base
);
485 // Avoid the use of errno as it is not available on Windows CE
486 if (string
== *stopPosition
|| longResult
<= std::numeric_limits
<int>::min() || longResult
>= std::numeric_limits
<int>::max())
488 *result
= static_cast<int>(longResult
);
492 static bool parseLong(const char* string
, char** stopPosition
, int base
, long* result
)
494 *result
= strtol(string
, stopPosition
, base
);
495 // Avoid the use of errno as it is not available on Windows CE
496 if (string
== *stopPosition
|| *result
== std::numeric_limits
<long>::min() || *result
== std::numeric_limits
<long>::max())
501 // Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
502 static double parseDateFromNullTerminatedCharacters(const char* dateString
, bool& haveTZ
, int& offset
)
507 // This parses a date in the form:
508 // Tuesday, 09-Nov-99 23:12:40 GMT
510 // Sat, 01-Jan-2000 08:00:00 GMT
512 // Sat, 01 Jan 2000 08:00:00 GMT
514 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
515 // ### non RFC formats, added for Javascript:
516 // [Wednesday] January 09 1999 23:12:40 GMT
517 // [Wednesday] January 09 23:12:40 GMT 1999
519 // We ignore the weekday.
521 // Skip leading space
522 skipSpacesAndComments(dateString
);
525 const char *wordStart
= dateString
;
526 // Check contents of first words if not number
527 while (*dateString
&& !isASCIIDigit(*dateString
)) {
528 if (isASCIISpace(*dateString
) || *dateString
== '(') {
529 if (dateString
- wordStart
>= 3)
530 month
= findMonth(wordStart
);
531 skipSpacesAndComments(dateString
);
532 wordStart
= dateString
;
537 // Missing delimiter between month and day (like "January29")?
538 if (month
== -1 && wordStart
!= dateString
)
539 month
= findMonth(wordStart
);
541 skipSpacesAndComments(dateString
);
544 return std::numeric_limits
<double>::quiet_NaN();
546 // ' 09-Nov-99 23:12:40 GMT'
549 if (!parseLong(dateString
, &newPosStr
, 10, &day
))
550 return std::numeric_limits
<double>::quiet_NaN();
551 dateString
= newPosStr
;
554 return std::numeric_limits
<double>::quiet_NaN();
557 return std::numeric_limits
<double>::quiet_NaN();
561 // ### where is the boundary and what happens below?
562 if (*dateString
!= '/')
563 return std::numeric_limits
<double>::quiet_NaN();
564 // looks like a YYYY/MM/DD date
566 return std::numeric_limits
<double>::quiet_NaN();
567 if (day
<= std::numeric_limits
<int>::min() || day
>= std::numeric_limits
<int>::max())
568 return std::numeric_limits
<double>::quiet_NaN();
569 year
= static_cast<int>(day
);
570 if (!parseLong(dateString
, &newPosStr
, 10, &month
))
571 return std::numeric_limits
<double>::quiet_NaN();
573 dateString
= newPosStr
;
574 if (*dateString
++ != '/' || !*dateString
)
575 return std::numeric_limits
<double>::quiet_NaN();
576 if (!parseLong(dateString
, &newPosStr
, 10, &day
))
577 return std::numeric_limits
<double>::quiet_NaN();
578 dateString
= newPosStr
;
579 } else if (*dateString
== '/' && month
== -1) {
581 // This looks like a MM/DD/YYYY date, not an RFC date.
582 month
= day
- 1; // 0-based
583 if (!parseLong(dateString
, &newPosStr
, 10, &day
))
584 return std::numeric_limits
<double>::quiet_NaN();
585 if (day
< 1 || day
> 31)
586 return std::numeric_limits
<double>::quiet_NaN();
587 dateString
= newPosStr
;
588 if (*dateString
== '/')
591 return std::numeric_limits
<double>::quiet_NaN();
593 if (*dateString
== '-')
596 skipSpacesAndComments(dateString
);
598 if (*dateString
== ',')
601 if (month
== -1) { // not found yet
602 month
= findMonth(dateString
);
604 return std::numeric_limits
<double>::quiet_NaN();
606 while (*dateString
&& *dateString
!= '-' && *dateString
!= ',' && !isASCIISpace(*dateString
))
610 return std::numeric_limits
<double>::quiet_NaN();
612 // '-99 23:12:40 GMT'
613 if (*dateString
!= '-' && *dateString
!= '/' && *dateString
!= ',' && !isASCIISpace(*dateString
))
614 return std::numeric_limits
<double>::quiet_NaN();
619 if (month
< 0 || month
> 11)
620 return std::numeric_limits
<double>::quiet_NaN();
623 if (year
<= 0 && *dateString
) {
624 if (!parseInt(dateString
, &newPosStr
, 10, &year
))
625 return std::numeric_limits
<double>::quiet_NaN();
628 // Don't fail if the time is missing.
633 dateString
= newPosStr
;
636 if (!(isASCIISpace(*newPosStr
) || *newPosStr
== ',')) {
637 if (*newPosStr
!= ':')
638 return std::numeric_limits
<double>::quiet_NaN();
639 // There was no year; the number was the hour.
642 // in the normal case (we parsed the year), advance to the next number
643 dateString
= ++newPosStr
;
644 skipSpacesAndComments(dateString
);
647 parseLong(dateString
, &newPosStr
, 10, &hour
);
648 // Do not check for errno here since we want to continue
649 // even if errno was set becasue we are still looking
652 // Read a number? If not, this might be a timezone name.
653 if (newPosStr
!= dateString
) {
654 dateString
= newPosStr
;
656 if (hour
< 0 || hour
> 23)
657 return std::numeric_limits
<double>::quiet_NaN();
660 return std::numeric_limits
<double>::quiet_NaN();
663 if (*dateString
++ != ':')
664 return std::numeric_limits
<double>::quiet_NaN();
666 if (!parseLong(dateString
, &newPosStr
, 10, &minute
))
667 return std::numeric_limits
<double>::quiet_NaN();
668 dateString
= newPosStr
;
670 if (minute
< 0 || minute
> 59)
671 return std::numeric_limits
<double>::quiet_NaN();
674 if (*dateString
&& *dateString
!= ':' && !isASCIISpace(*dateString
))
675 return std::numeric_limits
<double>::quiet_NaN();
677 // seconds are optional in rfc822 + rfc2822
678 if (*dateString
==':') {
681 if (!parseLong(dateString
, &newPosStr
, 10, &second
))
682 return std::numeric_limits
<double>::quiet_NaN();
683 dateString
= newPosStr
;
685 if (second
< 0 || second
> 59)
686 return std::numeric_limits
<double>::quiet_NaN();
689 skipSpacesAndComments(dateString
);
691 if (strncasecmp(dateString
, "AM", 2) == 0) {
693 return std::numeric_limits
<double>::quiet_NaN();
697 skipSpacesAndComments(dateString
);
698 } else if (strncasecmp(dateString
, "PM", 2) == 0) {
700 return std::numeric_limits
<double>::quiet_NaN();
704 skipSpacesAndComments(dateString
);
709 // The year may be after the time but before the time zone.
710 if (isASCIIDigit(*dateString
) && year
== -1) {
711 if (!parseInt(dateString
, &newPosStr
, 10, &year
))
712 return std::numeric_limits
<double>::quiet_NaN();
713 dateString
= newPosStr
;
714 skipSpacesAndComments(dateString
);
717 // Don't fail if the time zone is missing.
718 // Some websites omit the time zone (4275206).
720 if (strncasecmp(dateString
, "GMT", 3) == 0 || strncasecmp(dateString
, "UTC", 3) == 0) {
725 if (*dateString
== '+' || *dateString
== '-') {
727 if (!parseInt(dateString
, &newPosStr
, 10, &o
))
728 return std::numeric_limits
<double>::quiet_NaN();
729 dateString
= newPosStr
;
731 if (o
< -9959 || o
> 9959)
732 return std::numeric_limits
<double>::quiet_NaN();
734 int sgn
= (o
< 0) ? -1 : 1;
736 if (*dateString
!= ':') {
738 offset
= ((o
/ 100) * 60 + (o
% 100)) * sgn
;
740 offset
= o
* 60 * sgn
;
741 } else { // GMT+05:00
742 ++dateString
; // skip the ':'
744 if (!parseInt(dateString
, &newPosStr
, 10, &o2
))
745 return std::numeric_limits
<double>::quiet_NaN();
746 dateString
= newPosStr
;
747 offset
= (o
* 60 + o2
) * sgn
;
751 for (size_t i
= 0; i
< WTF_ARRAY_LENGTH(known_zones
); ++i
) {
752 if (0 == strncasecmp(dateString
, known_zones
[i
].tzName
, strlen(known_zones
[i
].tzName
))) {
753 offset
= known_zones
[i
].tzOffset
;
754 dateString
+= strlen(known_zones
[i
].tzName
);
762 skipSpacesAndComments(dateString
);
764 if (*dateString
&& year
== -1) {
765 if (!parseInt(dateString
, &newPosStr
, 10, &year
))
766 return std::numeric_limits
<double>::quiet_NaN();
767 dateString
= newPosStr
;
768 skipSpacesAndComments(dateString
);
773 return std::numeric_limits
<double>::quiet_NaN();
775 // Y2K: Handle 2 digit years.
776 if (year
>= 0 && year
< 100) {
783 return ymdhmsToSeconds(year
, month
+ 1, day
, hour
, minute
, second
) * msPerSecond
;
786 double parseDateFromNullTerminatedCharacters(const char* dateString
)
790 double ms
= parseDateFromNullTerminatedCharacters(dateString
, haveTZ
, offset
);
792 return std::numeric_limits
<double>::quiet_NaN();
794 // fall back to local timezone
796 double utcOffset
= calculateUTCOffset();
797 double dstOffset
= calculateDSTOffset(ms
, utcOffset
);
798 offset
= static_cast<int>((utcOffset
+ dstOffset
) / msPerMinute
);
800 return ms
- (offset
* msPerMinute
);
803 // See http://tools.ietf.org/html/rfc2822#section-3.3 for more information.
804 String
makeRFC2822DateString(unsigned dayOfWeek
, unsigned day
, unsigned month
, unsigned year
, unsigned hours
, unsigned minutes
, unsigned seconds
, int utcOffset
)
806 StringBuilder stringBuilder
;
807 stringBuilder
.append(weekdayName
[dayOfWeek
]);
808 stringBuilder
.appendLiteral(", ");
809 stringBuilder
.appendNumber(day
);
810 stringBuilder
.append(' ');
811 stringBuilder
.append(monthName
[month
]);
812 stringBuilder
.append(' ');
813 stringBuilder
.appendNumber(year
);
814 stringBuilder
.append(' ');
816 appendTwoDigitNumber(stringBuilder
, hours
);
817 stringBuilder
.append(':');
818 appendTwoDigitNumber(stringBuilder
, minutes
);
819 stringBuilder
.append(':');
820 appendTwoDigitNumber(stringBuilder
, seconds
);
821 stringBuilder
.append(' ');
823 stringBuilder
.append(utcOffset
> 0 ? '+' : '-');
824 int absoluteUTCOffset
= abs(utcOffset
);
825 appendTwoDigitNumber(stringBuilder
, absoluteUTCOffset
/ 60);
826 appendTwoDigitNumber(stringBuilder
, absoluteUTCOffset
% 60);
828 return stringBuilder
.toString();
831 double convertToLocalTime(double ms
)
833 double utcOffset
= calculateUTCOffset();
834 double dstOffset
= calculateDSTOffset(ms
, utcOffset
);
835 return (ms
+ utcOffset
+ dstOffset
);