1 // -*- c-basic-offset: 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.
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.
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.
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
26 #ifndef HAVE_SYS_TIMEB_H
27 #define HAVE_SYS_TIMEB_H 0
30 #if TIME_WITH_SYS_TIME
31 # include <sys/time.h>
41 #include <sys/timeb.h>
44 #ifdef HAVE_SYS_PARAM_H
45 # include <sys/param.h>
46 #endif // HAVE_SYS_PARAM_H
55 #include "date_object.h"
56 #include "error_object.h"
57 #include "operations.h"
59 #include "date_object.lut.h"
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;
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.
77 #include <CoreFoundation/CoreFoundation.h>
78 #include <CoreServices/CoreServices.h>
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)
89 #define ctime(x) NotAllowedToCallThis()
90 #define strftime(a, b, c, d) NotAllowedToCallThis()
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" };
95 static struct tm *tmUsingCF(time_t clock, CFTimeZoneRef timeZone)
97 static struct tm result;
98 static char timeZoneCString[128];
100 CFAbsoluteTime absoluteTime = clock - kCFAbsoluteTimeIntervalSince1970;
101 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absoluteTime, timeZone);
103 CFStringRef abbreviation = CFTimeZoneCopyAbbreviation(timeZone, absoluteTime);
104 CFStringGetCString(abbreviation, timeZoneCString, sizeof(timeZoneCString), kCFStringEncodingASCII);
105 CFRelease(abbreviation);
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;
122 static CFTimeZoneRef UTCTimeZone()
124 static CFTimeZoneRef zone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
128 static CFTimeZoneRef CopyLocalTimeZone()
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;
135 uint32_t status = notify_register_check("com.apple.system.timezone", ¬ificationToken);
136 if (status == NOTIFY_STATUS_OK) {
142 uint32_t status = notify_check(notificationToken, ¬ified);
143 if (status == NOTIFY_STATUS_OK && notified) {
144 CFTimeZoneResetSystem();
148 CFTimeZoneRef zone = CFTimeZoneCopyDefault();
152 zone = UTCTimeZone();
157 static struct tm *gmtimeUsingCF(const time_t *clock)
159 return tmUsingCF(*clock, UTCTimeZone());
162 static struct tm *localtimeUsingCF(const time_t *clock)
164 CFTimeZoneRef timeZone = CopyLocalTimeZone();
165 struct tm *result = tmUsingCF(*clock, timeZone);
170 static time_t timetUsingCF(struct tm *tm, CFTimeZoneRef timeZone)
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;
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) {
186 CFAbsoluteTime absoluteTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
187 CFTimeInterval interval = absoluteTime + kCFAbsoluteTimeIntervalSince1970;
188 if (interval > LONG_MAX) {
192 return (time_t) interval;
195 static time_t mktimeUsingCF(struct tm *tm)
197 CFTimeZoneRef timeZone = CopyLocalTimeZone();
198 time_t result = timetUsingCF(tm, timeZone);
203 static time_t timegmUsingCF(struct tm *tm)
205 return timetUsingCF(tm, UTCTimeZone());
208 static time_t timeUsingCF(time_t *clock)
210 time_t result = (time_t)(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970);
217 static UString formatDate(struct tm &tm)
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);
226 static UString formatDateUTCVariant(struct tm &tm)
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);
235 static UString formatTime(struct tm &tm)
238 if (tm.tm_gmtoff == 0) {
239 snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", tm.tm_hour, tm.tm_min, tm.tm_sec);
241 int offset = tm.tm_gmtoff;
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);
249 return UString(buffer);
252 static CFDateFormatterStyle styleFromArgString(const UString& string,CFDateFormatterStyle defaultStyle)
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;
266 static UString formatLocaleDate(KJS::ExecState *exec, double time, bool includeDate, bool includeTime, const KJS::List &args)
268 CFLocaleRef locale = CFLocaleCopyCurrent();
269 int argCount = args.size();
271 CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
272 CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
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);
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);
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.
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));
309 CFRelease(formatter);
312 return UString(buffer, length);
315 #endif // APPLE_CHANGES
319 static int day(double t)
321 return int(floor(t / msPerDay));
324 static double dayFromYear(int year)
326 return 365.0 * (year - 1970)
327 + floor((year - 1969) / 4.0)
328 - floor((year - 1901) / 100.0)
329 + floor((year - 1601) / 400.0);
332 // depending on whether it's a leap year or not
333 static int daysInYear(int year)
337 else if (year % 400 == 0)
339 else if (year % 100 == 0)
345 // time value of the start of a year
346 static double timeFromYear(int year)
348 return msPerDay * dayFromYear(year);
351 // year determined by time value
352 static int yearFromTime(double t)
354 // ### there must be an easier way
356 int y = 1970 + int(t / (365.25 * msPerDay));
358 if (timeFromYear(y) > t) {
361 } while (timeFromYear(y) > t);
363 while (timeFromYear(y + 1) < t)
370 // 0: Sunday, 1: Monday, etc.
371 static int weekDay(double t)
373 int wd = (day(t) + 4) % 7;
379 static double timeZoneOffset(const struct tm *t)
381 #if defined BSD || defined(__linux__) || defined(__APPLE__)
382 return -(t->tm_gmtoff / 60);
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!
389 return _timezone / 60 - (t->tm_isdst > 0 ? 60 : 0);
391 return timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 );
396 // ------------------------------ DateInstanceImp ------------------------------
398 const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
400 DateInstanceImp::DateInstanceImp(ObjectImp *proto)
405 // ------------------------------ DatePrototypeImp -----------------------------
407 const ClassInfo DatePrototypeImp::info = {"Date", 0, &dateTable, 0};
409 /* Source for date_object.lut.h
410 We use a negative ID to denote the "UTC" variant.
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
460 DatePrototypeImp::DatePrototypeImp(ExecState *,
461 ObjectPrototypeImp *objectProto)
462 : DateInstanceImp(objectProto)
465 setInternalValue(NumberImp::create(NaN));
466 // The constructor will be added later, after DateObjectImp has been built
469 Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
471 return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
474 // ------------------------------ DateProtoFuncImp -----------------------------
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.
483 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
486 bool DateProtoFuncImp::implementsCall() const
491 Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
493 if ((id == ToString || id == ValueOf || id == GetTime || id == SetTime) &&
494 !thisObj.inherits(&DateInstanceImp::info)) {
495 // non-generic function called on non-date object
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);
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);
514 Value v = thisObj.internalValue();
515 double milli = v.toNumber(exec);
525 case ToLocaleDateString:
526 case ToLocaleTimeString:
527 return String("Invalid Date");
538 case GetMilliSeconds:
539 case GetTimezoneOffset:
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);
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;
559 time_t tv = (time_t) floor(milli / 1000.0);
560 int ms = int(milli - tv * 1000.0);
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.
571 m -= timeZoneOffset(t) * msPerMinute;
572 t->tm_wday = weekDay(m);
578 result = String(formatDate(*t) + " " + formatTime(*t));
581 result = String(formatDate(*t));
584 result = String(formatTime(*t));
588 result = String(formatDateUTCVariant(*t) + " " + formatTime(*t));
591 result = String(formatLocaleDate(exec, secs, true, true, args));
593 case ToLocaleDateString:
594 result = String(formatLocaleDate(exec, secs, true, false, args));
596 case ToLocaleTimeString:
597 result = String(formatLocaleDate(exec, secs, false, true, args));
602 result = String(s.substr(0, s.size() - 1));
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);
616 setlocale(LC_TIME,oldlocale.c_str());
617 result = String(timebuffer);
620 strftime(timebuffer, bufsize, "%c", t);
621 result = String(timebuffer);
623 case ToLocaleDateString:
624 strftime(timebuffer, bufsize, "%x", t);
625 result = String(timebuffer);
627 case ToLocaleTimeString:
628 strftime(timebuffer, bufsize, "%X", t);
629 result = String(timebuffer);
633 result = Number(milli);
636 result = Number(milli);
639 // IE returns the full year even in getYear.
640 if ( exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat )
641 result = Number(1900 + t->tm_year);
643 result = Number(t->tm_year);
646 result = Number(1900 + t->tm_year);
649 result = Number(t->tm_mon);
652 result = Number(t->tm_mday);
655 result = Number(t->tm_wday);
658 result = Number(t->tm_hour);
661 result = Number(t->tm_min);
664 result = Number(t->tm_sec);
666 case GetMilliSeconds:
669 case GetTimezoneOffset:
670 #if defined BSD || defined(__APPLE__)
671 result = Number(-t->tm_gmtoff / 60);
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));
678 // FIXME: Using the daylight value was wrong for BSD, maybe wrong here too.
679 result = Number(( timezone / 60 - ( daylight ? 60 : 0 )));
684 milli = roundValue(exec,args[0]);
685 result = Number(milli);
686 thisObj.setInternalValue(result);
688 case SetMilliSeconds:
689 ms = args[0].toInt32(exec);
692 t->tm_sec = args[0].toInt32(exec);
693 if (args.size() >= 2)
694 ms = args[1].toInt32(exec);
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);
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);
713 t->tm_mday = args[0].toInt32(exec);
716 t->tm_mon = args[0].toInt32(exec);
717 if (args.size() >= 2)
718 t->tm_mday = args[1].toInt32(exec);
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);
728 t->tm_year = args[0].toInt32(exec) >= 1900 ? args[0].toInt32(exec) - 1900 : args[0].toInt32(exec);
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);
742 // ------------------------------ DateObjectImp --------------------------------
744 // TODO: MakeTime (15.9.11.1) etc. ?
746 DateObjectImp::DateObjectImp(ExecState *exec,
747 FunctionPrototypeImp *funcProto,
748 DatePrototypeImp *dateProto)
749 : InternalFunctionImp(funcProto)
753 // ECMA 15.9.4.1 Date.prototype
754 putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
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);
761 // no. of arguments for constructor
762 putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
765 bool DateObjectImp::implementsConstruct() const
771 Object DateObjectImp::construct(ExecState *exec, const List &args)
773 int numArgs = args.size();
776 fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
780 if (numArgs == 0) { // new Date() ECMA 15.9.3.3
782 # if defined(__BORLANDC__)
783 struct timeb timebuffer;
786 struct _timeb timebuffer;
789 double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
792 gettimeofday(&tv, 0L);
793 double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
796 } else if (numArgs == 1) {
797 UString s = args[0].toString(exec);
798 double d = s.toDouble();
800 value = parseDate(s);
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)))) {
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;
823 int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
824 value = makeTime(&t, ms, false);
828 Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
829 Object ret(new DateInstanceImp(proto.imp()));
830 ret.setInternalValue(Number(timeClip(value)));
834 bool DateObjectImp::implementsCall() const
840 Value DateObjectImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/)
843 fprintf(stderr,"DateObjectImp::call - current time\n");
847 struct tm *tm = localtime(&t);
848 return String(formatDate(*tm) + " " + formatTime(*tm));
850 UString s(ctime(&t));
852 // return formatted string minus trailing \n
853 return String(s.substr(0, s.size() - 1));
857 // ------------------------------ DateObjectFuncImp ----------------------------
859 DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
861 : InternalFunctionImp(funcProto), id(i)
864 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
867 bool DateObjectFuncImp::implementsCall() const
873 Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
876 return Number(parseDate(args[0].toString(exec)));
880 memset(&t, 0, sizeof(t));
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)))) {
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)
902 return Number(mktimeResult * 1000.0 + ms);
906 // -----------------------------------------------------------------------------
909 double KJS::parseDate(const UString &u)
912 fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
914 double /*time_t*/ seconds = KRFCDate_parseDate( u );
916 return seconds == invalidDate ? NaN : seconds * 1000.0;
919 ///// Awful duplication from krfcdate.cpp - we don't link to kdecore
921 static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
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
928 ret = 24*ret + hour; /* hours */
929 ret = 60*ret + minute; /* minutes */
930 ret = 60*ret + second; /* seconds */
935 static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
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 {
943 const char tzName[4];
959 double KJS::makeTime(struct tm *t, int ms, bool utc)
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;
969 (void)localtime(&zero);
970 # if defined(__BORLANDC__) || defined(__CYGWIN__)
971 utcOffset = - _timezone;
973 utcOffset = - timezone;
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
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;
996 return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
999 // returns 0-11 (Jan-Dec); -1 on failure
1000 static int findMonth(const char *monthStr)
1003 static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
1005 for (int i = 0; i < 3; ++i) {
1008 needle[i] = tolower(*monthStr++);
1011 const char *str = strstr(haystack, needle);
1013 int position = str - haystack;
1014 if (position % 3 == 0) {
1015 return position / 3;
1021 double KJS::KRFCDate_parseDate(const UString &_date)
1023 // This parse a date in the form:
1024 // Tuesday, 09-Nov-99 23:12:40 GMT
1026 // Sat, 01-Jan-2000 08:00:00 GMT
1028 // Sat, 01 Jan 2000 08:00:00 GMT
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
1035 // We ignore the weekday
1039 bool have_tz = false;
1041 const char *dateString = _date.ascii();
1043 int month = -1; // not set yet
1048 bool have_time = false;
1050 // for strtol error checking
1053 // Skip leading space
1054 while(isspace(*dateString))
1057 const char *wordStart = dateString;
1058 // Check contents of first words if not number
1059 while(*dateString && !isdigit(*dateString))
1061 if ( isspace(*dateString) && dateString - wordStart >= 3 )
1063 month = findMonth(wordStart);
1064 while(isspace(*dateString))
1066 wordStart = dateString;
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
1077 while(isspace(*dateString))
1083 // ' 09-Nov-99 23:12:40 GMT'
1084 day = strtol(dateString, &newPosStr, 10);
1087 dateString = newPosStr;
1095 // ### where is the boundary and what happens below?
1096 if (*dateString == '/' && day >= 1000) {
1097 // looks like a YYYY/MM/DD date
1101 month = strtol(dateString, &newPosStr, 10) - 1;
1104 dateString = newPosStr;
1105 if (*dateString++ != '/' || !*dateString)
1107 day = strtol(dateString, &newPosStr, 10);
1110 dateString = newPosStr;
1114 } else if (*dateString == '/' && day <= 12 && month == -1)
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);
1122 dateString = newPosStr;
1123 if (*dateString == '/')
1127 //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
1131 if (*dateString == '-')
1134 while(isspace(*dateString))
1137 if (*dateString == ',')
1140 if ( month == -1 ) // not found yet
1142 month = findMonth(dateString);
1146 while(*dateString && (*dateString != '-') && !isspace(*dateString))
1152 // '-99 23:12:40 GMT'
1153 if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
1158 if ((month < 0) || (month > 11))
1162 // '99 23:12:40 GMT'
1163 if (year <= 0 && *dateString) {
1164 year = strtol(dateString, &newPosStr, 10);
1169 // Don't fail if the time is missing.
1173 if (!isspace(*newPosStr)) {
1174 if ( *newPosStr == ':' ) // Ah, so there was no year, but the number was the hour
1178 } else // in the normal case (we parsed the year), advance to the next number
1179 dateString = ++newPosStr;
1181 hour = strtol(dateString, &newPosStr, 10);
1184 // read a number? if not this might be a timezone name
1185 if (newPosStr != dateString) {
1187 dateString = newPosStr;
1189 if ((hour < 0) || (hour > 23))
1196 if (*dateString++ != ':')
1199 minute = strtol(dateString, &newPosStr, 10);
1202 dateString = newPosStr;
1204 if ((minute < 0) || (minute > 59))
1208 if (*dateString && *dateString != ':' && !isspace(*dateString))
1211 // seconds are optional in rfc822 + rfc2822
1212 if (*dateString ==':') {
1215 second = strtol(dateString, &newPosStr, 10);
1218 dateString = newPosStr;
1220 if ((second < 0) || (second > 59))
1224 while(isspace(*dateString))
1227 if (strncasecmp(dateString, "AM", 2) == 0) {
1233 while (isspace(*dateString))
1235 } else if (strncasecmp(dateString, "PM", 2) == 0) {
1241 while (isspace(*dateString))
1246 dateString = newPosStr;
1249 // don't fail if the time zone is missing, some
1250 // broken mail-/news-clients omit the time zone
1253 if (strncasecmp(dateString, "GMT", 3) == 0 ||
1254 strncasecmp(dateString, "UTC", 3) == 0)
1260 while (isspace(*dateString))
1263 if (strncasecmp(dateString, "GMT", 3) == 0) {
1266 if ((*dateString == '+') || (*dateString == '-')) {
1267 offset = strtol(dateString, &newPosStr, 10);
1270 dateString = newPosStr;
1272 if ((offset < -9959) || (offset > 9959))
1275 int sgn = (offset < 0)? -1:1;
1276 offset = abs(offset);
1277 if ( *dateString == ':' ) { // GMT+05:00
1278 int offset2 = strtol(dateString, &newPosStr, 10);
1281 dateString = newPosStr;
1282 offset = (offset*60 + offset2)*sgn;
1285 offset = ((offset / 100)*60 + (offset % 100))*sgn;
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;
1298 while(isspace(*dateString))
1301 if ( *dateString && year == -1 ) {
1302 year = strtol(dateString, &newPosStr, 10);
1307 // Y2K: Solve 2 digit years
1308 if ((year >= 0) && (year < 50))
1311 if ((year >= 50) && (year < 100))
1312 year += 1900; // Y2K
1315 // fall back to midnight, local timezone
1317 memset(&t, 0, sizeof(tm));
1320 t.tm_year = year - 1900;
1328 // better not use mktime() as it can't handle the full year range
1329 return makeTime(&t, 0, false) / 1000.0;
1332 result = ymdhms_to_seconds(year, month+1, day, hour, minute, second) - (offset*60);
1337 double KJS::timeClip(double t)
1341 double at = fabs(t);
1344 return copysign(floor(at), t);