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"
61 const time_t invalidDate = -1;
65 // Originally, we wrote our own implementation that uses Core Foundation because of a performance problem in Mac OS X 10.2.
66 // But we need to keep using this rather than the standard library functions because this handles a larger range of dates.
69 #include <CoreFoundation/CoreFoundation.h>
70 #include <CoreServices/CoreServices.h>
75 #define gmtime(x) gmtimeUsingCF(x)
76 #define localtime(x) localtimeUsingCF(x)
77 #define mktime(x) mktimeUsingCF(x)
78 #define timegm(x) timegmUsingCF(x)
79 #define time(x) timeUsingCF(x)
81 #define ctime(x) NotAllowedToCallThis()
82 #define strftime(a, b, c, d) NotAllowedToCallThis()
84 static const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
85 static const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
87 static struct tm *tmUsingCF(time_t clock, CFTimeZoneRef timeZone)
89 static struct tm result;
90 static char timeZoneCString[128];
92 CFAbsoluteTime absoluteTime = clock - kCFAbsoluteTimeIntervalSince1970;
93 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absoluteTime, timeZone);
95 CFStringRef abbreviation = CFTimeZoneCopyAbbreviation(timeZone, absoluteTime);
96 CFStringGetCString(abbreviation, timeZoneCString, sizeof(timeZoneCString), kCFStringEncodingASCII);
97 CFRelease(abbreviation);
99 result.tm_sec = (int)date.second;
100 result.tm_min = date.minute;
101 result.tm_hour = date.hour;
102 result.tm_mday = date.day;
103 result.tm_mon = date.month - 1;
104 result.tm_year = date.year - 1900;
105 result.tm_wday = CFAbsoluteTimeGetDayOfWeek(absoluteTime, timeZone) % 7;
106 result.tm_yday = CFAbsoluteTimeGetDayOfYear(absoluteTime, timeZone) - 1;
107 result.tm_isdst = CFTimeZoneIsDaylightSavingTime(timeZone, absoluteTime);
108 result.tm_gmtoff = (int)CFTimeZoneGetSecondsFromGMT(timeZone, absoluteTime);
109 result.tm_zone = timeZoneCString;
114 static CFTimeZoneRef UTCTimeZone()
116 static CFTimeZoneRef zone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
120 static CFTimeZoneRef CopyLocalTimeZone()
122 // Check for a time zone notification, and tell CoreFoundation to re-get the time zone if it happened.
123 // Some day, CoreFoundation may do this itself, but for now it needs our help.
124 static bool registered = false;
125 static int notificationToken;
127 uint32_t status = notify_register_check("com.apple.system.timezone", ¬ificationToken);
128 if (status == NOTIFY_STATUS_OK) {
134 uint32_t status = notify_check(notificationToken, ¬ified);
135 if (status == NOTIFY_STATUS_OK && notified) {
136 CFTimeZoneResetSystem();
140 CFTimeZoneRef zone = CFTimeZoneCopyDefault();
144 zone = UTCTimeZone();
149 static struct tm *gmtimeUsingCF(const time_t *clock)
151 return tmUsingCF(*clock, UTCTimeZone());
154 static struct tm *localtimeUsingCF(const time_t *clock)
156 CFTimeZoneRef timeZone = CopyLocalTimeZone();
157 struct tm *result = tmUsingCF(*clock, timeZone);
162 static time_t timetUsingCF(struct tm *tm, CFTimeZoneRef timeZone)
164 CFGregorianDate date;
165 date.second = tm->tm_sec;
166 date.minute = tm->tm_min;
167 date.hour = tm->tm_hour;
168 date.day = tm->tm_mday;
169 date.month = tm->tm_mon + 1;
170 date.year = tm->tm_year + 1900;
172 // CFGregorianDateGetAbsoluteTime will go nuts if the year is too large or small,
173 // so we pick an arbitrary cutoff.
174 if (date.year < -2500 || date.year > 2500) {
178 CFAbsoluteTime absoluteTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
179 CFTimeInterval interval = absoluteTime + kCFAbsoluteTimeIntervalSince1970;
180 if (interval > LONG_MAX) {
184 return (time_t) interval;
187 static time_t mktimeUsingCF(struct tm *tm)
189 CFTimeZoneRef timeZone = CopyLocalTimeZone();
190 time_t result = timetUsingCF(tm, timeZone);
195 static time_t timegmUsingCF(struct tm *tm)
197 return timetUsingCF(tm, UTCTimeZone());
200 static time_t timeUsingCF(time_t *clock)
202 time_t result = (time_t)(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970);
209 static UString formatDate(struct tm &tm)
212 snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
213 weekdayName[(tm.tm_wday + 6) % 7],
214 monthName[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
218 static UString formatDateUTCVariant(struct tm &tm)
221 snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
222 weekdayName[(tm.tm_wday + 6) % 7],
223 tm.tm_mday, monthName[tm.tm_mon], tm.tm_year + 1900);
227 static UString formatTime(struct tm &tm)
230 if (tm.tm_gmtoff == 0) {
231 snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", tm.tm_hour, tm.tm_min, tm.tm_sec);
233 int offset = tm.tm_gmtoff;
237 snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
238 tm.tm_hour, tm.tm_min, tm.tm_sec,
239 tm.tm_gmtoff < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
241 return UString(buffer);
244 static CFDateFormatterStyle styleFromArgString(const UString& string,CFDateFormatterStyle defaultStyle)
246 CFDateFormatterStyle retVal = defaultStyle;
247 if (string == "short")
248 retVal = kCFDateFormatterShortStyle;
249 else if (string == "medium")
250 retVal = kCFDateFormatterMediumStyle;
251 else if (string == "long")
252 retVal = kCFDateFormatterLongStyle;
253 else if (string == "full")
254 retVal = kCFDateFormatterFullStyle;
258 static UString formatLocaleDate(KJS::ExecState *exec,time_t tv, bool includeDate, bool includeTime, const KJS::List &args)
260 LongDateTime longDateTime;
261 UCConvertCFAbsoluteTimeToLongDateTime(tv - kCFAbsoluteTimeIntervalSince1970, &longDateTime);
263 CFLocaleRef locale = CFLocaleCopyCurrent();
265 int argCount = args.size();
267 CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
268 CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
272 bool useCustomFormat = false;
273 UString customFormatString;
274 arg0String = args[0].toString(exec);
275 if ((arg0String == "custom") && (argCount >= 2)) {
276 useCustomFormat = true;
277 customFormatString = args[1].toString(exec);
278 } else if (includeDate && includeTime && (argCount >= 2)) {
279 arg1String = args[1].toString(exec);
280 dateStyle = styleFromArgString(arg0String,dateStyle);
281 timeStyle = styleFromArgString(arg1String,timeStyle);
282 } else if (includeDate && (argCount >= 1)) {
283 dateStyle = styleFromArgString(arg0String,dateStyle);
284 } else if (includeTime && (argCount >= 1)) {
285 timeStyle = styleFromArgString(arg0String,timeStyle);
287 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, dateStyle, timeStyle);
288 if (useCustomFormat) {
289 CFStringRef customFormatCFString = CFStringCreateWithCharacters(NULL,(UniChar*)customFormatString.data(),customFormatString.size());
290 CFDateFormatterSetFormat(formatter,customFormatCFString);
291 CFRelease(customFormatCFString);
293 CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(NULL, formatter, tv - kCFAbsoluteTimeIntervalSince1970);
295 // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters).
296 // That's not great error handling, but it just won't happen so it doesn't matter.
298 const size_t bufferLength = sizeof(buffer) / sizeof(buffer[0]);
299 size_t length = CFStringGetLength(string);
300 assert(length <= bufferLength);
301 if (length > bufferLength)
302 length = bufferLength;
303 CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast<UniChar *>(buffer));
306 CFRelease(formatter);
309 return UString(buffer, length);
312 #endif // APPLE_CHANGES
316 // ------------------------------ DateInstanceImp ------------------------------
318 const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
320 DateInstanceImp::DateInstanceImp(ObjectImp *proto)
325 // ------------------------------ DatePrototypeImp -----------------------------
327 const ClassInfo DatePrototypeImp::info = {"Date", 0, &dateTable, 0};
329 /* Source for date_object.lut.h
330 We use a negative ID to denote the "UTC" variant.
332 toString DateProtoFuncImp::ToString DontEnum|Function 0
333 toUTCString -DateProtoFuncImp::ToUTCString DontEnum|Function 0
334 toDateString DateProtoFuncImp::ToDateString DontEnum|Function 0
335 toTimeString DateProtoFuncImp::ToTimeString DontEnum|Function 0
336 toLocaleString DateProtoFuncImp::ToLocaleString DontEnum|Function 0
337 toLocaleDateString DateProtoFuncImp::ToLocaleDateString DontEnum|Function 0
338 toLocaleTimeString DateProtoFuncImp::ToLocaleTimeString DontEnum|Function 0
339 valueOf DateProtoFuncImp::ValueOf DontEnum|Function 0
340 getTime DateProtoFuncImp::GetTime DontEnum|Function 0
341 getFullYear DateProtoFuncImp::GetFullYear DontEnum|Function 0
342 getUTCFullYear -DateProtoFuncImp::GetFullYear DontEnum|Function 0
343 toGMTString -DateProtoFuncImp::ToGMTString DontEnum|Function 0
344 getMonth DateProtoFuncImp::GetMonth DontEnum|Function 0
345 getUTCMonth -DateProtoFuncImp::GetMonth DontEnum|Function 0
346 getDate DateProtoFuncImp::GetDate DontEnum|Function 0
347 getUTCDate -DateProtoFuncImp::GetDate DontEnum|Function 0
348 getDay DateProtoFuncImp::GetDay DontEnum|Function 0
349 getUTCDay -DateProtoFuncImp::GetDay DontEnum|Function 0
350 getHours DateProtoFuncImp::GetHours DontEnum|Function 0
351 getUTCHours -DateProtoFuncImp::GetHours DontEnum|Function 0
352 getMinutes DateProtoFuncImp::GetMinutes DontEnum|Function 0
353 getUTCMinutes -DateProtoFuncImp::GetMinutes DontEnum|Function 0
354 getSeconds DateProtoFuncImp::GetSeconds DontEnum|Function 0
355 getUTCSeconds -DateProtoFuncImp::GetSeconds DontEnum|Function 0
356 getMilliseconds DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
357 getUTCMilliseconds -DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
358 getTimezoneOffset DateProtoFuncImp::GetTimezoneOffset DontEnum|Function 0
359 setTime DateProtoFuncImp::SetTime DontEnum|Function 1
360 setMilliseconds DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
361 setUTCMilliseconds -DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
362 setSeconds DateProtoFuncImp::SetSeconds DontEnum|Function 2
363 setUTCSeconds -DateProtoFuncImp::SetSeconds DontEnum|Function 2
364 setMinutes DateProtoFuncImp::SetMinutes DontEnum|Function 3
365 setUTCMinutes -DateProtoFuncImp::SetMinutes DontEnum|Function 3
366 setHours DateProtoFuncImp::SetHours DontEnum|Function 4
367 setUTCHours -DateProtoFuncImp::SetHours DontEnum|Function 4
368 setDate DateProtoFuncImp::SetDate DontEnum|Function 1
369 setUTCDate -DateProtoFuncImp::SetDate DontEnum|Function 1
370 setMonth DateProtoFuncImp::SetMonth DontEnum|Function 2
371 setUTCMonth -DateProtoFuncImp::SetMonth DontEnum|Function 2
372 setFullYear DateProtoFuncImp::SetFullYear DontEnum|Function 3
373 setUTCFullYear -DateProtoFuncImp::SetFullYear DontEnum|Function 3
374 setYear DateProtoFuncImp::SetYear DontEnum|Function 1
375 getYear DateProtoFuncImp::GetYear DontEnum|Function 0
380 DatePrototypeImp::DatePrototypeImp(ExecState *,
381 ObjectPrototypeImp *objectProto)
382 : DateInstanceImp(objectProto)
385 setInternalValue(NumberImp::create(NaN));
386 // The constructor will be added later, after DateObjectImp has been built
389 Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
391 return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
394 // ------------------------------ DateProtoFuncImp -----------------------------
396 DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
397 : InternalFunctionImp(
398 static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
399 ), id(abs(i)), utc(i<0)
400 // We use a negative ID to denote the "UTC" variant.
403 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
406 bool DateProtoFuncImp::implementsCall() const
411 Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
413 if ((id == ToString || id == ValueOf || id == GetTime || id == SetTime) &&
414 !thisObj.inherits(&DateInstanceImp::info)) {
415 // non-generic function called on non-date object
417 // ToString and ValueOf are generic according to the spec, but the mozilla
418 // tests suggest otherwise...
419 Object err = Error::create(exec,TypeError);
420 exec->setException(err);
428 const int bufsize=100;
429 char timebuffer[bufsize];
430 CString oldlocale = setlocale(LC_TIME,NULL);
431 if (!oldlocale.c_str())
432 oldlocale = setlocale(LC_ALL, NULL);
434 Value v = thisObj.internalValue();
435 double milli = v.toNumber(exec);
445 case ToLocaleDateString:
446 case ToLocaleTimeString:
447 return String("Invalid Date");
458 case GetMilliSeconds:
459 case GetTimezoneOffset:
464 time_t tv = (time_t)(milli / 1000.0);
465 int ms = int(milli - tv * 1000.0);
476 result = String(formatDate(*t) + " " + formatTime(*t));
479 result = String(formatDate(*t));
482 result = String(formatTime(*t));
486 result = String(formatDateUTCVariant(*t) + " " + formatTime(*t));
489 result = String(formatLocaleDate(exec,tv, true, true,args));
491 case ToLocaleDateString:
492 result = String(formatLocaleDate(exec,tv, true, false,args));
494 case ToLocaleTimeString:
495 result = String(formatLocaleDate(exec,tv, false, true,args));
500 result = String(s.substr(0, s.size() - 1));
506 setlocale(LC_TIME,"C");
507 if (id == DateProtoFuncImp::ToDateString) {
508 strftime(timebuffer, bufsize, "%x",t);
509 } else if (id == DateProtoFuncImp::ToTimeString) {
510 strftime(timebuffer, bufsize, "%X",t);
511 } else { // toGMTString & toUTCString
512 strftime(timebuffer, bufsize, "%a, %d %b %Y %H:%M:%S %Z", t);
514 setlocale(LC_TIME,oldlocale.c_str());
515 result = String(timebuffer);
518 strftime(timebuffer, bufsize, "%c", t);
519 result = String(timebuffer);
521 case ToLocaleDateString:
522 strftime(timebuffer, bufsize, "%x", t);
523 result = String(timebuffer);
525 case ToLocaleTimeString:
526 strftime(timebuffer, bufsize, "%X", t);
527 result = String(timebuffer);
531 result = Number(milli);
534 result = Number(milli);
537 // IE returns the full year even in getYear.
538 if ( exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat )
539 result = Number(1900 + t->tm_year);
541 result = Number(t->tm_year);
544 result = Number(1900 + t->tm_year);
547 result = Number(t->tm_mon);
550 result = Number(t->tm_mday);
553 result = Number(t->tm_wday);
556 result = Number(t->tm_hour);
559 result = Number(t->tm_min);
562 result = Number(t->tm_sec);
564 case GetMilliSeconds:
567 case GetTimezoneOffset:
568 #if defined BSD || defined(__APPLE__)
569 result = Number(-t->tm_gmtoff / 60);
571 # if defined(__BORLANDC__)
572 #error please add daylight savings offset here!
573 // FIXME: Using the daylight value was wrong for BSD, maybe wrong here too.
574 result = Number(_timezone / 60 - (_daylight ? 60 : 0));
576 // FIXME: Using the daylight value was wrong for BSD, maybe wrong here too.
577 result = Number(( timezone / 60 - ( daylight ? 60 : 0 )));
582 milli = roundValue(exec,args[0]);
583 result = Number(milli);
584 thisObj.setInternalValue(result);
586 case SetMilliSeconds:
587 ms = args[0].toInt32(exec);
590 t->tm_sec = args[0].toInt32(exec);
591 if (args.size() >= 2)
592 ms = args[1].toInt32(exec);
595 t->tm_min = args[0].toInt32(exec);
596 if (args.size() >= 2)
597 t->tm_sec = args[1].toInt32(exec);
598 if (args.size() >= 3)
599 ms = args[2].toInt32(exec);
602 t->tm_hour = args[0].toInt32(exec);
603 if (args.size() >= 2)
604 t->tm_min = args[1].toInt32(exec);
605 if (args.size() >= 3)
606 t->tm_sec = args[2].toInt32(exec);
607 if (args.size() >= 4)
608 ms = args[3].toInt32(exec);
611 t->tm_mday = args[0].toInt32(exec);
614 t->tm_mon = args[0].toInt32(exec);
615 if (args.size() >= 2)
616 t->tm_mday = args[1].toInt32(exec);
619 t->tm_year = args[0].toInt32(exec) - 1900;
620 if (args.size() >= 2)
621 t->tm_mon = args[1].toInt32(exec);
622 if (args.size() >= 3)
623 t->tm_mday = args[2].toInt32(exec);
626 t->tm_year = args[0].toInt32(exec) >= 1900 ? args[0].toInt32(exec) - 1900 : args[0].toInt32(exec);
630 if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
631 id == SetMinutes || id == SetHours || id == SetDate ||
632 id == SetMonth || id == SetFullYear ) {
633 time_t mktimeResult = utc ? timegm(t) : mktime(t);
634 if (mktimeResult == invalidDate)
635 result = Number(NaN);
637 result = Number(mktimeResult * 1000.0 + ms);
638 thisObj.setInternalValue(result);
644 // ------------------------------ DateObjectImp --------------------------------
646 // TODO: MakeTime (15.9.11.1) etc. ?
648 DateObjectImp::DateObjectImp(ExecState *exec,
649 FunctionPrototypeImp *funcProto,
650 DatePrototypeImp *dateProto)
651 : InternalFunctionImp(funcProto)
655 // ECMA 15.9.4.1 Date.prototype
656 putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
658 static const Identifier parsePropertyName("parse");
659 putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
660 static const Identifier UTCPropertyName("UTC");
661 putDirect(UTCPropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC, 7), DontEnum);
663 // no. of arguments for constructor
664 putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
667 bool DateObjectImp::implementsConstruct() const
673 Object DateObjectImp::construct(ExecState *exec, const List &args)
675 int numArgs = args.size();
678 fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
682 if (numArgs == 0) { // new Date() ECMA 15.9.3.3
684 # if defined(__BORLANDC__)
685 struct timeb timebuffer;
688 struct _timeb timebuffer;
691 double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
694 gettimeofday(&tv, 0L);
695 double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
698 } else if (numArgs == 1) {
699 UString s = args[0].toString(exec);
700 double d = s.toDouble();
702 value = parseDate(s);
707 memset(&t, 0, sizeof(t));
708 if (isNaN(args[0].toNumber(exec))
709 || isNaN(args[1].toNumber(exec))
710 || (numArgs >= 3 && isNaN(args[2].toNumber(exec)))
711 || (numArgs >= 4 && isNaN(args[3].toNumber(exec)))
712 || (numArgs >= 5 && isNaN(args[4].toNumber(exec)))
713 || (numArgs >= 6 && isNaN(args[5].toNumber(exec)))
714 || (numArgs >= 7 && isNaN(args[6].toNumber(exec)))) {
717 int year = args[0].toInt32(exec);
718 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
719 t.tm_mon = args[1].toInt32(exec);
720 t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
721 t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
722 t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
723 t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
725 int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
726 time_t mktimeResult = mktime(&t);
727 if (mktimeResult == invalidDate)
730 value = mktimeResult * 1000.0 + ms;
734 Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
735 Object ret(new DateInstanceImp(proto.imp()));
736 ret.setInternalValue(Number(timeClip(value)));
740 bool DateObjectImp::implementsCall() const
746 Value DateObjectImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/)
749 fprintf(stderr,"DateObjectImp::call - current time\n");
753 struct tm *tm = localtime(&t);
754 return String(formatDate(*tm) + " " + formatTime(*tm));
756 UString s(ctime(&t));
758 // return formatted string minus trailing \n
759 return String(s.substr(0, s.size() - 1));
763 // ------------------------------ DateObjectFuncImp ----------------------------
765 DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
767 : InternalFunctionImp(funcProto), id(i)
770 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
773 bool DateObjectFuncImp::implementsCall() const
779 Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
782 return Number(parseDate(args[0].toString(exec)));
786 memset(&t, 0, sizeof(t));
788 if (isNaN(args[0].toNumber(exec))
789 || isNaN(args[1].toNumber(exec))
790 || (n >= 3 && isNaN(args[2].toNumber(exec)))
791 || (n >= 4 && isNaN(args[3].toNumber(exec)))
792 || (n >= 5 && isNaN(args[4].toNumber(exec)))
793 || (n >= 6 && isNaN(args[5].toNumber(exec)))
794 || (n >= 7 && isNaN(args[6].toNumber(exec)))) {
797 int year = args[0].toInt32(exec);
798 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
799 t.tm_mon = args[1].toInt32(exec);
800 t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
801 t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
802 t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
803 t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
804 int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
805 time_t mktimeResult = timegm(&t);
806 if (mktimeResult == invalidDate)
808 return Number(mktimeResult * 1000.0 + ms);
812 // -----------------------------------------------------------------------------
815 double KJS::parseDate(const UString &u)
818 fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
820 int firstSlash = u.find('/');
821 if ( firstSlash == -1 )
823 time_t seconds = KRFCDate_parseDate( u );
825 fprintf(stderr,"KRFCDate_parseDate returned seconds=%d\n",seconds);
827 if ( seconds == invalidDate )
830 return seconds * 1000.0;
834 // Found 12/31/2099 on some website -> obviously MM/DD/YYYY
835 int month = u.substr(0,firstSlash).toULong();
836 int secondSlash = u.find('/',firstSlash+1);
837 //fprintf(stdout,"KJS::parseDate firstSlash=%d, secondSlash=%d\n", firstSlash, secondSlash);
838 if ( secondSlash == -1 )
840 fprintf(stderr,"KJS::parseDate parsing for this format isn't implemented\n%s", u.ascii());
843 int day = u.substr(firstSlash+1,secondSlash-firstSlash-1).toULong();
844 int year = u.substr(secondSlash+1).toULong();
845 //fprintf(stdout,"KJS::parseDate day=%d, month=%d, year=%d\n", day, month, year);
847 memset( &t, 0, sizeof(t) );
849 year = (year > 2037) ? 2037 : year; // mktime is limited to 2037 !!!
851 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
852 t.tm_mon = month-1; // mktime wants 0-11 for some reason
854 time_t seconds = mktime(&t);
855 if ( seconds == invalidDate )
858 fprintf(stderr,"KJS::parseDate mktime returned -1.\n%s", u.ascii());
863 return seconds * 1000.0;
867 ///// Awful duplication from krfcdate.cpp - we don't link to kdecore
869 static unsigned int ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
871 unsigned int ret = (day - 32075) /* days */
872 + 1461L * (year + 4800L + (mon - 14) / 12) / 4
873 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
874 - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
876 ret = 24*ret + hour; /* hours */
877 ret = 60*ret + minute; /* minutes */
878 ret = 60*ret + second; /* seconds */
883 static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
885 // we follow the recommendation of rfc2822 to consider all
886 // obsolete time zones not listed here equivalent to "-0000"
887 static const struct {
904 static inline bool isSpaceOrTab(char c)
906 return c == ' ' || c == '\t';
909 time_t KJS::KRFCDate_parseDate(const UString &_date)
911 // This parse a date in the form:
912 // Wednesday, 09-Nov-99 23:12:40 GMT
914 // Sat, 01-Jan-2000 08:00:00 GMT
916 // Sat, 01 Jan 2000 08:00:00 GMT
918 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
919 // ### non RFC format, added for Javascript:
920 // [Wednesday] January 09 1999 23:12:40 GMT
922 // We ignore the weekday
926 const char *dateString = _date.ascii();
929 int month = -1; // not set yet
937 // Skip leading space
938 while (isSpaceOrTab(*dateString))
941 const char *wordStart = dateString;
942 // Check contents of first words if not number
943 while(*dateString && !isdigit(*dateString))
945 if ( isSpaceOrTab(*dateString) && dateString - wordStart >= 3 )
947 monthStr[0] = tolower(*wordStart++);
948 monthStr[1] = tolower(*wordStart++);
949 monthStr[2] = tolower(*wordStart++);
951 //fprintf(stderr,"KJS::parseDate found word starting with '%s'\n", monthStr);
952 const char *str = strstr(haystack, monthStr);
954 int position = str - haystack;
955 if (position % 3 == 0) {
956 month = position / 3; // Jan=00, Feb=01, Mar=02, ..
959 while (isSpaceOrTab(*dateString))
961 wordStart = dateString;
967 while (isSpaceOrTab(*dateString))
973 // ' 09-Nov-99 23:12:40 GMT'
974 day = strtol(dateString, &newPosStr, 10);
977 dateString = newPosStr;
979 if ((day < 1) || (day > 31))
984 if (*dateString == '-' || *dateString == ',')
987 while (isSpaceOrTab(*dateString))
990 if ( month == -1 ) // not found yet
992 for(int i=0; i < 3;i++)
994 if (!*dateString || (*dateString == '-') || isSpaceOrTab(*dateString))
996 monthStr[i] = tolower(*dateString++);
1000 newPosStr = (char*)strstr(haystack, monthStr);
1002 if (!newPosStr || (newPosStr - haystack) % 3 != 0)
1005 month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
1007 if ((month < 0) || (month > 11))
1010 while (*dateString && *dateString != '-' && !isSpaceOrTab(*dateString))
1016 // '-99 23:12:40 GMT'
1017 if (*dateString != '-' && !isSpaceOrTab(*dateString))
1022 if ((month < 0) || (month > 11))
1025 // '99 23:12:40 GMT'
1026 bool gotYear = true;
1027 year = strtol(dateString, &newPosStr, 10);
1030 dateString = newPosStr;
1032 // Don't fail if the time is missing.
1033 if (*dateString == ':' || (isSpaceOrTab(*dateString) && isdigit(dateString[1])))
1035 if (*dateString == ':') {
1042 hour = strtol(dateString, &newPosStr, 10);
1045 dateString = newPosStr;
1048 if ((hour < 0) || (hour > 23))
1055 if (*dateString++ != ':')
1058 minute = strtol(dateString, &newPosStr, 10);
1061 dateString = newPosStr;
1063 if ((minute < 0) || (minute > 59))
1066 // seconds are optional in rfc822 + rfc2822
1067 if (*dateString ==':') {
1070 second = strtol(dateString, &newPosStr, 10);
1073 dateString = newPosStr;
1075 if ((second < 0) || (second > 59))
1080 while (isSpaceOrTab(*dateString))
1084 year = strtol(dateString, &newPosStr, 10);
1087 while (isSpaceOrTab(*dateString))
1091 // Y2K: Solve 2 digit years
1092 if ((year >= 0) && (year < 50))
1095 if ((year >= 50) && (year < 100))
1096 year += 1900; // Y2K
1098 if ((year < 1900) || (year > 2500))
1101 if (strncasecmp(dateString, "AM", 2) == 0) {
1102 if (hour < 1 || hour > 12)
1107 while (isSpaceOrTab(*dateString))
1109 } else if (strncasecmp(dateString, "PM", 2) == 0) {
1110 if (hour < 1 || hour > 12)
1115 while (isSpaceOrTab(*dateString))
1119 // don't fail if the time zone is missing, some
1120 // broken mail-/news-clients omit the time zone
1122 if (*dateString == 0) {
1123 // Other web browsers interpret missing time zone as "current time zone".
1127 if (strncasecmp(dateString, "GMT", 3) == 0) {
1130 if ((*dateString == '+') || (*dateString == '-')) {
1131 offset = strtol(dateString, &newPosStr, 10);
1133 if (errno || (offset < -9959) || (offset > 9959))
1136 int sgn = (offset < 0)? -1:1;
1137 offset = abs(offset);
1138 offset = ((offset / 100)*60 + (offset % 100))*sgn;
1140 for (int i=0; known_zones[i].tzName != 0; i++) {
1141 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
1142 offset = known_zones[i].tzOffset;
1148 if (sizeof(time_t) == 4)
1180 tm.tm_year = year - 1900;
1187 result = mktime(&tm);
1189 result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
1191 // avoid negative time values
1192 if ((offset > 0) && (offset > result))
1195 result -= offset*60;
1202 double KJS::timeClip(double t)
1206 double at = fabs(t);
1209 return copysign(floor(at), t);