• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kjs
 

kjs

  • kjs
date_object.cpp
1/*
2 * This file is part of the KDE libraries
3 * Copyright (C) 1999-2005 Harri Porten (porten@kde.org)
4 * Copyright (C) 2004 Apple Computer, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#if TIME_WITH_SYS_TIME
27# include <sys/time.h>
28# include <time.h>
29#else
30#if HAVE_SYS_TIME_H
31#include <sys/time.h>
32#else
33# include <time.h>
34# endif
35#endif
36#ifdef HAVE_SYS_TIMEB_H
37#include <sys/timeb.h>
38#endif
39
40#include <errno.h>
41
42#ifdef HAVE_SYS_PARAM_H
43# include <sys/param.h>
44#endif // HAVE_SYS_PARAM_H
45
46#include <math.h>
47#include <string.h>
48#ifdef HAVE_STRINGS_H
49# include <strings.h>
50#endif
51#include <stdio.h>
52#include <stdlib.h>
53#include <locale.h>
54#include <ctype.h>
55#include <assert.h>
56#include <limits.h>
57
58#include "date_object.h"
59#include "error_object.h"
60#include "operations.h"
61
62#include "date_object.lut.h"
63
64#ifdef _MSC_VER
65# define strncasecmp(a,b,c) _strnicmp(a,b,c)
66#endif
67
68using namespace KJS;
69
70// come constants
71const time_t invalidDate = LONG_MIN;
72const double hoursPerDay = 24;
73const double minutesPerHour = 60;
74const double secondsPerMinute = 60;
75const double msPerSecond = 1000;
76const double msPerMinute = msPerSecond * secondsPerMinute;
77const double msPerHour = msPerMinute * minutesPerHour;
78const double msPerDay = msPerHour * hoursPerDay;
79static const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
80static const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
81
82static UString formatDate(struct tm &tm)
83{
84 char buffer[100];
85 snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
86 weekdayName[(tm.tm_wday + 6) % 7],
87 monthName[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
88 return buffer;
89}
90
91static UString formatDateUTCVariant(struct tm &tm)
92{
93 char buffer[100];
94 snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
95 weekdayName[(tm.tm_wday + 6) % 7],
96 tm.tm_mday, monthName[tm.tm_mon], tm.tm_year + 1900);
97 return buffer;
98}
99
100static UString formatTime(struct tm &tm)
101{
102 int tz;
103 char buffer[100];
104#if defined BSD || defined(__linux__) || defined(__APPLE__)
105 tz = tm.tm_gmtoff;
106#else
107# if defined(__BORLANDC__) || defined (__CYGWIN__)
108 tz = - _timezone;
109# else
110 tz = - timezone;
111# endif
112#endif
113 if (tz == 0) {
114 snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", tm.tm_hour, tm.tm_min, tm.tm_sec);
115 } else {
116 int offset = tz;
117 if (offset < 0) {
118 offset = -offset;
119 }
120 snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
121 tm.tm_hour, tm.tm_min, tm.tm_sec,
122 tz < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
123 }
124 return UString(buffer);
125}
126
127static int day(double t)
128{
129 return int(floor(t / msPerDay));
130}
131
132static double dayFromYear(int year)
133{
134 return 365.0 * (year - 1970)
135 + floor((year - 1969) / 4.0)
136 - floor((year - 1901) / 100.0)
137 + floor((year - 1601) / 400.0);
138}
139
140// depending on whether it's a leap year or not
141static int daysInYear(int year)
142{
143 if (year % 4 != 0)
144 return 365;
145 else if (year % 400 == 0)
146 return 366;
147 else if (year % 100 == 0)
148 return 365;
149 else
150 return 366;
151}
152
153// time value of the start of a year
154double timeFromYear(int year)
155{
156 return msPerDay * dayFromYear(year);
157}
158
159// year determined by time value
160int yearFromTime(double t)
161{
162 // ### there must be an easier way
163 // initial guess
164 int y = 1970 + int(t / (365.25 * msPerDay));
165 // adjustment
166 if (timeFromYear(y) > t) {
167 do {
168 --y;
169 } while (timeFromYear(y) > t);
170 } else {
171 while (timeFromYear(y + 1) < t)
172 ++y;
173 }
174
175 return y;
176}
177
178// 0: Sunday, 1: Monday, etc.
179int weekDay(double t)
180{
181 int wd = (day(t) + 4) % 7;
182 if (wd < 0)
183 wd += 7;
184 return wd;
185}
186
187static double timeZoneOffset(const struct tm *t)
188{
189#if defined BSD || defined(__linux__) || defined(__APPLE__)
190 return -(t->tm_gmtoff / 60);
191#else
192# if defined(__BORLANDC__) || defined(__CYGWIN__)
193// FIXME consider non one-hour DST change
194#if !defined(__CYGWIN__)
195#error please add daylight savings offset here!
196#endif
197 return _timezone / 60 - (t->tm_isdst > 0 ? 60 : 0);
198# else
199 return timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 );
200# endif
201#endif
202}
203
204// Converts a list of arguments sent to a Date member function into milliseconds, updating
205// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
206//
207// Format of member function: f([hour,] [min,] [sec,] [ms])
208static void fillStructuresUsingTimeArgs(ExecState *exec, const List &args, int maxArgs, double *ms, struct tm *t)
209{
210 double milliseconds = 0;
211 int idx = 0;
212 int numArgs = args.size();
213
214 // JS allows extra trailing arguments -- ignore them
215 if (numArgs > maxArgs)
216 numArgs = maxArgs;
217
218 // hours
219 if (maxArgs >= 4 && idx < numArgs) {
220 t->tm_hour = 0;
221 milliseconds += args[idx++].toInt32(exec) * msPerHour;
222 }
223
224 // minutes
225 if (maxArgs >= 3 && idx < numArgs) {
226 t->tm_min = 0;
227 milliseconds += args[idx++].toInt32(exec) * msPerMinute;
228 }
229
230 // seconds
231 if (maxArgs >= 2 && idx < numArgs) {
232 t->tm_sec = 0;
233 milliseconds += args[idx++].toInt32(exec) * msPerSecond;
234 }
235
236 // milliseconds
237 if (idx < numArgs) {
238 milliseconds += roundValue(exec, args[idx]);
239 } else {
240 milliseconds += *ms;
241 }
242
243 *ms = milliseconds;
244}
245
246// Converts a list of arguments sent to a Date member function into years, months, and milliseconds, updating
247// ms (representing milliseconds) and t (representing the rest of the date structure) appropriately.
248//
249// Format of member function: f([years,] [months,] [days])
250static void fillStructuresUsingDateArgs(ExecState *exec, const List &args, int maxArgs, double *ms, struct tm *t)
251{
252 int idx = 0;
253 int numArgs = args.size();
254
255 // JS allows extra trailing arguments -- ignore them
256 if (numArgs > maxArgs)
257 numArgs = maxArgs;
258
259 // years
260 if (maxArgs >= 3 && idx < numArgs) {
261 t->tm_year = args[idx++].toInt32(exec) - 1900;
262 }
263
264 // months
265 if (maxArgs >= 2 && idx < numArgs) {
266 t->tm_mon = args[idx++].toInt32(exec);
267 }
268
269 // days
270 if (idx < numArgs) {
271 t->tm_mday = 0;
272 *ms += args[idx].toInt32(exec) * msPerDay;
273 }
274}
275
276// ------------------------------ DateInstanceImp ------------------------------
277
278const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
279
280DateInstanceImp::DateInstanceImp(ObjectImp *proto)
281 : ObjectImp(proto)
282{
283}
284
285// ------------------------------ DatePrototypeImp -----------------------------
286
287const ClassInfo DatePrototypeImp::info = {"Date", &DateInstanceImp::info, &dateTable, 0};
288
289/* Source for date_object.lut.h
290 We use a negative ID to denote the "UTC" variant.
291@begin dateTable 61
292 toString DateProtoFuncImp::ToString DontEnum|Function 0
293 toUTCString DateProtoFuncImp::ToUTCString DontEnum|Function 0
294 toDateString DateProtoFuncImp::ToDateString DontEnum|Function 0
295 toTimeString DateProtoFuncImp::ToTimeString DontEnum|Function 0
296 toLocaleString DateProtoFuncImp::ToLocaleString DontEnum|Function 0
297 toLocaleDateString DateProtoFuncImp::ToLocaleDateString DontEnum|Function 0
298 toLocaleTimeString DateProtoFuncImp::ToLocaleTimeString DontEnum|Function 0
299 valueOf DateProtoFuncImp::ValueOf DontEnum|Function 0
300 getTime DateProtoFuncImp::GetTime DontEnum|Function 0
301 getFullYear DateProtoFuncImp::GetFullYear DontEnum|Function 0
302 getUTCFullYear -DateProtoFuncImp::GetFullYear DontEnum|Function 0
303 toGMTString DateProtoFuncImp::ToGMTString DontEnum|Function 0
304 getMonth DateProtoFuncImp::GetMonth DontEnum|Function 0
305 getUTCMonth -DateProtoFuncImp::GetMonth DontEnum|Function 0
306 getDate DateProtoFuncImp::GetDate DontEnum|Function 0
307 getUTCDate -DateProtoFuncImp::GetDate DontEnum|Function 0
308 getDay DateProtoFuncImp::GetDay DontEnum|Function 0
309 getUTCDay -DateProtoFuncImp::GetDay DontEnum|Function 0
310 getHours DateProtoFuncImp::GetHours DontEnum|Function 0
311 getUTCHours -DateProtoFuncImp::GetHours DontEnum|Function 0
312 getMinutes DateProtoFuncImp::GetMinutes DontEnum|Function 0
313 getUTCMinutes -DateProtoFuncImp::GetMinutes DontEnum|Function 0
314 getSeconds DateProtoFuncImp::GetSeconds DontEnum|Function 0
315 getUTCSeconds -DateProtoFuncImp::GetSeconds DontEnum|Function 0
316 getMilliseconds DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
317 getUTCMilliseconds -DateProtoFuncImp::GetMilliSeconds DontEnum|Function 0
318 getTimezoneOffset DateProtoFuncImp::GetTimezoneOffset DontEnum|Function 0
319 setTime DateProtoFuncImp::SetTime DontEnum|Function 1
320 setMilliseconds DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
321 setUTCMilliseconds -DateProtoFuncImp::SetMilliSeconds DontEnum|Function 1
322 setSeconds DateProtoFuncImp::SetSeconds DontEnum|Function 2
323 setUTCSeconds -DateProtoFuncImp::SetSeconds DontEnum|Function 2
324 setMinutes DateProtoFuncImp::SetMinutes DontEnum|Function 3
325 setUTCMinutes -DateProtoFuncImp::SetMinutes DontEnum|Function 3
326 setHours DateProtoFuncImp::SetHours DontEnum|Function 4
327 setUTCHours -DateProtoFuncImp::SetHours DontEnum|Function 4
328 setDate DateProtoFuncImp::SetDate DontEnum|Function 1
329 setUTCDate -DateProtoFuncImp::SetDate DontEnum|Function 1
330 setMonth DateProtoFuncImp::SetMonth DontEnum|Function 2
331 setUTCMonth -DateProtoFuncImp::SetMonth DontEnum|Function 2
332 setFullYear DateProtoFuncImp::SetFullYear DontEnum|Function 3
333 setUTCFullYear -DateProtoFuncImp::SetFullYear DontEnum|Function 3
334 setYear DateProtoFuncImp::SetYear DontEnum|Function 1
335 getYear DateProtoFuncImp::GetYear DontEnum|Function 0
336 toGMTString DateProtoFuncImp::ToGMTString DontEnum|Function 0
337@end
338*/
339// ECMA 15.9.4
340
341DatePrototypeImp::DatePrototypeImp(ExecState *,
342 ObjectPrototypeImp *objectProto)
343 : DateInstanceImp(objectProto)
344{
345 Value protect(this);
346 setInternalValue(Number(NaN));
347 // The constructor will be added later, after DateObjectImp has been built
348}
349
350Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
351{
352 return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
353}
354
355// ------------------------------ DateProtoFuncImp -----------------------------
356
357DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
358 : InternalFunctionImp(
359 static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
360 ), id(abs(i)), utc(i<0)
361 // We use a negative ID to denote the "UTC" variant.
362{
363 Value protect(this);
364 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
365}
366
367bool DateProtoFuncImp::implementsCall() const
368{
369 return true;
370}
371
372Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
373{
374 if (!thisObj.inherits(&DateInstanceImp::info)) {
375 // non-generic function called on non-date object
376
377 // ToString and ValueOf are generic according to the spec, but the mozilla
378 // tests suggest otherwise...
379 Object err = Error::create(exec,TypeError);
380 exec->setException(err);
381 return err;
382 }
383
384
385 Value result;
386 UString s;
387 const int bufsize=100;
388 char timebuffer[bufsize];
389 CString oldlocale = setlocale(LC_TIME,NULL);
390 if (!oldlocale.c_str())
391 oldlocale = setlocale(LC_ALL, NULL);
392 Value v = thisObj.internalValue();
393 double milli = v.toNumber(exec);
394 // special case: time value is NaN
395 if (isNaN(milli)) {
396 switch (id) {
397 case ToString:
398 case ToDateString:
399 case ToTimeString:
400 case ToGMTString:
401 case ToUTCString:
402 case ToLocaleString:
403 case ToLocaleDateString:
404 case ToLocaleTimeString:
405 return String("Invalid Date");
406 case ValueOf:
407 case GetTime:
408 case GetYear:
409 case GetFullYear:
410 case GetMonth:
411 case GetDate:
412 case GetDay:
413 case GetHours:
414 case GetMinutes:
415 case GetSeconds:
416 case GetMilliSeconds:
417 case GetTimezoneOffset:
418 case SetMilliSeconds:
419 case SetSeconds:
420 case SetMinutes:
421 case SetHours:
422 case SetDate:
423 case SetMonth:
424 case SetFullYear:
425 return Number(NaN);
426 }
427 }
428
429 if (id == SetTime) {
430 result = Number(roundValue(exec,args[0]));
431 thisObj.setInternalValue(result);
432 return result;
433 }
434
435 // check whether time value is outside time_t's usual range
436 // make the necessary transformations if necessary
437 int realYearOffset = 0;
438 double milliOffset = 0.0;
439 if (milli < 0 || milli >= timeFromYear(2038)) {
440 // ### ugly and probably not very precise
441 int realYear = yearFromTime(milli);
442 int base = daysInYear(realYear) == 365 ? 2001 : 2000;
443 milliOffset = timeFromYear(base) - timeFromYear(realYear);
444 milli += milliOffset;
445 realYearOffset = realYear - base;
446 }
447
448 time_t tv = (time_t) floor(milli / 1000.0);
449 double ms = milli - tv * 1000.0;
450
451 struct tm *t;
452 if ( (id == DateProtoFuncImp::ToGMTString) ||
453 (id == DateProtoFuncImp::ToUTCString) )
454 t = gmtime(&tv);
455 else if (id == DateProtoFuncImp::ToString)
456 t = localtime(&tv);
457 else if (utc)
458 t = gmtime(&tv);
459 else
460 t = localtime(&tv);
461
462 // we had an out of range year. use that one (plus/minus offset
463 // found by calculating tm_year) and fix the week day calculation
464 if (realYearOffset != 0) {
465 t->tm_year += realYearOffset;
466 milli -= milliOffset;
467 // our own weekday calculation. beware of need for local time.
468 double m = milli;
469 if (!utc)
470 m -= timeZoneOffset(t) * msPerMinute;
471 t->tm_wday = weekDay(m);
472 }
473
474 // trick gcc. We don't want the Y2K warnings.
475 const char xFormat[] = "%x";
476 const char cFormat[] = "%c";
477
478 switch (id) {
479 case ToString:
480 result = String(formatDate(*t) + " " + formatTime(*t));
481 break;
482 case ToDateString:
483 result = String(formatDate(*t));
484 break;
485 case ToTimeString:
486 result = String(formatTime(*t));
487 break;
488 case ToGMTString:
489 case ToUTCString:
490 result = String(formatDateUTCVariant(*t) + " " + formatTime(*t));
491 break;
492 case ToLocaleString:
493 strftime(timebuffer, bufsize, cFormat, t);
494 result = String(timebuffer);
495 break;
496 case ToLocaleDateString:
497 strftime(timebuffer, bufsize, xFormat, t);
498 result = String(timebuffer);
499 break;
500 case ToLocaleTimeString:
501 strftime(timebuffer, bufsize, "%X", t);
502 result = String(timebuffer);
503 break;
504 case ValueOf:
505 result = Number(milli);
506 break;
507 case GetTime:
508 result = Number(milli);
509 break;
510 case GetYear:
511 // IE returns the full year even in getYear.
512 if ( exec->dynamicInterpreter()->compatMode() != Interpreter::IECompat )
513 result = Number(t->tm_year);
514 else
515 result = Number(1900 + t->tm_year);
516 break;
517 case GetFullYear:
518 result = Number(1900 + t->tm_year);
519 break;
520 case GetMonth:
521 result = Number(t->tm_mon);
522 break;
523 case GetDate:
524 result = Number(t->tm_mday);
525 break;
526 case GetDay:
527 result = Number(t->tm_wday);
528 break;
529 case GetHours:
530 result = Number(t->tm_hour);
531 break;
532 case GetMinutes:
533 result = Number(t->tm_min);
534 break;
535 case GetSeconds:
536 result = Number(t->tm_sec);
537 break;
538 case GetMilliSeconds:
539 result = Number(ms);
540 break;
541 case GetTimezoneOffset:
542 result = Number(timeZoneOffset(t));
543 break;
544 case SetMilliSeconds:
545 fillStructuresUsingTimeArgs(exec, args, 1, &ms, t);
546 break;
547 case SetSeconds:
548 fillStructuresUsingTimeArgs(exec, args, 2, &ms, t);
549 break;
550 case SetMinutes:
551 fillStructuresUsingTimeArgs(exec, args, 3, &ms, t);
552 break;
553 case SetHours:
554 fillStructuresUsingTimeArgs(exec, args, 4, &ms, t);
555 break;
556 case SetDate:
557 fillStructuresUsingDateArgs(exec, args, 1, &ms, t);
558 break;
559 case SetMonth:
560 fillStructuresUsingDateArgs(exec, args, 2, &ms, t);
561 break;
562 case SetFullYear:
563 fillStructuresUsingDateArgs(exec, args, 3, &ms, t);
564 break;
565 case SetYear:
566 int y = args[0].toInt32(exec);
567 if (y < 1900) {
568 if (y >= 0 && y <= 99) {
569 t->tm_year = y;
570 } else {
571 fillStructuresUsingDateArgs(exec, args, 3, &ms, t);
572 }
573 } else {
574 t->tm_year = y - 1900;
575 }
576 break;
577 }
578
579 if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
580 id == SetMinutes || id == SetHours || id == SetDate ||
581 id == SetMonth || id == SetFullYear ) {
582 result = Number(makeTime(t, ms, utc));
583 thisObj.setInternalValue(result);
584 }
585
586 return result;
587}
588
589// ------------------------------ DateObjectImp --------------------------------
590
591// TODO: MakeTime (15.9.11.1) etc. ?
592
593DateObjectImp::DateObjectImp(ExecState *exec,
594 FunctionPrototypeImp *funcProto,
595 DatePrototypeImp *dateProto)
596 : InternalFunctionImp(funcProto)
597{
598 Value protect(this);
599
600 // ECMA 15.9.4.1 Date.prototype
601 putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
602
603 static const Identifier parsePropertyName("parse");
604 putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
605 static const Identifier UTCPropertyName("UTC");
606 putDirect(UTCPropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC, 7), DontEnum);
607
608 // no. of arguments for constructor
609 putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
610}
611
612bool DateObjectImp::implementsConstruct() const
613{
614 return true;
615}
616
617// ECMA 15.9.3
618Object DateObjectImp::construct(ExecState *exec, const List &args)
619{
620 int numArgs = args.size();
621
622#ifdef KJS_VERBOSE
623 fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
624#endif
625 double value;
626
627 if (numArgs == 0) { // new Date() ECMA 15.9.3.3
628#ifdef HAVE_SYS_TIMEB_H
629# if defined(__BORLANDC__)
630 struct timeb timebuffer;
631 ftime(&timebuffer);
632# else
633 struct _timeb timebuffer;
634 _ftime(&timebuffer);
635# endif
636 double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
637#else
638 struct timeval tv;
639 gettimeofday(&tv, 0L);
640 double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
641#endif
642 value = utc;
643 } else if (numArgs == 1) {
644 Value prim = args[0].toPrimitive(exec);
645 if (prim.isA(StringType))
646 value = parseDate(prim.toString(exec));
647 else
648 value = prim.toNumber(exec);
649 } else {
650 if (isNaN(args[0].toNumber(exec))
651 || isNaN(args[1].toNumber(exec))
652 || (numArgs >= 3 && isNaN(args[2].toNumber(exec)))
653 || (numArgs >= 4 && isNaN(args[3].toNumber(exec)))
654 || (numArgs >= 5 && isNaN(args[4].toNumber(exec)))
655 || (numArgs >= 6 && isNaN(args[5].toNumber(exec)))
656 || (numArgs >= 7 && isNaN(args[6].toNumber(exec)))) {
657 value = NaN;
658 } else {
659 struct tm t;
660 memset(&t, 0, sizeof(t));
661 int year = args[0].toInt32(exec);
662 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
663 t.tm_mon = args[1].toInt32(exec);
664 t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
665 t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
666 t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
667 t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
668 t.tm_isdst = -1;
669 int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
670 value = makeTime(&t, ms, false);
671 }
672 }
673
674 Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
675 Object ret(new DateInstanceImp(proto.imp()));
676 ret.setInternalValue(Number(timeClip(value)));
677 return ret;
678}
679
680bool DateObjectImp::implementsCall() const
681{
682 return true;
683}
684
685// ECMA 15.9.2
686Value DateObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
687{
688#ifdef KJS_VERBOSE
689 fprintf(stderr,"DateObjectImp::call - current time\n");
690#endif
691 time_t t = time(0L);
692 // FIXME: not threadsafe
693 struct tm *tm = localtime(&t);
694 return String(formatDate(*tm) + " " + formatTime(*tm));
695}
696
697// ------------------------------ DateObjectFuncImp ----------------------------
698
699DateObjectFuncImp::DateObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
700 int i, int len)
701 : InternalFunctionImp(funcProto), id(i)
702{
703 Value protect(this);
704 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
705}
706
707bool DateObjectFuncImp::implementsCall() const
708{
709 return true;
710}
711
712// ECMA 15.9.4.2 - 3
713Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
714{
715 if (id == Parse) {
716 return Number(parseDate(args[0].toString(exec)));
717 } else { // UTC
718 int n = args.size();
719 if (isNaN(args[0].toNumber(exec))
720 || isNaN(args[1].toNumber(exec))
721 || (n >= 3 && isNaN(args[2].toNumber(exec)))
722 || (n >= 4 && isNaN(args[3].toNumber(exec)))
723 || (n >= 5 && isNaN(args[4].toNumber(exec)))
724 || (n >= 6 && isNaN(args[5].toNumber(exec)))
725 || (n >= 7 && isNaN(args[6].toNumber(exec)))) {
726 return Number(NaN);
727 }
728
729 struct tm t;
730 memset(&t, 0, sizeof(t));
731 int year = args[0].toInt32(exec);
732 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
733 t.tm_mon = args[1].toInt32(exec);
734 t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
735 t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
736 t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
737 t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
738 int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
739 return Number(makeTime(&t, ms, true));
740 }
741}
742
743// -----------------------------------------------------------------------------
744
745
746double KJS::parseDate(const UString &u)
747{
748#ifdef KJS_VERBOSE
749 fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
750#endif
751 double /*time_t*/ seconds = KRFCDate_parseDate( u );
752
753 return seconds == invalidDate ? NaN : seconds * 1000.0;
754}
755
757
758static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
759{
760 //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
761
762 double ret = (day - 32075) /* days */
763 + 1461L * (year + 4800L + (mon - 14) / 12) / 4
764 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
765 - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
766 - 2440588;
767 ret = 24*ret + hour; /* hours */
768 ret = 60*ret + minute; /* minutes */
769 ret = 60*ret + second; /* seconds */
770
771 return ret;
772}
773
774// we follow the recommendation of rfc2822 to consider all
775// obsolete time zones not listed here equivalent to "-0000"
776static const struct KnownZone {
777#ifdef _WIN32
778 char tzName[4];
779#else
780 const char tzName[4];
781#endif
782 int tzOffset;
783} known_zones[] = {
784 { "UT", 0 },
785 { "GMT", 0 },
786 { "EST", -300 },
787 { "EDT", -240 },
788 { "CST", -360 },
789 { "CDT", -300 },
790 { "MST", -420 },
791 { "MDT", -360 },
792 { "PST", -480 },
793 { "PDT", -420 }
794};
795
796double KJS::makeTime(struct tm *t, double ms, bool utc)
797{
798 int utcOffset;
799 if (utc) {
800 time_t zero = 0;
801#if defined BSD || defined(__linux__) || defined(__APPLE__)
802 struct tm t3;
803 localtime_r(&zero, &t3);
804 utcOffset = t3.tm_gmtoff;
805 t->tm_isdst = t3.tm_isdst;
806#else
807 (void)localtime(&zero);
808# if defined(__BORLANDC__) || defined(__CYGWIN__)
809 utcOffset = - _timezone;
810# else
811 utcOffset = - timezone;
812# endif
813 t->tm_isdst = 0;
814#endif
815 } else {
816 utcOffset = 0;
817 t->tm_isdst = -1;
818 }
819
820 double yearOffset = 0.0;
821 if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
822 // we'll fool mktime() into believing that this year is within
823 // its normal, portable range (1970-2038) by setting tm_year to
824 // 2000 or 2001 and adding the difference in milliseconds later.
825 // choice between offset will depend on whether the year is a
826 // leap year or not.
827 int y = t->tm_year + 1900;
828 int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
829 const double baseTime = timeFromYear(baseYear);
830 yearOffset = timeFromYear(y) - baseTime;
831 t->tm_year = baseYear - 1900;
832 }
833
834 // Determine if we passed over a DST change boundary
835 if (!utc) {
836 time_t tval = mktime(t) + utcOffset + int((ms + yearOffset)/1000);
837 struct tm t3;
838 localtime_r(&tval, &t3);
839 t->tm_isdst = t3.tm_isdst;
840 }
841
842 return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
843}
844
845// returns 0-11 (Jan-Dec); -1 on failure
846static int findMonth(const char *monthStr)
847{
848 assert(monthStr);
849 static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
850 char needle[4];
851 for (int i = 0; i < 3; ++i) {
852 if (!*monthStr)
853 return -1;
854 needle[i] = tolower(*monthStr++);
855 }
856 needle[3] = '\0';
857 const char *str = strstr(haystack, needle);
858 if (str) {
859 int position = str - haystack;
860 if (position % 3 == 0) {
861 return position / 3;
862 }
863 }
864 return -1;
865}
866
867// maybe this should be more often than just isspace() but beware of
868// conflicts with : in time strings.
869static bool isSpaceLike(char c)
870{
871 return isspace(c) || c == ',' || c == ':' || c == '-';
872}
873
874double KJS::KRFCDate_parseDate(const UString &_date)
875{
876 // This parse a date in the form:
877 // Wednesday, 09-Nov-99 23:12:40 GMT
878 // or
879 // Sat, 01-Jan-2000 08:00:00 GMT
880 // or
881 // Sat, 01 Jan 2000 08:00:00 GMT
882 // or
883 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
884 // ### non RFC formats, added for Javascript:
885 // [Wednesday] January 09 1999 23:12:40 GMT
886 // [Wednesday] January 09 23:12:40 GMT 1999
887 //
888 // We ignore the weekday
889 //
890 double result = -1;
891 int offset = 0;
892 bool have_tz = false;
893 char *newPosStr;
894 const char *dateString = _date.ascii();
895 int day = 0;
896 int month = -1; // not set yet
897 int year = 0;
898 int hour = 0;
899 int minute = 0;
900 int second = 0;
901 bool have_time = false;
902
903 // Skip leading space
904 while(*dateString && isSpaceLike(*dateString))
905 dateString++;
906
907 const char *wordStart = dateString;
908 // Check contents of first words if not number
909 while(*dateString && !isdigit(*dateString))
910 {
911 if (isSpaceLike(*dateString) && dateString - wordStart >= 3)
912 {
913 month = findMonth(wordStart);
914 while(*dateString && isSpaceLike(*dateString))
915 dateString++;
916 wordStart = dateString;
917 }
918 else
919 dateString++;
920 }
921 // missing delimiter between month and day (like "January29")?
922 if (month == -1 && dateString && wordStart != dateString) {
923 month = findMonth(wordStart);
924 // TODO: emit warning about dubious format found
925 }
926
927 while(*dateString && isSpaceLike(*dateString))
928 dateString++;
929
930 if (!*dateString)
931 return invalidDate;
932
933 // ' 09-Nov-99 23:12:40 GMT'
934 errno = 0;
935 day = strtol(dateString, &newPosStr, 10);
936 if (errno)
937 return invalidDate;
938 dateString = newPosStr;
939
940 if (!*dateString)
941 return invalidDate;
942
943 if (day < 0)
944 return invalidDate;
945 if (day > 31) {
946 // ### where is the boundary and what happens below?
947 if (*dateString == '/') {
948 // looks like a YYYY/MM/DD date
949 if (!*++dateString)
950 return invalidDate;
951 year = day;
952 month = strtol(dateString, &newPosStr, 10) - 1;
953 if (errno)
954 return invalidDate;
955 dateString = newPosStr;
956 if (*dateString++ != '/' || !*dateString)
957 return invalidDate;
958 day = strtol(dateString, &newPosStr, 10);
959 if (errno)
960 return invalidDate;
961 dateString = newPosStr;
962 } else {
963 return invalidDate;
964 }
965 } else if (*dateString == '/' && month == -1)
966 {
967 dateString++;
968 // This looks like a MM/DD/YYYY date, not an RFC date.....
969 month = day - 1; // 0-based
970 day = strtol(dateString, &newPosStr, 10);
971 if (errno)
972 return invalidDate;
973 dateString = newPosStr;
974 if (*dateString == '/')
975 dateString++;
976 if (!*dateString)
977 return invalidDate;
978 //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
979 }
980 else
981 {
982 if (*dateString == '-')
983 dateString++;
984
985 while(*dateString && isSpaceLike(*dateString))
986 dateString++;
987
988 if (*dateString == ',')
989 dateString++;
990
991 if ( month == -1 ) // not found yet
992 {
993 month = findMonth(dateString);
994 if (month == -1)
995 return invalidDate;
996
997 while(*dateString && (*dateString != '-') && !isSpaceLike(*dateString))
998 dateString++;
999
1000 if (!*dateString)
1001 return invalidDate;
1002
1003 // '-99 23:12:40 GMT'
1004 if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
1005 return invalidDate;
1006 dateString++;
1007 }
1008
1009 if ((month < 0) || (month > 11))
1010 return invalidDate;
1011 }
1012
1013 // '99 23:12:40 GMT'
1014 if (year <= 0 && *dateString) {
1015 year = strtol(dateString, &newPosStr, 10);
1016 if (errno)
1017 return invalidDate;
1018 }
1019
1020 // Don't fail if the time is missing.
1021 if (*newPosStr)
1022 {
1023 // ' 23:12:40 GMT'
1024 if (*newPosStr == ':') // Ah, so there was no year, but the number was the hour
1025 year = -1;
1026 else if (isSpaceLike(*newPosStr)) // we parsed the year
1027 dateString = ++newPosStr;
1028 else
1029 return invalidDate;
1030
1031 hour = strtol(dateString, &newPosStr, 10);
1032
1033 // Do not check for errno here since we want to continue
1034 // even if errno was set becasue we are still looking
1035 // for the timezone!
1036 // read a number? if not this might be a timezone name
1037 if (newPosStr != dateString) {
1038 have_time = true;
1039 dateString = newPosStr;
1040
1041 if ((hour < 0) || (hour > 23))
1042 return invalidDate;
1043
1044 if (!*dateString)
1045 return invalidDate;
1046
1047 // ':12:40 GMT'
1048 if (*dateString++ != ':')
1049 return invalidDate;
1050
1051 minute = strtol(dateString, &newPosStr, 10);
1052 if (errno)
1053 return invalidDate;
1054 dateString = newPosStr;
1055
1056 if ((minute < 0) || (minute > 59))
1057 return invalidDate;
1058
1059 // ':40 GMT'
1060 if (*dateString && *dateString != ':' && !isspace(*dateString))
1061 return invalidDate;
1062
1063 // seconds are optional in rfc822 + rfc2822
1064 if (*dateString ==':') {
1065 dateString++;
1066
1067 second = strtol(dateString, &newPosStr, 10);
1068 if (errno)
1069 return invalidDate;
1070 dateString = newPosStr;
1071
1072 if ((second < 0) || (second > 59))
1073 return invalidDate;
1074
1075 // disallow trailing colon seconds
1076 if (*dateString == ':')
1077 return invalidDate;
1078 }
1079
1080 while(*dateString && isspace(*dateString))
1081 dateString++;
1082
1083 if (strncasecmp(dateString, "AM", 2) == 0) {
1084 if (hour > 12)
1085 return invalidDate;
1086 if (hour == 12)
1087 hour = 0;
1088 dateString += 2;
1089 while (isspace(*dateString))
1090 dateString++;
1091 } else if (strncasecmp(dateString, "PM", 2) == 0) {
1092 if (hour > 12)
1093 return invalidDate;
1094 if (hour != 12)
1095 hour += 12;
1096 dateString += 2;
1097 while (isspace(*dateString))
1098 dateString++;
1099 }
1100 }
1101 } else {
1102 dateString = newPosStr;
1103 }
1104
1105 // don't fail if the time zone is missing, some
1106 // broken mail-/news-clients omit the time zone
1107 if (*dateString) {
1108
1109 if (strncasecmp(dateString, "GMT", 3) == 0 ||
1110 strncasecmp(dateString, "UTC", 3) == 0)
1111 {
1112 dateString += 3;
1113 have_tz = true;
1114 }
1115
1116 while (*dateString && isspace(*dateString))
1117 ++dateString;
1118
1119 if (strncasecmp(dateString, "GMT", 3) == 0) {
1120 dateString += 3;
1121 }
1122 if ((*dateString == '+') || (*dateString == '-')) {
1123 offset = strtol(dateString, &newPosStr, 10);
1124 if (errno)
1125 return invalidDate;
1126 dateString = newPosStr;
1127
1128 if ((offset < -9959) || (offset > 9959))
1129 return invalidDate;
1130
1131 int sgn = (offset < 0)? -1:1;
1132 offset = abs(offset);
1133 if ( *dateString == ':' ) { // GMT+05:00
1134 int offset2 = strtol(dateString, &newPosStr, 10);
1135 if (errno)
1136 return invalidDate;
1137 dateString = newPosStr;
1138 offset = (offset*60 + offset2)*sgn;
1139 }
1140 else
1141 offset = ((offset / 100)*60 + (offset % 100))*sgn;
1142 have_tz = true;
1143 } else {
1144 for (int i=0; i < int(sizeof(known_zones)/sizeof(KnownZone)); i++) {
1145 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
1146 offset = known_zones[i].tzOffset;
1147 dateString += strlen(known_zones[i].tzName);
1148 have_tz = true;
1149 break;
1150 }
1151 }
1152 }
1153 }
1154
1155 while(*dateString && isspace(*dateString))
1156 dateString++;
1157
1158 if ( *dateString && year == -1 ) {
1159 year = strtol(dateString, &newPosStr, 10);
1160 if (errno)
1161 return invalidDate;
1162 dateString = newPosStr;
1163 }
1164
1165 while (isspace(*dateString))
1166 dateString++;
1167
1168#if 0
1169 // Trailing garbage
1170 if (*dateString != '\0')
1171 return invalidDate;
1172#endif
1173
1174 // Y2K: Solve 2 digit years
1175 if ((year >= 0) && (year < 50))
1176 year += 2000;
1177
1178 if ((year >= 50) && (year < 100))
1179 year += 1900; // Y2K
1180
1181 if (!have_tz) {
1182 // fall back to midnight, local timezone
1183 struct tm t;
1184 memset(&t, 0, sizeof(tm));
1185 t.tm_mday = day;
1186 t.tm_mon = month;
1187 t.tm_year = year - 1900;
1188 t.tm_isdst = -1;
1189 if (have_time) {
1190 t.tm_sec = second;
1191 t.tm_min = minute;
1192 t.tm_hour = hour;
1193 }
1194
1195 // better not use mktime() as it can't handle the full year range
1196 return makeTime(&t, 0, false) / 1000.0;
1197 }
1198
1199 result = ymdhms_to_seconds(year, month+1, day, hour, minute, second) - offset*60;
1200 return result;
1201}
1202
1203
1204double KJS::timeClip(double t)
1205{
1206 if (isInf(t))
1207 return NaN;
1208 double at = fabs(t);
1209 if (at > 8.64E15)
1210 return NaN;
1211 return floor(at) * (t != at ? -1 : 1);
1212}
1213
KJS::CString
8 bit char based string class
Definition ustring.h:165
KJS::Error::create
static Object create(ExecState *exec, ErrorType errtype=GeneralError, const char *message=0, int lineno=-1, int sourceId=-1)
Factory method for error objects.
Definition object.cpp:503
KJS::ExecState
Represents the current state of script execution.
Definition interpreter.h:438
KJS::ExecState::dynamicInterpreter
Interpreter * dynamicInterpreter() const
Returns the interpreter associated with this execution state.
Definition interpreter.h:452
KJS::ExecState::lexicalInterpreter
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
Definition interpreter.cpp:394
KJS::FunctionPrototypeImp
The initial value of Function.prototype (and thus all objects created with the Function constructor)
Definition function_object.h:34
KJS::Identifier
Represents an Identifier for a Javascript object.
Definition identifier.h:32
KJS::InternalFunctionImp
Base class for all function objects.
Definition function.h:40
KJS::Interpreter::builtinDatePrototype
Object builtinDatePrototype() const
Returns the builtin "Date.prototype" object.
Definition interpreter.cpp:248
KJS::List
Native list type.
Definition list.h:48
KJS::List::size
int size() const
Definition list.h:90
KJS::Number
Represents an primitive Number value.
Definition value.h:367
KJS::Object
Represents an Object.
Definition object.h:81
KJS::Object::setInternalValue
void setInternalValue(const Value &v)
Sets the internal value of the object.
Definition object.h:720
KJS::Object::internalValue
Value internalValue() const
Returns the internal value of the object.
Definition object.h:717
KJS::String
Represents an primitive String value.
Definition value.h:340
KJS::UString
Unicode string class.
Definition ustring.h:189
KJS::UString::ascii
char * ascii() const
Convert the Unicode string to plain ASCII chars chopping of any higher bytes.
Definition ustring.cpp:485
KJS::Value
Value objects are act as wrappers ("smart pointers") around ValueImp objects and their descendents.
Definition value.h:167
KJS::Value::toString
UString toString(ExecState *exec) const
Performs the ToString type conversion operation on this value (ECMA 9.8)
Definition value.h:246
KJS::Value::isA
bool isA(Type t) const
Checks whether or not the value is of a particular tpye.
Definition value.h:203
KJS::Value::toNumber
double toNumber(ExecState *exec) const
Performs the ToNumber type conversion operation on this value (ECMA 9.3)
Definition value.h:221
KJS::ClassInfo
Class Information.
Definition object.h:58

kjs

Skip menu "kjs"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kjs

Skip menu "kjs"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for kjs by doxygen 1.9.8
This website is maintained by Timothy Pearson.