• 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 (__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(__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 struct _timeb timebuffer;
630 _ftime(&timebuffer);
631 double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
632#else
633 struct timeval tv;
634 gettimeofday(&tv, 0L);
635 double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
636#endif
637 value = utc;
638 } else if (numArgs == 1) {
639 Value prim = args[0].toPrimitive(exec);
640 if (prim.isA(StringType))
641 value = parseDate(prim.toString(exec));
642 else
643 value = prim.toNumber(exec);
644 } else {
645 if (isNaN(args[0].toNumber(exec))
646 || isNaN(args[1].toNumber(exec))
647 || (numArgs >= 3 && isNaN(args[2].toNumber(exec)))
648 || (numArgs >= 4 && isNaN(args[3].toNumber(exec)))
649 || (numArgs >= 5 && isNaN(args[4].toNumber(exec)))
650 || (numArgs >= 6 && isNaN(args[5].toNumber(exec)))
651 || (numArgs >= 7 && isNaN(args[6].toNumber(exec)))) {
652 value = NaN;
653 } else {
654 struct tm t;
655 memset(&t, 0, sizeof(t));
656 int year = args[0].toInt32(exec);
657 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
658 t.tm_mon = args[1].toInt32(exec);
659 t.tm_mday = (numArgs >= 3) ? args[2].toInt32(exec) : 1;
660 t.tm_hour = (numArgs >= 4) ? args[3].toInt32(exec) : 0;
661 t.tm_min = (numArgs >= 5) ? args[4].toInt32(exec) : 0;
662 t.tm_sec = (numArgs >= 6) ? args[5].toInt32(exec) : 0;
663 t.tm_isdst = -1;
664 int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
665 value = makeTime(&t, ms, false);
666 }
667 }
668
669 Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
670 Object ret(new DateInstanceImp(proto.imp()));
671 ret.setInternalValue(Number(timeClip(value)));
672 return ret;
673}
674
675bool DateObjectImp::implementsCall() const
676{
677 return true;
678}
679
680// ECMA 15.9.2
681Value DateObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
682{
683#ifdef KJS_VERBOSE
684 fprintf(stderr,"DateObjectImp::call - current time\n");
685#endif
686 time_t t = time(0L);
687 // FIXME: not threadsafe
688 struct tm *tm = localtime(&t);
689 return String(formatDate(*tm) + " " + formatTime(*tm));
690}
691
692// ------------------------------ DateObjectFuncImp ----------------------------
693
694DateObjectFuncImp::DateObjectFuncImp(ExecState* /*exec*/, FunctionPrototypeImp *funcProto,
695 int i, int len)
696 : InternalFunctionImp(funcProto), id(i)
697{
698 Value protect(this);
699 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
700}
701
702bool DateObjectFuncImp::implementsCall() const
703{
704 return true;
705}
706
707// ECMA 15.9.4.2 - 3
708Value DateObjectFuncImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
709{
710 if (id == Parse) {
711 return Number(parseDate(args[0].toString(exec)));
712 } else { // UTC
713 int n = args.size();
714 if (isNaN(args[0].toNumber(exec))
715 || isNaN(args[1].toNumber(exec))
716 || (n >= 3 && isNaN(args[2].toNumber(exec)))
717 || (n >= 4 && isNaN(args[3].toNumber(exec)))
718 || (n >= 5 && isNaN(args[4].toNumber(exec)))
719 || (n >= 6 && isNaN(args[5].toNumber(exec)))
720 || (n >= 7 && isNaN(args[6].toNumber(exec)))) {
721 return Number(NaN);
722 }
723
724 struct tm t;
725 memset(&t, 0, sizeof(t));
726 int year = args[0].toInt32(exec);
727 t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
728 t.tm_mon = args[1].toInt32(exec);
729 t.tm_mday = (n >= 3) ? args[2].toInt32(exec) : 1;
730 t.tm_hour = (n >= 4) ? args[3].toInt32(exec) : 0;
731 t.tm_min = (n >= 5) ? args[4].toInt32(exec) : 0;
732 t.tm_sec = (n >= 6) ? args[5].toInt32(exec) : 0;
733 int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
734 return Number(makeTime(&t, ms, true));
735 }
736}
737
738// -----------------------------------------------------------------------------
739
740
741double KJS::parseDate(const UString &u)
742{
743#ifdef KJS_VERBOSE
744 fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
745#endif
746 double /*time_t*/ seconds = KRFCDate_parseDate( u );
747
748 return seconds == invalidDate ? NaN : seconds * 1000.0;
749}
750
752
753static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
754{
755 //printf("year=%d month=%d day=%d hour=%d minute=%d second=%d\n", year, mon, day, hour, minute, second);
756
757 double ret = (day - 32075) /* days */
758 + 1461L * (year + 4800L + (mon - 14) / 12) / 4
759 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
760 - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
761 - 2440588;
762 ret = 24*ret + hour; /* hours */
763 ret = 60*ret + minute; /* minutes */
764 ret = 60*ret + second; /* seconds */
765
766 return ret;
767}
768
769// we follow the recommendation of rfc2822 to consider all
770// obsolete time zones not listed here equivalent to "-0000"
771static const struct KnownZone {
772#ifdef _WIN32
773 char tzName[4];
774#else
775 const char tzName[4];
776#endif
777 int tzOffset;
778} known_zones[] = {
779 { "UT", 0 },
780 { "GMT", 0 },
781 { "EST", -300 },
782 { "EDT", -240 },
783 { "CST", -360 },
784 { "CDT", -300 },
785 { "MST", -420 },
786 { "MDT", -360 },
787 { "PST", -480 },
788 { "PDT", -420 }
789};
790
791double KJS::makeTime(struct tm *t, double ms, bool utc)
792{
793 int utcOffset;
794 if (utc) {
795 time_t zero = 0;
796#if defined BSD || defined(__linux__) || defined(__APPLE__)
797 struct tm t3;
798 localtime_r(&zero, &t3);
799 utcOffset = t3.tm_gmtoff;
800 t->tm_isdst = t3.tm_isdst;
801#else
802 (void)localtime(&zero);
803# if defined(__CYGWIN__)
804 utcOffset = - _timezone;
805# else
806 utcOffset = - timezone;
807# endif
808 t->tm_isdst = 0;
809#endif
810 } else {
811 utcOffset = 0;
812 t->tm_isdst = -1;
813 }
814
815 double yearOffset = 0.0;
816 if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
817 // we'll fool mktime() into believing that this year is within
818 // its normal, portable range (1970-2038) by setting tm_year to
819 // 2000 or 2001 and adding the difference in milliseconds later.
820 // choice between offset will depend on whether the year is a
821 // leap year or not.
822 int y = t->tm_year + 1900;
823 int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
824 const double baseTime = timeFromYear(baseYear);
825 yearOffset = timeFromYear(y) - baseTime;
826 t->tm_year = baseYear - 1900;
827 }
828
829 // Determine if we passed over a DST change boundary
830 if (!utc) {
831 time_t tval = mktime(t) + utcOffset + int((ms + yearOffset)/1000);
832 struct tm t3;
833 localtime_r(&tval, &t3);
834 t->tm_isdst = t3.tm_isdst;
835 }
836
837 return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
838}
839
840// returns 0-11 (Jan-Dec); -1 on failure
841static int findMonth(const char *monthStr)
842{
843 assert(monthStr);
844 static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
845 char needle[4];
846 for (int i = 0; i < 3; ++i) {
847 if (!*monthStr)
848 return -1;
849 needle[i] = tolower(*monthStr++);
850 }
851 needle[3] = '\0';
852 const char *str = strstr(haystack, needle);
853 if (str) {
854 int position = str - haystack;
855 if (position % 3 == 0) {
856 return position / 3;
857 }
858 }
859 return -1;
860}
861
862// maybe this should be more often than just isspace() but beware of
863// conflicts with : in time strings.
864static bool isSpaceLike(char c)
865{
866 return isspace(c) || c == ',' || c == ':' || c == '-';
867}
868
869double KJS::KRFCDate_parseDate(const UString &_date)
870{
871 // This parse a date in the form:
872 // Wednesday, 09-Nov-99 23:12:40 GMT
873 // or
874 // Sat, 01-Jan-2000 08:00:00 GMT
875 // or
876 // Sat, 01 Jan 2000 08:00:00 GMT
877 // or
878 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
879 // ### non RFC formats, added for Javascript:
880 // [Wednesday] January 09 1999 23:12:40 GMT
881 // [Wednesday] January 09 23:12:40 GMT 1999
882 //
883 // We ignore the weekday
884 //
885 double result = -1;
886 int offset = 0;
887 bool have_tz = false;
888 char *newPosStr;
889 const char *dateString = _date.ascii();
890 int day = 0;
891 int month = -1; // not set yet
892 int year = 0;
893 int hour = 0;
894 int minute = 0;
895 int second = 0;
896 bool have_time = false;
897
898 // Skip leading space
899 while(*dateString && isSpaceLike(*dateString))
900 dateString++;
901
902 const char *wordStart = dateString;
903 // Check contents of first words if not number
904 while(*dateString && !isdigit(*dateString))
905 {
906 if (isSpaceLike(*dateString) && dateString - wordStart >= 3)
907 {
908 month = findMonth(wordStart);
909 while(*dateString && isSpaceLike(*dateString))
910 dateString++;
911 wordStart = dateString;
912 }
913 else
914 dateString++;
915 }
916 // missing delimiter between month and day (like "January29")?
917 if (month == -1 && dateString && wordStart != dateString) {
918 month = findMonth(wordStart);
919 // TODO: emit warning about dubious format found
920 }
921
922 while(*dateString && isSpaceLike(*dateString))
923 dateString++;
924
925 if (!*dateString)
926 return invalidDate;
927
928 // ' 09-Nov-99 23:12:40 GMT'
929 errno = 0;
930 day = strtol(dateString, &newPosStr, 10);
931 if (errno)
932 return invalidDate;
933 dateString = newPosStr;
934
935 if (!*dateString)
936 return invalidDate;
937
938 if (day < 0)
939 return invalidDate;
940 if (day > 31) {
941 // ### where is the boundary and what happens below?
942 if (*dateString == '/') {
943 // looks like a YYYY/MM/DD date
944 if (!*++dateString)
945 return invalidDate;
946 year = day;
947 month = strtol(dateString, &newPosStr, 10) - 1;
948 if (errno)
949 return invalidDate;
950 dateString = newPosStr;
951 if (*dateString++ != '/' || !*dateString)
952 return invalidDate;
953 day = strtol(dateString, &newPosStr, 10);
954 if (errno)
955 return invalidDate;
956 dateString = newPosStr;
957 } else {
958 return invalidDate;
959 }
960 } else if (*dateString == '/' && month == -1)
961 {
962 dateString++;
963 // This looks like a MM/DD/YYYY date, not an RFC date.....
964 month = day - 1; // 0-based
965 day = strtol(dateString, &newPosStr, 10);
966 if (errno)
967 return invalidDate;
968 dateString = newPosStr;
969 if (*dateString == '/')
970 dateString++;
971 if (!*dateString)
972 return invalidDate;
973 //printf("month=%d day=%d dateString=%s\n", month, day, dateString);
974 }
975 else
976 {
977 if (*dateString == '-')
978 dateString++;
979
980 while(*dateString && isSpaceLike(*dateString))
981 dateString++;
982
983 if (*dateString == ',')
984 dateString++;
985
986 if ( month == -1 ) // not found yet
987 {
988 month = findMonth(dateString);
989 if (month == -1)
990 return invalidDate;
991
992 while(*dateString && (*dateString != '-') && !isSpaceLike(*dateString))
993 dateString++;
994
995 if (!*dateString)
996 return invalidDate;
997
998 // '-99 23:12:40 GMT'
999 if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
1000 return invalidDate;
1001 dateString++;
1002 }
1003
1004 if ((month < 0) || (month > 11))
1005 return invalidDate;
1006 }
1007
1008 // '99 23:12:40 GMT'
1009 if (year <= 0 && *dateString) {
1010 year = strtol(dateString, &newPosStr, 10);
1011 if (errno)
1012 return invalidDate;
1013 }
1014
1015 // Don't fail if the time is missing.
1016 if (*newPosStr)
1017 {
1018 // ' 23:12:40 GMT'
1019 if (*newPosStr == ':') // Ah, so there was no year, but the number was the hour
1020 year = -1;
1021 else if (isSpaceLike(*newPosStr)) // we parsed the year
1022 dateString = ++newPosStr;
1023 else
1024 return invalidDate;
1025
1026 hour = strtol(dateString, &newPosStr, 10);
1027
1028 // Do not check for errno here since we want to continue
1029 // even if errno was set becasue we are still looking
1030 // for the timezone!
1031 // read a number? if not this might be a timezone name
1032 if (newPosStr != dateString) {
1033 have_time = true;
1034 dateString = newPosStr;
1035
1036 if ((hour < 0) || (hour > 23))
1037 return invalidDate;
1038
1039 if (!*dateString)
1040 return invalidDate;
1041
1042 // ':12:40 GMT'
1043 if (*dateString++ != ':')
1044 return invalidDate;
1045
1046 minute = strtol(dateString, &newPosStr, 10);
1047 if (errno)
1048 return invalidDate;
1049 dateString = newPosStr;
1050
1051 if ((minute < 0) || (minute > 59))
1052 return invalidDate;
1053
1054 // ':40 GMT'
1055 if (*dateString && *dateString != ':' && !isspace(*dateString))
1056 return invalidDate;
1057
1058 // seconds are optional in rfc822 + rfc2822
1059 if (*dateString ==':') {
1060 dateString++;
1061
1062 second = strtol(dateString, &newPosStr, 10);
1063 if (errno)
1064 return invalidDate;
1065 dateString = newPosStr;
1066
1067 if ((second < 0) || (second > 59))
1068 return invalidDate;
1069
1070 // disallow trailing colon seconds
1071 if (*dateString == ':')
1072 return invalidDate;
1073 }
1074
1075 while(*dateString && isspace(*dateString))
1076 dateString++;
1077
1078 if (strncasecmp(dateString, "AM", 2) == 0) {
1079 if (hour > 12)
1080 return invalidDate;
1081 if (hour == 12)
1082 hour = 0;
1083 dateString += 2;
1084 while (isspace(*dateString))
1085 dateString++;
1086 } else if (strncasecmp(dateString, "PM", 2) == 0) {
1087 if (hour > 12)
1088 return invalidDate;
1089 if (hour != 12)
1090 hour += 12;
1091 dateString += 2;
1092 while (isspace(*dateString))
1093 dateString++;
1094 }
1095 }
1096 } else {
1097 dateString = newPosStr;
1098 }
1099
1100 // don't fail if the time zone is missing, some
1101 // broken mail-/news-clients omit the time zone
1102 if (*dateString) {
1103
1104 if (strncasecmp(dateString, "GMT", 3) == 0 ||
1105 strncasecmp(dateString, "UTC", 3) == 0)
1106 {
1107 dateString += 3;
1108 have_tz = true;
1109 }
1110
1111 while (*dateString && isspace(*dateString))
1112 ++dateString;
1113
1114 if (strncasecmp(dateString, "GMT", 3) == 0) {
1115 dateString += 3;
1116 }
1117 if ((*dateString == '+') || (*dateString == '-')) {
1118 offset = strtol(dateString, &newPosStr, 10);
1119 if (errno)
1120 return invalidDate;
1121 dateString = newPosStr;
1122
1123 if ((offset < -9959) || (offset > 9959))
1124 return invalidDate;
1125
1126 int sgn = (offset < 0)? -1:1;
1127 offset = abs(offset);
1128 if ( *dateString == ':' ) { // GMT+05:00
1129 int offset2 = strtol(dateString, &newPosStr, 10);
1130 if (errno)
1131 return invalidDate;
1132 dateString = newPosStr;
1133 offset = (offset*60 + offset2)*sgn;
1134 }
1135 else
1136 offset = ((offset / 100)*60 + (offset % 100))*sgn;
1137 have_tz = true;
1138 } else {
1139 for (int i=0; i < int(sizeof(known_zones)/sizeof(KnownZone)); i++) {
1140 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
1141 offset = known_zones[i].tzOffset;
1142 dateString += strlen(known_zones[i].tzName);
1143 have_tz = true;
1144 break;
1145 }
1146 }
1147 }
1148 }
1149
1150 while(*dateString && isspace(*dateString))
1151 dateString++;
1152
1153 if ( *dateString && year == -1 ) {
1154 year = strtol(dateString, &newPosStr, 10);
1155 if (errno)
1156 return invalidDate;
1157 dateString = newPosStr;
1158 }
1159
1160 while (isspace(*dateString))
1161 dateString++;
1162
1163#if 0
1164 // Trailing garbage
1165 if (*dateString != '\0')
1166 return invalidDate;
1167#endif
1168
1169 // Y2K: Solve 2 digit years
1170 if ((year >= 0) && (year < 50))
1171 year += 2000;
1172
1173 if ((year >= 50) && (year < 100))
1174 year += 1900; // Y2K
1175
1176 if (!have_tz) {
1177 // fall back to midnight, local timezone
1178 struct tm t;
1179 memset(&t, 0, sizeof(tm));
1180 t.tm_mday = day;
1181 t.tm_mon = month;
1182 t.tm_year = year - 1900;
1183 t.tm_isdst = -1;
1184 if (have_time) {
1185 t.tm_sec = second;
1186 t.tm_min = minute;
1187 t.tm_hour = hour;
1188 }
1189
1190 // better not use mktime() as it can't handle the full year range
1191 return makeTime(&t, 0, false) / 1000.0;
1192 }
1193
1194 result = ymdhms_to_seconds(year, month+1, day, hour, minute, second) - offset*60;
1195 return result;
1196}
1197
1198
1199double KJS::timeClip(double t)
1200{
1201 if (isInf(t))
1202 return NaN;
1203 double at = fabs(t);
1204 if (at > 8.64E15)
1205 return NaN;
1206 return floor(at) * (t != at ? -1 : 1);
1207}
1208
KJS::CString
8 bit char based string class
Definition: ustring.h:165
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.4
This website is maintained by Timothy Pearson.