7008c75c3c99925bc33c66035837b7771482750d
[WebKit-https.git] / JavaScriptCore / kjs / date_object.cpp
1 /*
2  *  This file is part of the KDE libraries
3  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
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 "config.h"
23 #include "date_object.h"
24 #include "date_object.lut.h"
25
26 #if HAVE(ERRNO_H)
27 #include <errno.h>
28 #endif
29
30 #if HAVE(SYS_PARAM_H)
31 #include <sys/param.h>
32 #endif
33
34 #if HAVE(SYS_TIME_H)
35 #include <sys/time.h>
36 #endif
37
38 #if HAVE(SYS_TIMEB_H)
39 #include <sys/timeb.h>
40 #endif
41
42 #include <ctype.h>
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 <string.h>
50 #include <time.h>
51
52 #include "error_object.h"
53 #include "operations.h"
54 #include <DateMath.h>
55
56 #include <wtf/MathExtras.h>
57 #include <wtf/StringExtras.h>
58
59 #if PLATFORM(MAC)
60     #include <CoreFoundation/CoreFoundation.h>
61 #endif
62
63 namespace KJS {
64
65 static double parseDate(const UString&);
66 static double timeClip(double);
67
68 inline int gmtoffset(const tm& t)
69 {
70 #if PLATFORM(WIN_OS)
71     // Time is supposed to be in the current timezone.
72     return -(_timezone / 60 - (t.tm_isdst != 0 ? 60 : 0 )) * 60;
73 #else
74     return t.tm_gmtoff;
75 #endif
76 }
77
78
79 /**
80  * @internal
81  *
82  * Class to implement all methods that are properties of the
83  * Date object
84  */
85 class DateObjectFuncImp : public InternalFunctionImp {
86 public:
87     DateObjectFuncImp(ExecState *, FunctionPrototype *, int i, int len, const Identifier& );
88
89     virtual JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args);
90
91     enum { Parse, UTC };
92
93 private:
94     int id;
95 };
96
97 #if PLATFORM(MAC)
98
99 static CFDateFormatterStyle styleFromArgString(const UString& string, CFDateFormatterStyle defaultStyle)
100 {
101     if (string == "short")
102         return kCFDateFormatterShortStyle;
103     if (string == "medium")
104         return kCFDateFormatterMediumStyle;
105     if (string == "long")
106         return kCFDateFormatterLongStyle;
107     if (string == "full")
108         return kCFDateFormatterFullStyle;
109     return defaultStyle;
110 }
111
112 static UString formatLocaleDate(ExecState *exec, double time, bool includeDate, bool includeTime, const List &args)
113 {
114     CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
115     CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
116
117     bool useCustomFormat = false;
118     UString customFormatString;
119
120     UString arg0String = args[0]->toString(exec);
121     if (arg0String == "custom" && !args[1]->isUndefined()) {
122         useCustomFormat = true;
123         customFormatString = args[1]->toString(exec);
124     } else if (includeDate && includeTime && !args[1]->isUndefined()) {
125         dateStyle = styleFromArgString(arg0String, dateStyle);
126         timeStyle = styleFromArgString(args[1]->toString(exec), timeStyle);
127     } else if (includeDate && !args[0]->isUndefined()) {
128         dateStyle = styleFromArgString(arg0String, dateStyle);
129     } else if (includeTime && !args[0]->isUndefined()) {
130         timeStyle = styleFromArgString(arg0String, timeStyle);
131     }
132
133     CFLocaleRef locale = CFLocaleCopyCurrent();
134     CFDateFormatterRef formatter = CFDateFormatterCreate(0, locale, dateStyle, timeStyle);
135     CFRelease(locale);
136
137     if (useCustomFormat) {
138         CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, (UniChar *)customFormatString.data(), customFormatString.size());
139         CFDateFormatterSetFormat(formatter, customFormatCFString);
140         CFRelease(customFormatCFString);
141     }
142
143     CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(0, formatter, time - kCFAbsoluteTimeIntervalSince1970);
144
145     CFRelease(formatter);
146
147     // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters).
148     // That's not great error handling, but it just won't happen so it doesn't matter.
149     UChar buffer[200];
150     const size_t bufferLength = sizeof(buffer) / sizeof(buffer[0]);
151     size_t length = CFStringGetLength(string);
152     assert(length <= bufferLength);
153     if (length > bufferLength)
154         length = bufferLength;
155     CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast<UniChar *>(buffer));
156
157     CFRelease(string);
158
159     return UString(buffer, length);
160 }
161
162 #endif // PLATFORM(MAC)
163
164 static UString formatDate(const tm &t)
165 {
166     char buffer[100];
167     snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
168         weekdayName[(t.tm_wday + 6) % 7],
169         monthName[t.tm_mon], t.tm_mday, t.tm_year + 1900);
170     return buffer;
171 }
172
173 static UString formatDateUTCVariant(const tm &t)
174 {
175     char buffer[100];
176     snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
177         weekdayName[(t.tm_wday + 6) % 7],
178         t.tm_mday, monthName[t.tm_mon], t.tm_year + 1900);
179     return buffer;
180 }
181
182 static UString formatTime(const tm &t, bool utc)
183 {
184     char buffer[100];
185     if (utc) {
186         snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.tm_hour, t.tm_min, t.tm_sec);
187     } else {
188         int offset = abs(gmtoffset(t));
189 #if PLATFORM(WIN_OS)
190         char tzname[70];
191         strftime(tzname, sizeof(tzname), "%Z", &t);
192 #else
193         const char *tzname = t.tm_zone;
194 #endif
195         if (tzname) {
196             snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d (%s)",
197                 t.tm_hour, t.tm_min, t.tm_sec,
198                 gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, tzname);
199         } else {
200             snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
201                 t.tm_hour, t.tm_min, t.tm_sec,
202                 gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
203         }
204     }
205     return UString(buffer);
206 }
207
208 // Converts a list of arguments sent to a Date member function into milliseconds, updating
209 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
210 //
211 // Format of member function: f([hour,] [min,] [sec,] [ms])
212 static void fillStructuresUsingTimeArgs(ExecState *exec, const List &args, int maxArgs, double *ms, tm *t)
213 {
214     double milliseconds = 0;
215     int idx = 0;
216     int numArgs = args.size();
217     
218     // JS allows extra trailing arguments -- ignore them
219     if (numArgs > maxArgs)
220         numArgs = maxArgs;
221
222     // hours
223     if (maxArgs >= 4 && idx < numArgs) {
224         t->tm_hour = 0;
225         milliseconds += args[idx++]->toInt32(exec) * msPerHour;
226     }
227
228     // minutes
229     if (maxArgs >= 3 && idx < numArgs) {
230         t->tm_min = 0;
231         milliseconds += args[idx++]->toInt32(exec) * msPerMinute;
232     }
233     
234     // seconds
235     if (maxArgs >= 2 && idx < numArgs) {
236         t->tm_sec = 0;
237         milliseconds += args[idx++]->toInt32(exec) * msPerSecond;
238     }
239     
240     // milliseconds
241     if (idx < numArgs) {
242         milliseconds += roundValue(exec, args[idx]);
243     } else {
244         milliseconds += *ms;
245     }
246     
247     *ms = milliseconds;
248 }
249
250 // Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating
251 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
252 //
253 // Format of member function: f([years,] [months,] [days])
254 static void fillStructuresUsingDateArgs(ExecState *exec, const List &args, int maxArgs, double *ms, tm *t)
255 {
256     int idx = 0;
257     int numArgs = args.size();
258   
259     // JS allows extra trailing arguments -- ignore them
260     if (numArgs > maxArgs)
261         numArgs = maxArgs;
262   
263     // years
264     if (maxArgs >= 3 && idx < numArgs)
265         t->tm_year = args[idx++]->toInt32(exec) - 1900;
266   
267     // months
268     if (maxArgs >= 2 && idx < numArgs)
269         t->tm_mon = args[idx++]->toInt32(exec);
270   
271     // days
272     if (idx < numArgs) {
273         t->tm_mday = 0;
274         *ms += args[idx]->toInt32(exec) * msPerDay;
275     }
276 }
277
278 // ------------------------------ DateInstance ------------------------------
279
280 const ClassInfo DateInstance::info = {"Date", 0, 0, 0};
281
282 DateInstance::DateInstance(JSObject *proto)
283   : JSWrapperObject(proto)
284 {
285 }
286
287 bool DateInstance::getTime(tm &t, int &offset) const
288 {
289     double milli = internalValue()->getNumber();
290     if (isNaN(milli))
291         return false;
292     
293     msToTM(milli, false, t);
294     offset = gmtoffset(t);
295     return true;
296 }
297
298 bool DateInstance::getUTCTime(tm &t) const
299 {
300     double milli = internalValue()->getNumber();
301     if (isNaN(milli))
302         return false;
303     
304     msToTM(milli, true, t);
305     return true;
306 }
307
308 bool DateInstance::getTime(double &milli, int &offset) const
309 {
310     milli = internalValue()->getNumber();
311     if (isNaN(milli))
312         return false;
313     
314     tm t;
315     msToTM(milli, false, t);
316     offset = gmtoffset(t);
317     return true;
318 }
319
320 bool DateInstance::getUTCTime(double &milli) const
321 {
322     milli = internalValue()->getNumber();
323     if (isNaN(milli))
324         return false;
325     
326     return true;
327 }
328
329 static inline bool isTime_tSigned()
330 {
331     time_t minusOne = (time_t)(-1);
332     return minusOne < 0;
333 }
334
335 // ------------------------------ DatePrototype -----------------------------
336
337 const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, &dateTable, 0};
338
339 /* Source for date_object.lut.h
340    We use a negative ID to denote the "UTC" variant.
341 @begin dateTable 61
342   toString              DateProtoFunc::ToString                 DontEnum|Function       0
343   toUTCString           -DateProtoFunc::ToUTCString             DontEnum|Function       0
344   toDateString          DateProtoFunc::ToDateString             DontEnum|Function       0
345   toTimeString          DateProtoFunc::ToTimeString             DontEnum|Function       0
346   toLocaleString        DateProtoFunc::ToLocaleString           DontEnum|Function       0
347   toLocaleDateString    DateProtoFunc::ToLocaleDateString       DontEnum|Function       0
348   toLocaleTimeString    DateProtoFunc::ToLocaleTimeString       DontEnum|Function       0
349   valueOf               DateProtoFunc::ValueOf                  DontEnum|Function       0
350   getTime               DateProtoFunc::GetTime                  DontEnum|Function       0
351   getFullYear           DateProtoFunc::GetFullYear              DontEnum|Function       0
352   getUTCFullYear        -DateProtoFunc::GetFullYear             DontEnum|Function       0
353   toGMTString           -DateProtoFunc::ToGMTString             DontEnum|Function       0
354   getMonth              DateProtoFunc::GetMonth                 DontEnum|Function       0
355   getUTCMonth           -DateProtoFunc::GetMonth                DontEnum|Function       0
356   getDate               DateProtoFunc::GetDate                  DontEnum|Function       0
357   getUTCDate            -DateProtoFunc::GetDate                 DontEnum|Function       0
358   getDay                DateProtoFunc::GetDay                   DontEnum|Function       0
359   getUTCDay             -DateProtoFunc::GetDay                  DontEnum|Function       0
360   getHours              DateProtoFunc::GetHours                 DontEnum|Function       0
361   getUTCHours           -DateProtoFunc::GetHours                DontEnum|Function       0
362   getMinutes            DateProtoFunc::GetMinutes               DontEnum|Function       0
363   getUTCMinutes         -DateProtoFunc::GetMinutes              DontEnum|Function       0
364   getSeconds            DateProtoFunc::GetSeconds               DontEnum|Function       0
365   getUTCSeconds         -DateProtoFunc::GetSeconds              DontEnum|Function       0
366   getMilliseconds       DateProtoFunc::GetMilliSeconds          DontEnum|Function       0
367   getUTCMilliseconds    -DateProtoFunc::GetMilliSeconds         DontEnum|Function       0
368   getTimezoneOffset     DateProtoFunc::GetTimezoneOffset        DontEnum|Function       0
369   setTime               DateProtoFunc::SetTime                  DontEnum|Function       1
370   setMilliseconds       DateProtoFunc::SetMilliSeconds          DontEnum|Function       1
371   setUTCMilliseconds    -DateProtoFunc::SetMilliSeconds         DontEnum|Function       1
372   setSeconds            DateProtoFunc::SetSeconds               DontEnum|Function       2
373   setUTCSeconds         -DateProtoFunc::SetSeconds              DontEnum|Function       2
374   setMinutes            DateProtoFunc::SetMinutes               DontEnum|Function       3
375   setUTCMinutes         -DateProtoFunc::SetMinutes              DontEnum|Function       3
376   setHours              DateProtoFunc::SetHours                 DontEnum|Function       4
377   setUTCHours           -DateProtoFunc::SetHours                DontEnum|Function       4
378   setDate               DateProtoFunc::SetDate                  DontEnum|Function       1
379   setUTCDate            -DateProtoFunc::SetDate                 DontEnum|Function       1
380   setMonth              DateProtoFunc::SetMonth                 DontEnum|Function       2
381   setUTCMonth           -DateProtoFunc::SetMonth                DontEnum|Function       2
382   setFullYear           DateProtoFunc::SetFullYear              DontEnum|Function       3
383   setUTCFullYear        -DateProtoFunc::SetFullYear             DontEnum|Function       3
384   setYear               DateProtoFunc::SetYear                  DontEnum|Function       1
385   getYear               DateProtoFunc::GetYear                  DontEnum|Function       0
386 @end
387 */
388 // ECMA 15.9.4
389
390 DatePrototype::DatePrototype(ExecState *, ObjectPrototype *objectProto)
391   : DateInstance(objectProto)
392 {
393     setInternalValue(jsNaN());
394     // The constructor will be added later, after DateObjectImp has been built.
395 }
396
397 bool DatePrototype::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
398 {
399     return getStaticFunctionSlot<DateProtoFunc, JSObject>(exec, &dateTable, this, propertyName, slot);
400 }
401
402 // ------------------------------ DateProtoFunc -----------------------------
403
404 DateProtoFunc::DateProtoFunc(ExecState *exec, int i, int len, const Identifier& name)
405   : InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
406   , id(abs(i))
407   , utc(i < 0)
408   // We use a negative ID to denote the "UTC" variant.
409 {
410     putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
411 }
412
413 JSValue *DateProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
414 {
415   if (!thisObj->inherits(&DateInstance::info))
416     return throwError(exec, TypeError);
417
418   DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
419
420   JSValue *result = 0;
421   UString s;
422 #if !PLATFORM(DARWIN)
423   const int bufsize=100;
424   char timebuffer[bufsize];
425   CString oldlocale = setlocale(LC_TIME, 0);
426   if (!oldlocale.size())
427     oldlocale = setlocale(LC_ALL, 0);
428   // FIXME: Where's the code to set the locale back to oldlocale?
429 #endif
430   JSValue *v = thisDateObj->internalValue();
431   double milli = v->toNumber(exec);
432   if (isNaN(milli)) {
433     switch (id) {
434       case ToString:
435       case ToDateString:
436       case ToTimeString:
437       case ToGMTString:
438       case ToUTCString:
439       case ToLocaleString:
440       case ToLocaleDateString:
441       case ToLocaleTimeString:
442         return jsString("Invalid Date");
443       case ValueOf:
444       case GetTime:
445       case GetYear:
446       case GetFullYear:
447       case GetMonth:
448       case GetDate:
449       case GetDay:
450       case GetHours:
451       case GetMinutes:
452       case GetSeconds:
453       case GetMilliSeconds:
454       case GetTimezoneOffset:
455         return jsNaN();
456     }
457   }
458   
459   double secs = floor(milli / msPerSecond);
460   double ms = milli - secs * msPerSecond;
461
462   tm t;
463   msToTM(milli, utc, t);
464
465   switch (id) {
466   case ToString:
467     return jsString(formatDate(t) + " " + formatTime(t, utc));
468   case ToDateString:
469     return jsString(formatDate(t));
470     break;
471   case ToTimeString:
472     return jsString(formatTime(t, utc));
473     break;
474   case ToGMTString:
475   case ToUTCString:
476     return jsString(formatDateUTCVariant(t) + " " + formatTime(t, utc));
477     break;
478 #if PLATFORM(MAC)
479   case ToLocaleString:
480     return jsString(formatLocaleDate(exec, secs, true, true, args));
481     break;
482   case ToLocaleDateString:
483     return jsString(formatLocaleDate(exec, secs, true, false, args));
484     break;
485   case ToLocaleTimeString:
486     return jsString(formatLocaleDate(exec, secs, false, true, args));
487     break;
488 #else
489   case ToLocaleString:
490     strftime(timebuffer, bufsize, "%c", &t);
491     return jsString(timebuffer);
492     break;
493   case ToLocaleDateString:
494     strftime(timebuffer, bufsize, "%x", &t);
495     return jsString(timebuffer);
496     break;
497   case ToLocaleTimeString:
498     strftime(timebuffer, bufsize, "%X", &t);
499     return jsString(timebuffer);
500     break;
501 #endif
502   case ValueOf:
503   case GetTime:
504     return jsNumber(milli);
505   case GetYear:
506     // IE returns the full year even in getYear.
507     if (exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat)
508       return jsNumber(1900 + t.tm_year);
509     return jsNumber(t.tm_year);
510   case GetFullYear:
511     return jsNumber(1900 + t.tm_year);
512   case GetMonth:
513     return jsNumber(t.tm_mon);
514   case GetDate:
515     return jsNumber(t.tm_mday);
516   case GetDay:
517     return jsNumber(t.tm_wday);
518   case GetHours:
519     return jsNumber(t.tm_hour);
520   case GetMinutes:
521     return jsNumber(t.tm_min);
522   case GetSeconds:
523     return jsNumber(t.tm_sec);
524   case GetMilliSeconds:
525     return jsNumber(ms);
526   case GetTimezoneOffset:
527     return jsNumber(-gmtoffset(t) / 60);
528   case SetTime:
529     milli = roundValue(exec, args[0]);
530     result = jsNumber(milli);
531     thisDateObj->setInternalValue(result);
532     break;
533   case SetMilliSeconds:
534     fillStructuresUsingTimeArgs(exec, args, 1, &ms, &t);
535     break;
536   case SetSeconds:
537     fillStructuresUsingTimeArgs(exec, args, 2, &ms, &t);
538     break;
539   case SetMinutes:
540     fillStructuresUsingTimeArgs(exec, args, 3, &ms, &t);
541     break;
542   case SetHours:
543     fillStructuresUsingTimeArgs(exec, args, 4, &ms, &t);
544     break;
545   case SetDate:
546     fillStructuresUsingDateArgs(exec, args, 1, &ms, &t);
547     break;
548   case SetMonth:
549     fillStructuresUsingDateArgs(exec, args, 2, &ms, &t);    
550     break;
551   case SetFullYear:
552     fillStructuresUsingDateArgs(exec, args, 3, &ms, &t);
553     break;
554   case SetYear:
555     t.tm_year = (args[0]->toInt32(exec) > 99 || args[0]->toInt32(exec) < 0) ? args[0]->toInt32(exec) - 1900 : args[0]->toInt32(exec);
556     break;
557   }
558
559   if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
560       id == SetMinutes || id == SetHours || id == SetDate ||
561       id == SetMonth || id == SetFullYear ) {
562     result = jsNumber(dateToMseconds(&t, ms, utc));
563     thisDateObj->setInternalValue(result);
564   }
565   
566   return result;
567 }
568
569 // ------------------------------ DateObjectImp --------------------------------
570
571 // TODO: MakeTime (15.9.11.1) etc. ?
572
573 DateObjectImp::DateObjectImp(ExecState *exec,
574                              FunctionPrototype *funcProto,
575                              DatePrototype *dateProto)
576   : InternalFunctionImp(funcProto)
577 {
578   // ECMA 15.9.4.1 Date.prototype
579   putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
580
581   static const Identifier parsePropertyName("parse");
582   putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, parsePropertyName), DontEnum);
583   static const Identifier UTCPropertyName("UTC");
584   putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, UTCPropertyName), DontEnum);
585
586   // no. of arguments for constructor
587   putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
588 }
589
590 bool DateObjectImp::implementsConstruct() const
591 {
592     return true;
593 }
594
595 // ECMA 15.9.3
596 JSObject *DateObjectImp::construct(ExecState *exec, const List &args)
597 {
598   int numArgs = args.size();
599
600   double value;
601
602   if (numArgs == 0) { // new Date() ECMA 15.9.3.3
603 #if PLATFORM(WIN_OS)
604 #if COMPILER(BORLAND)
605     struct timeb timebuffer;
606     ftime(&timebuffer);
607 #else
608     struct _timeb timebuffer;
609     _ftime(&timebuffer);
610 #endif
611     double utc = timebuffer.time * msPerSecond + timebuffer.millitm;
612 #else
613     struct timeval tv;
614     gettimeofday(&tv, 0);
615     double utc = floor(tv.tv_sec * msPerSecond + tv.tv_usec / 1000);
616 #endif
617     value = utc;
618   } else if (numArgs == 1) {
619     if (args[0]->isObject(&DateInstance::info))
620       value = static_cast<DateInstance*>(args[0])->internalValue()->toNumber(exec);
621     else {
622       JSValue* primitive = args[0]->toPrimitive(exec);
623       if (primitive->isString())
624         value = parseDate(primitive->getString());
625       else
626         value = primitive->toNumber(exec);
627     }
628   } else {
629     if (isNaN(args[0]->toNumber(exec))
630         || isNaN(args[1]->toNumber(exec))
631         || (numArgs >= 3 && isNaN(args[2]->toNumber(exec)))
632         || (numArgs >= 4 && isNaN(args[3]->toNumber(exec)))
633         || (numArgs >= 5 && isNaN(args[4]->toNumber(exec)))
634         || (numArgs >= 6 && isNaN(args[5]->toNumber(exec)))
635         || (numArgs >= 7 && isNaN(args[6]->toNumber(exec)))) {
636       value = NaN;
637     } else {
638       tm t;
639       memset(&t, 0, sizeof(t));
640       int year = args[0]->toInt32(exec);
641       t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
642       t.tm_mon = args[1]->toInt32(exec);
643       t.tm_mday = (numArgs >= 3) ? args[2]->toInt32(exec) : 1;
644       t.tm_hour = (numArgs >= 4) ? args[3]->toInt32(exec) : 0;
645       t.tm_min = (numArgs >= 5) ? args[4]->toInt32(exec) : 0;
646       t.tm_sec = (numArgs >= 6) ? args[5]->toInt32(exec) : 0;
647       t.tm_isdst = -1;
648       double ms = (numArgs >= 7) ? roundValue(exec, args[6]) : 0;
649       value = dateToMseconds(&t, ms, false);
650     }
651   }
652   
653   DateInstance *ret = new DateInstance(exec->lexicalInterpreter()->builtinDatePrototype());
654   ret->setInternalValue(jsNumber(timeClip(value)));
655   return ret;
656 }
657
658 // ECMA 15.9.2
659 JSValue *DateObjectImp::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/)
660 {
661     time_t t = time(0);
662     tm ts = *localtime(&t);
663     return jsString(formatDate(ts) + " " + formatTime(ts, false));
664 }
665
666 // ------------------------------ DateObjectFuncImp ----------------------------
667
668 DateObjectFuncImp::DateObjectFuncImp(ExecState*, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
669     : InternalFunctionImp(funcProto, name), id(i)
670 {
671     putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
672 }
673
674 // ECMA 15.9.4.2 - 3
675 JSValue *DateObjectFuncImp::callAsFunction(ExecState* exec, JSObject*, const List& args)
676 {
677   if (id == Parse) {
678     return jsNumber(parseDate(args[0]->toString(exec)));
679   }
680   else { // UTC
681     int n = args.size();
682     if (isNaN(args[0]->toNumber(exec))
683         || isNaN(args[1]->toNumber(exec))
684         || (n >= 3 && isNaN(args[2]->toNumber(exec)))
685         || (n >= 4 && isNaN(args[3]->toNumber(exec)))
686         || (n >= 5 && isNaN(args[4]->toNumber(exec)))
687         || (n >= 6 && isNaN(args[5]->toNumber(exec)))
688         || (n >= 7 && isNaN(args[6]->toNumber(exec)))) {
689       return jsNaN();
690     }
691
692     tm t;
693     memset(&t, 0, sizeof(t));
694     int year = args[0]->toInt32(exec);
695     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
696     t.tm_mon = args[1]->toInt32(exec);
697     t.tm_mday = (n >= 3) ? args[2]->toInt32(exec) : 1;
698     t.tm_hour = (n >= 4) ? args[3]->toInt32(exec) : 0;
699     t.tm_min = (n >= 5) ? args[4]->toInt32(exec) : 0;
700     t.tm_sec = (n >= 6) ? args[5]->toInt32(exec) : 0;
701     double ms = (n >= 7) ? roundValue(exec, args[6]) : 0;
702     return jsNumber(dateToMseconds(&t, ms, true));
703   }
704 }
705
706 // -----------------------------------------------------------------------------
707
708 // Code originally from krfcdate.cpp, but we don't want to use kdecore, and we want double range.
709
710 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second)
711 {
712     double days = (day - 32075)
713         + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
714         + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
715         - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
716         - 2440588;
717     return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
718 }
719
720 // We follow the recommendation of RFC 2822 to consider all
721 // obsolete time zones not listed here equivalent to "-0000".
722 static const struct KnownZone {
723 #if !PLATFORM(WIN_OS)
724     const
725 #endif
726         char tzName[4];
727     int tzOffset;
728 } known_zones[] = {
729     { "UT", 0 },
730     { "GMT", 0 },
731     { "EST", -300 },
732     { "EDT", -240 },
733     { "CST", -360 },
734     { "CDT", -300 },
735     { "MST", -420 },
736     { "MDT", -360 },
737     { "PST", -480 },
738     { "PDT", -420 }
739 };
740
741 inline static void skipSpacesAndComments(const char *&s)
742 {
743     int nesting = 0;
744     char ch;
745     while ((ch = *s)) {
746         if (!isspace(ch)) {
747             if (ch == '(')
748                 nesting++;
749             else if (ch == ')' && nesting > 0)
750                 nesting--;
751             else if (nesting == 0)
752                 break;
753         }
754         s++;
755     }
756 }
757
758 // returns 0-11 (Jan-Dec); -1 on failure
759 static int findMonth(const char *monthStr)
760 {
761     assert(monthStr);
762     char needle[4];
763     for (int i = 0; i < 3; ++i) {
764         if (!*monthStr)
765             return -1;
766         needle[i] = tolower(*monthStr++);
767     }
768     needle[3] = '\0';
769     const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
770     const char *str = strstr(haystack, needle);
771     if (str) {
772         int position = str - haystack;
773         if (position % 3 == 0)
774             return position / 3;
775     }
776     return -1;
777 }
778
779 static double parseDate(const UString &date)
780 {
781     // This parses a date in the form:
782     //     Tuesday, 09-Nov-99 23:12:40 GMT
783     // or
784     //     Sat, 01-Jan-2000 08:00:00 GMT
785     // or
786     //     Sat, 01 Jan 2000 08:00:00 GMT
787     // or
788     //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
789     // ### non RFC formats, added for Javascript:
790     //     [Wednesday] January 09 1999 23:12:40 GMT
791     //     [Wednesday] January 09 23:12:40 GMT 1999
792     //
793     // We ignore the weekday.
794
795     CString dateCString = date.UTF8String();
796     const char *dateString = dateCString.c_str();
797      
798     // Skip leading space
799     skipSpacesAndComments(dateString);
800
801     long month = -1;
802     const char *wordStart = dateString;
803     // Check contents of first words if not number
804     while (*dateString && !isdigit(*dateString)) {
805         if (isspace(*dateString) || *dateString == '(') {
806             if (dateString - wordStart >= 3)
807                 month = findMonth(wordStart);
808             skipSpacesAndComments(dateString);
809             wordStart = dateString;
810         } else
811            dateString++;
812     }
813
814     // Missing delimiter between month and day (like "January29")?
815     if (month == -1 && dateString && wordStart != dateString)
816         month = findMonth(wordStart);
817
818     skipSpacesAndComments(dateString);
819
820     if (!*dateString)
821         return NaN;
822
823     // ' 09-Nov-99 23:12:40 GMT'
824     char *newPosStr;
825     errno = 0;
826     long day = strtol(dateString, &newPosStr, 10);
827     if (errno)
828         return NaN;
829     dateString = newPosStr;
830
831     if (!*dateString)
832         return NaN;
833
834     if (day < 0)
835         return NaN;
836
837     long year = 0;
838     if (day > 31) {
839         // ### where is the boundary and what happens below?
840         if (*dateString != '/')
841             return NaN;
842         // looks like a YYYY/MM/DD date
843         if (!*++dateString)
844             return NaN;
845         year = day;
846         month = strtol(dateString, &newPosStr, 10) - 1;
847         if (errno)
848             return NaN;
849         dateString = newPosStr;
850         if (*dateString++ != '/' || !*dateString)
851             return NaN;
852         day = strtol(dateString, &newPosStr, 10);
853         if (errno)
854             return NaN;
855         dateString = newPosStr;
856     } else if (*dateString == '/' && month == -1) {
857         dateString++;
858         // This looks like a MM/DD/YYYY date, not an RFC date.
859         month = day - 1; // 0-based
860         day = strtol(dateString, &newPosStr, 10);
861         if (errno)
862             return NaN;
863         if (day < 1 || day > 31)
864             return NaN;
865         dateString = newPosStr;
866         if (*dateString == '/')
867             dateString++;
868         if (!*dateString)
869             return NaN;
870      } else {
871         if (*dateString == '-')
872             dateString++;
873
874         skipSpacesAndComments(dateString);
875
876         if (*dateString == ',')
877             dateString++;
878
879         if (month == -1) { // not found yet
880             month = findMonth(dateString);
881             if (month == -1)
882                 return NaN;
883
884             while (*dateString && (*dateString != '-') && !isspace(*dateString))
885                 dateString++;
886
887             if (!*dateString)
888                 return NaN;
889
890             // '-99 23:12:40 GMT'
891             if (*dateString != '-' && *dateString != '/' && !isspace(*dateString))
892                 return NaN;
893             dateString++;
894         }
895     }
896
897     if (month < 0 || month > 11)
898         return NaN;
899
900     // '99 23:12:40 GMT'
901     if (year <= 0 && *dateString) {
902         year = strtol(dateString, &newPosStr, 10);
903         if (errno)
904             return NaN;
905     }
906     
907     // Don't fail if the time is missing.
908     long hour = 0;
909     long minute = 0;
910     long second = 0;
911     if (!*newPosStr)
912         dateString = newPosStr;
913     else {
914         // ' 23:12:40 GMT'
915         if (!isspace(*newPosStr)) {
916             if (*newPosStr != ':')
917                 return NaN;
918             // There was no year; the number was the hour.
919             year = -1;
920         } else {
921             // in the normal case (we parsed the year), advance to the next number
922             dateString = ++newPosStr;
923             skipSpacesAndComments(dateString);
924         }
925
926         hour = strtol(dateString, &newPosStr, 10);
927         // Do not check for errno here since we want to continue
928         // even if errno was set becasue we are still looking
929         // for the timezone!
930
931         // Read a number? If not, this might be a timezone name.
932         if (newPosStr != dateString) {
933             dateString = newPosStr;
934
935             if (hour < 0 || hour > 23)
936                 return NaN;
937
938             if (!*dateString)
939                 return NaN;
940
941             // ':12:40 GMT'
942             if (*dateString++ != ':')
943                 return NaN;
944
945             minute = strtol(dateString, &newPosStr, 10);
946             if (errno)
947                 return NaN;
948             dateString = newPosStr;
949
950             if (minute < 0 || minute > 59)
951                 return NaN;
952
953             // ':40 GMT'
954             if (*dateString && *dateString != ':' && !isspace(*dateString))
955                 return NaN;
956
957             // seconds are optional in rfc822 + rfc2822
958             if (*dateString ==':') {
959                 dateString++;
960
961                 second = strtol(dateString, &newPosStr, 10);
962                 if (errno)
963                     return NaN;
964                 dateString = newPosStr;
965             
966                 if (second < 0 || second > 59)
967                     return NaN;
968             }
969
970             skipSpacesAndComments(dateString);
971
972             if (strncasecmp(dateString, "AM", 2) == 0) {
973                 if (hour > 12)
974                     return NaN;
975                 if (hour == 12)
976                     hour = 0;
977                 dateString += 2;
978                 skipSpacesAndComments(dateString);
979             } else if (strncasecmp(dateString, "PM", 2) == 0) {
980                 if (hour > 12)
981                     return NaN;
982                 if (hour != 12)
983                     hour += 12;
984                 dateString += 2;
985                 skipSpacesAndComments(dateString);
986             }
987         }
988     }
989
990     bool haveTZ = false;
991     int offset = 0;
992
993     // Don't fail if the time zone is missing. 
994     // Some websites omit the time zone (4275206).
995     if (*dateString) {
996         if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
997             dateString += 3;
998             haveTZ = true;
999         }
1000
1001         if (*dateString == '+' || *dateString == '-') {
1002             long o = strtol(dateString, &newPosStr, 10);
1003             if (errno)
1004                 return NaN;
1005             dateString = newPosStr;
1006
1007             if (o < -9959 || o > 9959)
1008                 return NaN;
1009
1010             int sgn = (o < 0) ? -1 : 1;
1011             o = abs(o);
1012             if (*dateString != ':') {
1013                 offset = ((o / 100) * 60 + (o % 100)) * sgn;
1014             } else { // GMT+05:00
1015                 long o2 = strtol(dateString, &newPosStr, 10);
1016                 if (errno)
1017                     return NaN;
1018                 dateString = newPosStr;
1019                 offset = (o * 60 + o2) * sgn;
1020             }
1021             haveTZ = true;
1022         } else {
1023             for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) {
1024                 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
1025                     offset = known_zones[i].tzOffset;
1026                     dateString += strlen(known_zones[i].tzName);
1027                     haveTZ = true;
1028                     break;
1029                 }
1030             }
1031         }
1032     }
1033
1034     skipSpacesAndComments(dateString);
1035
1036     if (*dateString && year == -1) {
1037         year = strtol(dateString, &newPosStr, 10);
1038         if (errno)
1039             return NaN;
1040         dateString = newPosStr;
1041     }
1042      
1043     skipSpacesAndComments(dateString);
1044      
1045     // Trailing garbage
1046     if (*dateString)
1047         return NaN;
1048
1049     // Y2K: Handle 2 digit years.
1050     if (year >= 0 && year < 100) {
1051         if (year < 50)
1052             year += 2000;
1053         else
1054             year += 1900;
1055     }
1056
1057     // fall back to local timezone
1058     if (!haveTZ) {
1059         tm t;
1060         memset(&t, 0, sizeof(tm));
1061         t.tm_mday = day;
1062         t.tm_mon = month;
1063         t.tm_year = year - 1900;
1064         t.tm_isdst = -1;
1065         t.tm_sec = second;
1066         t.tm_min = minute;
1067         t.tm_hour = hour;
1068
1069         // Use our dateToMseconds() rather than mktime() as the latter can't handle the full year range.
1070         return dateToMseconds(&t, 0, false);
1071     }
1072
1073     return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
1074 }
1075
1076 double timeClip(double t)
1077 {
1078     if (!isfinite(t))
1079         return NaN;
1080     double at = fabs(t);
1081     if (at > 8.64E15)
1082         return NaN;
1083     return copysign(floor(at), t);
1084 }
1085
1086 }