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

kjs

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

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
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kjs by doxygen 1.8.1.2
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |