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