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