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