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