JavaScriptCore:
[WebKit-https.git] / JavaScriptCore / kjs / date_object.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
18  *  USA
19  *
20  */
21
22 #include "config.h"
23 #include "date_object.h"
24 #include "date_object.lut.h"
25 #include "internal.h"
26
27 #if HAVE(ERRNO_H)
28 #include <errno.h>
29 #endif
30
31 #if HAVE(SYS_PARAM_H)
32 #include <sys/param.h>
33 #endif
34
35 #if HAVE(SYS_TIME_H)
36 #include <sys/time.h>
37 #endif
38
39 #if HAVE(SYS_TIMEB_H)
40 #include <sys/timeb.h>
41 #endif
42
43 #include <float.h>
44 #include <limits.h>
45 #include <locale.h>
46 #include <math.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <time.h>
51
52 #include "error_object.h"
53 #include "operations.h"
54 #include "DateMath.h"
55
56 #include <wtf/ASCIICType.h>
57 #include <wtf/Assertions.h>
58 #include <wtf/MathExtras.h>
59 #include <wtf/StringExtras.h>
60 #include <wtf/UnusedParam.h>
61
62 #if PLATFORM(MAC)
63     #include <CoreFoundation/CoreFoundation.h>
64 #endif
65
66 using namespace WTF;
67
68 namespace KJS {
69
70 static double parseDate(const UString&);
71 static double timeClip(double);
72
73 inline int gmtoffset(const GregorianDateTime& t)
74 {
75     return t.utcOffset;
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 #else
163
164 enum LocaleDateTimeFormat { LocaleDateAndTime, LocaleDate, LocaleTime };
165  
166 static JSCell* formatLocaleDate(const GregorianDateTime& gdt, const LocaleDateTimeFormat format)
167 {
168     static const char* formatStrings[] = {"%#c", "%#x", "%X"};
169  
170     // Offset year if needed
171     struct tm localTM = gdt;
172     int year = gdt.year + 1900;
173     bool yearNeedsOffset = year < 1900 || year > 2038;
174     if (yearNeedsOffset) {
175         localTM.tm_year = equivalentYearForDST(year) - 1900;
176      }
177  
178     // Do the formatting
179     const int bufsize=128;
180     char timebuffer[bufsize];
181     size_t ret = strftime(timebuffer, bufsize, formatStrings[format], &localTM);
182  
183     if ( ret == 0 )
184         return jsString("");
185  
186     // Copy original into the buffer
187     if (yearNeedsOffset && format != LocaleTime) {
188         static const int yearLen = 5;   // FIXME will be a problem in the year 10,000
189         char yearString[yearLen];
190  
191         snprintf(yearString, yearLen, "%d", localTM.tm_year + 1900);
192         char* yearLocation = strstr(timebuffer, yearString);
193         snprintf(yearString, yearLen, "%d", year);
194  
195         strncpy(yearLocation, yearString, yearLen - 1);
196     }
197  
198     return jsString(timebuffer);
199 }
200
201 #endif // PLATFORM(WIN_OS)
202
203 static UString formatDate(const GregorianDateTime &t)
204 {
205     char buffer[100];
206     snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
207         weekdayName[(t.weekDay + 6) % 7],
208         monthName[t.month], t.monthDay, t.year + 1900);
209     return buffer;
210 }
211
212 static UString formatDateUTCVariant(const GregorianDateTime &t)
213 {
214     char buffer[100];
215     snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
216         weekdayName[(t.weekDay + 6) % 7],
217         t.monthDay, monthName[t.month], t.year + 1900);
218     return buffer;
219 }
220
221 static UString formatTime(const GregorianDateTime &t, bool utc)
222 {
223     char buffer[100];
224     if (utc) {
225         snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.hour, t.minute, t.second);
226     } else {
227         int offset = abs(gmtoffset(t));
228         char tzname[70];
229         struct tm gtm = t;
230         strftime(tzname, sizeof(tzname), "%Z", &gtm);
231
232         if (tzname[0]) {
233             snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d (%s)",
234                 t.hour, t.minute, t.second,
235                 gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, tzname);
236         } else {
237             snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
238                 t.hour, t.minute, t.second,
239                 gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
240         }
241     }
242     return UString(buffer);
243 }
244
245 // Converts a list of arguments sent to a Date member function into milliseconds, updating
246 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
247 //
248 // Format of member function: f([hour,] [min,] [sec,] [ms])
249 static bool fillStructuresUsingTimeArgs(ExecState* exec, const List& args, int maxArgs, double* ms, GregorianDateTime* t)
250 {
251     double milliseconds = 0;
252     bool ok = true;
253     int idx = 0;
254     int numArgs = args.size();
255     
256     // JS allows extra trailing arguments -- ignore them
257     if (numArgs > maxArgs)
258         numArgs = maxArgs;
259
260     // hours
261     if (maxArgs >= 4 && idx < numArgs) {
262         t->hour = 0;
263         milliseconds += args[idx++]->toInt32(exec, ok) * msPerHour;
264     }
265
266     // minutes
267     if (maxArgs >= 3 && idx < numArgs && ok) {
268         t->minute = 0;
269         milliseconds += args[idx++]->toInt32(exec, ok) * msPerMinute;
270     }
271     
272     // seconds
273     if (maxArgs >= 2 && idx < numArgs && ok) {
274         t->second = 0;
275         milliseconds += args[idx++]->toInt32(exec, ok) * msPerSecond;
276     }
277     
278     if (!ok)
279         return false;
280         
281     // milliseconds
282     if (idx < numArgs) {
283         double millis = args[idx]->toNumber(exec);
284         ok = isfinite(millis);
285         milliseconds += millis;
286     } else
287         milliseconds += *ms;
288     
289     *ms = milliseconds;
290     return ok;
291 }
292
293 // Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating
294 // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
295 //
296 // Format of member function: f([years,] [months,] [days])
297 static bool fillStructuresUsingDateArgs(ExecState *exec, const List &args, int maxArgs, double *ms, GregorianDateTime *t)
298 {
299     int idx = 0;
300     bool ok = true;
301     int numArgs = args.size();
302   
303     // JS allows extra trailing arguments -- ignore them
304     if (numArgs > maxArgs)
305         numArgs = maxArgs;
306   
307     // years
308     if (maxArgs >= 3 && idx < numArgs)
309         t->year = args[idx++]->toInt32(exec, ok) - 1900;
310     
311     // months
312     if (maxArgs >= 2 && idx < numArgs && ok)   
313         t->month = args[idx++]->toInt32(exec, ok);
314     
315     // days
316     if (idx < numArgs && ok) {   
317         t->monthDay = 0;
318         *ms += args[idx]->toInt32(exec, ok) * msPerDay;
319     }
320     
321     return ok;
322 }
323
324 // ------------------------------ DateInstance ------------------------------
325
326 const ClassInfo DateInstance::info = {"Date", 0, 0};
327
328 DateInstance::DateInstance(JSObject *proto)
329   : JSWrapperObject(proto)
330 {
331 }
332
333 bool DateInstance::getTime(GregorianDateTime &t, int &offset) const
334 {
335     double milli = internalValue()->getNumber();
336     if (isnan(milli))
337         return false;
338     
339     msToGregorianDateTime(milli, false, t);
340     offset = gmtoffset(t);
341     return true;
342 }
343
344 bool DateInstance::getUTCTime(GregorianDateTime &t) const
345 {
346     double milli = internalValue()->getNumber();
347     if (isnan(milli))
348         return false;
349     
350     msToGregorianDateTime(milli, true, t);
351     return true;
352 }
353
354 bool DateInstance::getTime(double &milli, int &offset) const
355 {
356     milli = internalValue()->getNumber();
357     if (isnan(milli))
358         return false;
359     
360     GregorianDateTime t;
361     msToGregorianDateTime(milli, false, t);
362     offset = gmtoffset(t);
363     return true;
364 }
365
366 bool DateInstance::getUTCTime(double &milli) const
367 {
368     milli = internalValue()->getNumber();
369     if (isnan(milli))
370         return false;
371     
372     return true;
373 }
374
375 static inline bool isTime_tSigned()
376 {
377     time_t minusOne = (time_t)(-1);
378     return minusOne < 0;
379 }
380
381 // ------------------------------ DatePrototype -----------------------------
382
383 const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, &dateTable};
384
385 /* Source for date_object.lut.h
386    FIXMEL We could use templates to simplify the UTC variants.
387 @begin dateTable 61
388   toString              dateProtoFuncToString                DontEnum|Function       0
389   toUTCString           dateProtoFuncToUTCString             DontEnum|Function       0
390   toDateString          dateProtoFuncToDateString            DontEnum|Function       0
391   toTimeString          dateProtoFuncToTimeString            DontEnum|Function       0
392   toLocaleString        dateProtoFuncToLocaleString          DontEnum|Function       0
393   toLocaleDateString    dateProtoFuncToLocaleDateString      DontEnum|Function       0
394   toLocaleTimeString    dateProtoFuncToLocaleTimeString      DontEnum|Function       0
395   valueOf               dateProtoFuncValueOf                 DontEnum|Function       0
396   getTime               dateProtoFuncGetTime                 DontEnum|Function       0
397   getFullYear           dateProtoFuncGetFullYear             DontEnum|Function       0
398   getUTCFullYear        dateProtoFuncGetUTCFullYear          DontEnum|Function       0
399   toGMTString           dateProtoFuncToGMTString             DontEnum|Function       0
400   getMonth              dateProtoFuncGetMonth                DontEnum|Function       0
401   getUTCMonth           dateProtoFuncGetUTCMonth             DontEnum|Function       0
402   getDate               dateProtoFuncGetDate                 DontEnum|Function       0
403   getUTCDate            dateProtoFuncGetUTCDate              DontEnum|Function       0
404   getDay                dateProtoFuncGetDay                  DontEnum|Function       0
405   getUTCDay             dateProtoFuncGetUTCDay               DontEnum|Function       0
406   getHours              dateProtoFuncGetHours                DontEnum|Function       0
407   getUTCHours           dateProtoFuncGetUTCHours             DontEnum|Function       0
408   getMinutes            dateProtoFuncGetMinutes              DontEnum|Function       0
409   getUTCMinutes         dateProtoFuncGetUTCMinutes           DontEnum|Function       0
410   getSeconds            dateProtoFuncGetSeconds              DontEnum|Function       0
411   getUTCSeconds         dateProtoFuncGetUTCSeconds           DontEnum|Function       0
412   getMilliseconds       dateProtoFuncGetMilliSeconds         DontEnum|Function       0
413   getUTCMilliseconds    dateProtoFuncGetUTCMilliseconds      DontEnum|Function       0
414   getTimezoneOffset     dateProtoFuncGetTimezoneOffset       DontEnum|Function       0
415   setTime               dateProtoFuncSetTime                 DontEnum|Function       1
416   setMilliseconds       dateProtoFuncSetMilliSeconds         DontEnum|Function       1
417   setUTCMilliseconds    dateProtoFuncSetUTCMilliseconds      DontEnum|Function       1
418   setSeconds            dateProtoFuncSetSeconds              DontEnum|Function       2
419   setUTCSeconds         dateProtoFuncSetUTCSeconds           DontEnum|Function       2
420   setMinutes            dateProtoFuncSetMinutes              DontEnum|Function       3
421   setUTCMinutes         dateProtoFuncSetUTCMinutes           DontEnum|Function       3
422   setHours              dateProtoFuncSetHours                DontEnum|Function       4
423   setUTCHours           dateProtoFuncSetUTCHours             DontEnum|Function       4
424   setDate               dateProtoFuncSetDate                 DontEnum|Function       1
425   setUTCDate            dateProtoFuncSetUTCDate              DontEnum|Function       1
426   setMonth              dateProtoFuncSetMonth                DontEnum|Function       2
427   setUTCMonth           dateProtoFuncSetUTCMonth             DontEnum|Function       2
428   setFullYear           dateProtoFuncSetFullYear             DontEnum|Function       3
429   setUTCFullYear        dateProtoFuncSetUTCFullYear          DontEnum|Function       3
430   setYear               dateProtoFuncSetYear                 DontEnum|Function       1
431   getYear               dateProtoFuncGetYear                 DontEnum|Function       0
432 @end
433 */
434 // ECMA 15.9.4
435
436 DatePrototype::DatePrototype(ExecState *, ObjectPrototype *objectProto)
437   : DateInstance(objectProto)
438 {
439     setInternalValue(jsNaN());
440     // The constructor will be added later, after DateObjectImp has been built.
441 }
442
443 bool DatePrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
444 {
445     return getStaticFunctionSlot<JSObject>(exec, &dateTable, this, propertyName, slot);
446 }
447
448 // ------------------------------ DateObjectImp --------------------------------
449
450 // TODO: MakeTime (15.9.11.1) etc. ?
451
452 DateObjectImp::DateObjectImp(ExecState* exec, FunctionPrototype* funcProto, DatePrototype* dateProto)
453   : InternalFunctionImp(funcProto, dateProto->classInfo()->className)
454 {
455   static const Identifier* parsePropertyName = new Identifier("parse");
456   static const Identifier* UTCPropertyName = new Identifier("UTC");
457
458   putDirect(exec->propertyNames().prototype, dateProto, DontEnum|DontDelete|ReadOnly);
459   putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, *parsePropertyName), DontEnum);
460   putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, *UTCPropertyName), DontEnum);
461   putDirect(exec->propertyNames().length, 7, ReadOnly|DontDelete|DontEnum);
462 }
463
464 bool DateObjectImp::implementsConstruct() const
465 {
466     return true;
467 }
468
469 // ECMA 15.9.3
470 JSObject *DateObjectImp::construct(ExecState *exec, const List &args)
471 {
472   int numArgs = args.size();
473
474   double value;
475
476   if (numArgs == 0) { // new Date() ECMA 15.9.3.3
477     value = getCurrentUTCTime();
478   } else if (numArgs == 1) {
479     if (args[0]->isObject(&DateInstance::info))
480       value = static_cast<DateInstance*>(args[0])->internalValue()->toNumber(exec);
481     else {
482       JSValue* primitive = args[0]->toPrimitive(exec);
483       if (primitive->isString())
484         value = parseDate(primitive->getString());
485       else
486         value = primitive->toNumber(exec);
487     }
488   } else {
489     if (isnan(args[0]->toNumber(exec))
490         || isnan(args[1]->toNumber(exec))
491         || (numArgs >= 3 && isnan(args[2]->toNumber(exec)))
492         || (numArgs >= 4 && isnan(args[3]->toNumber(exec)))
493         || (numArgs >= 5 && isnan(args[4]->toNumber(exec)))
494         || (numArgs >= 6 && isnan(args[5]->toNumber(exec)))
495         || (numArgs >= 7 && isnan(args[6]->toNumber(exec)))) {
496       value = NaN;
497     } else {
498       GregorianDateTime t;
499       int year = args[0]->toInt32(exec);
500       t.year = (year >= 0 && year <= 99) ? year : year - 1900;
501       t.month = args[1]->toInt32(exec);
502       t.monthDay = (numArgs >= 3) ? args[2]->toInt32(exec) : 1;
503       t.hour = args[3]->toInt32(exec);
504       t.minute = args[4]->toInt32(exec);
505       t.second = args[5]->toInt32(exec);
506       t.isDST = -1;
507       double ms = (numArgs >= 7) ? args[6]->toNumber(exec) : 0;
508       value = gregorianDateTimeToMS(t, ms, false);
509     }
510   }
511   
512   DateInstance *ret = new DateInstance(exec->lexicalGlobalObject()->datePrototype());
513   ret->setInternalValue(jsNumber(timeClip(value)));
514   return ret;
515 }
516
517 // ECMA 15.9.2
518 JSValue *DateObjectImp::callAsFunction(ExecState * /*exec*/, JSObject * /*thisObj*/, const List &/*args*/)
519 {
520     time_t t = time(0);
521     GregorianDateTime ts(*localtime(&t));
522     return jsString(formatDate(ts) + " " + formatTime(ts, false));
523 }
524
525 // ------------------------------ DateObjectFuncImp ----------------------------
526
527 DateObjectFuncImp::DateObjectFuncImp(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
528     : InternalFunctionImp(funcProto, name), id(i)
529 {
530     putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
531 }
532
533 // ECMA 15.9.4.2 - 3
534 JSValue *DateObjectFuncImp::callAsFunction(ExecState* exec, JSObject*, const List& args)
535 {
536   if (id == Parse) {
537     return jsNumber(parseDate(args[0]->toString(exec)));
538   }
539   else { // UTC
540     int n = args.size();
541     if (isnan(args[0]->toNumber(exec))
542         || isnan(args[1]->toNumber(exec))
543         || (n >= 3 && isnan(args[2]->toNumber(exec)))
544         || (n >= 4 && isnan(args[3]->toNumber(exec)))
545         || (n >= 5 && isnan(args[4]->toNumber(exec)))
546         || (n >= 6 && isnan(args[5]->toNumber(exec)))
547         || (n >= 7 && isnan(args[6]->toNumber(exec)))) {
548       return jsNaN();
549     }
550
551     GregorianDateTime t;
552     memset(&t, 0, sizeof(t));
553     int year = args[0]->toInt32(exec);
554     t.year = (year >= 0 && year <= 99) ? year : year - 1900;
555     t.month = args[1]->toInt32(exec);
556     t.monthDay = (n >= 3) ? args[2]->toInt32(exec) : 1;
557     t.hour = args[3]->toInt32(exec);
558     t.minute = args[4]->toInt32(exec);
559     t.second = args[5]->toInt32(exec);
560     double ms = (n >= 7) ? args[6]->toNumber(exec) : 0;
561     return jsNumber(gregorianDateTimeToMS(t, ms, true));
562   }
563 }
564
565 // -----------------------------------------------------------------------------
566
567 // Code originally from krfcdate.cpp, but we don't want to use kdecore, and we want double range.
568
569 static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second)
570 {
571     double days = (day - 32075)
572         + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
573         + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
574         - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
575         - 2440588;
576     return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
577 }
578
579 // We follow the recommendation of RFC 2822 to consider all
580 // obsolete time zones not listed here equivalent to "-0000".
581 static const struct KnownZone {
582 #if !PLATFORM(WIN_OS)
583     const
584 #endif
585         char tzName[4];
586     int tzOffset;
587 } known_zones[] = {
588     { "UT", 0 },
589     { "GMT", 0 },
590     { "EST", -300 },
591     { "EDT", -240 },
592     { "CST", -360 },
593     { "CDT", -300 },
594     { "MST", -420 },
595     { "MDT", -360 },
596     { "PST", -480 },
597     { "PDT", -420 }
598 };
599
600 inline static void skipSpacesAndComments(const char*& s)
601 {
602     int nesting = 0;
603     char ch;
604     while ((ch = *s)) {
605         if (!isASCIISpace(ch)) {
606             if (ch == '(')
607                 nesting++;
608             else if (ch == ')' && nesting > 0)
609                 nesting--;
610             else if (nesting == 0)
611                 break;
612         }
613         s++;
614     }
615 }
616
617 // returns 0-11 (Jan-Dec); -1 on failure
618 static int findMonth(const char* monthStr)
619 {
620     ASSERT(monthStr);
621     char needle[4];
622     for (int i = 0; i < 3; ++i) {
623         if (!*monthStr)
624             return -1;
625         needle[i] = static_cast<char>(toASCIILower(*monthStr++));
626     }
627     needle[3] = '\0';
628     const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
629     const char *str = strstr(haystack, needle);
630     if (str) {
631         int position = static_cast<int>(str - haystack);
632         if (position % 3 == 0)
633             return position / 3;
634     }
635     return -1;
636 }
637
638 static double parseDate(const UString &date)
639 {
640     // This parses a date in the form:
641     //     Tuesday, 09-Nov-99 23:12:40 GMT
642     // or
643     //     Sat, 01-Jan-2000 08:00:00 GMT
644     // or
645     //     Sat, 01 Jan 2000 08:00:00 GMT
646     // or
647     //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
648     // ### non RFC formats, added for Javascript:
649     //     [Wednesday] January 09 1999 23:12:40 GMT
650     //     [Wednesday] January 09 23:12:40 GMT 1999
651     //
652     // We ignore the weekday.
653
654     CString dateCString = date.UTF8String();
655     const char *dateString = dateCString.c_str();
656      
657     // Skip leading space
658     skipSpacesAndComments(dateString);
659
660     long month = -1;
661     const char *wordStart = dateString;
662     // Check contents of first words if not number
663     while (*dateString && !isASCIIDigit(*dateString)) {
664         if (isASCIISpace(*dateString) || *dateString == '(') {
665             if (dateString - wordStart >= 3)
666                 month = findMonth(wordStart);
667             skipSpacesAndComments(dateString);
668             wordStart = dateString;
669         } else
670            dateString++;
671     }
672
673     // Missing delimiter between month and day (like "January29")?
674     if (month == -1 && wordStart != dateString)
675         month = findMonth(wordStart);
676
677     skipSpacesAndComments(dateString);
678
679     if (!*dateString)
680         return NaN;
681
682     // ' 09-Nov-99 23:12:40 GMT'
683     char *newPosStr;
684     errno = 0;
685     long day = strtol(dateString, &newPosStr, 10);
686     if (errno)
687         return NaN;
688     dateString = newPosStr;
689
690     if (!*dateString)
691         return NaN;
692
693     if (day < 0)
694         return NaN;
695
696     long year = 0;
697     if (day > 31) {
698         // ### where is the boundary and what happens below?
699         if (*dateString != '/')
700             return NaN;
701         // looks like a YYYY/MM/DD date
702         if (!*++dateString)
703             return NaN;
704         year = day;
705         month = strtol(dateString, &newPosStr, 10) - 1;
706         if (errno)
707             return NaN;
708         dateString = newPosStr;
709         if (*dateString++ != '/' || !*dateString)
710             return NaN;
711         day = strtol(dateString, &newPosStr, 10);
712         if (errno)
713             return NaN;
714         dateString = newPosStr;
715     } else if (*dateString == '/' && month == -1) {
716         dateString++;
717         // This looks like a MM/DD/YYYY date, not an RFC date.
718         month = day - 1; // 0-based
719         day = strtol(dateString, &newPosStr, 10);
720         if (errno)
721             return NaN;
722         if (day < 1 || day > 31)
723             return NaN;
724         dateString = newPosStr;
725         if (*dateString == '/')
726             dateString++;
727         if (!*dateString)
728             return NaN;
729      } else {
730         if (*dateString == '-')
731             dateString++;
732
733         skipSpacesAndComments(dateString);
734
735         if (*dateString == ',')
736             dateString++;
737
738         if (month == -1) { // not found yet
739             month = findMonth(dateString);
740             if (month == -1)
741                 return NaN;
742
743             while (*dateString && *dateString != '-' && *dateString != ',' && !isASCIISpace(*dateString))
744                 dateString++;
745
746             if (!*dateString)
747                 return NaN;
748
749             // '-99 23:12:40 GMT'
750             if (*dateString != '-' && *dateString != '/' && *dateString != ',' && !isASCIISpace(*dateString))
751                 return NaN;
752             dateString++;
753         }
754     }
755
756     if (month < 0 || month > 11)
757         return NaN;
758
759     // '99 23:12:40 GMT'
760     if (year <= 0 && *dateString) {
761         year = strtol(dateString, &newPosStr, 10);
762         if (errno)
763             return NaN;
764     }
765     
766     // Don't fail if the time is missing.
767     long hour = 0;
768     long minute = 0;
769     long second = 0;
770     if (!*newPosStr)
771         dateString = newPosStr;
772     else {
773         // ' 23:12:40 GMT'
774         if (!(isASCIISpace(*newPosStr) || *newPosStr == ',')) {
775             if (*newPosStr != ':')
776                 return NaN;
777             // There was no year; the number was the hour.
778             year = -1;
779         } else {
780             // in the normal case (we parsed the year), advance to the next number
781             dateString = ++newPosStr;
782             skipSpacesAndComments(dateString);
783         }
784
785         hour = strtol(dateString, &newPosStr, 10);
786         // Do not check for errno here since we want to continue
787         // even if errno was set becasue we are still looking
788         // for the timezone!
789
790         // Read a number? If not, this might be a timezone name.
791         if (newPosStr != dateString) {
792             dateString = newPosStr;
793
794             if (hour < 0 || hour > 23)
795                 return NaN;
796
797             if (!*dateString)
798                 return NaN;
799
800             // ':12:40 GMT'
801             if (*dateString++ != ':')
802                 return NaN;
803
804             minute = strtol(dateString, &newPosStr, 10);
805             if (errno)
806                 return NaN;
807             dateString = newPosStr;
808
809             if (minute < 0 || minute > 59)
810                 return NaN;
811
812             // ':40 GMT'
813             if (*dateString && *dateString != ':' && !isASCIISpace(*dateString))
814                 return NaN;
815
816             // seconds are optional in rfc822 + rfc2822
817             if (*dateString ==':') {
818                 dateString++;
819
820                 second = strtol(dateString, &newPosStr, 10);
821                 if (errno)
822                     return NaN;
823                 dateString = newPosStr;
824             
825                 if (second < 0 || second > 59)
826                     return NaN;
827             }
828
829             skipSpacesAndComments(dateString);
830
831             if (strncasecmp(dateString, "AM", 2) == 0) {
832                 if (hour > 12)
833                     return NaN;
834                 if (hour == 12)
835                     hour = 0;
836                 dateString += 2;
837                 skipSpacesAndComments(dateString);
838             } else if (strncasecmp(dateString, "PM", 2) == 0) {
839                 if (hour > 12)
840                     return NaN;
841                 if (hour != 12)
842                     hour += 12;
843                 dateString += 2;
844                 skipSpacesAndComments(dateString);
845             }
846         }
847     }
848
849     bool haveTZ = false;
850     int offset = 0;
851
852     // Don't fail if the time zone is missing. 
853     // Some websites omit the time zone (4275206).
854     if (*dateString) {
855         if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
856             dateString += 3;
857             haveTZ = true;
858         }
859
860         if (*dateString == '+' || *dateString == '-') {
861             long o = strtol(dateString, &newPosStr, 10);
862             if (errno)
863                 return NaN;
864             dateString = newPosStr;
865
866             if (o < -9959 || o > 9959)
867                 return NaN;
868
869             int sgn = (o < 0) ? -1 : 1;
870             o = abs(o);
871             if (*dateString != ':') {
872                 offset = ((o / 100) * 60 + (o % 100)) * sgn;
873             } else { // GMT+05:00
874                 long o2 = strtol(dateString, &newPosStr, 10);
875                 if (errno)
876                     return NaN;
877                 dateString = newPosStr;
878                 offset = (o * 60 + o2) * sgn;
879             }
880             haveTZ = true;
881         } else {
882             for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) {
883                 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
884                     offset = known_zones[i].tzOffset;
885                     dateString += strlen(known_zones[i].tzName);
886                     haveTZ = true;
887                     break;
888                 }
889             }
890         }
891     }
892
893     skipSpacesAndComments(dateString);
894
895     if (*dateString && year == -1) {
896         year = strtol(dateString, &newPosStr, 10);
897         if (errno)
898             return NaN;
899         dateString = newPosStr;
900     }
901      
902     skipSpacesAndComments(dateString);
903      
904     // Trailing garbage
905     if (*dateString)
906         return NaN;
907
908     // Y2K: Handle 2 digit years.
909     if (year >= 0 && year < 100) {
910         if (year < 50)
911             year += 2000;
912         else
913             year += 1900;
914     }
915
916     // fall back to local timezone
917     if (!haveTZ) {
918         GregorianDateTime t;
919         memset(&t, 0, sizeof(tm));
920         t.monthDay = day;
921         t.month = month;
922         t.year = year - 1900;
923         t.isDST = -1;
924         t.second = second;
925         t.minute = minute;
926         t.hour = hour;
927
928         // Use our gregorianDateTimeToMS() rather than mktime() as the latter can't handle the full year range.
929         return gregorianDateTimeToMS(t, 0, false);
930     }
931
932     return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
933 }
934
935 double timeClip(double t)
936 {
937     if (!isfinite(t))
938         return NaN;
939     if (fabs(t) > 8.64E15)
940         return NaN;
941     return trunc(t);
942 }
943
944 // Functions
945
946 JSValue* dateProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&)
947 {
948     if (!thisObj->inherits(&DateInstance::info))
949         return throwError(exec, TypeError);
950
951     const bool utc = false;
952
953     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
954     JSValue* v = thisDateObj->internalValue();
955     double milli = v->toNumber(exec);
956     if (isnan(milli))
957         return jsString("Invalid Date");
958
959     GregorianDateTime t;
960     msToGregorianDateTime(milli, utc, t);
961     return jsString(formatDate(t) + " " + formatTime(t, utc));
962 }
963
964 JSValue* dateProtoFuncToUTCString(ExecState* exec, JSObject* thisObj, const List&)
965 {
966     if (!thisObj->inherits(&DateInstance::info))
967         return throwError(exec, TypeError);
968
969     const bool utc = true;
970
971     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
972     JSValue* v = thisDateObj->internalValue();
973     double milli = v->toNumber(exec);
974     if (isnan(milli))
975         return jsString("Invalid Date");
976
977     GregorianDateTime t;
978     msToGregorianDateTime(milli, utc, t);
979     return jsString(formatDateUTCVariant(t) + " " + formatTime(t, utc));
980 }
981
982 JSValue* dateProtoFuncToDateString(ExecState* exec, JSObject* thisObj, const List&)
983 {
984     if (!thisObj->inherits(&DateInstance::info))
985         return throwError(exec, TypeError);
986
987     const bool utc = false;
988
989     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
990     JSValue* v = thisDateObj->internalValue();
991     double milli = v->toNumber(exec);
992     if (isnan(milli))
993         return jsString("Invalid Date");
994
995     GregorianDateTime t;
996     msToGregorianDateTime(milli, utc, t);
997     return jsString(formatDate(t));
998 }
999
1000 JSValue* dateProtoFuncToTimeString(ExecState* exec, JSObject* thisObj, const List&)
1001 {
1002     if (!thisObj->inherits(&DateInstance::info))
1003         return throwError(exec, TypeError);
1004
1005     const bool utc = false;
1006
1007     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1008     JSValue* v = thisDateObj->internalValue();
1009     double milli = v->toNumber(exec);
1010     if (isnan(milli))
1011         return jsString("Invalid Date");
1012
1013     GregorianDateTime t;
1014     msToGregorianDateTime(milli, utc, t);
1015     return jsString(formatTime(t, utc));
1016 }
1017
1018 JSValue* dateProtoFuncToLocaleString(ExecState* exec, JSObject* thisObj, const List& args)
1019 {
1020     if (!thisObj->inherits(&DateInstance::info))
1021         return throwError(exec, TypeError);
1022
1023     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1024     JSValue* v = thisDateObj->internalValue();
1025     double milli = v->toNumber(exec);
1026     if (isnan(milli))
1027         return jsString("Invalid Date");
1028
1029 #if PLATFORM(MAC)
1030     double secs = floor(milli / msPerSecond);
1031     return jsString(formatLocaleDate(exec, secs, true, true, args));
1032 #else
1033     UNUSED_PARAM(args);
1034
1035     const bool utc = false;
1036
1037     GregorianDateTime t;
1038     msToGregorianDateTime(milli, utc, t);
1039     return formatLocaleDate(t, LocaleDateAndTime);
1040 #endif
1041 }
1042
1043 JSValue* dateProtoFuncToLocaleDateString(ExecState* exec, JSObject* thisObj, const List& args)
1044 {
1045     if (!thisObj->inherits(&DateInstance::info))
1046         return throwError(exec, TypeError);
1047
1048     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1049     JSValue* v = thisDateObj->internalValue();
1050     double milli = v->toNumber(exec);
1051     if (isnan(milli))
1052         return jsString("Invalid Date");
1053
1054 #if PLATFORM(MAC)
1055     double secs = floor(milli / msPerSecond);
1056     return jsString(formatLocaleDate(exec, secs, true, false, args));
1057 #else
1058     UNUSED_PARAM(args);
1059
1060     const bool utc = false;
1061
1062     GregorianDateTime t;
1063     msToGregorianDateTime(milli, utc, t);
1064     return formatLocaleDate(t, LocaleDate);
1065 #endif
1066 }
1067
1068 JSValue* dateProtoFuncToLocaleTimeString(ExecState* exec, JSObject* thisObj, const List& args)
1069 {
1070     if (!thisObj->inherits(&DateInstance::info))
1071         return throwError(exec, TypeError);
1072
1073     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1074     JSValue* v = thisDateObj->internalValue();
1075     double milli = v->toNumber(exec);
1076     if (isnan(milli))
1077         return jsString("Invalid Date");
1078
1079 #if PLATFORM(MAC)
1080     double secs = floor(milli / msPerSecond);
1081     return jsString(formatLocaleDate(exec, secs, false, true, args));
1082 #else
1083     UNUSED_PARAM(args);
1084
1085     const bool utc = false;
1086
1087     GregorianDateTime t;
1088     msToGregorianDateTime(milli, utc, t);
1089     return formatLocaleDate(t, LocaleTime);
1090 #endif
1091 }
1092
1093 JSValue* dateProtoFuncValueOf(ExecState* exec, JSObject* thisObj, const List&)
1094 {
1095     if (!thisObj->inherits(&DateInstance::info))
1096         return throwError(exec, TypeError);
1097
1098     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1099     JSValue* v = thisDateObj->internalValue();
1100     double milli = v->toNumber(exec);
1101     if (isnan(milli))
1102         return jsNaN();
1103
1104     return jsNumber(milli);
1105 }
1106
1107 JSValue* dateProtoFuncGetTime(ExecState* exec, JSObject* thisObj, const List&)
1108 {
1109     if (!thisObj->inherits(&DateInstance::info))
1110         return throwError(exec, TypeError);
1111
1112     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1113     JSValue* v = thisDateObj->internalValue();
1114     double milli = v->toNumber(exec);
1115     if (isnan(milli))
1116         return jsNaN();
1117
1118     return jsNumber(milli);
1119 }
1120
1121 JSValue* dateProtoFuncGetFullYear(ExecState* exec, JSObject* thisObj, const List&)
1122 {
1123     if (!thisObj->inherits(&DateInstance::info))
1124         return throwError(exec, TypeError);
1125
1126     const bool utc = false;
1127
1128     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1129     JSValue* v = thisDateObj->internalValue();
1130     double milli = v->toNumber(exec);
1131     if (isnan(milli))
1132         return jsNaN();
1133
1134     GregorianDateTime t;
1135     msToGregorianDateTime(milli, utc, t);
1136     return jsNumber(1900 + t.year);
1137 }
1138
1139 JSValue* dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject* thisObj, const List&)
1140 {
1141     if (!thisObj->inherits(&DateInstance::info))
1142         return throwError(exec, TypeError);
1143
1144     const bool utc = true;
1145
1146     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1147     JSValue* v = thisDateObj->internalValue();
1148     double milli = v->toNumber(exec);
1149     if (isnan(milli))
1150         return jsNaN();
1151
1152     GregorianDateTime t;
1153     msToGregorianDateTime(milli, utc, t);
1154     return jsNumber(1900 + t.year);
1155 }
1156
1157 JSValue* dateProtoFuncToGMTString(ExecState* exec, JSObject* thisObj, const List&)
1158 {
1159     if (!thisObj->inherits(&DateInstance::info))
1160         return throwError(exec, TypeError);
1161
1162     const bool utc = true;
1163
1164     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1165     JSValue* v = thisDateObj->internalValue();
1166     double milli = v->toNumber(exec);
1167     if (isnan(milli))
1168         return jsString("Invalid Date");
1169
1170     GregorianDateTime t;
1171     msToGregorianDateTime(milli, utc, t);
1172     return jsString(formatDateUTCVariant(t) + " " + formatTime(t, utc));
1173 }
1174
1175 JSValue* dateProtoFuncGetMonth(ExecState* exec, JSObject* thisObj, const List&)
1176 {
1177     if (!thisObj->inherits(&DateInstance::info))
1178         return throwError(exec, TypeError);
1179
1180     const bool utc = false;
1181
1182     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1183     JSValue* v = thisDateObj->internalValue();
1184     double milli = v->toNumber(exec);
1185     if (isnan(milli))
1186         return jsNaN();
1187
1188     GregorianDateTime t;
1189     msToGregorianDateTime(milli, utc, t);
1190     return jsNumber(t.month);
1191 }
1192
1193 JSValue* dateProtoFuncGetUTCMonth(ExecState* exec, JSObject* thisObj, const List&)
1194 {
1195     if (!thisObj->inherits(&DateInstance::info))
1196         return throwError(exec, TypeError);
1197
1198     const bool utc = true;
1199
1200     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1201     JSValue* v = thisDateObj->internalValue();
1202     double milli = v->toNumber(exec);
1203     if (isnan(milli))
1204         return jsNaN();
1205
1206     GregorianDateTime t;
1207     msToGregorianDateTime(milli, utc, t);
1208     return jsNumber(t.month);
1209 }
1210
1211 JSValue* dateProtoFuncGetDate(ExecState* exec, JSObject* thisObj, const List&)
1212 {
1213     if (!thisObj->inherits(&DateInstance::info))
1214         return throwError(exec, TypeError);
1215
1216     const bool utc = false;
1217
1218     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1219     JSValue* v = thisDateObj->internalValue();
1220     double milli = v->toNumber(exec);
1221     if (isnan(milli))
1222         return jsNaN();
1223
1224     GregorianDateTime t;
1225     msToGregorianDateTime(milli, utc, t);
1226     return jsNumber(t.monthDay);
1227 }
1228
1229 JSValue* dateProtoFuncGetUTCDate(ExecState* exec, JSObject* thisObj, const List&)
1230 {
1231     if (!thisObj->inherits(&DateInstance::info))
1232         return throwError(exec, TypeError);
1233
1234     const bool utc = true;
1235
1236     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1237     JSValue* v = thisDateObj->internalValue();
1238     double milli = v->toNumber(exec);
1239     if (isnan(milli))
1240         return jsNaN();
1241
1242     GregorianDateTime t;
1243     msToGregorianDateTime(milli, utc, t);
1244     return jsNumber(t.monthDay);
1245 }
1246
1247 JSValue* dateProtoFuncGetDay(ExecState* exec, JSObject* thisObj, const List&)
1248 {
1249     if (!thisObj->inherits(&DateInstance::info))
1250         return throwError(exec, TypeError);
1251
1252     const bool utc = false;
1253
1254     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1255     JSValue* v = thisDateObj->internalValue();
1256     double milli = v->toNumber(exec);
1257     if (isnan(milli))
1258         return jsNaN();
1259
1260     GregorianDateTime t;
1261     msToGregorianDateTime(milli, utc, t);
1262     return jsNumber(t.weekDay);
1263 }
1264
1265 JSValue* dateProtoFuncGetUTCDay(ExecState* exec, JSObject* thisObj, const List&)
1266 {
1267     if (!thisObj->inherits(&DateInstance::info))
1268         return throwError(exec, TypeError);
1269
1270     const bool utc = true;
1271
1272     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1273     JSValue* v = thisDateObj->internalValue();
1274     double milli = v->toNumber(exec);
1275     if (isnan(milli))
1276         return jsNaN();
1277
1278     GregorianDateTime t;
1279     msToGregorianDateTime(milli, utc, t);
1280     return jsNumber(t.weekDay);
1281 }
1282
1283 JSValue* dateProtoFuncGetHours(ExecState* exec, JSObject* thisObj, const List&)
1284 {
1285     if (!thisObj->inherits(&DateInstance::info))
1286         return throwError(exec, TypeError);
1287
1288     const bool utc = false;
1289
1290     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1291     JSValue* v = thisDateObj->internalValue();
1292     double milli = v->toNumber(exec);
1293     if (isnan(milli))
1294         return jsNaN();
1295
1296     GregorianDateTime t;
1297     msToGregorianDateTime(milli, utc, t);
1298     return jsNumber(t.hour);
1299 }
1300
1301 JSValue* dateProtoFuncGetUTCHours(ExecState* exec, JSObject* thisObj, const List&)
1302 {
1303     if (!thisObj->inherits(&DateInstance::info))
1304         return throwError(exec, TypeError);
1305
1306     const bool utc = true;
1307
1308     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1309     JSValue* v = thisDateObj->internalValue();
1310     double milli = v->toNumber(exec);
1311     if (isnan(milli))
1312         return jsNaN();
1313
1314     GregorianDateTime t;
1315     msToGregorianDateTime(milli, utc, t);
1316     return jsNumber(t.hour);
1317 }
1318
1319 JSValue* dateProtoFuncGetMinutes(ExecState* exec, JSObject* thisObj, const List&)
1320 {
1321     if (!thisObj->inherits(&DateInstance::info))
1322         return throwError(exec, TypeError);
1323
1324     const bool utc = false;
1325
1326     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1327     JSValue* v = thisDateObj->internalValue();
1328     double milli = v->toNumber(exec);
1329     if (isnan(milli))
1330         return jsNaN();
1331
1332     GregorianDateTime t;
1333     msToGregorianDateTime(milli, utc, t);
1334     return jsNumber(t.minute);
1335 }
1336
1337 JSValue* dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject* thisObj, const List&)
1338 {
1339     if (!thisObj->inherits(&DateInstance::info))
1340         return throwError(exec, TypeError);
1341
1342     const bool utc = true;
1343
1344     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1345     JSValue* v = thisDateObj->internalValue();
1346     double milli = v->toNumber(exec);
1347     if (isnan(milli))
1348         return jsNaN();
1349
1350     GregorianDateTime t;
1351     msToGregorianDateTime(milli, utc, t);
1352     return jsNumber(t.minute);
1353 }
1354
1355 JSValue* dateProtoFuncGetSeconds(ExecState* exec, JSObject* thisObj, const List&)
1356 {
1357     if (!thisObj->inherits(&DateInstance::info))
1358         return throwError(exec, TypeError);
1359
1360     const bool utc = false;
1361
1362     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1363     JSValue* v = thisDateObj->internalValue();
1364     double milli = v->toNumber(exec);
1365     if (isnan(milli))
1366         return jsNaN();
1367
1368     GregorianDateTime t;
1369     msToGregorianDateTime(milli, utc, t);
1370     return jsNumber(t.second);
1371 }
1372
1373 JSValue* dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject* thisObj, const List&)
1374 {
1375     if (!thisObj->inherits(&DateInstance::info))
1376         return throwError(exec, TypeError);
1377
1378     const bool utc = true;
1379
1380     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1381     JSValue* v = thisDateObj->internalValue();
1382     double milli = v->toNumber(exec);
1383     if (isnan(milli))
1384         return jsNaN();
1385
1386     GregorianDateTime t;
1387     msToGregorianDateTime(milli, utc, t);
1388     return jsNumber(t.second);
1389 }
1390
1391 JSValue* dateProtoFuncGetMilliSeconds(ExecState* exec, JSObject* thisObj, const List&)
1392 {
1393     if (!thisObj->inherits(&DateInstance::info))
1394         return throwError(exec, TypeError);
1395
1396     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1397     JSValue* v = thisDateObj->internalValue();
1398     double milli = v->toNumber(exec);
1399     if (isnan(milli))
1400         return jsNaN();
1401
1402     double secs = floor(milli / msPerSecond);
1403     double ms = milli - secs * msPerSecond;
1404     return jsNumber(ms);
1405 }
1406
1407 JSValue* dateProtoFuncGetUTCMilliseconds(ExecState* exec, JSObject* thisObj, const List&)
1408 {
1409     if (!thisObj->inherits(&DateInstance::info))
1410         return throwError(exec, TypeError);
1411
1412     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1413     JSValue* v = thisDateObj->internalValue();
1414     double milli = v->toNumber(exec);
1415     if (isnan(milli))
1416         return jsNaN();
1417
1418     double secs = floor(milli / msPerSecond);
1419     double ms = milli - secs * msPerSecond;
1420     return jsNumber(ms);
1421 }
1422
1423 JSValue* dateProtoFuncGetTimezoneOffset(ExecState* exec, JSObject* thisObj, const List&)
1424 {
1425     if (!thisObj->inherits(&DateInstance::info))
1426         return throwError(exec, TypeError);
1427
1428     const bool utc = false;
1429
1430     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1431     JSValue* v = thisDateObj->internalValue();
1432     double milli = v->toNumber(exec);
1433     if (isnan(milli))
1434         return jsNaN();
1435
1436     GregorianDateTime t;
1437     msToGregorianDateTime(milli, utc, t);
1438     return jsNumber(-gmtoffset(t) / minutesPerHour);
1439 }
1440
1441 JSValue* dateProtoFuncSetTime(ExecState* exec, JSObject* thisObj, const List& args)
1442 {
1443     if (!thisObj->inherits(&DateInstance::info))
1444         return throwError(exec, TypeError);
1445
1446     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1447
1448     double milli = timeClip(args[0]->toNumber(exec));
1449     JSValue* result = jsNumber(milli);
1450     thisDateObj->setInternalValue(result);
1451     return result;
1452 }
1453
1454 static JSValue* setNewValueFromTimeArgs(ExecState* exec, JSObject* thisObj, const List& args, int numArgsToUse, bool inputIsUTC)
1455 {
1456     if (!thisObj->inherits(&DateInstance::info))
1457         return throwError(exec, TypeError);
1458
1459     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj);
1460     JSValue* v = thisDateObj->internalValue();
1461     double milli = v->toNumber(exec);
1462     
1463     if (args.isEmpty() || isnan(milli)) {
1464         JSValue* result = jsNaN();
1465         thisDateObj->setInternalValue(result);
1466         return result;
1467     }
1468      
1469     double secs = floor(milli / msPerSecond);
1470     double ms = milli - secs * msPerSecond;
1471
1472     GregorianDateTime t;
1473     msToGregorianDateTime(milli, inputIsUTC, t);
1474
1475     if (!fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &t)) {
1476         JSValue* result = jsNaN();
1477         thisDateObj->setInternalValue(result);
1478         return result;
1479     } 
1480     
1481     JSValue* result = jsNumber(gregorianDateTimeToMS(t, ms, inputIsUTC));
1482     thisDateObj->setInternalValue(result);
1483     return result;
1484 }
1485
1486 static JSValue* setNewValueFromDateArgs(ExecState* exec, JSObject* thisObj, const List& args, int numArgsToUse, bool inputIsUTC)
1487 {
1488     if (!thisObj->inherits(&DateInstance::info))
1489         return throwError(exec, TypeError);
1490
1491     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj);
1492     if (args.isEmpty()) {
1493         JSValue* result = jsNaN();
1494         thisDateObj->setInternalValue(result);
1495         return result;
1496     }      
1497     
1498     JSValue* v = thisDateObj->internalValue();
1499     double milli = v->toNumber(exec);
1500     double ms = 0;
1501
1502     GregorianDateTime t;
1503     if (numArgsToUse == 3 && isnan(milli))
1504         // Based on ECMA 262 15.9.5.40 - .41 (set[UTC]FullYear)
1505         // the time must be reset to +0 if it is NaN. 
1506         msToGregorianDateTime(0, true, t);
1507     else {
1508         double secs = floor(milli / msPerSecond);
1509         ms = milli - secs * msPerSecond;
1510         msToGregorianDateTime(milli, inputIsUTC, t);
1511     }
1512     
1513     if (!fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &t)) {
1514         JSValue* result = jsNaN();
1515         thisDateObj->setInternalValue(result);
1516         return result;
1517     } 
1518            
1519     JSValue* result = jsNumber(gregorianDateTimeToMS(t, ms, inputIsUTC));
1520     thisDateObj->setInternalValue(result);
1521     return result;
1522 }
1523
1524 JSValue* dateProtoFuncSetMilliSeconds(ExecState* exec, JSObject* thisObj, const List& args)
1525 {
1526     const bool inputIsUTC = false;
1527     return setNewValueFromTimeArgs(exec, thisObj, args, 1, inputIsUTC);
1528 }
1529
1530 JSValue* dateProtoFuncSetUTCMilliseconds(ExecState* exec, JSObject* thisObj, const List& args)
1531 {
1532     const bool inputIsUTC = true;
1533     return setNewValueFromTimeArgs(exec, thisObj, args, 1, inputIsUTC);
1534 }
1535
1536 JSValue* dateProtoFuncSetSeconds(ExecState* exec, JSObject* thisObj, const List& args)
1537 {
1538     const bool inputIsUTC = false;
1539     return setNewValueFromTimeArgs(exec, thisObj, args, 2, inputIsUTC);
1540 }
1541
1542 JSValue* dateProtoFuncSetUTCSeconds(ExecState* exec, JSObject* thisObj, const List& args)
1543 {
1544     const bool inputIsUTC = true;
1545     return setNewValueFromTimeArgs(exec, thisObj, args, 2, inputIsUTC);
1546 }
1547
1548 JSValue* dateProtoFuncSetMinutes(ExecState* exec, JSObject* thisObj, const List& args)
1549 {
1550     const bool inputIsUTC = false;
1551     return setNewValueFromTimeArgs(exec, thisObj, args, 3, inputIsUTC);
1552 }
1553
1554 JSValue* dateProtoFuncSetUTCMinutes(ExecState* exec, JSObject* thisObj, const List& args)
1555 {
1556     const bool inputIsUTC = true;
1557     return setNewValueFromTimeArgs(exec, thisObj, args, 3, inputIsUTC);
1558 }
1559
1560 JSValue* dateProtoFuncSetHours(ExecState* exec, JSObject* thisObj, const List& args)
1561 {
1562     const bool inputIsUTC = false;
1563     return setNewValueFromTimeArgs(exec, thisObj, args, 4, inputIsUTC);
1564 }
1565
1566 JSValue* dateProtoFuncSetUTCHours(ExecState* exec, JSObject* thisObj, const List& args)
1567 {
1568     const bool inputIsUTC = true;
1569     return setNewValueFromTimeArgs(exec, thisObj, args, 4, inputIsUTC);
1570 }
1571
1572 JSValue* dateProtoFuncSetDate(ExecState* exec, JSObject* thisObj, const List& args)
1573 {
1574     const bool inputIsUTC = false;
1575     return setNewValueFromDateArgs(exec, thisObj, args, 1, inputIsUTC);
1576 }
1577
1578 JSValue* dateProtoFuncSetUTCDate(ExecState* exec, JSObject* thisObj, const List& args)
1579 {
1580     const bool inputIsUTC = true;
1581     return setNewValueFromDateArgs(exec, thisObj, args, 1, inputIsUTC);
1582 }
1583
1584 JSValue* dateProtoFuncSetMonth(ExecState* exec, JSObject* thisObj, const List& args)
1585 {
1586     const bool inputIsUTC = false;
1587     return setNewValueFromDateArgs(exec, thisObj, args, 2, inputIsUTC);
1588 }
1589
1590 JSValue* dateProtoFuncSetUTCMonth(ExecState* exec, JSObject* thisObj, const List& args)
1591 {
1592     const bool inputIsUTC = true;
1593     return setNewValueFromDateArgs(exec, thisObj, args, 2, inputIsUTC);
1594 }
1595
1596 JSValue* dateProtoFuncSetFullYear(ExecState* exec, JSObject* thisObj, const List& args)
1597 {
1598     const bool inputIsUTC = false;
1599     return setNewValueFromDateArgs(exec, thisObj, args, 3, inputIsUTC);
1600 }
1601
1602 JSValue* dateProtoFuncSetUTCFullYear(ExecState* exec, JSObject* thisObj, const List& args)
1603 {
1604     const bool inputIsUTC = true;
1605     return setNewValueFromDateArgs(exec, thisObj, args, 3, inputIsUTC);
1606 }
1607
1608 JSValue* dateProtoFuncSetYear(ExecState* exec, JSObject* thisObj, const List& args)
1609 {
1610     if (!thisObj->inherits(&DateInstance::info))
1611         return throwError(exec, TypeError);
1612
1613     const bool utc = false;
1614
1615     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj);     
1616     if (args.isEmpty()) { 
1617         JSValue* result = jsNaN();
1618         thisDateObj->setInternalValue(result);
1619         return result;
1620     }
1621     
1622     JSValue* v = thisDateObj->internalValue();
1623     double milli = v->toNumber(exec);
1624     double ms = 0;
1625
1626     GregorianDateTime t;
1627     if (isnan(milli))
1628         // Based on ECMA 262 B.2.5 (setYear)
1629         // the time must be reset to +0 if it is NaN. 
1630         msToGregorianDateTime(0, true, t);
1631     else {   
1632         double secs = floor(milli / msPerSecond);
1633         ms = milli - secs * msPerSecond;
1634         msToGregorianDateTime(milli, utc, t);
1635     }
1636     
1637     bool ok = true;
1638     int32_t year = args[0]->toInt32(exec, ok);
1639     if (!ok) {
1640         JSValue* result = jsNaN();
1641         thisDateObj->setInternalValue(result);
1642         return result;
1643     }
1644             
1645     t.year = (year > 99 || year < 0) ? year - 1900 : year;
1646     JSValue* result = jsNumber(gregorianDateTimeToMS(t, ms, utc));
1647     thisDateObj->setInternalValue(result);
1648     return result;
1649 }
1650
1651 JSValue* dateProtoFuncGetYear(ExecState* exec, JSObject* thisObj, const List&)
1652 {
1653     if (!thisObj->inherits(&DateInstance::info))
1654         return throwError(exec, TypeError);
1655
1656     const bool utc = false;
1657
1658     DateInstance* thisDateObj = static_cast<DateInstance*>(thisObj); 
1659     JSValue* v = thisDateObj->internalValue();
1660     double milli = v->toNumber(exec);
1661     if (isnan(milli))
1662         return jsNaN();
1663
1664     GregorianDateTime t;
1665     msToGregorianDateTime(milli, utc, t);
1666
1667     // NOTE: IE returns the full year even in getYear.
1668     return jsNumber(t.year);
1669 }
1670
1671 } // namespace KJS