KJS

date_object.cpp
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (C) 1999-2000 Harri Porten ([email protected])
4  * Copyright (C) 2004 Apple Computer, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  */
21 
22 #include "date_object.h"
23 #include "global.h"
24 #include "date_object.lut.h"
25 #include "internal.h"
26 
27 #if HAVE_ERRNO_H
28 #include <errno.h>
29 #endif
30 
31 #if HAVE_SYS_PARAM_H
32 #include <sys/param.h>
33 #endif
34 
35 #if HAVE_SYS_TIME_H
36 #include <sys/time.h>
37 #endif
38 
39 #if HAVE_SYS_TIMEB_H
40 #include <sys/timeb.h>
41 #endif
42 
43 #include <float.h>
44 #include <limits.h>
45 #include <locale.h>
46 #include <math.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <cstring>
50 #include <time.h>
51 
52 #if PLATFORM(SOLARIS_OS)
53 #include <strings.h>
54 #endif
55 
56 #include "error_object.h"
57 #include "operations.h"
58 
59 #if PLATFORM(MAC)
60 #include <CoreFoundation/CoreFoundation.h>
61 #endif
62 
63 #if PLATFORM(WIN_OS)
64 #include <windows.h>
65 #if defined(WTF_COMPILER_MSVC)
66 #define copysign(x, y) _copysign(x, y)
67 #define snprintf _snprintf
68 #endif
69 #if !defined(WTF_COMPILER_GCC)
70 #ifndef strncasecmp
71 #define strncasecmp(x, y, z) strnicmp(x, y, z)
72 #endif
73 #endif
74 #endif
75 
76 #include "wtf/DisallowCType.h"
77 #include "wtf/ASCIICType.h"
78 
79 // GCC cstring uses these automatically, but not all implementations do.
80 using std::strlen;
81 using std::strcpy;
82 using std::strncpy;
83 using std::memset;
84 using std::memcpy;
85 
86 using namespace WTF;
87 
88 inline int gmtoffset(const tm &t)
89 {
90 #if PLATFORM(WIN_OS)
91  // Time is supposed to be in the current timezone.
92  // FIXME: Use undocumented _dstbias?
93  return -(_timezone / 60 - (t.tm_isdst > 0 ? 60 : 0)) * 60;
94 #else
95 #if HAVE_TM_GMTOFF
96  return t.tm_gmtoff;
97 #else
98  return - timezone;
99 #endif
100 #endif
101 }
102 
103 namespace KJS
104 {
105 
106 /**
107  * @internal
108  *
109  * Class to implement all methods that are properties of the
110  * Date object
111  */
112 class DateObjectFuncImp : public InternalFunctionImp
113 {
114 public:
115  DateObjectFuncImp(ExecState *, FunctionPrototype *, int i, int len, const Identifier &);
116 
117  JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args) override;
118 
119  enum { Parse, UTC, Now };
120 
121 private:
122  int id;
123 };
124 
125 // some constants
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;
133 
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" };
136 
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);
141 
142 #if PLATFORM(MAC)
143 
144 static CFDateFormatterStyle styleFromArgString(const UString &string, CFDateFormatterStyle defaultStyle)
145 {
146  if (string == "short") {
147  return kCFDateFormatterShortStyle;
148  }
149  if (string == "medium") {
150  return kCFDateFormatterMediumStyle;
151  }
152  if (string == "long") {
153  return kCFDateFormatterLongStyle;
154  }
155  if (string == "full") {
156  return kCFDateFormatterFullStyle;
157  }
158  return defaultStyle;
159 }
160 
161 static UString formatLocaleDate(ExecState *exec, double time, bool includeDate, bool includeTime, const List &args)
162 {
163  CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
164  CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
165 
166  bool useCustomFormat = false;
167  UString customFormatString;
168 
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);
182  }
183 
184  CFLocaleRef locale = CFLocaleCopyCurrent();
185  CFDateFormatterRef formatter = CFDateFormatterCreate(0, locale, dateStyle, timeStyle);
186  CFRelease(locale);
187 
188  if (useCustomFormat) {
189  CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, (UniChar *)customFormatString.data(), customFormatString.size());
190  CFDateFormatterSetFormat(formatter, customFormatCFString);
191  CFRelease(customFormatCFString);
192  }
193 
194  CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(0, formatter, time - kCFAbsoluteTimeIntervalSince1970);
195 
196  CFRelease(formatter);
197 
198  // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters).
199  // That's not great error handling, but it just won't happen so it doesn't matter.
200  UChar buffer[200];
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;
206  }
207  CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast<UniChar *>(buffer));
208 
209  CFRelease(string);
210 
211  return UString(buffer, length);
212 }
213 
214 #endif // PLATFORM(MAC)
215 
216 static UString formatDate(const tm &t)
217 {
218  char buffer[100];
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);
223 }
224 
225 static UString formatDateUTCVariant(const tm &t)
226 {
227  char buffer[100];
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);
232 }
233 
234 static UString formatDateISOVariant(const tm &t, bool utc, double absoluteMS)
235 {
236  char buffer[100];
237  // YYYY-MM-DD
238  int len;
239  if (utc) {
240  len = snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d",
241  t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
242  } else {
243  int offset = gmtoffset(t);
244  tm t_fixed;
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);
248  }
249  return UString(buffer, len);
250 }
251 
252 static UString formatTime(const tm &t, bool utc)
253 {
254  char buffer[100];
255  int len;
256  if (utc) {
257  // FIXME: why not on windows?
258 #if !PLATFORM(WIN_OS)
259  ASSERT(gmtoffset(t) == 0);
260 #endif
261  len = snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.tm_hour, t.tm_min, t.tm_sec);
262  } else {
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);
267  }
268  return UString(buffer, len);
269 }
270 
271 static UString formatTimeISOVariant(const tm &t, bool utc, double absoluteMS, double ms)
272 {
273  char buffer[100];
274  // HH:mm:ss.sss
275  int len;
276  if (utc) {
277  len = snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%03d",
278  t.tm_hour, t.tm_min, t.tm_sec, int(ms));
279  } else {
280  int offset = gmtoffset(t);
281  tm t_fixed;
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));
285  }
286  return UString(buffer, len);
287 }
288 
289 static int day(double t)
290 {
291  return int(floor(t / msPerDay));
292 }
293 
294 static double dayFromYear(int year)
295 {
296  return 365.0 * (year - 1970)
297  + floor((year - 1969) / 4.0)
298  - floor((year - 1901) / 100.0)
299  + floor((year - 1601) / 400.0);
300 }
301 
302 // based on the rule for whether it's a leap year or not
303 static int daysInYear(int year)
304 {
305  if (year % 4 != 0) {
306  return 365;
307  }
308  if (year % 400 == 0) {
309  return 366;
310  }
311  if (year % 100 == 0) {
312  return 365;
313  }
314  return 366;
315 }
316 
317 // time value of the start of a year
318 static double timeFromYear(int year)
319 {
320  return msPerDay * dayFromYear(year);
321 }
322 
323 // year determined by time value
324 static int yearFromTime(double t)
325 {
326  // ### there must be an easier way
327 
328  // initial guess
329  int y = 1970 + int(t / (365.25 * msPerDay));
330 
331  // adjustment
332  if (timeFromYear(y) > t) {
333  do {
334  --y;
335  } while (timeFromYear(y) > t);
336  } else {
337  while (timeFromYear(y + 1) < t) {
338  ++y;
339  }
340  }
341 
342  return y;
343 }
344 
345 // 0: Sunday, 1: Monday, etc.
346 static int weekDay(double t)
347 {
348  int wd = (day(t) + 4) % 7;
349  if (wd < 0) {
350  wd += 7;
351  }
352  return wd;
353 }
354 
355 // Converts a list of arguments sent to a Date member function into milliseconds, updating
356 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
357 //
358 // Format of member function: f([hour,] [min,] [sec,] [ms])
359 static double setTimeFields(ExecState *exec, const List &args, int id, double ms, tm *t)
360 {
361  assert(DateProtoFunc::SetSeconds - DateProtoFunc::SetMilliSeconds + 1 == 2);
362  assert(DateProtoFunc::SetMinutes - DateProtoFunc::SetMilliSeconds + 1 == 3);
363  assert(DateProtoFunc::SetHours - DateProtoFunc::SetMilliSeconds + 1 == 4);
364 
365  assert(id == DateProtoFunc::SetMilliSeconds || id == DateProtoFunc::SetSeconds ||
366  id == DateProtoFunc::SetMinutes || id == DateProtoFunc::SetHours);
367 
368  int maxArgs = id - DateProtoFunc::SetMilliSeconds + 1;
369  double milliseconds = 0;
370  int idx = 0;
371  int numArgs = args.size();
372 
373  // JS allows extra trailing arguments -- ignore them
374  if (numArgs > maxArgs) {
375  numArgs = maxArgs;
376  }
377 
378  // hours
379  if (maxArgs >= 4 && idx < numArgs) {
380  t->tm_hour = 0;
381  milliseconds += JSValue::toInt32(args[idx++], exec) * msPerHour;
382  }
383 
384  // minutes
385  if (maxArgs >= 3 && idx < numArgs) {
386  t->tm_min = 0;
387  milliseconds += JSValue::toInt32(args[idx++], exec) * msPerMinute;
388  }
389 
390  // seconds
391  if (maxArgs >= 2 && idx < numArgs) {
392  t->tm_sec = 0;
393  milliseconds += JSValue::toInt32(args[idx++], exec) * msPerSecond;
394  }
395 
396  // milliseconds
397  if (idx < numArgs) {
398  milliseconds += roundValue(exec, args[idx]);
399  } else {
400  milliseconds += ms;
401  }
402 
403  return milliseconds;
404 }
405 
406 // Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating
407 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
408 //
409 // Format of member function: f([years,] [months,] [days])
410 static double setDateFields(ExecState *exec, const List &args, int id, double ms, tm *t)
411 {
412  assert(DateProtoFunc::SetMonth - DateProtoFunc::SetDate + 1 == 2);
413  assert(DateProtoFunc::SetFullYear - DateProtoFunc::SetDate + 1 == 3);
414 
415  assert(id == DateProtoFunc::SetDate || id == DateProtoFunc::SetMonth || id == DateProtoFunc::SetFullYear);
416 
417  int maxArgs = id - DateProtoFunc::SetDate + 1;
418  int idx = 0;
419  int numArgs = args.size();
420 
421  // JS allows extra trailing arguments -- ignore them
422  if (numArgs > maxArgs) {
423  numArgs = maxArgs;
424  }
425 
426  // years
427  if (maxArgs >= 3 && idx < numArgs) {
428  t->tm_year = JSValue::toInt32(args[idx++], exec) - 1900;
429  }
430 
431  // months
432  if (maxArgs >= 2 && idx < numArgs) {
433  t->tm_mon = JSValue::toInt32(args[idx++], exec);
434  }
435 
436  // days
437  if (idx < numArgs) {
438  t->tm_mday = 0;
439  ms += JSValue::toInt32(args[idx], exec) * msPerDay;
440  }
441 
442  return ms;
443 }
444 
445 // ------------------------------ DateInstance ------------------------------
446 
447 const ClassInfo DateInstance::info = {"Date", nullptr, nullptr, nullptr};
448 
449 DateInstance::DateInstance(JSObject *proto)
450  : JSWrapperObject(proto)
451 {
452 }
453 
454 JSObject *DateInstance::valueClone(Interpreter *targetCtx) const
455 {
456  DateInstance *copy = new DateInstance(targetCtx->builtinDatePrototype());
457  copy->setInternalValue(internalValue());
458  return copy;
459 }
460 
461 bool DateInstance::getTime(tm &t, int &offset) const
462 {
463  double milli = JSValue::getNumber(internalValue());
464  if (isNaN(milli)) {
465  return false;
466  }
467 
468  millisecondsToTM(milli, false, &t);
469  offset = gmtoffset(t);
470  return true;
471 }
472 
473 bool DateInstance::getUTCTime(tm &t) const
474 {
475  double milli = JSValue::getNumber(internalValue());
476  if (isNaN(milli)) {
477  return false;
478  }
479 
480  millisecondsToTM(milli, true, &t);
481  return true;
482 }
483 
484 bool DateInstance::getTime(double &milli, int &offset) const
485 {
486  milli = JSValue::getNumber(internalValue());
487  if (isNaN(milli)) {
488  return false;
489  }
490 
491  tm t;
492  millisecondsToTM(milli, false, &t);
493  offset = gmtoffset(t);
494  return true;
495 }
496 
497 bool DateInstance::getUTCTime(double &milli) const
498 {
499  milli = JSValue::getNumber(internalValue());
500  if (isNaN(milli)) {
501  return false;
502  }
503 
504  return true;
505 }
506 
507 static inline bool isTime_tSigned()
508 {
509  time_t minusOne = (time_t)(-1);
510  return minusOne < 0;
511 }
512 
513 static void millisecondsToTM(double milli, bool utc, tm *t)
514 {
515  // check whether time value is outside time_t's usual range
516  // make the necessary transformations if necessary
517  static bool time_tIsSigned = isTime_tSigned();
518 #if PLATFORM(WIN_OS)
519  static double time_tMin = 0; //on windows localtime/gmtime returns NULL for pre 1970 dates
520 #else
521  static double time_tMin = (time_tIsSigned ? - (double)(1ULL << (8 * sizeof(time_t) - 1)) : 0);
522 #endif
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);
527 
528  if (secs < time_tMin || secs > time_tMax) {
529  // ### ugly and probably not very precise
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;
535  }
536 
537  time_t tv = (time_t) floor(milli / msPerSecond);
538 
539  *t = *(utc ? gmtime(&tv) : localtime(&tv));
540  // We had an out of range year. Restore the year (plus/minus offset
541  // found by calculating tm_year) and fix the week day calculation.
542  if (realYearOffset != 0) {
543  t->tm_year += realYearOffset;
544  milli -= milliOffset;
545  // Do our own weekday calculation. Use time zone offset to handle local time.
546  double m = milli;
547  if (!utc) {
548  m += gmtoffset(*t) * msPerSecond;
549  }
550  t->tm_wday = weekDay(m);
551  }
552 }
553 
554 static bool isNaNorInf(double value)
555 {
556  return isNaN(value) || isInf(value);
557 }
558 
559 // ------------------------------ DatePrototype -----------------------------
560 
561 const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, &dateTable, nullptr};
562 
563 /* Source for date_object.lut.h
564  We use a negative ID to denote the "UTC" variant.
565 @begin dateTable 61
566  toString DateProtoFunc::ToString DontEnum|Function 0
567  toUTCString -DateProtoFunc::ToUTCString DontEnum|Function 0
568  toDateString DateProtoFunc::ToDateString DontEnum|Function 0
569  toTimeString DateProtoFunc::ToTimeString DontEnum|Function 0
570  toISOString DateProtoFunc::ToISOString DontEnum|Function 0
571  toJSON DateProtoFunc::ToJSON DontEnum|Function 1
572  toLocaleString DateProtoFunc::ToLocaleString DontEnum|Function 0
573  toLocaleDateString DateProtoFunc::ToLocaleDateString DontEnum|Function 0
574  toLocaleTimeString DateProtoFunc::ToLocaleTimeString DontEnum|Function 0
575  valueOf DateProtoFunc::ValueOf DontEnum|Function 0
576  getTime DateProtoFunc::GetTime DontEnum|Function 0
577  getFullYear DateProtoFunc::GetFullYear DontEnum|Function 0
578  getUTCFullYear -DateProtoFunc::GetFullYear DontEnum|Function 0
579  toGMTString -DateProtoFunc::ToGMTString DontEnum|Function 0
580  getMonth DateProtoFunc::GetMonth DontEnum|Function 0
581  getUTCMonth -DateProtoFunc::GetMonth DontEnum|Function 0
582  getDate DateProtoFunc::GetDate DontEnum|Function 0
583  getUTCDate -DateProtoFunc::GetDate DontEnum|Function 0
584  getDay DateProtoFunc::GetDay DontEnum|Function 0
585  getUTCDay -DateProtoFunc::GetDay DontEnum|Function 0
586  getHours DateProtoFunc::GetHours DontEnum|Function 0
587  getUTCHours -DateProtoFunc::GetHours DontEnum|Function 0
588  getMinutes DateProtoFunc::GetMinutes DontEnum|Function 0
589  getUTCMinutes -DateProtoFunc::GetMinutes DontEnum|Function 0
590  getSeconds DateProtoFunc::GetSeconds DontEnum|Function 0
591  getUTCSeconds -DateProtoFunc::GetSeconds DontEnum|Function 0
592  getMilliseconds DateProtoFunc::GetMilliSeconds DontEnum|Function 0
593  getUTCMilliseconds -DateProtoFunc::GetMilliSeconds DontEnum|Function 0
594  getTimezoneOffset DateProtoFunc::GetTimezoneOffset DontEnum|Function 0
595  setTime DateProtoFunc::SetTime DontEnum|Function 1
596  setMilliseconds DateProtoFunc::SetMilliSeconds DontEnum|Function 1
597  setUTCMilliseconds -DateProtoFunc::SetMilliSeconds DontEnum|Function 1
598  setSeconds DateProtoFunc::SetSeconds DontEnum|Function 2
599  setUTCSeconds -DateProtoFunc::SetSeconds DontEnum|Function 2
600  setMinutes DateProtoFunc::SetMinutes DontEnum|Function 3
601  setUTCMinutes -DateProtoFunc::SetMinutes DontEnum|Function 3
602  setHours DateProtoFunc::SetHours DontEnum|Function 4
603  setUTCHours -DateProtoFunc::SetHours DontEnum|Function 4
604  setDate DateProtoFunc::SetDate DontEnum|Function 1
605  setUTCDate -DateProtoFunc::SetDate DontEnum|Function 1
606  setMonth DateProtoFunc::SetMonth DontEnum|Function 2
607  setUTCMonth -DateProtoFunc::SetMonth DontEnum|Function 2
608  setFullYear DateProtoFunc::SetFullYear DontEnum|Function 3
609  setUTCFullYear -DateProtoFunc::SetFullYear DontEnum|Function 3
610  setYear DateProtoFunc::SetYear DontEnum|Function 1
611  getYear DateProtoFunc::GetYear DontEnum|Function 0
612 @end
613 */
614 // ECMA 15.9.4
615 
616 DatePrototype::DatePrototype(ExecState *, ObjectPrototype *objectProto)
617  : DateInstance(objectProto)
618 {
619  setInternalValue(jsNaN());
620  // The constructor will be added later, after DateObjectImp has been built.
621 }
622 
623 bool DatePrototype::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
624 {
625  return getStaticFunctionSlot<DateProtoFunc, JSObject>(exec, &dateTable, this, propertyName, slot);
626 }
627 
628 // ------------------------------ DateProtoFunc -----------------------------
629 
630 DateProtoFunc::DateProtoFunc(ExecState *exec, int i, int len, const Identifier &name)
631  : InternalFunctionImp(static_cast<FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
632  , id(abs(i))
633  , utc(i < 0)
634  // We use a negative ID to denote the "UTC" variant.
635 {
636  putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
637 }
638 
639 JSValue *DateProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
640 {
641  if (id == ToJSON) {
642  JSValue *tv = thisObj->toPrimitive(exec, NumberType);
643  if (JSValue::isNumber(tv)) {
644  double ms = JSValue::toNumber(tv, exec);
645  if (isNaNorInf(ms)) {
646  return jsNull();
647  }
648  }
649 
650  JSValue *toISO = thisObj->get(exec, exec->propertyNames().toISOString);
651  if (!JSValue::implementsCall(toISO)) {
652  return throwError(exec, TypeError, "toISOString is not callable");
653  }
654  JSObject *toISOobj = JSValue::toObject(toISO, exec);
655  if (!toISOobj) {
656  return throwError(exec, TypeError, "toISOString is not callable");
657  }
658  return toISOobj->call(exec, thisObj, List::empty());
659  }
660 
661  if (!thisObj->inherits(&DateInstance::info)) {
662  if (id == ToString)
663  return jsString("Invalid Date");
664  return throwError(exec, TypeError, "Incompatible object");
665  }
666 
667  DateInstance *thisDateObj = static_cast<DateInstance *>(thisObj);
668 
669  JSValue *result = nullptr;
670 #if !PLATFORM(MAC)
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);
676  }
677  // FIXME: Where's the code to set the locale back to oldlocale?
678 #endif
679  JSValue *v = thisDateObj->internalValue();
680  double milli = JSValue::toNumber(v, exec);
681  if (isNaN(milli)) {
682  switch (id) {
683  case ToString:
684  case ToDateString:
685  case ToTimeString:
686  case ToGMTString:
687  case ToUTCString:
688  case ToLocaleString:
689  case ToLocaleDateString:
690  case ToLocaleTimeString:
691  return jsString("Invalid Date", 12);
692  case ValueOf:
693  case GetTime:
694  case GetYear:
695  case GetFullYear:
696  case GetMonth:
697  case GetDate:
698  case GetDay:
699  case GetHours:
700  case GetMinutes:
701  case GetSeconds:
702  case GetMilliSeconds:
703  case GetTimezoneOffset:
704  case SetMilliSeconds:
705  case SetSeconds:
706  case SetMinutes:
707  case SetHours:
708  case SetDate:
709  case SetMonth:
710  case SetFullYear:
711  return jsNaN();
712  case ToISOString:
713  return throwError(exec, RangeError, "Invalid Date");
714  }
715  }
716 
717  if (id == SetTime) {
718  double milli = roundValue(exec, args[0]);
719  result = jsNumber(timeClip(milli));
720  thisDateObj->setInternalValue(result);
721  return result;
722  }
723 
724  double secs = floor(milli / msPerSecond);
725  double ms = milli - secs * msPerSecond;
726 
727  tm t;
728  millisecondsToTM(milli, utc, &t);
729 
730  switch (id) {
731  case ToString:
732  return jsString(formatDate(t).append(' ').append(formatTime(t, utc)));
733 
734  case ToDateString:
735  return jsString(formatDate(t));
736 
737  case ToTimeString:
738  return jsString(formatTime(t, utc));
739 
740  case ToGMTString:
741  case ToUTCString:
742  return jsString(formatDateUTCVariant(t).append(' ').append(formatTime(t, utc)));
743  case ToISOString:
744  return jsString(formatDateISOVariant(t, utc, milli).append('T').append(formatTimeISOVariant(t, utc, milli, ms)).append('Z'));
745 
746 #if PLATFORM(MAC)
747  case ToLocaleString:
748  return jsString(formatLocaleDate(exec, secs, true, true, args));
749 
750  case ToLocaleDateString:
751  return jsString(formatLocaleDate(exec, secs, true, false, args));
752 
753  case ToLocaleTimeString:
754  return jsString(formatLocaleDate(exec, secs, false, true, args));
755 
756 #else
757  case ToLocaleString:
758  return jsString(timebuffer, strftime(timebuffer, bufsize, "%c", &t));
759 
760  case ToLocaleDateString:
761  return jsString(timebuffer, strftime(timebuffer, bufsize, "%x", &t));
762 
763  case ToLocaleTimeString:
764  return jsString(timebuffer, strftime(timebuffer, bufsize, "%X", &t));
765 
766 #endif
767  case ValueOf:
768  case GetTime:
769  return jsNumber(milli);
770  case GetYear:
771  // IE returns the full year even in getYear.
772  if (exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat) {
773  return jsNumber(1900 + t.tm_year);
774  }
775  return jsNumber(t.tm_year);
776  case GetFullYear:
777  return jsNumber(1900 + t.tm_year);
778  case GetMonth:
779  return jsNumber(t.tm_mon);
780  case GetDate:
781  return jsNumber(t.tm_mday);
782  case GetDay:
783  return jsNumber(t.tm_wday);
784  case GetHours:
785  return jsNumber(t.tm_hour);
786  case GetMinutes:
787  return jsNumber(t.tm_min);
788  case GetSeconds:
789  return jsNumber(t.tm_sec);
790  case GetMilliSeconds:
791  return jsNumber(ms);
792  case GetTimezoneOffset:
793  return jsNumber(-gmtoffset(t) / 60);
794 
795  case SetMilliSeconds:
796  case SetSeconds:
797  case SetMinutes:
798  case SetHours:
799  ms = args.size() > 0 ? setTimeFields(exec, args, id, ms, &t) : NaN;
800  break;
801 
802  case SetDate:
803  case SetMonth:
804  case SetFullYear:
805  ms = args.size() > 0 ? setDateFields(exec, args, id, ms, &t) : NaN;
806  break;
807 
808  case SetYear: {
809  int32_t year = JSValue::toInt32(args[0], exec);
810  t.tm_year = (year > 99 || year < 0) ? year - 1900 : year;
811  break;
812  }
813  }
814 
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);
820  }
821 
822  return result;
823 }
824 
825 // ------------------------------ DateObjectImp --------------------------------
826 
827 DateObjectImp::DateObjectImp(ExecState *exec,
828  FunctionPrototype *funcProto,
829  DatePrototype *dateProto)
830  : InternalFunctionImp(funcProto)
831 {
832  // ECMA 15.9.4.1 Date.prototype
833  static const Identifier *parsePropertyName = new Identifier("parse");
834  static const Identifier *UTCPropertyName = new Identifier("UTC");
835  static const Identifier *nowPropertyName = new Identifier("now");
836 
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);
841 
842  // no. of arguments for constructor
843  putDirect(exec->propertyNames().length, 7, ReadOnly | DontDelete | DontEnum);
844 }
845 
846 bool DateObjectImp::implementsConstruct() const
847 {
848  return true;
849 }
850 
851 static double getCurrentUTCTime()
852 {
853 #if PLATFORM(WIN_OS)
854 #if defined(WTF_COMPILER_BORLAND)
855  struct timeb timebuffer;
856  ftime(&timebuffer);
857 #else
858  struct _timeb timebuffer;
859  _ftime(&timebuffer);
860 #endif
861  double utc = timebuffer.time * msPerSecond + timebuffer.millitm;
862 #else
863  struct timeval tv;
864  gettimeofday(&tv, nullptr);
865  double utc = floor(tv.tv_sec * msPerSecond + tv.tv_usec / 1000);
866 #endif
867  return utc;
868 }
869 
870 static double makeTimeFromList(ExecState *exec, const List &args, bool utc)
871 {
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)))) {
880  return NaN;
881  }
882 
883  tm t;
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;
892  if (!utc) {
893  t.tm_isdst = -1;
894  }
895  double ms = (numArgs >= 7) ? roundValue(exec, args[6]) : 0;
896  return makeTime(&t, ms, utc);
897 }
898 
899 // ECMA 15.9.3
900 JSObject *DateObjectImp::construct(ExecState *exec, const List &args)
901 {
902  int numArgs = args.size();
903  double value;
904 
905  if (numArgs == 0) { // new Date() ECMA 15.9.3.3
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);
911  } else {
912  JSValue *primitive = JSValue::toPrimitive(arg0, exec);
913  if (JSValue::isString(primitive)) {
914  value = parseDate(JSValue::getString(primitive));
915  } else {
916  value = JSValue::toNumber(primitive, exec);
917  }
918  }
919  } else {
920  value = makeTimeFromList(exec, args, false);
921  }
922 
923  DateInstance *ret = new DateInstance(exec->lexicalInterpreter()->builtinDatePrototype());
924  ret->setInternalValue(jsNumber(timeClip(value)));
925  return ret;
926 }
927 
928 // ECMA 15.9.2
929 JSValue *DateObjectImp::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/)
930 {
931  time_t t = time(nullptr);
932  tm ts = *localtime(&t);
933  return jsString(formatDate(ts).append(' ').append(formatTime(ts, false)));
934 }
935 
936 // ------------------------------ DateObjectFuncImp ----------------------------
937 
938 DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototype *funcProto, int i, int len, const Identifier &name)
939  : InternalFunctionImp(funcProto, name), id(i)
940 {
941  putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
942 }
943 
944 // ECMA 15.9.4.2 - 3
945 JSValue *DateObjectFuncImp::callAsFunction(ExecState *exec, JSObject *, const List &args)
946 {
947  if (id == Parse) {
948  return jsNumber(parseDate(JSValue::toString(args[0], exec)));
949  } else if (id == Now) {
950  return jsNumber(getCurrentUTCTime());
951  } else { // UTC
952  return jsNumber(makeTimeFromList(exec, args, true));
953  }
954 }
955 
956 // -----------------------------------------------------------------------------
957 
958 // Code originally from krfcdate.cpp, but we don't want to use kdecore, and we want double range.
959 
960 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, double second)
961 {
962  // in which case is the floor() needed? breaks day value of
963  // "new Date('Thu Nov 5 2065 18:15:30 GMT+0500')"
964 #if 0
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)
969  - 2440588;
970 #else
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
975  - 2440588;
976 #endif
977  return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
978 }
979 
980 // We follow the recommendation of RFC 2822 to consider all
981 // obsolete time zones not listed here equivalent to "-0000".
982 static const struct KnownZone {
983 #if !PLATFORM(WIN_OS)
984  const
985 #endif
986  char tzName[4];
987  int tzOffset;
988 } known_zones[] = {
989  { "UT", 0 },
990  { "GMT", 0 },
991  { "EST", -300 },
992  { "EDT", -240 },
993  { "CST", -360 },
994  { "CDT", -300 },
995  { "MST", -420 },
996  { "MDT", -360 },
997  { "PST", -480 },
998  { "PDT", -420 }
999 };
1000 
1001 #if PLATFORM(WIN_OS)
1002 void FileTimeToUnixTime(LPFILETIME pft, double *pt)
1003 {
1004  ULARGE_INTEGER ull;
1005  ull.LowPart = pft->dwLowDateTime;
1006  ull.HighPart = pft->dwHighDateTime;
1007  *pt = (double)(ull.QuadPart / 10000000ULL) - 11644473600ULL;
1008 }
1009 
1010 void SystemTimeToUnixTime(LPSYSTEMTIME pst, double *pt)
1011 {
1012  FILETIME ft;
1013  SystemTimeToFileTime(pst, &ft);
1014  FileTimeToUnixTime(&ft, pt);
1015 }
1016 #endif
1017 
1018 static double makeTime(tm *t, double ms, bool utc)
1019 {
1020  int utcOffset;
1021  if (utc) {
1022  time_t zero = 0;
1023 #if PLATFORM(WIN_OS)
1024  // FIXME: not thread safe
1025  (void)localtime(&zero);
1026 #if defined(WTF_COMPILER_BORLAND) || defined(WTF_COMPILER_CYGWIN) || defined(WTF_COMPILER_MSVC)
1027  utcOffset = - _timezone;
1028 #else
1029  utcOffset = - timezone;
1030 #endif
1031  t->tm_isdst = 0;
1032 #elif PLATFORM(DARWIN)
1033  utcOffset = 0;
1034  t->tm_isdst = 0;
1035 #else
1036  tm t3;
1037  localtime_r(&zero, &t3);
1038  utcOffset = gmtoffset(t3);
1039  t->tm_isdst = t3.tm_isdst;
1040 #endif
1041  } else {
1042  utcOffset = 0;
1043  t->tm_isdst = -1;
1044  }
1045 
1046 #if !PLATFORM(WIN_OS)
1047  double yearOffset = 0.0;
1048  if (t->tm_year < (1971 - 1900) || t->tm_year > (2037 - 1900)) {
1049  // we'll fool mktime() into believing that this year is within
1050  // its normal, portable range (1970-2038) by setting tm_year to
1051  // 2000 or 2001 and adding the difference in milliseconds later.
1052  // choice between offset will depend on whether the year is a
1053  // leap year or not.
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;
1059  }
1060 
1061  // Determine whether DST is in effect. mktime() can't do this for us because
1062  // it doesn't know about ms and yearOffset.
1063  // NOTE: Casting values of large magnitude to time_t (long) will
1064  // produce incorrect results, but there's no other option when calling localtime_r().
1065  if (!utc) {
1066  time_t tval = mktime(t) + (time_t)((ms + yearOffset) / 1000);
1067  tm t3 = *localtime(&tval);
1068  t->tm_isdst = t3.tm_isdst;
1069  }
1070 
1071  return (mktime(t) + utcOffset) * msPerSecond + ms + yearOffset;
1072 #else
1073  SYSTEMTIME st, dt;
1074  double tval;
1075 
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;
1084 
1085  TzSpecificLocalTimeToSystemTime(0, &st, &dt);
1086  SystemTimeToUnixTime(&dt, &tval);
1087 
1088  return (tval + utcOffset) * msPerSecond + ms;
1089 #endif
1090 }
1091 
1092 inline static bool isSpaceLike(char c)
1093 {
1094  return isASCIISpace(c) || c == ',' || c == ':' || c == '-';
1095 }
1096 
1097 static const char *skipSpacesAndComments(const char *s)
1098 {
1099  int nesting = 0;
1100  char ch;
1101  while ((ch = *s)) {
1102  // interpret - before a number as a sign rather than a comment char
1103  if (ch == '-' && isASCIIDigit(*(s + 1))) {
1104  break;
1105  }
1106  if (!isSpaceLike(ch)) {
1107  if (ch == '(') {
1108  nesting++;
1109  } else if (ch == ')' && nesting > 0) {
1110  nesting--;
1111  } else if (nesting == 0) {
1112  break;
1113  }
1114  }
1115  s++;
1116  }
1117  return s;
1118 }
1119 
1120 // returns 0-11 (Jan-Dec); -1 on failure
1121 static int findMonth(const char *monthStr)
1122 {
1123  assert(monthStr);
1124  char needle[4];
1125  for (int i = 0; i < 3; ++i) {
1126  if (!*monthStr) {
1127  return -1;
1128  }
1129  needle[i] = toASCIILower(*monthStr++);
1130  }
1131  needle[3] = '\0';
1132  const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
1133  const char *str = strstr(haystack, needle);
1134  if (str) {
1135  int position = str - haystack;
1136  if (position % 3 == 0) {
1137  return position / 3;
1138  }
1139  }
1140  return -1;
1141 }
1142 
1143 static bool isTwoDigits(const char *str)
1144 {
1145  return isASCIIDigit(str[0]) && isASCIIDigit(str[1]);
1146 }
1147 
1148 static int twoDigit(const char *str)
1149 {
1150  return (str[0] - '0') * 10 + str[1] - '0';
1151 }
1152 
1153 static double parseDate(const UString &date)
1154 {
1155  // This parses a date in the form:
1156  // Tuesday, 09-Nov-99 23:12:40 GMT
1157  // or
1158  // Sat, 01-Jan-2000 08:00:00 GMT
1159  // or
1160  // Sat, 01 Jan 2000 08:00:00 GMT
1161  // or
1162  // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
1163  // ### non RFC formats, added for Javascript:
1164  // [Wednesday] January 09 1999 23:12:40 GMT
1165  // [Wednesday] January 09 23:12:40 GMT 1999
1166  //
1167  // We ignore the weekday.
1168 
1169  CString dateCString = date.UTF8String();
1170  const char *dateString = dateCString.c_str();
1171  if (!dateString) {
1172  return NaN;
1173  }
1174 
1175  // Skip leading space
1176  dateString = skipSpacesAndComments(dateString);
1177 
1178  // ISO 8601: "YYYY-MM-DD('T'|'t')hh:mm:ss[.S+]['Z']"
1179  // e.g. "2006-06-15T23:12:10.207830Z"
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) {
1190  return NaN;
1191  }
1192  int hour = 0, minute = 0;
1193  double second = 0;
1194  dateString += 10;
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) {
1202  return NaN;
1203  }
1204  dateString += 6;
1205  if (dateString[0] == ':' &&
1206  isTwoDigits(dateString + 1)) {
1207  second = twoDigit(dateString + 1);
1208  if (second > 59) {
1209  return NaN;
1210  }
1211  dateString += 3;
1212  if (dateString[0] == '.' &&
1213  isASCIIDigit(dateString[1])) {
1214  dateString++;
1215  double div = 10;
1216  do {
1217  second += (dateString[0] - '0') / div;
1218  div *= 10;
1219  } while (isASCIIDigit(*++dateString));
1220  }
1221  }
1222  }
1223 
1224  if (dateString[0] == 'Z') {
1225  tm t;
1226  memset(&t, 0, sizeof(tm));
1227  int secs = int(second);
1228  t.tm_sec = secs;
1229  t.tm_min = minute;
1230  t.tm_hour = hour;
1231  t.tm_mday = day;
1232  t.tm_mon = month;
1233  t.tm_year = year - 1900;
1234  // t.tm_isdst = -1;
1235 
1236  // Use our makeTime() rather than mktime() as the latter can't handle the full year range.
1237  return makeTime(&t, (second - secs) * 1000, true);
1238  }
1239 
1240  int offset = 0;
1241  return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
1242  }
1243 
1244  long month = -1;
1245  const char *wordStart = dateString;
1246  // Check contents of first words if not number
1247  while (*dateString && !isASCIIDigit(*dateString)) {
1248  if (isASCIISpace(*dateString) || *dateString == '(') {
1249  if (dateString - wordStart >= 3) {
1250  month = findMonth(wordStart);
1251  }
1252  dateString = skipSpacesAndComments(dateString);
1253  wordStart = dateString;
1254  } else {
1255  dateString++;
1256  }
1257  }
1258 
1259  // Missing delimiter between month and day (like "January29")?
1260  if (month == -1 && wordStart != dateString) {
1261  month = findMonth(wordStart);
1262  }
1263 
1264  dateString = skipSpacesAndComments(dateString);
1265 
1266  if (!*dateString) {
1267  return NaN;
1268  }
1269 
1270  // ' 09-Nov-99 23:12:40 GMT'
1271  char *newPosStr;
1272  errno = 0;
1273  long day = strtol(dateString, &newPosStr, 10);
1274  dateString = newPosStr;
1275 
1276  if (errno || day < 0 || !*dateString) {
1277  return NaN;
1278  }
1279 
1280  long year = 0;
1281  if (day > 31) {
1282  // ### where is the boundary and what happens below?
1283  if (*dateString != '/') {
1284  return NaN;
1285  }
1286  // looks like a YYYY/MM/DD date
1287  if (!*++dateString) {
1288  return NaN;
1289  }
1290  year = day;
1291  month = strtol(dateString, &newPosStr, 10) - 1;
1292  if (errno) {
1293  return NaN;
1294  }
1295  dateString = newPosStr;
1296  if (*dateString++ != '/' || !*dateString) {
1297  return NaN;
1298  }
1299  day = strtol(dateString, &newPosStr, 10);
1300  if (errno) {
1301  return NaN;
1302  }
1303  dateString = newPosStr;
1304  } else if (*dateString == '/' && month == -1) {
1305  dateString++;
1306  // This looks like a MM/DD/YYYY date, not an RFC date.
1307  month = day - 1; // 0-based
1308  day = strtol(dateString, &newPosStr, 10);
1309  if (errno) {
1310  return NaN;
1311  }
1312  dateString = newPosStr;
1313  if (*dateString == '/') {
1314  dateString++;
1315  }
1316  if (!*dateString) {
1317  return NaN;
1318  }
1319  } else {
1320  if (*dateString == '-') {
1321  dateString++;
1322  }
1323 
1324  dateString = skipSpacesAndComments(dateString);
1325 
1326  if (*dateString == ',') {
1327  dateString++;
1328  }
1329 
1330  if (month == -1) { // not found yet
1331  month = findMonth(dateString);
1332  if (month == -1) {
1333  return NaN;
1334  }
1335 
1336  while (*dateString && (*dateString != '-') && !isASCIISpace(*dateString)) {
1337  dateString++;
1338  }
1339 
1340  if (!*dateString) {
1341  return NaN;
1342  }
1343 
1344  // '-99 23:12:40 GMT'
1345  if (*dateString != '-' && *dateString != '/' && !isASCIISpace(*dateString)) {
1346  return NaN;
1347  }
1348  dateString++;
1349  }
1350 
1351  if (month < 0 || month > 11) {
1352  return NaN;
1353  }
1354  }
1355 
1356  // '99 23:12:40 GMT'
1357  if (year <= 0 && *dateString) {
1358  year = strtol(dateString, &newPosStr, 10);
1359  if (errno) {
1360  return NaN;
1361  }
1362  }
1363 
1364  // Don't fail if the time is missing.
1365  long hour = 0;
1366  long minute = 0;
1367  long second = 0;
1368  if (!*newPosStr) {
1369  dateString = newPosStr;
1370  } else {
1371  // ' 23:12:40 GMT'
1372  if (*newPosStr == ':') {
1373  // There was no year; the number was the hour.
1374  year = -1;
1375  } else if (isSpaceLike(*newPosStr)) {
1376  // in the normal case (we parsed the year), advance to the next number
1377  dateString = skipSpacesAndComments(newPosStr + 1);
1378  } else {
1379  return NaN;
1380  }
1381 
1382  hour = strtol(dateString, &newPosStr, 10);
1383  // Do not check for errno here since we want to continue
1384  // even if errno was set because we are still looking
1385  // for the timezone!
1386 
1387  // Read a number? If not, this might be a timezone name.
1388  if (newPosStr != dateString) {
1389  dateString = newPosStr;
1390 
1391  if (hour < 0 || hour > 23) {
1392  return NaN;
1393  }
1394 
1395  if (!*dateString) {
1396  return NaN;
1397  }
1398 
1399  // ':12:40 GMT'
1400  if (*dateString++ != ':') {
1401  return NaN;
1402  }
1403 
1404  minute = strtol(dateString, &newPosStr, 10);
1405  if (errno) {
1406  return NaN;
1407  }
1408  dateString = newPosStr;
1409 
1410  if (minute < 0 || minute > 59) {
1411  return NaN;
1412  }
1413 
1414  // ':40 GMT'
1415  if (*dateString && *dateString != ':' && !isASCIISpace(*dateString)) {
1416  return NaN;
1417  }
1418 
1419  // seconds are optional in rfc822 + rfc2822
1420  if (*dateString == ':') {
1421  dateString++;
1422 
1423  second = strtol(dateString, &newPosStr, 10);
1424  if (errno) {
1425  return NaN;
1426  }
1427  dateString = newPosStr;
1428 
1429  if (second < 0 || second > 59) {
1430  return NaN;
1431  }
1432 
1433  // disallow trailing colon seconds
1434  if (*dateString == ':') {
1435  return NaN;
1436  }
1437  }
1438 
1439  dateString = skipSpacesAndComments(dateString);
1440 
1441  if (strncasecmp(dateString, "AM", 2) == 0) {
1442  if (hour > 12) {
1443  return NaN;
1444  }
1445  if (hour == 12) {
1446  hour = 0;
1447  }
1448  dateString = skipSpacesAndComments(dateString + 2);
1449  } else if (strncasecmp(dateString, "PM", 2) == 0) {
1450  if (hour > 12) {
1451  return NaN;
1452  }
1453  if (hour != 12) {
1454  hour += 12;
1455  }
1456  dateString = skipSpacesAndComments(dateString + 2);
1457  }
1458  }
1459  }
1460 
1461  bool haveTZ = false;
1462  int offset = 0;
1463 
1464  // Don't fail if the time zone is missing.
1465  // Some websites omit the time zone (4275206).
1466  if (*dateString) {
1467  if (strncasecmp(dateString, "GMT", 3) == 0 ||
1468  strncasecmp(dateString, "UTC", 3) == 0) {
1469  dateString += 3;
1470  haveTZ = true;
1471  }
1472 
1473  if (*dateString == '+' || *dateString == '-') {
1474  long o = strtol(dateString, &newPosStr, 10);
1475  if (errno) {
1476  return NaN;
1477  }
1478  dateString = newPosStr;
1479 
1480  if (o < -9959 || o > 9959) {
1481  return NaN;
1482  }
1483 
1484  int sgn = (o < 0) ? -1 : 1;
1485  o = abs(o);
1486  if (*dateString != ':') {
1487  offset = ((o / 100) * 60 + (o % 100)) * sgn;
1488  } else { // GMT+05:00
1489  dateString++;
1490  long o2 = strtol(dateString, &newPosStr, 10);
1491  if (errno) {
1492  return NaN;
1493  }
1494  dateString = newPosStr;
1495  offset = (o * 60 + o2) * sgn;
1496  }
1497  haveTZ = true;
1498  } else {
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);
1503  haveTZ = true;
1504  break;
1505  }
1506  }
1507  }
1508  }
1509 
1510  dateString = skipSpacesAndComments(dateString);
1511 
1512  if (*dateString && year == -1) {
1513  year = strtol(dateString, &newPosStr, 10);
1514  if (errno) {
1515  return NaN;
1516  }
1517  dateString = newPosStr;
1518  }
1519 
1520  dateString = skipSpacesAndComments(dateString);
1521 
1522  // Trailing garbage
1523  if (*dateString) {
1524  return NaN;
1525  }
1526 
1527  // Y2K: Handle 2 digit years.
1528  if (year >= 0 && year < 100) {
1529  if (year < 50) {
1530  year += 2000;
1531  } else {
1532  year += 1900;
1533  }
1534  }
1535 
1536  // fall back to local timezone
1537  if (!haveTZ) {
1538  tm t;
1539  memset(&t, 0, sizeof(tm));
1540  t.tm_mday = day;
1541  t.tm_mon = month;
1542  t.tm_year = year - 1900;
1543  t.tm_isdst = -1;
1544  t.tm_sec = second;
1545  t.tm_min = minute;
1546  t.tm_hour = hour;
1547 
1548  // Use our makeTime() rather than mktime() as the latter can't handle the full year range.
1549  return makeTime(&t, 0, false);
1550  }
1551 
1552  return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
1553 }
1554 
1555 double timeClip(double t)
1556 {
1557  if (isNaN(t) || isInf(t)) {
1558  return NaN;
1559  }
1560  double at = fabs(t);
1561  if (at > 8.64E15) {
1562  return NaN;
1563  }
1564  return copysign(floor(at), t);
1565 }
1566 
1567 } // namespace KJS
bool implementsCall() const
Whether or not the value implements the call() method.
Definition: value.h:752
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
LocaleWrapper locale()
const char * name(StandardAction id)
static const List & empty()
Returns a pointer to a static instance of an empty list.
Definition: list.cpp:311
virtual QVariant callAsFunction(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args)
bool isNaN(double x)
bool isInf(double x)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Mar 27 2023 04:10:38 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.