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