22 #include "date_object.h"
24 #include "date_object.lut.h"
32 #include <sys/param.h>
40 #include <sys/timeb.h>
52 #if PLATFORM(SOLARIS_OS)
56 #include "error_object.h"
57 #include "operations.h"
60 #include <CoreFoundation/CoreFoundation.h>
65 #if defined(WTF_COMPILER_MSVC)
66 #define copysign(x, y) _copysign(x, y)
67 #define snprintf _snprintf
69 #if !defined(WTF_COMPILER_GCC)
71 #define strncasecmp(x, y, z) strnicmp(x, y, z)
76 #include "wtf/DisallowCType.h"
77 #include "wtf/ASCIICType.h"
88 inline int gmtoffset(
const tm &t)
93 return -(_timezone / 60 - (t.tm_isdst > 0 ? 60 : 0)) * 60;
112 class DateObjectFuncImp :
public InternalFunctionImp
115 DateObjectFuncImp(ExecState *, FunctionPrototype *,
int i,
int len,
const Identifier &);
117 JSValue *
callAsFunction(ExecState *, JSObject *thisObj,
const List &args)
override;
119 enum { Parse,
UTC, Now };
126 const double hoursPerDay = 24;
127 const double minutesPerHour = 60;
128 const double secondsPerMinute = 60;
129 const double msPerSecond = 1000;
130 const double msPerMinute = 60 * 1000;
131 const double msPerHour = 60 * 60 * 1000;
132 const double msPerDay = 24 * 60 * 60 * 1000;
134 static const char *
const weekdayName[7] = {
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun" };
135 static const char *
const monthName[12] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec" };
137 static double makeTime(tm *,
double ms,
bool utc);
138 static double parseDate(
const UString &);
139 static double timeClip(
double);
140 static void millisecondsToTM(
double milli,
bool utc, tm *t);
144 static CFDateFormatterStyle styleFromArgString(
const UString &
string, CFDateFormatterStyle defaultStyle)
146 if (
string ==
"short") {
147 return kCFDateFormatterShortStyle;
149 if (
string ==
"medium") {
150 return kCFDateFormatterMediumStyle;
152 if (
string ==
"long") {
153 return kCFDateFormatterLongStyle;
155 if (
string ==
"full") {
156 return kCFDateFormatterFullStyle;
161 static UString formatLocaleDate(ExecState *exec,
double time,
bool includeDate,
bool includeTime,
const List &args)
163 CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
164 CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
166 bool useCustomFormat =
false;
167 UString customFormatString;
169 JSValue *arg0 = args[0];
170 JSValue *arg1 = args[1];
171 UString arg0String = arg0->toString(exec);
172 if (arg0String ==
"custom" && !arg1->isUndefined()) {
173 useCustomFormat =
true;
174 customFormatString = arg1->toString(exec);
175 }
else if (includeDate && includeTime && !arg1->isUndefined()) {
176 dateStyle = styleFromArgString(arg0String, dateStyle);
177 timeStyle = styleFromArgString(arg1->toString(exec), timeStyle);
178 }
else if (includeDate && !arg0->isUndefined()) {
179 dateStyle = styleFromArgString(arg0String, dateStyle);
180 }
else if (includeTime && !arg0->isUndefined()) {
181 timeStyle = styleFromArgString(arg0String, timeStyle);
184 CFLocaleRef
locale = CFLocaleCopyCurrent();
185 CFDateFormatterRef formatter = CFDateFormatterCreate(0, locale, dateStyle, timeStyle);
188 if (useCustomFormat) {
189 CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, (UniChar *)customFormatString.data(), customFormatString.size());
190 CFDateFormatterSetFormat(formatter, customFormatCFString);
191 CFRelease(customFormatCFString);
194 CFStringRef
string = CFDateFormatterCreateStringWithAbsoluteTime(0, formatter, time - kCFAbsoluteTimeIntervalSince1970);
196 CFRelease(formatter);
201 const size_t bufferLength =
sizeof(buffer) /
sizeof(buffer[0]);
202 size_t length = CFStringGetLength(
string);
203 assert(length <= bufferLength);
204 if (length > bufferLength) {
205 length = bufferLength;
207 CFStringGetCharacters(
string, CFRangeMake(0, length),
reinterpret_cast<UniChar *
>(buffer));
211 return UString(buffer, length);
214 #endif // PLATFORM(MAC)
216 static UString formatDate(
const tm &t)
219 int len = snprintf(buffer,
sizeof(buffer),
"%s %s %02d %04d",
220 weekdayName[(t.tm_wday + 6) % 7],
221 monthName[t.tm_mon], t.tm_mday, t.tm_year + 1900);
222 return UString(buffer, len);
225 static UString formatDateUTCVariant(
const tm &t)
228 int len = snprintf(buffer,
sizeof(buffer),
"%s, %02d %s %04d",
229 weekdayName[(t.tm_wday + 6) % 7],
230 t.tm_mday, monthName[t.tm_mon], t.tm_year + 1900);
231 return UString(buffer, len);
234 static UString formatDateISOVariant(
const tm &t,
bool utc,
double absoluteMS)
240 len = snprintf(buffer,
sizeof(buffer),
"%04d-%02d-%02d",
241 t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
243 int offset = gmtoffset(t);
245 millisecondsToTM(absoluteMS - offset * 1000,
true, &t_fixed);
246 len = snprintf(buffer,
sizeof(buffer),
"%04d-%02d-%02d",
247 t_fixed.tm_year + 1900, t_fixed.tm_mon + 1, t_fixed.tm_mday);
249 return UString(buffer, len);
252 static UString formatTime(
const tm &t,
bool utc)
258 #if !PLATFORM(WIN_OS)
259 ASSERT(gmtoffset(t) == 0);
261 len = snprintf(buffer,
sizeof(buffer),
"%02d:%02d:%02d GMT", t.tm_hour, t.tm_min, t.tm_sec);
263 int offset = abs(gmtoffset(t));
264 len = snprintf(buffer,
sizeof(buffer),
"%02d:%02d:%02d GMT%c%02d%02d",
265 t.tm_hour, t.tm_min, t.tm_sec,
266 gmtoffset(t) < 0 ?
'-' :
'+', offset / (60 * 60), (offset / 60) % 60);
268 return UString(buffer, len);
271 static UString formatTimeISOVariant(
const tm &t,
bool utc,
double absoluteMS,
double ms)
277 len = snprintf(buffer,
sizeof(buffer),
"%02d:%02d:%02d.%03d",
278 t.tm_hour, t.tm_min, t.tm_sec,
int(ms));
280 int offset = gmtoffset(t);
282 millisecondsToTM(absoluteMS - offset * 1000,
true, &t_fixed);
283 len = snprintf(buffer,
sizeof(buffer),
"%02d:%02d:%02d.%03d",
284 t_fixed.tm_hour, t_fixed.tm_min, t_fixed.tm_sec,
int(ms));
286 return UString(buffer, len);
289 static int day(
double t)
291 return int(floor(t / msPerDay));
294 static double dayFromYear(
int year)
296 return 365.0 * (year - 1970)
297 + floor((year - 1969) / 4.0)
298 - floor((year - 1901) / 100.0)
299 + floor((year - 1601) / 400.0);
303 static int daysInYear(
int year)
308 if (year % 400 == 0) {
311 if (year % 100 == 0) {
318 static double timeFromYear(
int year)
320 return msPerDay * dayFromYear(year);
324 static int yearFromTime(
double t)
329 int y = 1970 + int(t / (365.25 * msPerDay));
332 if (timeFromYear(y) > t) {
335 }
while (timeFromYear(y) > t);
337 while (timeFromYear(y + 1) < t) {
346 static int weekDay(
double t)
348 int wd = (day(t) + 4) % 7;
359 static double setTimeFields(ExecState *exec,
const List &args,
int id,
double ms, tm *t)
361 assert(DateProtoFunc::SetSeconds - DateProtoFunc::SetMilliSeconds + 1 == 2);
362 assert(DateProtoFunc::SetMinutes - DateProtoFunc::SetMilliSeconds + 1 == 3);
363 assert(DateProtoFunc::SetHours - DateProtoFunc::SetMilliSeconds + 1 == 4);
365 assert(
id == DateProtoFunc::SetMilliSeconds ||
id == DateProtoFunc::SetSeconds ||
366 id == DateProtoFunc::SetMinutes ||
id == DateProtoFunc::SetHours);
368 int maxArgs =
id - DateProtoFunc::SetMilliSeconds + 1;
369 double milliseconds = 0;
371 int numArgs = args.size();
374 if (numArgs > maxArgs) {
379 if (maxArgs >= 4 && idx < numArgs) {
381 milliseconds += JSValue::toInt32(args[idx++], exec) * msPerHour;
385 if (maxArgs >= 3 && idx < numArgs) {
387 milliseconds += JSValue::toInt32(args[idx++], exec) * msPerMinute;
391 if (maxArgs >= 2 && idx < numArgs) {
393 milliseconds += JSValue::toInt32(args[idx++], exec) * msPerSecond;
398 milliseconds += roundValue(exec, args[idx]);
410 static double setDateFields(ExecState *exec,
const List &args,
int id,
double ms, tm *t)
412 assert(DateProtoFunc::SetMonth - DateProtoFunc::SetDate + 1 == 2);
413 assert(DateProtoFunc::SetFullYear - DateProtoFunc::SetDate + 1 == 3);
415 assert(
id == DateProtoFunc::SetDate ||
id == DateProtoFunc::SetMonth ||
id == DateProtoFunc::SetFullYear);
417 int maxArgs =
id - DateProtoFunc::SetDate + 1;
419 int numArgs = args.size();
422 if (numArgs > maxArgs) {
427 if (maxArgs >= 3 && idx < numArgs) {
428 t->tm_year = JSValue::toInt32(args[idx++], exec) - 1900;
432 if (maxArgs >= 2 && idx < numArgs) {
433 t->tm_mon = JSValue::toInt32(args[idx++], exec);
439 ms += JSValue::toInt32(args[idx], exec) * msPerDay;
447 const ClassInfo DateInstance::info = {
"Date",
nullptr,
nullptr,
nullptr};
449 DateInstance::DateInstance(JSObject *proto)
450 : JSWrapperObject(proto)
454 JSObject *DateInstance::valueClone(Interpreter *targetCtx)
const
456 DateInstance *
copy =
new DateInstance(targetCtx->builtinDatePrototype());
457 copy->setInternalValue(internalValue());
461 bool DateInstance::getTime(tm &t,
int &offset)
const
463 double milli = JSValue::getNumber(internalValue());
468 millisecondsToTM(milli,
false, &t);
469 offset = gmtoffset(t);
473 bool DateInstance::getUTCTime(tm &t)
const
475 double milli = JSValue::getNumber(internalValue());
480 millisecondsToTM(milli,
true, &t);
484 bool DateInstance::getTime(
double &milli,
int &offset)
const
486 milli = JSValue::getNumber(internalValue());
492 millisecondsToTM(milli,
false, &t);
493 offset = gmtoffset(t);
497 bool DateInstance::getUTCTime(
double &milli)
const
499 milli = JSValue::getNumber(internalValue());
507 static inline bool isTime_tSigned()
509 time_t minusOne = (time_t)(-1);
513 static void millisecondsToTM(
double milli,
bool utc, tm *t)
517 static bool time_tIsSigned = isTime_tSigned();
519 static double time_tMin = 0;
521 static double time_tMin = (time_tIsSigned ? - (double)(1ULL << (8 *
sizeof(time_t) - 1)) : 0);
523 static double time_tMax = (time_tIsSigned ? (1ULL << (8 *
sizeof(time_t) - 1)) - 1 : 2 * (
double)(1ULL << (8 *
sizeof(time_t) - 1)) - 1);
524 int realYearOffset = 0;
525 double milliOffset = 0.0;
526 double secs = floor(milli / msPerSecond);
528 if (secs < time_tMin || secs > time_tMax) {
530 int realYear = yearFromTime(milli);
531 int base = daysInYear(realYear) == 365 ? 2001 : 2000;
532 milliOffset = timeFromYear(base) - timeFromYear(realYear);
533 milli += milliOffset;
534 realYearOffset = realYear - base;
537 time_t tv = (time_t) floor(milli / msPerSecond);
539 *t = *(utc ? gmtime(&tv) : localtime(&tv));
542 if (realYearOffset != 0) {
543 t->tm_year += realYearOffset;
544 milli -= milliOffset;
548 m += gmtoffset(*t) * msPerSecond;
550 t->tm_wday = weekDay(m);
554 static bool isNaNorInf(
double value)
561 const ClassInfo DatePrototype::info = {
"Date", &DateInstance::info, &dateTable,
nullptr};
616 DatePrototype::DatePrototype(ExecState *, ObjectPrototype *objectProto)
617 : DateInstance(objectProto)
619 setInternalValue(jsNaN());
623 bool DatePrototype::getOwnPropertySlot(ExecState *exec,
const Identifier &propertyName, PropertySlot &slot)
625 return getStaticFunctionSlot<DateProtoFunc, JSObject>(exec, &dateTable,
this, propertyName, slot);
630 DateProtoFunc::DateProtoFunc(ExecState *exec,
int i,
int len,
const Identifier &name)
631 : InternalFunctionImp(static_cast<FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype()),
name)
636 putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
639 JSValue *DateProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj,
const List &args)
642 JSValue *tv = thisObj->toPrimitive(exec, NumberType);
643 if (JSValue::isNumber(tv)) {
644 double ms = JSValue::toNumber(tv, exec);
645 if (isNaNorInf(ms)) {
650 JSValue *toISO = thisObj->get(exec, exec->propertyNames().toISOString);
652 return throwError(exec, TypeError,
"toISOString is not callable");
654 JSObject *toISOobj = JSValue::toObject(toISO, exec);
656 return throwError(exec, TypeError,
"toISOString is not callable");
658 return toISOobj->call(exec, thisObj,
List::empty());
661 if (!thisObj->inherits(&DateInstance::info)) {
663 return jsString(
"Invalid Date");
664 return throwError(exec, TypeError,
"Incompatible object");
667 DateInstance *thisDateObj =
static_cast<DateInstance *
>(thisObj);
669 JSValue *result =
nullptr;
671 const int bufsize = 100;
672 char timebuffer[bufsize];
673 CString oldlocale = setlocale(LC_TIME,
nullptr);
674 if (!oldlocale.size()) {
675 oldlocale = setlocale(LC_ALL,
nullptr);
679 JSValue *v = thisDateObj->internalValue();
680 double milli = JSValue::toNumber(v, exec);
689 case ToLocaleDateString:
690 case ToLocaleTimeString:
691 return jsString(
"Invalid Date", 12);
702 case GetMilliSeconds:
703 case GetTimezoneOffset:
704 case SetMilliSeconds:
713 return throwError(exec, RangeError,
"Invalid Date");
718 double milli = roundValue(exec, args[0]);
719 result = jsNumber(timeClip(milli));
720 thisDateObj->setInternalValue(result);
724 double secs = floor(milli / msPerSecond);
725 double ms = milli - secs * msPerSecond;
728 millisecondsToTM(milli, utc, &t);
732 return jsString(formatDate(t).append(
' ').append(formatTime(t, utc)));
735 return jsString(formatDate(t));
738 return jsString(formatTime(t, utc));
742 return jsString(formatDateUTCVariant(t).append(
' ').append(formatTime(t, utc)));
744 return jsString(formatDateISOVariant(t, utc, milli).append(
'T').append(formatTimeISOVariant(t, utc, milli, ms)).append(
'Z'));
748 return jsString(formatLocaleDate(exec, secs,
true,
true, args));
750 case ToLocaleDateString:
751 return jsString(formatLocaleDate(exec, secs,
true,
false, args));
753 case ToLocaleTimeString:
754 return jsString(formatLocaleDate(exec, secs,
false,
true, args));
758 return jsString(timebuffer, strftime(timebuffer, bufsize,
"%c", &t));
760 case ToLocaleDateString:
761 return jsString(timebuffer, strftime(timebuffer, bufsize,
"%x", &t));
763 case ToLocaleTimeString:
764 return jsString(timebuffer, strftime(timebuffer, bufsize,
"%X", &t));
769 return jsNumber(milli);
772 if (exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat) {
773 return jsNumber(1900 + t.tm_year);
775 return jsNumber(t.tm_year);
777 return jsNumber(1900 + t.tm_year);
779 return jsNumber(t.tm_mon);
781 return jsNumber(t.tm_mday);
783 return jsNumber(t.tm_wday);
785 return jsNumber(t.tm_hour);
787 return jsNumber(t.tm_min);
789 return jsNumber(t.tm_sec);
790 case GetMilliSeconds:
792 case GetTimezoneOffset:
793 return jsNumber(-gmtoffset(t) / 60);
795 case SetMilliSeconds:
799 ms = args.size() > 0 ? setTimeFields(exec, args,
id, ms, &t) : NaN;
805 ms = args.size() > 0 ? setDateFields(exec, args,
id, ms, &t) : NaN;
809 int32_t year = JSValue::toInt32(args[0], exec);
810 t.tm_year = (year > 99 || year < 0) ? year - 1900 : year;
815 if (
id == SetYear ||
id == SetMilliSeconds ||
id == SetSeconds ||
816 id == SetMinutes ||
id == SetHours ||
id == SetDate ||
817 id == SetMonth ||
id == SetFullYear) {
818 result = jsNumber(
isNaN(ms) ? ms : timeClip(makeTime(&t, ms, utc)));
819 thisDateObj->setInternalValue(result);
827 DateObjectImp::DateObjectImp(ExecState *exec,
828 FunctionPrototype *funcProto,
829 DatePrototype *dateProto)
830 : InternalFunctionImp(funcProto)
833 static const Identifier *parsePropertyName =
new Identifier(
"parse");
834 static const Identifier *UTCPropertyName =
new Identifier(
"UTC");
835 static const Identifier *nowPropertyName =
new Identifier(
"now");
837 putDirect(exec->propertyNames().prototype, dateProto, DontEnum | DontDelete | ReadOnly);
838 putDirectFunction(
new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, *parsePropertyName), DontEnum);
839 putDirectFunction(
new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, *UTCPropertyName), DontEnum);
840 putDirectFunction(
new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Now, 0, *nowPropertyName), DontEnum);
843 putDirect(exec->propertyNames().length, 7, ReadOnly | DontDelete | DontEnum);
846 bool DateObjectImp::implementsConstruct()
const
851 static double getCurrentUTCTime()
854 #if defined(WTF_COMPILER_BORLAND)
855 struct timeb timebuffer;
858 struct _timeb timebuffer;
861 double utc = timebuffer.time * msPerSecond + timebuffer.millitm;
864 gettimeofday(&tv,
nullptr);
865 double utc = floor(tv.tv_sec * msPerSecond + tv.tv_usec / 1000);
870 static double makeTimeFromList(ExecState *exec,
const List &args,
bool utc)
872 const int numArgs = args.size();
873 if (isNaNorInf(JSValue::toNumber(args[0], exec))
874 || isNaNorInf(JSValue::toNumber(args[1], exec))
875 || (numArgs >= 3 && isNaNorInf(JSValue::toNumber(args[2], exec)))
876 || (numArgs >= 4 && isNaNorInf(JSValue::toNumber(args[3], exec)))
877 || (numArgs >= 5 && isNaNorInf(JSValue::toNumber(args[4], exec)))
878 || (numArgs >= 6 && isNaNorInf(JSValue::toNumber(args[5], exec)))
879 || (numArgs >= 7 && isNaNorInf(JSValue::toNumber(args[6], exec)))) {
884 memset(&t, 0,
sizeof(t));
885 int year = JSValue::toInt32(args[0], exec);
886 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
887 t.tm_mon = JSValue::toInt32(args[1], exec);
888 t.tm_mday = (numArgs >= 3) ? JSValue::toInt32(args[2], exec) : 1;
889 t.tm_hour = (numArgs >= 4) ? JSValue::toInt32(args[3], exec) : 0;
890 t.tm_min = (numArgs >= 5) ? JSValue::toInt32(args[4], exec) : 0;
891 t.tm_sec = (numArgs >= 6) ? JSValue::toInt32(args[5], exec) : 0;
895 double ms = (numArgs >= 7) ? roundValue(exec, args[6]) : 0;
896 return makeTime(&t, ms, utc);
900 JSObject *DateObjectImp::construct(ExecState *exec,
const List &args)
902 int numArgs = args.size();
906 value = getCurrentUTCTime();
907 }
else if (numArgs == 1) {
908 JSValue *arg0 = args[0];
909 if (JSValue::isObject(arg0, &DateInstance::info)) {
910 value = JSValue::toNumber(
static_cast<DateInstance *
>(arg0)->internalValue(), exec);
912 JSValue *primitive = JSValue::toPrimitive(arg0, exec);
913 if (JSValue::isString(primitive)) {
914 value = parseDate(JSValue::getString(primitive));
916 value = JSValue::toNumber(primitive, exec);
920 value = makeTimeFromList(exec, args,
false);
923 DateInstance *ret =
new DateInstance(exec->lexicalInterpreter()->builtinDatePrototype());
924 ret->setInternalValue(jsNumber(timeClip(value)));
931 time_t t = time(
nullptr);
932 tm ts = *localtime(&t);
933 return jsString(formatDate(ts).append(
' ').append(formatTime(ts,
false)));
938 DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototype *funcProto,
int i,
int len,
const Identifier &name)
939 : InternalFunctionImp(funcProto,
name), id(i)
941 putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
948 return jsNumber(parseDate(JSValue::toString(args[0], exec)));
949 }
else if (
id == Now) {
950 return jsNumber(getCurrentUTCTime());
952 return jsNumber(makeTimeFromList(exec, args,
true));
960 static inline double ymdhmsToSeconds(
long year,
int mon,
int day,
int hour,
int minute,
double second)
965 double days = (day - 32075)
966 + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
967 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
968 - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
971 double days = (day - 32075)
972 + 1461 * (year + 4800 + (mon - 14) / 12) / 4
973 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
974 - 3 * ((year + 4900 + (mon - 14) / 12) / 100) / 4
977 return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
982 static const struct KnownZone {
983 #if !PLATFORM(WIN_OS)
1001 #if PLATFORM(WIN_OS)
1002 void FileTimeToUnixTime(LPFILETIME pft,
double *pt)
1005 ull.LowPart = pft->dwLowDateTime;
1006 ull.HighPart = pft->dwHighDateTime;
1007 *pt = (double)(ull.QuadPart / 10000000ULL) - 11644473600ULL;
1010 void SystemTimeToUnixTime(LPSYSTEMTIME pst,
double *pt)
1013 SystemTimeToFileTime(pst, &ft);
1014 FileTimeToUnixTime(&ft, pt);
1018 static double makeTime(tm *t,
double ms,
bool utc)
1023 #if PLATFORM(WIN_OS)
1025 (void)localtime(&zero);
1026 #if defined(WTF_COMPILER_BORLAND) || defined(WTF_COMPILER_CYGWIN) || defined(WTF_COMPILER_MSVC)
1027 utcOffset = - _timezone;
1029 utcOffset = - timezone;
1032 #elif PLATFORM(DARWIN)
1037 localtime_r(&zero, &t3);
1038 utcOffset = gmtoffset(t3);
1039 t->tm_isdst = t3.tm_isdst;
1046 #if !PLATFORM(WIN_OS)
1047 double yearOffset = 0.0;
1048 if (t->tm_year < (1971 - 1900) || t->tm_year > (2037 - 1900)) {
1054 int y = t->tm_year + 1900;
1055 int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
1056 double baseTime = timeFromYear(baseYear);
1057 yearOffset = timeFromYear(y) - baseTime;
1058 t->tm_year = baseYear - 1900;
1066 time_t tval = mktime(t) + (time_t)((ms + yearOffset) / 1000);
1067 tm t3 = *localtime(&tval);
1068 t->tm_isdst = t3.tm_isdst;
1071 return (mktime(t) + utcOffset) * msPerSecond + ms + yearOffset;
1076 st.wYear = 1900 + t->tm_year;
1077 st.wMonth = t->tm_mon + 1;
1078 st.wDayOfWeek = t->tm_wday;
1079 st.wDay = t->tm_mday;
1080 st.wHour = t->tm_hour;
1081 st.wMinute = t->tm_min;
1082 st.wSecond = t->tm_sec;
1083 st.wMilliseconds = 0;
1085 TzSpecificLocalTimeToSystemTime(0, &st, &dt);
1086 SystemTimeToUnixTime(&dt, &tval);
1088 return (tval + utcOffset) * msPerSecond + ms;
1092 inline static bool isSpaceLike(
char c)
1094 return isASCIISpace(c) || c ==
',' || c ==
':' || c ==
'-';
1097 static const char *skipSpacesAndComments(
const char *s)
1103 if (ch ==
'-' && isASCIIDigit(*(s + 1))) {
1106 if (!isSpaceLike(ch)) {
1109 }
else if (ch ==
')' && nesting > 0) {
1111 }
else if (nesting == 0) {
1121 static int findMonth(
const char *monthStr)
1125 for (
int i = 0; i < 3; ++i) {
1129 needle[i] = toASCIILower(*monthStr++);
1132 const char *haystack =
"janfebmaraprmayjunjulaugsepoctnovdec";
1133 const char *str = strstr(haystack, needle);
1135 int position = str - haystack;
1136 if (position % 3 == 0) {
1137 return position / 3;
1143 static bool isTwoDigits(
const char *str)
1145 return isASCIIDigit(str[0]) && isASCIIDigit(str[1]);
1148 static int twoDigit(
const char *str)
1150 return (str[0] -
'0') * 10 + str[1] -
'0';
1153 static double parseDate(
const UString &date)
1169 CString dateCString = date.UTF8String();
1170 const char *dateString = dateCString.c_str();
1176 dateString = skipSpacesAndComments(dateString);
1180 if (isTwoDigits(dateString) &&
1181 isTwoDigits(dateString + 2) &&
1182 dateString[4] ==
'-' &&
1183 isTwoDigits(dateString + 5) &&
1184 dateString[7] ==
'-' &&
1185 isTwoDigits(dateString + 8)) {
1186 int year = twoDigit(dateString) * 100 + twoDigit(dateString + 2);
1187 int month = twoDigit(dateString + 5) - 1;
1188 int day = twoDigit(dateString + 8);
1189 if (month > 11 || day < 1 || day > 31) {
1192 int hour = 0, minute = 0;
1195 if ((dateString[0] | 0x20) ==
't' &&
1196 isTwoDigits(dateString + 1) &&
1197 dateString[3] ==
':' &&
1198 isTwoDigits(dateString + 4)) {
1199 hour = twoDigit(dateString + 1);
1200 minute = twoDigit(dateString + 4);
1201 if (hour > 23 || minute > 59) {
1205 if (dateString[0] ==
':' &&
1206 isTwoDigits(dateString + 1)) {
1207 second = twoDigit(dateString + 1);
1212 if (dateString[0] ==
'.' &&
1213 isASCIIDigit(dateString[1])) {
1217 second += (dateString[0] -
'0') / div;
1219 }
while (isASCIIDigit(*++dateString));
1224 if (dateString[0] ==
'Z') {
1226 memset(&t, 0,
sizeof(tm));
1227 int secs = int(second);
1233 t.tm_year = year - 1900;
1237 return makeTime(&t, (second - secs) * 1000,
true);
1241 return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
1245 const char *wordStart = dateString;
1247 while (*dateString && !isASCIIDigit(*dateString)) {
1248 if (isASCIISpace(*dateString) || *dateString ==
'(') {
1249 if (dateString - wordStart >= 3) {
1250 month = findMonth(wordStart);
1252 dateString = skipSpacesAndComments(dateString);
1253 wordStart = dateString;
1260 if (month == -1 && wordStart != dateString) {
1261 month = findMonth(wordStart);
1264 dateString = skipSpacesAndComments(dateString);
1273 long day = strtol(dateString, &newPosStr, 10);
1274 dateString = newPosStr;
1276 if (errno || day < 0 || !*dateString) {
1283 if (*dateString !=
'/') {
1287 if (!*++dateString) {
1291 month = strtol(dateString, &newPosStr, 10) - 1;
1295 dateString = newPosStr;
1296 if (*dateString++ !=
'/' || !*dateString) {
1299 day = strtol(dateString, &newPosStr, 10);
1303 dateString = newPosStr;
1304 }
else if (*dateString ==
'/' && month == -1) {
1308 day = strtol(dateString, &newPosStr, 10);
1312 dateString = newPosStr;
1313 if (*dateString ==
'/') {
1320 if (*dateString ==
'-') {
1324 dateString = skipSpacesAndComments(dateString);
1326 if (*dateString ==
',') {
1331 month = findMonth(dateString);
1336 while (*dateString && (*dateString !=
'-') && !isASCIISpace(*dateString)) {
1345 if (*dateString !=
'-' && *dateString !=
'/' && !isASCIISpace(*dateString)) {
1351 if (month < 0 || month > 11) {
1357 if (year <= 0 && *dateString) {
1358 year = strtol(dateString, &newPosStr, 10);
1369 dateString = newPosStr;
1372 if (*newPosStr ==
':') {
1375 }
else if (isSpaceLike(*newPosStr)) {
1377 dateString = skipSpacesAndComments(newPosStr + 1);
1382 hour = strtol(dateString, &newPosStr, 10);
1388 if (newPosStr != dateString) {
1389 dateString = newPosStr;
1391 if (hour < 0 || hour > 23) {
1400 if (*dateString++ !=
':') {
1404 minute = strtol(dateString, &newPosStr, 10);
1408 dateString = newPosStr;
1410 if (minute < 0 || minute > 59) {
1415 if (*dateString && *dateString !=
':' && !isASCIISpace(*dateString)) {
1420 if (*dateString ==
':') {
1423 second = strtol(dateString, &newPosStr, 10);
1427 dateString = newPosStr;
1429 if (second < 0 || second > 59) {
1434 if (*dateString ==
':') {
1439 dateString = skipSpacesAndComments(dateString);
1441 if (strncasecmp(dateString,
"AM", 2) == 0) {
1448 dateString = skipSpacesAndComments(dateString + 2);
1449 }
else if (strncasecmp(dateString,
"PM", 2) == 0) {
1456 dateString = skipSpacesAndComments(dateString + 2);
1461 bool haveTZ =
false;
1467 if (strncasecmp(dateString,
"GMT", 3) == 0 ||
1468 strncasecmp(dateString,
"UTC", 3) == 0) {
1473 if (*dateString ==
'+' || *dateString ==
'-') {
1474 long o = strtol(dateString, &newPosStr, 10);
1478 dateString = newPosStr;
1480 if (o < -9959 || o > 9959) {
1484 int sgn = (o < 0) ? -1 : 1;
1486 if (*dateString !=
':') {
1487 offset = ((o / 100) * 60 + (o % 100)) * sgn;
1490 long o2 = strtol(dateString, &newPosStr, 10);
1494 dateString = newPosStr;
1495 offset = (o * 60 + o2) * sgn;
1499 for (
int i = 0; i < int(
sizeof(known_zones) /
sizeof(KnownZone)); i++) {
1500 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
1501 offset = known_zones[i].tzOffset;
1502 dateString += strlen(known_zones[i].tzName);
1510 dateString = skipSpacesAndComments(dateString);
1512 if (*dateString && year == -1) {
1513 year = strtol(dateString, &newPosStr, 10);
1517 dateString = newPosStr;
1520 dateString = skipSpacesAndComments(dateString);
1528 if (year >= 0 && year < 100) {
1539 memset(&t, 0,
sizeof(tm));
1542 t.tm_year = year - 1900;
1549 return makeTime(&t, 0,
false);
1552 return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
1555 double timeClip(
double t)
1560 double at = fabs(t);
1564 return copysign(floor(at), t);