919c56dfca640d115fd081af883f35c67b5177e3
[WebKit-https.git] / JavaScriptCore / kjs / date_object.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
5  *  Copyright (C) 2004 Apple Computer, Inc.
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 #ifndef HAVE_SYS_TIMEB_H
27 #define HAVE_SYS_TIMEB_H 0
28 #endif
29
30 #if TIME_WITH_SYS_TIME
31 # include <sys/time.h>
32 # include <time.h>
33 #else
34 #if HAVE_SYS_TIME_H
35 #include <sys/time.h>
36 #else
37 #  include <time.h>
38 # endif
39 #endif
40 #if HAVE_SYS_TIMEB_H
41 #include <sys/timeb.h>
42 #endif
43
44 #ifdef HAVE_SYS_PARAM_H
45 #  include <sys/param.h>
46 #endif // HAVE_SYS_PARAM_H
47
48 #include <math.h>
49 #include <string.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <locale.h>
53 #include <ctype.h>
54
55 #include "date_object.h"
56 #include "error_object.h"
57 #include "operations.h"
58
59 #include "date_object.lut.h"
60
61 const time_t invalidDate = -1;
62
63 #if APPLE_CHANGES
64
65 // Originally, we wrote our own implementation that uses Core Foundation because of a performance problem in Mac OS X 10.2.
66 // But we need to keep using this rather than the standard library functions because this handles a larger range of dates.
67
68 #include <notify.h>
69 #include <CoreFoundation/CoreFoundation.h>
70 #include <CoreServices/CoreServices.h>
71
72 using KJS::UChar;
73 using KJS::UString;
74
75 #define gmtime(x) gmtimeUsingCF(x)
76 #define localtime(x) localtimeUsingCF(x)
77 #define mktime(x) mktimeUsingCF(x)
78 #define timegm(x) timegmUsingCF(x)
79 #define time(x) timeUsingCF(x)
80
81 #define ctime(x) NotAllowedToCallThis()
82 #define strftime(a, b, c, d) NotAllowedToCallThis()
83
84 static const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
85 static const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
86     
87 static struct tm *tmUsingCF(time_t clock, CFTimeZoneRef timeZone)
88 {
89     static struct tm result;
90     static char timeZoneCString[128];
91     
92     CFAbsoluteTime absoluteTime = clock - kCFAbsoluteTimeIntervalSince1970;
93     CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absoluteTime, timeZone);
94
95     CFStringRef abbreviation = CFTimeZoneCopyAbbreviation(timeZone, absoluteTime);
96     CFStringGetCString(abbreviation, timeZoneCString, sizeof(timeZoneCString), kCFStringEncodingASCII);
97     CFRelease(abbreviation);
98
99     result.tm_sec = (int)date.second;
100     result.tm_min = date.minute;
101     result.tm_hour = date.hour;
102     result.tm_mday = date.day;
103     result.tm_mon = date.month - 1;
104     result.tm_year = date.year - 1900;
105     result.tm_wday = CFAbsoluteTimeGetDayOfWeek(absoluteTime, timeZone) % 7;
106     result.tm_yday = CFAbsoluteTimeGetDayOfYear(absoluteTime, timeZone) - 1;
107     result.tm_isdst = CFTimeZoneIsDaylightSavingTime(timeZone, absoluteTime);
108     result.tm_gmtoff = (int)CFTimeZoneGetSecondsFromGMT(timeZone, absoluteTime);
109     result.tm_zone = timeZoneCString;
110     
111     return &result;
112 }
113
114 static CFTimeZoneRef UTCTimeZone()
115 {
116     static CFTimeZoneRef zone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
117     return zone;
118 }
119
120 static CFTimeZoneRef CopyLocalTimeZone()
121 {
122     // Check for a time zone notification, and tell CoreFoundation to re-get the time zone if it happened.
123     // Some day, CoreFoundation may do this itself, but for now it needs our help.
124     static bool registered = false;
125     static int notificationToken;
126     if (!registered) {
127         uint32_t status = notify_register_check("com.apple.system.timezone", &notificationToken);
128         if (status == NOTIFY_STATUS_OK) {
129             registered = true;
130         }
131     }
132     if (registered) {
133         int notified;
134         uint32_t status = notify_check(notificationToken, &notified);
135         if (status == NOTIFY_STATUS_OK && notified) {
136             CFTimeZoneResetSystem();
137         }
138     }
139
140     CFTimeZoneRef zone = CFTimeZoneCopyDefault();
141     if (zone) {
142         return zone;
143     }
144     zone = UTCTimeZone();
145     CFRetain(zone);
146     return zone;
147 }
148
149 static struct tm *gmtimeUsingCF(const time_t *clock)
150 {
151     return tmUsingCF(*clock, UTCTimeZone());
152 }
153
154 static struct tm *localtimeUsingCF(const time_t *clock)
155 {
156     CFTimeZoneRef timeZone = CopyLocalTimeZone();
157     struct tm *result = tmUsingCF(*clock, timeZone);
158     CFRelease(timeZone);
159     return result;
160 }
161
162 static time_t timetUsingCF(struct tm *tm, CFTimeZoneRef timeZone)
163 {
164     CFGregorianDate date;
165     date.second = tm->tm_sec;
166     date.minute = tm->tm_min;
167     date.hour = tm->tm_hour;
168     date.day = tm->tm_mday;
169     date.month = tm->tm_mon + 1;
170     date.year = tm->tm_year + 1900;
171
172     // CFGregorianDateGetAbsoluteTime will go nuts if the year is too large or small,
173     // so we pick an arbitrary cutoff.
174     if (date.year < -2500 || date.year > 2500) {
175         return invalidDate;
176     }
177
178     CFAbsoluteTime absoluteTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
179     CFTimeInterval interval = absoluteTime + kCFAbsoluteTimeIntervalSince1970;
180     if (interval > LONG_MAX) {
181         interval = LONG_MAX;
182     }
183
184     return (time_t) interval;
185 }
186
187 static time_t mktimeUsingCF(struct tm *tm)
188 {
189     CFTimeZoneRef timeZone = CopyLocalTimeZone();
190     time_t result = timetUsingCF(tm, timeZone);
191     CFRelease(timeZone);
192     return result;
193 }
194
195 static time_t timegmUsingCF(struct tm *tm)
196 {
197     return timetUsingCF(tm, UTCTimeZone());
198 }
199
200 static time_t timeUsingCF(time_t *clock)
201 {
202     time_t result = (time_t)(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970);
203     if (clock) {
204         *clock = result;
205     }
206     return result;
207 }
208
209 static UString formatDate(struct tm &tm)
210 {
211     char buffer[100];
212     snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
213         weekdayName[(tm.tm_wday + 6) % 7],
214         monthName[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
215     return buffer;
216 }
217
218 static UString formatDateUTCVariant(struct tm &tm)
219 {
220     char buffer[100];
221     snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
222         weekdayName[(tm.tm_wday + 6) % 7],
223         tm.tm_mday, monthName[tm.tm_mon], tm.tm_year + 1900);
224     return buffer;
225 }
226
227 static UString formatTime(struct tm &tm)
228 {
229     char buffer[100];
230     if (tm.tm_gmtoff == 0) {
231         snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", tm.tm_hour, tm.tm_min, tm.tm_sec);
232     } else {
233         int offset = tm.tm_gmtoff;
234         if (offset < 0) {
235             offset = -offset;
236         }
237         snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
238             tm.tm_hour, tm.tm_min, tm.tm_sec,
239             tm.tm_gmtoff < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
240     }
241     return UString(buffer);
242 }
243
244 static CFDateFormatterStyle styleFromArgString(const UString& string,CFDateFormatterStyle defaultStyle)
245 {
246     CFDateFormatterStyle retVal = defaultStyle;
247     if (string == "short")
248         retVal = kCFDateFormatterShortStyle;
249     else if (string == "medium")
250         retVal = kCFDateFormatterMediumStyle;
251     else if (string == "long")
252         retVal = kCFDateFormatterLongStyle;
253     else if (string == "full")
254         retVal = kCFDateFormatterFullStyle;
255     return retVal;
256 }
257
258 static UString formatLocaleDate(KJS::ExecState *exec,time_t tv, bool includeDate, bool includeTime, const KJS::List &args)
259 {
260     LongDateTime longDateTime;
261     UCConvertCFAbsoluteTimeToLongDateTime(tv - kCFAbsoluteTimeIntervalSince1970, &longDateTime);
262
263     CFLocaleRef locale = CFLocaleCopyCurrent();
264     
265     int argCount = args.size();
266     
267     CFDateFormatterStyle    dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
268     CFDateFormatterStyle    timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
269
270     UString     arg0String;
271     UString     arg1String;
272     bool        useCustomFormat = false;
273     UString     customFormatString;
274     arg0String = args[0].toString(exec);
275     if ((arg0String == "custom") && (argCount >= 2)) {
276         useCustomFormat = true;
277         customFormatString = args[1].toString(exec);
278     } else if (includeDate && includeTime && (argCount >= 2)) {
279         arg1String = args[1].toString(exec);
280         dateStyle = styleFromArgString(arg0String,dateStyle);
281         timeStyle = styleFromArgString(arg1String,timeStyle);
282     } else if (includeDate && (argCount >= 1)) {
283         dateStyle = styleFromArgString(arg0String,dateStyle);
284     } else if (includeTime && (argCount >= 1)) {
285         timeStyle = styleFromArgString(arg0String,timeStyle);
286     }
287     CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, dateStyle, timeStyle);
288     if (useCustomFormat) {
289         CFStringRef     customFormatCFString = CFStringCreateWithCharacters(NULL,(UniChar*)customFormatString.data(),customFormatString.size());
290         CFDateFormatterSetFormat(formatter,customFormatCFString);
291         CFRelease(customFormatCFString);
292     }
293     CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(NULL, formatter, tv - kCFAbsoluteTimeIntervalSince1970);
294
295     // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters).
296     // That's not great error handling, but it just won't happen so it doesn't matter.
297     UChar buffer[200];
298     const size_t bufferLength = sizeof(buffer) / sizeof(buffer[0]);
299     size_t length = CFStringGetLength(string);
300     assert(length <= bufferLength);
301     if (length > bufferLength)
302         length = bufferLength;
303     CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast<UniChar *>(buffer));
304
305     CFRelease(string);
306     CFRelease(formatter);
307     CFRelease(locale);
308     
309     return UString(buffer, length);
310 }
311
312 #endif // APPLE_CHANGES
313
314 using namespace KJS;
315
316 // ------------------------------ DateInstanceImp ------------------------------
317
318 const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
319
320 DateInstanceImp::DateInstanceImp(ObjectImp *proto)
321   : ObjectImp(proto)
322 {
323 }
324
325 // ------------------------------ DatePrototypeImp -----------------------------
326
327 const ClassInfo DatePrototypeImp::info = {"Date", 0, &dateTable, 0};
328
329 /* Source for date_object.lut.h
330    We use a negative ID to denote the "UTC" variant.
331 @begin dateTable 61
332   toString              DateProtoFuncImp::ToString              DontEnum|Function       0
333   toUTCString           -DateProtoFuncImp::ToUTCString          DontEnum|Function       0
334   toDateString          DateProtoFuncImp::ToDateString          DontEnum|Function       0
335   toTimeString          DateProtoFuncImp::ToTimeString          DontEnum|Function       0
336   toLocaleString        DateProtoFuncImp::ToLocaleString        DontEnum|Function       0
337   toLocaleDateString    DateProtoFuncImp::ToLocaleDateString    DontEnum|Function       0
338   toLocaleTimeString    DateProtoFuncImp::ToLocaleTimeString    DontEnum|Function       0
339   valueOf               DateProtoFuncImp::ValueOf               DontEnum|Function       0
340   getTime               DateProtoFuncImp::GetTime               DontEnum|Function       0
341   getFullYear           DateProtoFuncImp::GetFullYear           DontEnum|Function       0
342   getUTCFullYear        -DateProtoFuncImp::GetFullYear          DontEnum|Function       0
343   toGMTString           -DateProtoFuncImp::ToGMTString          DontEnum|Function       0
344   getMonth              DateProtoFuncImp::GetMonth              DontEnum|Function       0
345   getUTCMonth           -DateProtoFuncImp::GetMonth             DontEnum|Function       0
346   getDate               DateProtoFuncImp::GetDate               DontEnum|Function       0
347   getUTCDate            -DateProtoFuncImp::GetDate              DontEnum|Function       0
348   getDay                DateProtoFuncImp::GetDay                DontEnum|Function       0
349   getUTCDay             -DateProtoFuncImp::GetDay               DontEnum|Function       0
350   getHours              DateProtoFuncImp::GetHours              DontEnum|Function       0
351   getUTCHours           -DateProtoFuncImp::GetHours             DontEnum|Function       0
352   getMinutes            DateProtoFuncImp::GetMinutes            DontEnum|Function       0
353   getUTCMinutes         -DateProtoFuncImp::GetMinutes           DontEnum|Function       0
354   getSeconds            DateProtoFuncImp::GetSeconds            DontEnum|Function       0
355   getUTCSeconds         -DateProtoFuncImp::GetSeconds           DontEnum|Function       0
356   getMilliseconds       DateProtoFuncImp::GetMilliSeconds       DontEnum|Function       0
357   getUTCMilliseconds    -DateProtoFuncImp::GetMilliSeconds      DontEnum|Function       0
358   getTimezoneOffset     DateProtoFuncImp::GetTimezoneOffset     DontEnum|Function       0
359   setTime               DateProtoFuncImp::SetTime               DontEnum|Function       1
360   setMilliseconds       DateProtoFuncImp::SetMilliSeconds       DontEnum|Function       1
361   setUTCMilliseconds    -DateProtoFuncImp::SetMilliSeconds      DontEnum|Function       1
362   setSeconds            DateProtoFuncImp::SetSeconds            DontEnum|Function       2
363   setUTCSeconds         -DateProtoFuncImp::SetSeconds           DontEnum|Function       2
364   setMinutes            DateProtoFuncImp::SetMinutes            DontEnum|Function       3
365   setUTCMinutes         -DateProtoFuncImp::SetMinutes           DontEnum|Function       3
366   setHours              DateProtoFuncImp::SetHours              DontEnum|Function       4
367   setUTCHours           -DateProtoFuncImp::SetHours             DontEnum|Function       4
368   setDate               DateProtoFuncImp::SetDate               DontEnum|Function       1
369   setUTCDate            -DateProtoFuncImp::SetDate              DontEnum|Function       1
370   setMonth              DateProtoFuncImp::SetMonth              DontEnum|Function       2
371   setUTCMonth           -DateProtoFuncImp::SetMonth             DontEnum|Function       2
372   setFullYear           DateProtoFuncImp::SetFullYear           DontEnum|Function       3
373   setUTCFullYear        -DateProtoFuncImp::SetFullYear          DontEnum|Function       3
374   setYear               DateProtoFuncImp::SetYear               DontEnum|Function       1
375   getYear               DateProtoFuncImp::GetYear               DontEnum|Function       0
376 @end
377 */
378 // ECMA 15.9.4
379
380 DatePrototypeImp::DatePrototypeImp(ExecState *,
381                                    ObjectPrototypeImp *objectProto)
382   : DateInstanceImp(objectProto)
383 {
384   Value protect(this);
385   setInternalValue(NumberImp::create(NaN));
386   // The constructor will be added later, after DateObjectImp has been built
387 }
388
389 Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
390 {
391   return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
392 }
393
394 // ------------------------------ DateProtoFuncImp -----------------------------
395
396 DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
397   : InternalFunctionImp(
398     static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
399     ), id(abs(i)), utc(i<0)
400   // We use a negative ID to denote the "UTC" variant.
401 {
402   Value protect(this);
403   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
404 }
405
406 bool DateProtoFuncImp::implementsCall() const
407 {
408   return true;
409 }
410
411 Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
412 {
413   if ((id == ToString || id == ValueOf || id == GetTime || id == SetTime) &&
414       !thisObj.inherits(&DateInstanceImp::info)) {
415     // non-generic function called on non-date object
416
417     // ToString and ValueOf are generic according to the spec, but the mozilla
418     // tests suggest otherwise...
419     Object err = Error::create(exec,TypeError);
420     exec->setException(err);
421     return err;
422   }
423
424
425   Value result;
426   UString s;
427 #if !APPLE_CHANGES
428   const int bufsize=100;
429   char timebuffer[bufsize];
430   CString oldlocale = setlocale(LC_TIME,NULL);
431   if (!oldlocale.c_str())
432     oldlocale = setlocale(LC_ALL, NULL);
433 #endif
434   Value v = thisObj.internalValue();
435   double milli = v.toNumber(exec);
436   
437   if (isNaN(milli)) {
438     switch (id) {
439       case ToString:
440       case ToDateString:
441       case ToTimeString:
442       case ToGMTString:
443       case ToUTCString:
444       case ToLocaleString:
445       case ToLocaleDateString:
446       case ToLocaleTimeString:
447         return String("Invalid Date");
448       case ValueOf:
449       case GetTime:
450       case GetYear:
451       case GetFullYear:
452       case GetMonth:
453       case GetDate:
454       case GetDay:
455       case GetHours:
456       case GetMinutes:
457       case GetSeconds:
458       case GetMilliSeconds:
459       case GetTimezoneOffset:
460         return Number(NaN);
461     }
462   }
463   
464   time_t tv = (time_t)(milli / 1000.0);
465   int ms = int(milli - tv * 1000.0);
466
467   struct tm *t;
468   if (utc)
469     t = gmtime(&tv);
470   else
471     t = localtime(&tv);
472
473   switch (id) {
474 #if APPLE_CHANGES
475   case ToString:
476     result = String(formatDate(*t) + " " + formatTime(*t));
477     break;
478   case ToDateString:
479     result = String(formatDate(*t));
480     break;
481   case ToTimeString:
482     result = String(formatTime(*t));
483     break;
484   case ToGMTString:
485   case ToUTCString:
486     result = String(formatDateUTCVariant(*t) + " " + formatTime(*t));
487     break;
488   case ToLocaleString:
489     result = String(formatLocaleDate(exec,tv, true, true,args));
490     break;
491   case ToLocaleDateString:
492     result = String(formatLocaleDate(exec,tv, true, false,args));
493     break;
494   case ToLocaleTimeString:
495     result = String(formatLocaleDate(exec,tv, false, true,args));
496     break;
497 #else
498   case ToString:
499     s = ctime(&tv);
500     result = String(s.substr(0, s.size() - 1));
501     break;
502   case ToDateString:
503   case ToTimeString:
504   case ToGMTString:
505   case ToUTCString:
506     setlocale(LC_TIME,"C");
507     if (id == DateProtoFuncImp::ToDateString) {
508       strftime(timebuffer, bufsize, "%x",t);
509     } else if (id == DateProtoFuncImp::ToTimeString) {
510       strftime(timebuffer, bufsize, "%X",t);
511     } else { // toGMTString & toUTCString
512       strftime(timebuffer, bufsize, "%a, %d %b %Y %H:%M:%S %Z", t);
513     }
514     setlocale(LC_TIME,oldlocale.c_str());
515     result = String(timebuffer);
516     break;
517   case ToLocaleString:
518     strftime(timebuffer, bufsize, "%c", t);
519     result = String(timebuffer);
520     break;
521   case ToLocaleDateString:
522     strftime(timebuffer, bufsize, "%x", t);
523     result = String(timebuffer);
524     break;
525   case ToLocaleTimeString:
526     strftime(timebuffer, bufsize, "%X", t);
527     result = String(timebuffer);
528     break;
529 #endif
530   case ValueOf:
531     result = Number(milli);
532     break;
533   case GetTime:
534     result = Number(milli);
535     break;
536   case GetYear:
537     // IE returns the full year even in getYear.
538     if ( exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat )
539       result = Number(1900 + t->tm_year);
540     else
541       result = Number(t->tm_year);
542     break;
543   case GetFullYear:
544     result = Number(1900 + t->tm_year);
545     break;
546   case GetMonth:
547     result = Number(t->tm_mon);
548     break;
549   case GetDate:
550     result = Number(t->tm_mday);
551     break;
552   case GetDay:
553     result = Number(t->tm_wday);
554     break;
555   case GetHours:
556     result = Number(t->tm_hour);
557     break;
558   case GetMinutes:
559     result = Number(t->tm_min);
560     break;
561   case GetSeconds:
562     result = Number(t->tm_sec);
563     break;
564   case GetMilliSeconds:
565     result = Number(ms);
566     break;
567   case GetTimezoneOffset:
568 #if defined BSD || defined(__APPLE__)
569     result = Number(-t->tm_gmtoff / 60);
570 #else
571 #  if defined(__BORLANDC__)
572 #error please add daylight savings offset here!
573     // FIXME: Using the daylight value was wrong for BSD, maybe wrong here too.
574     result = Number(_timezone / 60 - (_daylight ? 60 : 0));
575 #  else
576     // FIXME: Using the daylight value was wrong for BSD, maybe wrong here too.
577     result = Number(( timezone / 60 - ( daylight ? 60 : 0 )));
578 #  endif
579 #endif
580     break;
581   case SetTime:
582     milli = roundValue(exec,args[0]);
583     result = Number(milli);
584     thisObj.setInternalValue(result);
585     break;
586   case SetMilliSeconds:
587     ms = args[0].toInt32(exec);
588     break;
589   case SetSeconds:
590     t->tm_sec = args[0].toInt32(exec);
591     if (args.size() >= 2)
592       ms = args[1].toInt32(exec);
593     break;
594   case SetMinutes:
595     t->tm_min = args[0].toInt32(exec);
596     if (args.size() >= 2)
597       t->tm_sec = args[1].toInt32(exec);
598     if (args.size() >= 3)
599       ms = args[2].toInt32(exec);
600     break;
601   case SetHours:
602     t->tm_hour = args[0].toInt32(exec);
603     if (args.size() >= 2)
604       t->tm_min = args[1].toInt32(exec);
605     if (args.size() >= 3)
606       t->tm_sec = args[2].toInt32(exec);
607     if (args.size() >= 4)
608       ms = args[3].toInt32(exec);
609     break;
610   case SetDate:
611     t->tm_mday = args[0].toInt32(exec);
612     break;
613   case SetMonth:
614     t->tm_mon = args[0].toInt32(exec);
615     if (args.size() >= 2)
616       t->tm_mday = args[1].toInt32(exec);
617     break;
618   case SetFullYear:
619     t->tm_year = args[0].toInt32(exec) - 1900;
620     if (args.size() >= 2)
621       t->tm_mon = args[1].toInt32(exec);
622     if (args.size() >= 3)
623       t->tm_mday = args[2].toInt32(exec);
624     break;
625   case SetYear:
626     t->tm_year = args[0].toInt32(exec) >= 1900 ? args[0].toInt32(exec) - 1900 : args[0].toInt32(exec);
627     break;
628   }
629
630   if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
631       id == SetMinutes || id == SetHours || id == SetDate ||
632       id == SetMonth || id == SetFullYear ) {
633     time_t mktimeResult = utc ? timegm(t) : mktime(t);
634     if (mktimeResult == invalidDate)
635       result = Number(NaN);
636     else
637       result = Number(mktimeResult * 1000.0 + ms);
638     thisObj.setInternalValue(result);
639   }
640
641   return result;
642 }
643
644 // ------------------------------ DateObjectImp --------------------------------
645
646 // TODO: MakeTime (15.9.11.1) etc. ?
647
648 DateObjectImp::DateObjectImp(ExecState *exec,
649                              FunctionPrototypeImp *funcProto,
650                              DatePrototypeImp *dateProto)
651   : InternalFunctionImp(funcProto)
652 {
653   Value protect(this);
654   
655   // ECMA 15.9.4.1 Date.prototype
656   putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
657
658   static const Identifier parsePropertyName("parse");
659   putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
660   static const Identifier UTCPropertyName("UTC");
661   putDirect(UTCPropertyName,   new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC,   7),   DontEnum);
662
663   // no. of arguments for constructor
664   putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
665 }
666
667 bool DateObjectImp::implementsConstruct() const
668 {
669   return true;
670 }
671
672 // ECMA 15.9.3
673 Object DateObjectImp::construct(ExecState *exec, const List &args)
674 {
675   int numArgs = args.size();
676
677 #ifdef KJS_VERBOSE
678   fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
679 #endif
680   double value;
681
682   if (numArgs == 0) { // new Date() ECMA 15.9.3.3
683 #if HAVE_SYS_TIMEB_H
684 #  if defined(__BORLANDC__)
685     struct timeb timebuffer;
686     ftime(&timebuffer);
687 #  else
688     struct _timeb timebuffer;
689     _ftime(&timebuffer);
690 #  endif
691     double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
692 #else
693     struct timeval tv;
694     gettimeofday(&tv, 0L);
695     double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
696 #endif
697     value = utc;
698   } else if (numArgs == 1) {
699     UString s = args[0].toString(exec);
700     double d = s.toDouble();
701     if (isNaN(d))
702       value = parseDate(s);
703     else
704       value = d;
705   } else {
706     struct tm t;
707     memset(&t, 0, sizeof(t));
708     if (isNaN(args[0].toNumber(exec))
709         || isNaN(args[1].toNumber(exec))
710         || (numArgs >= 3 && isNaN(args[2].toNumber(exec)))
711         || (numArgs >= 4 && isNaN(args[3].toNumber(exec)))
712         || (numArgs >= 5 && isNaN(args[4].toNumber(exec)))
713         || (numArgs >= 6 && isNaN(args[5].toNumber(exec)))
714         || (numArgs >= 7 && isNaN(args[6].toNumber(exec)))) {
715       value = NaN;
716     } else {
717       int year = args[0].toInt32(exec);
718       t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
719       t.tm_mon = args[1].toInt32(exec);
720       t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
721       t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
722       t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
723       t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
724       t.tm_isdst = -1;
725       int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
726       time_t mktimeResult = mktime(&t);
727       if (mktimeResult == invalidDate)
728         value = NaN;
729       else
730         value = mktimeResult * 1000.0 + ms;
731     }
732   }
733
734   Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
735   Object ret(new DateInstanceImp(proto.imp()));
736   ret.setInternalValue(Number(timeClip(value)));
737   return ret;
738 }
739
740 bool DateObjectImp::implementsCall() const
741 {
742   return true;
743 }
744
745 // ECMA 15.9.2
746 Value DateObjectImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/)
747 {
748 #ifdef KJS_VERBOSE
749   fprintf(stderr,"DateObjectImp::call - current time\n");
750 #endif
751   time_t t = time(0L);
752 #if APPLE_CHANGES
753   struct tm *tm = localtime(&t);
754   return String(formatDate(*tm) + " " + formatTime(*tm));
755 #else
756   UString s(ctime(&t));
757
758   // return formatted string minus trailing \n
759   return String(s.substr(0, s.size() - 1));
760 #endif
761 }
762
763 // ------------------------------ DateObjectFuncImp ----------------------------
764
765 DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
766                                      int i, int len)
767   : InternalFunctionImp(funcProto), id(i)
768 {
769   Value protect(this);
770   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
771 }
772
773 bool DateObjectFuncImp::implementsCall() const
774 {
775   return true;
776 }
777
778 // ECMA 15.9.4.2 - 3
779 Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
780 {
781   if (id == Parse) {
782     return Number(parseDate(args[0].toString(exec)));
783   }
784   else { // UTC
785     struct tm t;
786     memset(&t, 0, sizeof(t));
787     int n = args.size();
788     if (isNaN(args[0].toNumber(exec))
789         || isNaN(args[1].toNumber(exec))
790         || (n >= 3 && isNaN(args[2].toNumber(exec)))
791         || (n >= 4 && isNaN(args[3].toNumber(exec)))
792         || (n >= 5 && isNaN(args[4].toNumber(exec)))
793         || (n >= 6 && isNaN(args[5].toNumber(exec)))
794         || (n >= 7 && isNaN(args[6].toNumber(exec)))) {
795       return Number(NaN);
796     }
797     int year = args[0].toInt32(exec);
798     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
799     t.tm_mon = args[1].toInt32(exec);
800     t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
801     t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
802     t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
803     t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
804     int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
805     time_t mktimeResult = timegm(&t);
806     if (mktimeResult == invalidDate)
807       return Number(NaN);
808     return Number(mktimeResult * 1000.0 + ms);
809   }
810 }
811
812 // -----------------------------------------------------------------------------
813
814
815 double KJS::parseDate(const UString &u)
816 {
817 #ifdef KJS_VERBOSE
818   fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
819 #endif
820   int firstSlash = u.find('/');
821   if ( firstSlash == -1 )
822   {
823     time_t seconds = KRFCDate_parseDate( u );
824 #ifdef KJS_VERBOSE
825     fprintf(stderr,"KRFCDate_parseDate returned seconds=%d\n",seconds);
826 #endif
827     if ( seconds == invalidDate )
828       return NaN;
829     else
830       return seconds * 1000.0;
831   }
832   else
833   {
834     // Found 12/31/2099 on some website -> obviously MM/DD/YYYY
835     int month = u.substr(0,firstSlash).toULong();
836     int secondSlash = u.find('/',firstSlash+1);
837     //fprintf(stdout,"KJS::parseDate firstSlash=%d, secondSlash=%d\n", firstSlash, secondSlash);
838     if ( secondSlash == -1 )
839     {
840       fprintf(stderr,"KJS::parseDate parsing for this format isn't implemented\n%s", u.ascii());
841       return NaN;
842     }
843     int day = u.substr(firstSlash+1,secondSlash-firstSlash-1).toULong();
844     int year = u.substr(secondSlash+1).toULong();
845     //fprintf(stdout,"KJS::parseDate day=%d, month=%d, year=%d\n", day, month, year);
846     struct tm t;
847     memset( &t, 0, sizeof(t) );
848 #if !APPLE_CHANGES
849     year = (year > 2037) ? 2037 : year; // mktime is limited to 2037 !!!
850 #endif
851     t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
852     t.tm_mon = month-1; // mktime wants 0-11 for some reason
853     t.tm_mday = day;
854     time_t seconds = mktime(&t);
855     if ( seconds == invalidDate )
856     {
857 #if !APPLE_CHANGES
858       fprintf(stderr,"KJS::parseDate mktime returned -1.\n%s", u.ascii());
859 #endif
860       return NaN;
861     }
862     else
863       return seconds * 1000.0;
864   }
865 }
866
867 ///// Awful duplication from krfcdate.cpp - we don't link to kdecore
868
869 static unsigned int ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
870 {
871     unsigned int ret = (day - 32075)       /* days */
872             + 1461L * (year + 4800L + (mon - 14) / 12) / 4
873             + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
874             - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
875             - 2440588;
876     ret = 24*ret + hour;     /* hours   */
877     ret = 60*ret + minute;   /* minutes */
878     ret = 60*ret + second;   /* seconds */
879
880     return ret;
881 }
882
883 static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
884
885 // we follow the recommendation of rfc2822 to consider all
886 // obsolete time zones not listed here equivalent to "-0000"
887 static const struct {
888     const char *tzName;
889     int tzOffset;
890 } known_zones[] = {
891     { "UT", 0 },
892     { "GMT", 0 },
893     { "EST", -300 },
894     { "EDT", -240 },
895     { "CST", -360 },
896     { "CDT", -300 },
897     { "MST", -420 },
898     { "MDT", -360 },
899     { "PST", -480 },
900     { "PDT", -420 },
901     { 0, 0 }
902 };
903
904 static inline bool isSpaceOrTab(char c)
905 {
906     return c == ' ' || c == '\t';
907 }
908
909 time_t KJS::KRFCDate_parseDate(const UString &_date)
910 {
911      // This parse a date in the form:
912      //     Wednesday, 09-Nov-99 23:12:40 GMT
913      // or
914      //     Sat, 01-Jan-2000 08:00:00 GMT
915      // or
916      //     Sat, 01 Jan 2000 08:00:00 GMT
917      // or
918      //     01 Jan 99 22:00 +0100    (exceptions in rfc822/rfc2822)
919      // ### non RFC format, added for Javascript:
920      //     [Wednesday] January 09 1999 23:12:40 GMT
921      //
922      // We ignore the weekday
923      //
924      int offset = 0;
925      char *newPosStr;
926      const char *dateString = _date.ascii();
927      int day = 0;
928      char monthStr[4];
929      int month = -1; // not set yet
930      int year = 0;
931      int hour = 0;
932      int minute = 0;
933      int second = 0;
934
935      errno = 0;
936
937      // Skip leading space
938      while (isSpaceOrTab(*dateString))
939         dateString++;
940
941      const char *wordStart = dateString;
942      // Check contents of first words if not number
943      while(*dateString && !isdigit(*dateString))
944      {
945         if ( isSpaceOrTab(*dateString) && dateString - wordStart >= 3 )
946         {
947           monthStr[0] = tolower(*wordStart++);
948           monthStr[1] = tolower(*wordStart++);
949           monthStr[2] = tolower(*wordStart++);
950           monthStr[3] = '\0';
951           //fprintf(stderr,"KJS::parseDate found word starting with '%s'\n", monthStr);
952           const char *str = strstr(haystack, monthStr);
953           if (str) {
954             int position = str - haystack;
955             if (position % 3 == 0) {
956               month = position / 3; // Jan=00, Feb=01, Mar=02, ..
957             }
958           }
959           while (isSpaceOrTab(*dateString))
960              dateString++;
961           wordStart = dateString;
962         }
963         else
964            dateString++;
965      }
966
967      while (isSpaceOrTab(*dateString))
968         dateString++;
969
970      if (!*dateString)
971         return invalidDate;
972
973      // ' 09-Nov-99 23:12:40 GMT'
974      day = strtol(dateString, &newPosStr, 10);
975      if (errno)
976         return invalidDate;
977      dateString = newPosStr;
978
979      if ((day < 1) || (day > 31))
980         return invalidDate;
981      if (!*dateString)
982         return invalidDate;
983
984      if (*dateString == '-' || *dateString == ',')
985         dateString++;
986
987      while (isSpaceOrTab(*dateString))
988         dateString++;
989
990      if ( month == -1 ) // not found yet
991      {
992         for(int i=0; i < 3;i++)
993         {
994            if (!*dateString || (*dateString == '-') || isSpaceOrTab(*dateString))
995               return invalidDate;
996            monthStr[i] = tolower(*dateString++);
997         }
998         monthStr[3] = '\0';
999
1000         newPosStr = (char*)strstr(haystack, monthStr);
1001
1002         if (!newPosStr || (newPosStr - haystack) % 3 != 0)
1003            return invalidDate;
1004
1005         month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
1006
1007         if ((month < 0) || (month > 11))
1008            return invalidDate;
1009
1010         while (*dateString && *dateString != '-' && !isSpaceOrTab(*dateString))
1011            dateString++;
1012
1013         if (!*dateString)
1014            return invalidDate;
1015
1016         // '-99 23:12:40 GMT'
1017         if (*dateString != '-' && !isSpaceOrTab(*dateString))
1018            return invalidDate;
1019         dateString++;
1020      }
1021
1022      if ((month < 0) || (month > 11))
1023         return invalidDate;
1024
1025      // '99 23:12:40 GMT'
1026      bool gotYear = true;
1027      year = strtol(dateString, &newPosStr, 10);
1028      if (errno)
1029         return invalidDate;
1030      dateString = newPosStr;
1031
1032      // Don't fail if the time is missing.
1033      if (*dateString == ':' || (isSpaceOrTab(*dateString) && isdigit(dateString[1])))
1034      {
1035         if (*dateString == ':') {
1036           hour = year;
1037           gotYear = false;
1038         } else {
1039           // ' 23:12:40 GMT'
1040           ++dateString;
1041         
1042           hour = strtol(dateString, &newPosStr, 10);
1043           if (errno)
1044             return invalidDate;
1045           dateString = newPosStr;
1046         }
1047
1048         if ((hour < 0) || (hour > 23))
1049            return invalidDate;
1050
1051         if (!*dateString)
1052            return invalidDate;
1053
1054         // ':12:40 GMT'
1055         if (*dateString++ != ':')
1056            return invalidDate;
1057
1058         minute = strtol(dateString, &newPosStr, 10);
1059         if (errno)
1060           return invalidDate;
1061         dateString = newPosStr;
1062
1063         if ((minute < 0) || (minute > 59))
1064            return invalidDate;
1065
1066         // seconds are optional in rfc822 + rfc2822
1067         if (*dateString ==':') {
1068            dateString++;
1069
1070            second = strtol(dateString, &newPosStr, 10);
1071            if (errno)
1072              return invalidDate;
1073            dateString = newPosStr;
1074
1075            if ((second < 0) || (second > 59))
1076               return invalidDate;
1077         }
1078      }
1079      
1080      while (isSpaceOrTab(*dateString))
1081         dateString++;
1082
1083      if (!gotYear) {
1084         year = strtol(dateString, &newPosStr, 10);
1085         if (errno)
1086           return invalidDate;
1087         while (isSpaceOrTab(*dateString))
1088            dateString++;
1089      }
1090
1091      // Y2K: Solve 2 digit years
1092      if ((year >= 0) && (year < 50))
1093          year += 2000;
1094
1095      if ((year >= 50) && (year < 100))
1096          year += 1900;  // Y2K
1097
1098      if ((year < 1900) || (year > 2500))
1099         return invalidDate;
1100
1101      if (strncasecmp(dateString, "AM", 2) == 0) {
1102         if (hour < 1 || hour > 12)
1103             return invalidDate;
1104         if (hour == 12)
1105             hour = 0;
1106         dateString += 2;
1107         while (isSpaceOrTab(*dateString))
1108            dateString++;
1109      } else if (strncasecmp(dateString, "PM", 2) == 0) {
1110         if (hour < 1 || hour > 12)
1111             return invalidDate;
1112         if (hour != 12)
1113             hour += 12;
1114         dateString += 2;
1115         while (isSpaceOrTab(*dateString))
1116            dateString++;
1117      }
1118
1119      // don't fail if the time zone is missing, some
1120      // broken mail-/news-clients omit the time zone
1121      bool localTime;
1122      if (*dateString == 0) {
1123         // Other web browsers interpret missing time zone as "current time zone".
1124         localTime = true;
1125      } else {
1126         localTime = false;
1127         if (strncasecmp(dateString, "GMT", 3) == 0) {
1128             dateString += 3;
1129         }
1130         if ((*dateString == '+') || (*dateString == '-')) {
1131            offset = strtol(dateString, &newPosStr, 10);
1132
1133            if (errno || (offset < -9959) || (offset > 9959))
1134               return invalidDate;
1135
1136            int sgn = (offset < 0)? -1:1;
1137            offset = abs(offset);
1138            offset = ((offset / 100)*60 + (offset % 100))*sgn;
1139         } else {
1140            for (int i=0; known_zones[i].tzName != 0; i++) {
1141               if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
1142                  offset = known_zones[i].tzOffset;
1143                  break;
1144               }
1145            }
1146         }
1147      }
1148      if (sizeof(time_t) == 4)
1149      {
1150          if ((time_t)-1 < 0)
1151          {
1152             if (year >= 2038)
1153             {
1154                year = 2038;
1155                month = 0;
1156                day = 1;
1157                hour = 0;
1158                minute = 0;
1159                second = 0;
1160             }
1161          }
1162          else
1163          {
1164             if (year >= 2115)
1165             {
1166                year = 2115;
1167                month = 0;
1168                day = 1;
1169                hour = 0;
1170                minute = 0;
1171                second = 0;
1172             }
1173          }
1174      }
1175
1176     time_t result;
1177      
1178     if (localTime) {
1179       struct tm tm;
1180       tm.tm_year = year - 1900;
1181       tm.tm_mon = month;
1182       tm.tm_mday = day;
1183       tm.tm_hour = hour;
1184       tm.tm_min = minute;
1185       tm.tm_sec = second;
1186       tm.tm_isdst = -1;
1187       result = mktime(&tm);
1188     } else {
1189      result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
1190
1191      // avoid negative time values
1192      if ((offset > 0) && (offset > result))
1193         offset = 0;
1194
1195      result -= offset*60;
1196     }
1197
1198      return result;
1199 }
1200
1201
1202 double KJS::timeClip(double t)
1203 {
1204     if (!isfinite(t))
1205         return NaN;
1206     double at = fabs(t);
1207     if (at > 8.64E15)
1208         return NaN;
1209     return copysign(floor(at), t);
1210 }