• 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 
68 using namespace KJS;
69 
70 // come constants
71 const time_t invalidDate = LONG_MIN;
72 const double hoursPerDay = 24;
73 const double minutesPerHour = 60;
74 const double secondsPerMinute = 60;
75 const double msPerSecond = 1000;
76 const double msPerMinute = msPerSecond * secondsPerMinute;
77 const double msPerHour = msPerMinute * minutesPerHour;
78 const double msPerDay = msPerHour * hoursPerDay;
79 static const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
80 static const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
81 
82 static 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 
91 static 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 
100 static 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 
127 static int day(double t)
128 {
129  return int(floor(t / msPerDay));
130 }
131 
132 static 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
141 static 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
154 double timeFromYear(int year)
155 {
156  return msPerDay * dayFromYear(year);
157 }
158 
159 // year determined by time value
160 int 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.
179 int weekDay(double t)
180 {
181  int wd = (day(t) + 4) % 7;
182  if (wd < 0)
183  wd += 7;
184  return wd;
185 }
186 
187 static 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])
208 static 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])
250 static 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 
278 const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
279 
280 DateInstanceImp::DateInstanceImp(ObjectImp *proto)
281  : ObjectImp(proto)
282 {
283 }
284 
285 // ------------------------------ DatePrototypeImp -----------------------------
286 
287 const 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 
341 DatePrototypeImp::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 
350 Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
351 {
352  return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
353 }
354 
355 // ------------------------------ DateProtoFuncImp -----------------------------
356 
357 DateProtoFuncImp::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 
367 bool DateProtoFuncImp::implementsCall() const
368 {
369  return true;
370 }
371 
372 Value 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 
593 DateObjectImp::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 
612 bool DateObjectImp::implementsConstruct() const
613 {
614  return true;
615 }
616 
617 // ECMA 15.9.3
618 Object 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 
680 bool DateObjectImp::implementsCall() const
681 {
682  return true;
683 }
684 
685 // ECMA 15.9.2
686 Value 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 
699 DateObjectFuncImp::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 
707 bool DateObjectFuncImp::implementsCall() const
708 {
709  return true;
710 }
711 
712 // ECMA 15.9.4.2 - 3
713 Value 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 
746 double 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 
758 static 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"
776 static 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 
796 double 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
846 static 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.
869 static bool isSpaceLike(char c)
870 {
871  return isspace(c) || c == ',' || c == ':' || c == '-';
872 }
873 
874 double 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 
1204 double 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::Value
Value objects are act as wrappers ("smart pointers") around ValueImp objects and their descendents...
Definition: value.h:167
KJS::InternalFunctionImp
Base class for all function objects.
Definition: function.h:40
KJS::ExecState::dynamicInterpreter
Interpreter * dynamicInterpreter() const
Returns the interpreter associated with this execution state.
Definition: interpreter.h:452
KJS::Number
Represents an primitive Number value.
Definition: value.h:367
KJS::ExecState::lexicalInterpreter
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
Definition: interpreter.cpp:394
KJS::Object::setInternalValue
void setInternalValue(const Value &v)
Sets the internal value of the object.
Definition: object.h:720
KJS::Interpreter::builtinDatePrototype
Object builtinDatePrototype() const
Returns the builtin "Date.prototype" object.
Definition: interpreter.cpp:248
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::FunctionPrototypeImp
The initial value of Function.prototype (and thus all objects created with the Function constructor) ...
Definition: function_object.h:34
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::Object
Represents an Object.
Definition: object.h:81
KJS::Value::isA
bool isA(Type t) const
Checks whether or not the value is of a particular tpye.
Definition: value.h:203
KJS::UString
Unicode string class.
Definition: ustring.h:189
KJS::String
Represents an primitive String value.
Definition: value.h:340
KJS
Definition: array_instance.h:27
KJS::Object::internalValue
Value internalValue() const
Returns the internal value of the object.
Definition: object.h:717
KJS::List::size
int size() const
Definition: list.h:90
KJS::List
Native list type.
Definition: list.h:48
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::ClassInfo
Class Information.
Definition: object.h:58
KJS::UString::ascii
char * ascii() const
Convert the Unicode string to plain ASCII chars chopping of any higher bytes.
Definition: ustring.cpp:485
KJS::ExecState
Represents the current state of script execution.
Definition: interpreter.h:438
KJS::Identifier
Represents an Identifier for a Javascript object.
Definition: identifier.h:32

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
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for kjs by doxygen 1.8.8
This website is maintained by Timothy Pearson.