update copyright date
[gnash.git] / libcore / asobj / Date_as.cpp
blobc31bca2cd8471458025122276cc286ccd7a280cb
1 // Date_as.cpp: ActionScript class for date and time, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
4 // 2011 Free SoftwareFoundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program 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
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 // Implements methods of the ActionScript "Date" class for Gnash
23 // TODO: What does Flash setTime() do/return if you hand it 0 parameters?
25 // Flash player handles a huge range of dates, including
26 // thousands of years BC. The timestamp value is correspondingly
27 // large: it is a double, which has a minimum size of 8 bytes
28 // in the C++ standard. Methods provided by <ctime>
29 // generally rely on time_t whose size varies according to platform.
30 // It is not big enough to deal with all valid SWF timestamps,
31 // so this class uses its own methods to convert to and from
32 // a time struct and the time stamp.
33 //
35 // FEATURES:
36 // SWF Player does not seem to respect TZ or the zoneinfo database;
37 // It changes to/from daylight saving time according to its own rules.
38 // We use the operating system's localtime routines.
40 // SWF player does bizarre things for some argument combinations,
41 // returning datestamps of /6.*e+19 We don't bother doing this...
43 // Boost date-time handles a larger range of correct dates than
44 // the usual C and system functions. However, it is still limited
45 // to POSIX to 1 Jan 1400 - 31 Dec 9999 and will not handle
46 // dates at all outside this range. SWF isn't really that
47 // bothered by correctness; rather, it needs to handle a vast
48 // range of dates consistently. See http://www.boost.org/doc/html/date_time.html
50 // Pros:
52 // * OS portability is done by libboost, not here;
54 // Cons:
56 // * It doesn't handle fractions of milliseconds (and who cares?);
57 // * Mapping between boost's coherent date_time methods and SWF's
58 // idiosyncratic ones to implement this class' methods is more tricky;
59 // * It brings the need to handle all boundary cases and exceptions
60 // explicitly (e.g. mapping of 38 Nov to 8 Dec, mapping negative
61 // month/day-of-month/hours/min/secs/millisecs into the previous
62 // year/month/day/hour/min/sec and so on).
63 // * It doesn't do what ActionScript wants. This is the best reason
64 // not to use it for time and date functions (though for portable
65 // timing it might well be useful).
67 #include "log.h"
68 #include "GnashNumeric.h"
69 #include "Date_as.h"
70 #include "fn_call.h"
71 #include "Global_as.h"
72 #include "GnashException.h"
73 #include "NativeFunction.h"
74 #include "ClockTime.h"
75 #include "VM.h"
77 #include "namedStrings.h"
78 #include <cmath>
79 #include <boost/format.hpp>
81 // All clock time / localtime functions are in libbase/Time.cpp,
82 // so that portability problems are all in one place. It saves
83 // a lot of rebuilding too.
85 namespace gnash {
87 namespace {
89 // A time struct to contain the broken-down time.
90 struct GnashTime
92 boost::int32_t millisecond;
93 boost::int32_t second;
94 boost::int32_t minute;
95 boost::int32_t hour;
96 boost::int32_t monthday;
97 boost::int32_t weekday;
98 boost::int32_t month;
99 boost::int32_t year;
100 boost::int32_t timeZoneOffset;
103 static const int daysInMonth[2][12] = {
104 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
105 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
108 // Forward declarations
110 void attachDateInterface(as_object& o);
111 void attachDateStaticInterface(as_object& o);
113 // Seconds and milliseconds should be exactly the same whether in UTC
114 // or in localtime, so we always use localtime.
115 as_value date_new(const fn_call& fn);
116 as_value date_getTime(const fn_call& fn);
117 as_value date_setTime(const fn_call& fn);
118 as_value date_getTimezoneOffset(const fn_call& fn);
119 as_value date_getYear(const fn_call& fn);
120 as_value date_getFullYear(const fn_call& fn);
121 as_value date_getMonth(const fn_call& fn);
122 as_value date_getDate(const fn_call& fn);
123 as_value date_getDay(const fn_call& fn);
124 as_value date_getHours(const fn_call& fn);
125 as_value date_getMinutes(const fn_call& fn);
126 as_value date_getSeconds(const fn_call& fn);
127 as_value date_getMilliseconds(const fn_call& fn);
128 as_value date_getUTCFullYear(const fn_call& fn);
129 as_value date_getUTCYear(const fn_call& fn);
130 as_value date_getUTCMonth(const fn_call& fn);
131 as_value date_getutcdate(const fn_call& fn);
132 as_value date_getUTCDay(const fn_call& fn);
133 as_value date_getUTCHours(const fn_call& fn);
134 as_value date_getUTCMinutes(const fn_call& fn);
135 template<bool utc> as_value date_setDate(const fn_call& fn);
136 template<bool utc> as_value date_setfullyear(const fn_call& fn);
137 template<bool utc> as_value date_setHours(const fn_call& fn);
138 template<bool utc> as_value date_setMilliseconds(const fn_call& fn);
139 template<bool utc> as_value date_setMinutes(const fn_call& fn);
140 template<bool utc> as_value date_setmonth(const fn_call& fn);
141 template<bool utc> as_value date_setSeconds(const fn_call& fn);
142 as_value date_setYear(const fn_call& fn);
143 as_value date_tostring(const fn_call& fn);
144 as_value date_UTC(const fn_call& fn);
146 void fillGnashTime(double time, GnashTime& gt);
147 double makeTimeValue(GnashTime& gt);
148 void localTime(double time, GnashTime& gt);
149 void universalTime(double time, GnashTime& gt);
150 int localTimeZoneOffset(double time);
152 double rogue_date_args(const fn_call& fn, unsigned maxargs);
153 void localTime(double time, GnashTime& gt);
154 void universalTime(double time, GnashTime& gt);
155 template <typename T> void truncateDouble(T& target, double value);
159 Date_as::Date_as(double value)
161 _timeValue(value)
165 std::string
166 Date_as::toString() const
168 const char* monthname[12] = { "Jan", "Feb", "Mar",
169 "Apr", "May", "Jun",
170 "Jul", "Aug", "Sep",
171 "Oct", "Nov", "Dec" };
173 const char* dayweekname[7] = { "Sun", "Mon", "Tue", "Wed",
174 "Thu", "Fri", "Sat" };
176 /// NaN and infinities all print as "Invalid Date"
177 if (isNaN(_timeValue) || isInf(_timeValue)) {
178 return "Invalid Date";
181 // The date value split out to year, month, day, hour etc and millisecs
182 GnashTime gt;
183 // Time zone offset (including DST) as hours and minutes east of GMT
185 localTime(_timeValue, gt);
187 int offsetHours = gt.timeZoneOffset / 60;
188 int offsetMinutes = gt.timeZoneOffset % 60;
190 // If timezone is negative, both hours and minutes will be negative
191 // but for the purpose of printing a string, only the hour needs to
192 // produce a minus sign.
193 if (offsetMinutes < 0) offsetMinutes = -offsetMinutes;
195 boost::format dateFormat("%s %s %d %02d:%02d:%02d GMT%+03d%02d %d");
196 dateFormat % dayweekname[gt.weekday] % monthname[gt.month]
197 % gt.monthday % gt.hour % gt.minute % gt.second
198 % offsetHours % offsetMinutes % (gt.year + 1900);
200 return dateFormat.str();
204 void
205 date_class_init(as_object& global, const ObjectURI& uri)
208 Global_as& gl = getGlobal(global);
209 as_object* proto = createObject(gl);
210 as_object* cl = gl.createClass(&date_new, proto);
211 attachDateInterface(*proto);
213 const int flags = PropFlags::readOnly;
214 cl->set_member_flags(NSV::PROP_uuPROTOuu, flags);
215 cl->set_member_flags(NSV::PROP_CONSTRUCTOR, flags);
216 cl->set_member_flags(NSV::PROP_PROTOTYPE, flags);
218 // Attach static interface to class (Date.UTC)
219 attachDateStaticInterface(*cl);
221 // Register _global.Date
222 global.init_member(uri, cl, as_object::DefaultFlags);
226 void
227 registerDateNative(as_object& global)
229 VM& vm = getVM(global);
231 vm.registerNative(date_getFullYear, 103, 0);
232 vm.registerNative(date_getYear, 103, 1);
233 vm.registerNative(date_getMonth, 103, 2);
234 vm.registerNative(date_getDate, 103, 3);
235 vm.registerNative(date_getDay, 103, 4);
236 vm.registerNative(date_getHours, 103, 5);
237 vm.registerNative(date_getMinutes, 103, 6);
238 vm.registerNative(date_getSeconds, 103, 7);
239 vm.registerNative(date_getMilliseconds, 103, 8);
240 vm.registerNative(date_setfullyear<false>, 103, 9);
241 vm.registerNative(date_setmonth<false>, 103, 10);
242 vm.registerNative(date_setDate<false>, 103, 11);
243 vm.registerNative(date_setHours<false>, 103, 12);
244 vm.registerNative(date_setMinutes<false>, 103, 13);
245 vm.registerNative(date_setSeconds<false>, 103, 14);
246 vm.registerNative(date_setMilliseconds<false>, 103, 15);
247 vm.registerNative(date_getTime, 103, 16);
248 vm.registerNative(date_setTime, 103, 17);
249 vm.registerNative(date_getTimezoneOffset, 103, 18);
250 vm.registerNative(date_tostring, 103, 19);
251 vm.registerNative(date_setYear, 103, 20);
252 vm.registerNative(date_getUTCFullYear, 103, 128);
253 vm.registerNative(date_getUTCYear, 103, 129);
254 vm.registerNative(date_getUTCMonth, 103, 130);
255 vm.registerNative(date_getutcdate, 103, 131);
256 vm.registerNative(date_getUTCDay, 103, 132);
257 vm.registerNative(date_getUTCHours, 103, 133);
258 vm.registerNative(date_getUTCMinutes, 103, 134);
260 // These two are deliberately the same as non-UTC methods
261 // as there should be no difference:
262 vm.registerNative(date_getSeconds, 103, 135);
263 vm.registerNative(date_getMilliseconds, 103, 136);
265 vm.registerNative(date_setfullyear<true>, 103, 137);
266 vm.registerNative(date_setmonth<true>, 103, 138);
267 vm.registerNative(date_setDate<true>, 103, 139);
268 vm.registerNative(date_setHours<true>, 103, 140);
269 vm.registerNative(date_setMinutes<true>, 103, 141);
270 vm.registerNative(date_setSeconds<true>, 103, 142);
271 vm.registerNative(date_setMilliseconds<true>, 103, 143);
273 //vm.registerNative(date_new, 103, 256);
275 vm.registerNative(date_UTC, 103, 257);
280 namespace {
282 // Helpers for calendar algorithms
283 inline bool
284 isLeapYear(boost::int32_t year)
286 return !((year + 1900) % 400) ||
287 ( !((year + 1900) % 4) && ((year + 1900) % 100));
291 inline boost::int32_t
292 countLeapYears(boost::int32_t year)
294 return year / 4 - year / 100 + year / 400;
298 /// Return the broken-down time as a local time.
299 void
300 localTime(double time, GnashTime& gt)
302 // find local timezone offset for the desired time.
303 gt.timeZoneOffset = localTimeZoneOffset(time);
304 fillGnashTime(time, gt);
307 /// Return the broken-down time as UTC
308 void
309 universalTime(double time, GnashTime& gt)
311 // No time zone needed.
312 gt.timeZoneOffset = 0;
313 fillGnashTime(time, gt);
317 /// Safely truncate a double to an integer, returning the min()
318 /// limit on overflow.
319 template <typename T>
320 void truncateDouble(T& target, double value)
322 if (value < std::numeric_limits<T>::min() ||
323 value > std::numeric_limits<T>::max())
325 target = std::numeric_limits<T>::min();
326 return;
328 target = static_cast<T>(value);
331 // As UTC offset is measured in minutes, we can use the same
332 // functions to get seconds and milliseconds in local and utc time.
333 // But setting either of them can have a knock-on effect on minutes
334 // and hours, so both need their own set functions.
335 void
336 attachDateInterface(as_object& o)
338 VM& vm = getVM(o);
340 o.init_member("getFullYear", vm.getNative(103, 0));
341 o.init_member("getYear", vm.getNative(103, 1));
342 o.init_member("getMonth", vm.getNative(103, 2));
343 o.init_member("getDate", vm.getNative(103, 3));
344 o.init_member("getDay", vm.getNative(103, 4));
345 o.init_member("getHours", vm.getNative(103, 5));
346 o.init_member("getMinutes", vm.getNative(103, 6));
347 o.init_member("getSeconds", vm.getNative(103, 7));
348 o.init_member("getMilliseconds", vm.getNative(103, 8));
349 o.init_member("setFullYear", vm.getNative(103, 9));
350 o.init_member("setMonth", vm.getNative(103, 10));
351 o.init_member("setDate", vm.getNative(103, 11));
352 o.init_member("setHours", vm.getNative(103, 12));
353 o.init_member("setMinutes", vm.getNative(103, 13));
354 o.init_member("setSeconds", vm.getNative(103, 14));
355 o.init_member("setMilliseconds", vm.getNative(103, 15));
356 o.init_member("getTime", vm.getNative(103, 16));
357 o.init_member("setTime", vm.getNative(103, 17));
358 o.init_member("getTimezoneOffset", vm.getNative(103, 18));
359 o.init_member("toString", vm.getNative(103, 19));
360 o.init_member("setYear", vm.getNative(103, 20));
361 o.init_member("getUTCFullYear", vm.getNative(103, 128));
362 o.init_member("getUTCYear", vm.getNative(103, 129));
363 o.init_member("getUTCMonth", vm.getNative(103, 130));
364 o.init_member("getUTCDate", vm.getNative(103, 131));
365 o.init_member("getUTCDay", vm.getNative(103, 132));
366 o.init_member("getUTCHours", vm.getNative(103, 133));
367 o.init_member("getUTCMinutes", vm.getNative(103, 134));
368 o.init_member("getUTCSeconds", vm.getNative(103, 135));
369 o.init_member("getUTCMilliseconds", vm.getNative(103, 136));
370 o.init_member("setUTCFullYear", vm.getNative(103, 137));
371 o.init_member("setUTCMonth", vm.getNative(103, 138));
372 o.init_member("setUTCDate", vm.getNative(103, 139));
373 o.init_member("setUTCHours", vm.getNative(103, 140));
374 o.init_member("setUTCMinutes", vm.getNative(103, 141));
375 o.init_member("setUTCSeconds", vm.getNative(103, 142));
376 o.init_member("setUTCMilliseconds", vm.getNative(103, 143));
378 o.init_member("valueOf", getMember(o, getURI(vm, "getTime")));
382 void
383 attachDateStaticInterface(as_object& o)
385 VM& vm = getVM(o);
386 const int flags = as_object::DefaultFlags | PropFlags::readOnly;
387 o.init_member("UTC", vm.getNative(103, 257), flags);
390 /// \brief Date constructor
392 /// The constructor has three forms: 0 args, 1 arg and 2-7 args.
393 /// new Date() sets the Date to the current time of day
394 /// new Date(undefined[,*]) does the same.
395 /// new Date(timeValue:Number) sets the date to a number of milliseconds since
396 /// 1 Jan 1970 UTC
397 /// new Date(year, month[,date[,hour[,minute[,second[,millisecond]]]]])
398 /// creates a Date date object and sets it to a specified year/month etc
399 /// in local time.
400 /// year 0-99 means 1900-1999, other positive values are gregorian years
401 /// and negative values are years prior to 1900. Thus the only way to
402 /// specify the year 50AD is as -1850.
403 /// Defaults are 0 except for date (day of month) whose default it 1.
404 as_value
405 date_new(const fn_call& fn)
408 as_object* obj = fn.this_ptr;
410 // The Date ctor called as a conversion function constructs a new
411 // date.
412 if (!fn.isInstantiation()) {
413 Global_as& gl = getGlobal(fn);
414 as_function* ctor = getMember(gl, NSV::CLASS_DATE).to_function();
415 if (!ctor) return as_value();
416 fn_call::Args args;
417 return constructInstance(*ctor, fn.env(), args);
420 // Reject all date specifications containing Infinities and NaNs.
421 // The commercial player does different things according to which
422 // args are NaNs or Infinities:
423 // for now, we just use rogue_date_args' algorithm
424 double foo;
425 if (( foo = rogue_date_args(fn, 7)) != 0.0) {
426 obj->setRelay(new Date_as(foo));
427 return as_value();
430 if (fn.nargs < 1 || fn.arg(0).is_undefined()) {
431 // Time now
432 obj->setRelay(new Date_as);
434 else if (fn.nargs == 1) {
435 // Set the value in milliseconds since 1970 UTC
436 obj->setRelay(new Date_as(toNumber(fn.arg(0), getVM(fn))));
438 else {
439 // Create a time from the supplied (at least 2) arguments.
440 GnashTime gt;
442 gt.millisecond = 0;
443 gt.second = 0;
444 gt.minute = 0;
445 gt.hour = 0;
446 gt.monthday = 1;
447 gt.month = toInt(fn.arg(1), getVM(fn));
449 int year = toInt(fn.arg(0), getVM(fn));
451 // GnashTime.year is the value since 1900 (like struct tm)
452 // negative value is a year before 1900. A year between 0
453 // and 99 is the year since 1900 (which is the same arithmetic).
454 if (year < 100) gt.year = year;
456 // A value of 100 or more is a full year and must be
457 // converted to years since 1900
458 else gt.year = year - 1900;
460 switch (fn.nargs) {
461 default:
462 IF_VERBOSE_ASCODING_ERRORS(
463 log_aserror(_("Date constructor called with more than 7 "
464 "arguments"));
466 case 7:
467 // fractions of milliseconds are ignored
468 gt.millisecond = toInt(fn.arg(6), getVM(fn));
469 case 6:
470 gt.second = toInt(fn.arg(5), getVM(fn));
471 case 5:
472 gt.minute = toInt(fn.arg(4), getVM(fn));
473 case 4:
474 gt.hour = toInt(fn.arg(3), getVM(fn));
475 case 3:
476 gt.monthday = toInt(fn.arg(2), getVM(fn));
477 case 2:
478 break;
479 // Done already
482 // The arguments are in local time: subtract the local time offset
483 // at the desired time to get UTC. This may not be completely correct
484 // due to shortcomings in the timezoneoffset calculation, but should
485 // be internally consistent.
486 double localTime = makeTimeValue(gt);
487 obj->setRelay(new Date_as(
488 localTime - clocktime::getTimeZoneOffset(localTime) * 60000));
491 return as_value();
495 // ========= Functions to get dates in various ways ========
498 // Date.getTime() is implemented by Date.valueOf()
500 /// Return true if the date is invalid.
501 inline
502 bool invalidDate(double timeValue)
504 return (isNaN(timeValue) || isInf(timeValue));
507 /// Returns an element of the Date object as an as_value
509 /// An invalid date value is returned as NaN (this is probably not correct,
510 /// as the pp returns something weird in this case).
512 /// @param dateFunc The date function (either localTime or universalTime)
513 /// to use to break the time value into elements.
514 /// @param element A pointer-to-data-member of the GnashTime struct,
515 /// specifying which element to return.
516 /// @param timeValue The time value to break into elements.
517 /// @param adjustment Adjust the result by this amount (used for full year).
518 template<typename T>
519 inline as_value timeElement(T dateFunc, boost::int32_t GnashTime::* element,
520 double timeValue, int adjustment = 0)
522 if (invalidDate(timeValue)) return as_value();
523 GnashTime gt;
524 dateFunc(timeValue, gt);
525 return as_value(gt.*element + adjustment);
529 /// Date.getYear()
531 /// Returns a Date's Gregorian year minus 1900 according to local time.
532 as_value
533 date_getYear(const fn_call& fn)
535 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
536 return timeElement(localTime, &GnashTime::year, date->getTimeValue());
539 /// \brief Date.getFullYear
540 /// returns a Date's Gregorian year according to local time.
541 as_value
542 date_getFullYear(const fn_call& fn)
544 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
545 return timeElement(
546 localTime, &GnashTime::year, date->getTimeValue(), 1900);
549 /// \brief Date.getMonth
550 /// returns a Date's month in the range 0 to 11.
551 as_value
552 date_getMonth(const fn_call& fn)
554 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
555 return timeElement(localTime, &GnashTime::month, date->getTimeValue());
558 /// \brief Date.getDate
559 /// returns a Date's day-of-month, from 1 to 31 according to local time.
560 as_value
561 date_getDate(const fn_call& fn)
563 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
564 return timeElement(localTime, &GnashTime::monthday, date->getTimeValue());
567 /// \brief Date.getDay
568 /// returns the day of the week for a Date according to local time,
569 /// where 0 is Sunday and 6 is Saturday.
570 as_value
571 date_getDay(const fn_call& fn)
573 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
574 return timeElement(localTime, &GnashTime::weekday, date->getTimeValue());
578 /// \brief Date.getHours
579 /// Returns the hour number for a Date, from 0 to 23, according to local time.
580 as_value
581 date_getHours(const fn_call& fn)
583 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
584 return timeElement(localTime, &GnashTime::hour, date->getTimeValue());
587 /// \brief Date.getMinutes
588 /// returns a Date's minutes, from 0-59, according to localtime.
589 /// (Yes, some places do have a fractions of an hour's timezone offset
590 /// or daylight saving time!)
591 as_value
592 date_getMinutes(const fn_call& fn)
594 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
595 return timeElement(localTime, &GnashTime::minute, date->getTimeValue());
598 /// \brief Date.getSeconds
599 /// returns a Date's seconds, from 0-59.
600 /// Localtime should be irrelevant.
601 as_value
602 date_getSeconds(const fn_call& fn)
604 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
605 return timeElement(localTime, &GnashTime::second, date->getTimeValue());
608 /// \brief Date.getMilliseconds
609 /// returns a Date's millisecond component as an integer from 0 to 999.
610 /// Localtime is irrelevant!
612 // Also implements Date.getUTCMilliseconds
613 as_value
614 date_getMilliseconds(const fn_call& fn)
616 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
617 return timeElement(
618 localTime, &GnashTime::millisecond, date->getTimeValue());
622 // The same functions for universal time.
624 as_value
625 date_getUTCFullYear(const fn_call& fn)
627 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
628 return timeElement(universalTime, &GnashTime::year,
629 date->getTimeValue(), 1900);
632 as_value
633 date_getUTCYear(const fn_call& fn)
635 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
636 return timeElement(universalTime, &GnashTime::year, date->getTimeValue());
639 as_value
640 date_getUTCMonth(const fn_call& fn)
642 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
643 return timeElement(universalTime, &GnashTime::month, date->getTimeValue());
646 as_value
647 date_getutcdate(const fn_call& fn)
649 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
650 return timeElement(
651 universalTime, &GnashTime::monthday, date->getTimeValue());
655 as_value
656 date_getUTCDay(const fn_call& fn)
658 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
659 return timeElement(
660 universalTime, &GnashTime::weekday, date->getTimeValue());
663 as_value
664 date_getUTCHours(const fn_call& fn)
666 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
667 return timeElement(
668 universalTime, &GnashTime::hour, date->getTimeValue());
671 as_value
672 date_getUTCMinutes(const fn_call& fn)
674 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
675 return timeElement(universalTime, &GnashTime::minute, date->getTimeValue());
679 // Return the difference between UTC and localtime in minutes.
680 inline int
681 localTimeZoneOffset(double time)
683 // This simply has to return the difference in minutes
684 // between UTC (Greenwich Mean Time, GMT) and the localtime.
685 // Obviously, this includes Daylight Saving Time if it applies.
686 return clocktime::getTimeZoneOffset(time);
690 /// \brief Date.getTimezoneOffset
691 /// returns the difference between localtime and UTC that was in effect at the
692 /// time specified by a Date object, according to local timezone and DST.
693 /// For example, if you are in GMT+0100, the offset is -60
694 as_value
695 date_getTimezoneOffset(const fn_call& fn)
697 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
698 return as_value(-localTimeZoneOffset(date->getTimeValue()));
703 // ========= Functions to set dates in various ways ========
706 /// Date.setTime
708 /// sets a Date in milliseconds after January 1, 1970 00:00 UTC.
709 /// The return value is the same as the parameter.
711 /// If no arguments are passed or the first argument is undefined, the time
712 /// value is set to NaN.
714 /// Partial milliseconds are just ignored. The permissible range
715 /// is +/- 8.64+e15 (magic numbers).
716 as_value
717 date_setTime(const fn_call& fn)
719 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
721 if (fn.nargs < 1 || fn.arg(0).is_undefined()) {
722 IF_VERBOSE_ASCODING_ERRORS(
723 log_aserror(_("Date.setTime needs one argument"));
725 date->setTimeValue(NaN);
727 else {
728 // returns a double
729 const double magicMaxValue = 8.64e+15;
730 double d = toNumber(fn.arg(0), getVM(fn));
732 if (!isFinite(d) || std::abs(d) > magicMaxValue) {
733 date->setTimeValue(NaN);
735 else {
736 // Knock off the decimal part.
737 date->setTimeValue(d < 0 ? std::ceil(d) : std::floor(d));
741 if (fn.nargs > 1) {
742 IF_VERBOSE_ASCODING_ERRORS(
743 log_aserror(_("Date.setTime was called with more than one "
744 "argument"));
748 return as_value(date->getTimeValue());
752 // Functions to set just some components of a Date.
754 // We do this by exploding the datestamp into the calendar components,
755 // setting the fields that are to be changed, then converting back.
758 // The Adobe player 9 behaves strangely. e.g., after "new date = Date(0)":
759 // date.setYear(1970); date.setMonth(1); date.setDate(29); gives Mar 1 but
760 // date.setYear(1970); date.setDate(29); date.setMonth(1); gives Feb 28
762 // We need two sets of the same functions: those that take localtime values
763 // and those that take UTC (GMT) values.
764 // Since there are a lot of them and they are hairy, we write one set that,
765 // if an additional extra parameter is passed, switch to working in UTC
766 // instead. Apart from the bottom-level conversions they are identical.
768 void
769 gnashTimeToDate(GnashTime& gt, Date_as& date, bool utc)
771 // Needs timezone.
772 if (utc) date.setTimeValue(makeTimeValue(gt));
774 else {
775 double localTime = makeTimeValue(gt);
776 date.setTimeValue(localTime -
777 clocktime::getTimeZoneOffset(localTime) * 60000);
781 void
782 dateToGnashTime(Date_as& date, GnashTime& gt, bool utc)
784 // Needs timezone.
785 if (utc) universalTime(date.getTimeValue(), gt);
786 else localTime(date.getTimeValue(), gt);
790 // Compound functions that can set one, two, three or four fields at once.
792 // There are two flavours: those that work with localtime and those that do so
793 // in UTC (except for setYear, which has no UTC version). We avoid duplication
794 // by passing an extra parameter "utc": if true, we use the UTC conversion
795 // functions, otherwise the localtime ones.
797 // All non-UTC functions take dates/times to be in local time and their return
798 // value is the new date in UTC milliseconds after 1/1/1970 00:00 UTC.
801 /// \brief Date.setFullYear(year[,month[,day]])
803 /// If the month and date parameters are specified, they are set in local time.
804 /// year: A four-digit number specifying a year.
805 /// Two-digit numbers do not represent four-digit years;
806 /// for example, 99 is not the year 1999, but the year 99.
807 /// month: An integer from 0 (January) to 11 (December). [optional]
808 /// day: An integer from 1 to 31. [optional]
810 /// If the month and/or day are omitted, they are left at their current values.
811 /// If changing the year or month results in an impossible date, it is
812 /// normalised: 29 Feb becomes 1 Mar, 31 April becomes 1 May etc.
814 // When changing the year/month/date from a date in Daylight Saving Time to a
815 // date not in DST or vice versa, with setYear and setFullYear the hour of day
816 // remains the same in *local time* not in UTC.
817 // So if a date object is set to midnight in January and you change the date
818 // to June, it will still be midnight localtime.
820 // When using setUTCFullYear instead, the time of day remains the same *in UTC*
821 // so, in the northern hemisphere, changing midnight from Jan to June gives
822 // 01:00 localtime.
824 // Heaven knows what happens if it is 1.30 localtime and you change the date
825 // to the day the clocks go forward.
826 template<bool utc>
827 as_value
828 date_setfullyear(const fn_call& fn)
830 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
832 if (fn.nargs < 1) {
833 IF_VERBOSE_ASCODING_ERRORS(
834 log_aserror(_("Date.setFullYear needs one argument"));
836 date->setTimeValue(NaN);
838 else if (rogue_date_args(fn, 3) != 0.0) {
839 date->setTimeValue(NaN);
841 else {
842 GnashTime gt;
843 dateToGnashTime(*date, gt, utc);
844 gt.year = toInt(fn.arg(0), getVM(fn)) - 1900;
845 if (fn.nargs >= 2) gt.month = toInt(fn.arg(1), getVM(fn));
846 if (fn.nargs >= 3) gt.monthday = toInt(fn.arg(2), getVM(fn));
847 gnashTimeToDate(gt, *date, utc);
849 return as_value(date->getTimeValue());
852 /// \brief Date.setYear(year[,month[,day]])
853 /// if year is 0-99, this means 1900-1999, otherwise it is a Gregorian year.
854 /// Negative values for year set negative years (years BC).
855 /// This means that you cannot set a Date to the years 0-99 AD using setYear().
856 /// "month" is 0 - 11 and day 1 - 31 as usual.
858 /// If month and/or day are omitted, they are left unchanged except:
859 /// - when the day is 29, 30 or 31 and changing to a month that has less days,
860 /// the month gets set to the following one and the date should wrap,
861 /// becoming 1, 2 or 3.
862 /// - when changing from 29 Feb in a leap year to a non-leap year, the date
863 /// should end up at March 1st of the same year.
865 // There is no setUTCYear() function.
866 as_value
867 date_setYear(const fn_call& fn)
869 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
871 // assert(fn.nargs == 1);
872 if (fn.nargs < 1) {
873 IF_VERBOSE_ASCODING_ERRORS(
874 log_aserror(_("Date.setYear needs one argument"));
876 date->setTimeValue(NaN);
878 else if (rogue_date_args(fn, 3) != 0.0) {
879 date->setTimeValue(NaN);
881 else {
882 GnashTime gt;
884 dateToGnashTime(*date, gt, false);
886 // TODO: Should truncation be done before or after subtracting 1900?
888 double year = toNumber(fn.arg(0), getVM(fn));
889 if (year < 0 || year > 100) year -= 1900;
891 truncateDouble(gt.year, year);
893 if (fn.nargs >= 2) gt.month = toInt(fn.arg(1), getVM(fn));
894 if (fn.nargs >= 3) gt.monthday = toInt(fn.arg(2), getVM(fn));
895 if (fn.nargs > 3) {
896 IF_VERBOSE_ASCODING_ERRORS(
897 log_aserror(_("Date.setYear was called with more than three "
898 "arguments"));
901 gnashTimeToDate(gt, *date, false); // utc=false: use localtime
903 return as_value(date->getTimeValue());
906 /// \brief Date.setMonth(month[,day])
907 /// sets the month (0-11) and day-of-month (1-31) components of a Date.
909 /// If the day argument is omitted, the new month has less days than the
910 /// old one and the new day is beyond the end of the month,
911 /// the day should be set to the last day of the specified month.
912 /// This implementation currently wraps it into the next month, which is wrong.
914 // If no arguments are given or if an invalid type is given,
915 // the commercial player sets the month to January in the same year.
916 // Only if the second parameter is present and has a non-numeric value,
917 // the result is NaN.
918 // We do not do the same because it's a bugger to code.
919 template<bool utc>
920 as_value
921 date_setmonth(const fn_call& fn)
923 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
925 // assert(fn.nargs >= 1 && fn.nargs <= 2);
926 if (fn.nargs < 1) {
927 IF_VERBOSE_ASCODING_ERRORS(
928 log_aserror(_("Date.set%sMonth needs one argument"),
929 utc ? "UTC" : "");
931 date->setTimeValue(NaN);
933 else if (rogue_date_args(fn, 2) != 0.0) {
934 date->setTimeValue(NaN);
936 else {
938 GnashTime gt;
940 dateToGnashTime(*date, gt, utc);
942 // It seems odd, but FlashPlayer takes all bad month values to mean
943 // January
944 double monthvalue = toNumber(fn.arg(0), getVM(fn));
945 if (isNaN(monthvalue) || isInf(monthvalue)) monthvalue = 0.0;
946 truncateDouble(gt.month, monthvalue);
948 // If the day-of-month value is invalid instead, the result is NaN.
949 if (fn.nargs >= 2) {
950 double mdayvalue = toNumber(fn.arg(1), getVM(fn));
951 if (isNaN(mdayvalue) || isInf(mdayvalue)) {
952 date->setTimeValue(NaN);
953 return as_value(date->getTimeValue());
955 else {
956 truncateDouble(gt.monthday, mdayvalue);
959 if (fn.nargs > 2) {
960 IF_VERBOSE_ASCODING_ERRORS(
961 log_aserror(_("Date.set%sMonth was called with more than three "
962 "arguments"), utc ? "UTC" : "");
965 gnashTimeToDate(gt, *date, utc);
967 return as_value(date->getTimeValue());
970 /// \brief Date.setDate(day)
971 /// Set the day-of-month (1-31) for a Date object.
972 /// If the day-of-month is beyond the end of the current month, it wraps into
973 /// the first days of the following month. This also happens if you set the
974 /// day > 31. Example: setting the 35th in January results in Feb 4th.
975 template<bool utc>
976 as_value
977 date_setDate(const fn_call& fn)
979 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
981 if (fn.nargs < 1) {
982 IF_VERBOSE_ASCODING_ERRORS(
983 log_aserror(_("Date.set%sDate needs one argument"), utc ? "UTC" : "");
985 date->setTimeValue(NaN); // Is what FlashPlayer sets
986 } else if (rogue_date_args(fn, 1) != 0.0) {
987 date->setTimeValue(NaN);
988 } else {
989 GnashTime gt;
991 dateToGnashTime(*date, gt, utc);
992 gt.monthday = toInt(fn.arg(0), getVM(fn));
993 gnashTimeToDate(gt, *date, utc);
995 if (fn.nargs > 1) {
996 IF_VERBOSE_ASCODING_ERRORS(
997 log_aserror(_("Date.set%sDate was called with more than one argument"),
998 utc ? "UTC" : "");
1001 return as_value(date->getTimeValue());
1004 /// \brief Date.setHours(hour[,min[,sec[,millisec]]])
1005 /// change the time-of-day in a Date object. If optional fields are omitted,
1006 /// their values in the Date object are left the same as they were.
1008 /// If hour>23 or min/sec>59, these are accepted and wrap into the following
1009 /// minute, hour or calendar day.
1010 /// Similarly, negative values carry you back into the previous minute/hour/day.
1012 /// Only the integer part of millisec is used, truncating it, not rounding it.
1013 /// The only way to set a fractional number of milliseconds is to use
1014 /// setTime(n) or call the constructor with one argument.
1015 template<bool utc>
1016 as_value
1017 date_setHours(const fn_call& fn)
1019 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
1021 // assert(fn.nargs >= 1 && fn.nargs <= 4);
1022 if (fn.nargs < 1) {
1023 IF_VERBOSE_ASCODING_ERRORS(
1024 log_aserror(_("Date.set%sHours needs one argument"),
1025 utc ? "UTC" : "");
1027 date->setTimeValue(NaN); // Is what FlashPlayer sets
1029 else if (rogue_date_args(fn, 4) != 0.0) {
1030 date->setTimeValue(NaN);
1032 else {
1034 GnashTime gt;
1036 dateToGnashTime(*date, gt, utc);
1037 gt.hour = toInt(fn.arg(0), getVM(fn));
1038 if (fn.nargs >= 2) gt.minute = toInt(fn.arg(1), getVM(fn));
1039 if (fn.nargs >= 3) gt.second = toInt(fn.arg(2), getVM(fn));
1040 if (fn.nargs >= 4) gt.millisecond = toInt(fn.arg(3), getVM(fn));
1041 if (fn.nargs > 4) {
1042 IF_VERBOSE_ASCODING_ERRORS(
1043 log_aserror(_("Date.set%sHours was called with more than "
1044 "four arguments"), utc ? "UTC" : "");
1048 gnashTimeToDate(gt, *date, utc);
1050 return as_value(date->getTimeValue());
1053 /// \brief Date.setMinutes(minutes[,secs[,millisecs]])
1054 /// change the time-of-day in a Date object. If optional fields are omitted,
1055 /// their values in the Date object are left the same as they were.
1057 /// If min/sec>59, these are accepted and wrap into the following minute, hour
1058 /// or calendar day.
1059 /// Similarly, negative values carry you back into the previous minute/hour/day.
1060 template<bool utc>
1061 as_value
1062 date_setMinutes(const fn_call& fn)
1064 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
1066 //assert(fn.nargs >= 1 && fn.nargs <= 3);
1067 if (fn.nargs < 1) {
1068 IF_VERBOSE_ASCODING_ERRORS(
1069 log_aserror(_("Date.set%sMinutes needs one argument"),
1070 utc ? "UTC" : "");
1072 date->setTimeValue(NaN); // FlashPlayer instead leaves the date set to
1073 // a random value such as 9th December 2077 BC
1075 else if (rogue_date_args(fn, 3) != 0.0) {
1076 date->setTimeValue(NaN);
1078 else {
1079 GnashTime gt;
1081 dateToGnashTime(*date, gt, utc);
1082 gt.minute = toInt(fn.arg(0), getVM(fn));
1083 if (fn.nargs >= 2) gt.second = toInt(fn.arg(1), getVM(fn));
1084 if (fn.nargs >= 3) gt.millisecond = toInt(fn.arg(2), getVM(fn));
1085 if (fn.nargs > 3) {
1086 IF_VERBOSE_ASCODING_ERRORS(
1087 log_aserror(_("Date.set%sMinutes was called with more than "
1088 "three arguments"), utc ? "UTC" : "");
1091 gnashTimeToDate(gt, *date, utc);
1093 return as_value(date->getTimeValue());
1096 /// \brief Date.setSeconds(secs[,millisecs])
1097 /// set the "seconds" component in a date object.
1099 /// Values <0, >59 for secs or >999 for millisecs take the date back to the
1100 /// previous minute (or hour or calendar day) or on to the following ones.
1101 template<bool utc>
1102 as_value
1103 date_setSeconds(const fn_call& fn)
1105 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
1107 // assert(fn.nargs >= 1 && fn.nargs <= 2);
1108 if (fn.nargs < 1) {
1109 IF_VERBOSE_ASCODING_ERRORS(
1110 log_aserror(_("Date.set%sSeconds needs one argument"),
1111 utc ? "UTC" : "");
1113 date->setTimeValue(NaN); // Same as commercial player
1115 else if (rogue_date_args(fn, 2) != 0.0) {
1116 date->setTimeValue(NaN);
1118 else {
1119 // We *could* set seconds [and milliseconds] without breaking the
1120 // structure out and reasembling it. We do it the same way as the
1121 // rest for simplicity and in case anyone's date routines ever
1122 // take account of leap seconds.
1123 GnashTime gt;
1125 dateToGnashTime(*date, gt, utc);
1126 gt.second = toInt(fn.arg(0), getVM(fn));
1127 if (fn.nargs >= 2) gt.millisecond = toInt(fn.arg(1), getVM(fn));
1128 if (fn.nargs > 2) {
1129 IF_VERBOSE_ASCODING_ERRORS(
1130 log_aserror(_("Date.set%sMinutes was called with more than "
1131 "three arguments"), utc ? "UTC" : "");
1135 gnashTimeToDate(gt, *date, utc);
1137 return as_value(date->getTimeValue());
1140 template<bool utc>
1141 as_value
1142 date_setMilliseconds(const fn_call& fn)
1144 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
1146 if (fn.nargs < 1) {
1147 IF_VERBOSE_ASCODING_ERRORS(
1148 log_aserror(_("Date.set%sMilliseconds needs one argument"),
1149 utc ? "UTC" : "");
1151 date->setTimeValue(NaN);
1153 else if (rogue_date_args(fn, 1) != 0.0) {
1154 date->setTimeValue(NaN);
1156 else {
1158 GnashTime gt;
1160 dateToGnashTime(*date, gt, utc);
1161 truncateDouble(gt.millisecond, toNumber(fn.arg(0), getVM(fn)));
1163 if (fn.nargs > 1) {
1164 IF_VERBOSE_ASCODING_ERRORS(
1165 log_aserror(_("Date.setMilliseconds was called with more "
1166 "than one argument"), utc ? "UTC" : "");
1170 // This is both setMilliseconds and setUTCMilliseconds.
1171 // Use utc to avoid needless worrying about timezones.
1172 gnashTimeToDate(gt, *date, utc);
1175 return as_value(date->getTimeValue());
1179 /// \brief Date.toString()
1180 /// convert a Date to a printable string.
1181 /// The format is "Thu Jan 1 00:00:00 GMT+0000 1970" and it is displayed in
1182 /// local time.
1183 as_value
1184 date_tostring(const fn_call& fn)
1186 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
1187 return as_value(date->toString());
1190 // Date.UTC(year:Number,month[,day[,hour[,minute[,second[,millisecond]]]]]
1192 // Convert a UTC date/time specification to number of milliseconds since
1193 // 1 Jan 1970 00:00 UTC.
1195 // unspecified optional arguments default to 0 except for day-of-month,
1196 // which defaults to 1.
1198 // year is a Gregorian year; special values 0 to 99 mean 1900 to 1999 so it is
1199 // impossible to specify the year 55 AD using this interface.
1201 // Any fractional part in the number of milliseconds is ignored (truncated)
1203 // If 0 or 1 argument are passed, the result is the "undefined" value.
1205 // This probably doesn't handle exceptional cases such as NaNs and infinities
1206 // the same as the commercial player. What that does is:
1207 // - if any argument is NaN, the result is NaN
1208 // - if one or more of the optional arguments are +Infinity,
1209 // the result is +Infinity
1210 // - if one or more of the optional arguments are -Infinity,
1211 // the result is -Infinity
1212 // - if both +Infinity and -Infinity are present in the optional args,
1213 // or if one of the first two arguments is not numeric (including Inf),
1214 // the result is NaN.
1215 // Actually, given a first parameter of Infinity,-Infinity or NaN,
1216 // it returns -6.77681005679712e+19 but that's just crazy.
1218 // We test for < 2 parameters and return undefined, but given any other
1219 // non-numeric arguments we give NaN.
1220 as_value
1221 date_UTC(const fn_call& fn) {
1223 GnashTime gt; // Date structure for values down to milliseconds
1225 if (fn.nargs < 2) {
1226 IF_VERBOSE_ASCODING_ERRORS(
1227 log_aserror(_("Date.UTC needs one argument"));
1229 return as_value(); // undefined
1232 double result;
1234 // Check for presence of NaNs and Infinities in the arguments
1235 // and return the appropriate value if so.
1236 if ( (result = rogue_date_args(fn, 7)) != 0.0) {
1237 return as_value(NaN);
1240 // Preset default values
1241 // Year and month are always given explicitly
1242 gt.monthday = 1;
1243 gt.hour = 0;
1244 gt.minute = 0;
1245 gt.second = 0;
1246 gt.millisecond = 0;
1248 switch (fn.nargs) {
1249 default: // More than 7
1250 IF_VERBOSE_ASCODING_ERRORS(
1251 log_aserror(_("Date.UTC was called with more than 7 arguments"));
1253 case 7:
1254 // millisecs is double, but fractions of millisecs are ignored.
1255 gt.millisecond = toInt(fn.arg(6), getVM(fn));
1256 case 6:
1257 gt.second = toInt(fn.arg(5), getVM(fn));
1258 case 5:
1259 gt.minute = toInt(fn.arg(4), getVM(fn));
1260 case 4:
1261 gt.hour = toInt(fn.arg(3), getVM(fn));
1262 case 3:
1263 gt.monthday = toInt(fn.arg(2), getVM(fn));
1264 case 2: // these last two are always performed
1265 gt.month = toInt(fn.arg(1), getVM(fn));
1267 boost::int32_t year = 0;
1268 truncateDouble(year, toNumber(fn.arg(0), getVM(fn)));
1269 if (year < 100) gt.year = year;
1270 else gt.year = year - 1900;
1274 result = makeTimeValue(gt);
1275 return as_value(result);
1278 // Auxillary function checks for Infinities and NaN in a function's args and
1279 // returns 0.0 if there are none,
1280 // plus (or minus) infinity if positive (or negative) infinites are present,
1281 // NaN is there are NaNs present, or a mixture of positive and negative infs.
1282 double
1283 rogue_date_args(const fn_call& fn, unsigned maxargs)
1285 // Two flags: Did we find any +Infinity (or -Infinity) values in the
1286 // argument list? If so, "infinity" must be set to the kind that we
1287 // found.
1288 bool plusinf = false;
1289 bool minusinf = false;
1290 double infinity = 0.0; // The kind of infinity we found.
1291 // 0.0 == none yet.
1293 // Only check the present parameters, up to the stated maximum number
1294 if (fn.nargs < maxargs) maxargs = fn.nargs;
1296 for (unsigned int i = 0; i < maxargs; i++) {
1297 double arg = toNumber(fn.arg(i), getVM(fn));
1299 if (isNaN(arg)) return(NaN);
1301 if (isInf(arg)) {
1302 if (arg > 0) { // Plus infinity
1303 plusinf = true;
1305 else { // Minus infinity
1306 minusinf = true;
1308 // Remember the kind of infinity we found
1309 infinity = arg;
1312 // If both kinds of infinity were present in the args,
1313 // the result is NaN.
1314 if (plusinf && minusinf) return(NaN);
1316 // If only one kind of infinity was in the args, return that.
1317 if (plusinf || minusinf) return(infinity);
1319 // Otherwise indicate that the function arguments contained
1320 // no rogue values
1321 return(0.0);
1324 /// \brief Date.getTime() returns the number of milliseconds since midnight
1325 /// January 1, 1970 00:00 UTC, for a Date. The return value can be a fractional
1326 /// number of milliseconds.
1327 as_value date_getTime(const fn_call& fn)
1329 Date_as* date = ensure<ThisIsNative<Date_as> >(fn);
1330 return as_value(date->getTimeValue());
1335 /// Date conversion functions
1339 // Converts a time struct into a swf timestamp. Similar to
1340 // mktime, but not limited by the size of time_t. The mathematical
1341 // algorithm looks nicer, but does not cope with large dates.
1342 // Bumping up the int size or using doubles more might help - I
1343 // haven't really looked at it.
1344 // The first algorithm appears to mimic flash behaviour for
1345 // all dates, though it's a bit ugly.
1346 double
1347 makeTimeValue(GnashTime& t)
1350 // First, adjust years to deal with strange month
1351 // values.
1353 // Add or substract more than 12 months from the year
1354 // and adjust months to a valid value.
1355 t.year += t.month / 12;
1356 t.month %= 12;
1358 // Any negative remainder rolls back to the previous year.
1359 if (t.month < 0) {
1360 --t.year;
1361 t.month += 12;
1364 // Now work out the years from 1970 in days.
1366 // Use a temporary 1970-based year for clarity.
1367 const boost::int32_t ouryear = t.year - 70;
1369 // Count the leap years between 1970-1-1 and the beginning of our year.
1370 // 1970 - 1972: no leap years
1371 // 1970 - 1968: one leap year
1372 // Adding one less than the required year gives this behaviour.
1373 boost::int32_t day = countLeapYears(ouryear + 1969) - countLeapYears(1970);
1374 day += ouryear * 365;
1376 /// The year 0 was a leap year, but countLeapYears won't calculate it.
1377 if (ouryear <= -1970) --day;
1379 // Add days for each month. Month must be 0 - 11;
1380 for (int i = 0; i < t.month; i++)
1382 assert (t.month < 12);
1383 day += daysInMonth[isLeapYear(t.year)][i];
1386 // Add the days of the month
1387 day += t.monthday - 1;
1389 /// Work out the timestamp
1390 double ret = day * 86400000.0;
1391 ret += t.hour * 3600000.0;
1392 ret += t.minute * 60000.0;
1393 ret += t.second * 1000.0;
1394 ret += t.millisecond;
1395 return ret;
1399 // The brute force way of converting days into years since the epoch.
1400 // This also reduces the number of days accurately. Its disadvantage is,
1401 // of course, that it iterates; its advantage that it's always correct.
1402 boost::int32_t
1403 getYearBruteForce(boost::int32_t& days)
1405 boost::int32_t year = 1970;
1407 // Handle 400-year blocks - which always have the same
1408 // number of days (14097) - to cut down on iterations.
1409 year += (days / 146097) * 400;
1410 days %= 146097;
1412 if (days >= 0)
1414 for (;;)
1416 bool isleap = isLeapYear(year - 1900);
1417 if (days < (isleap ? 366 : 365)) break;
1418 year++;
1419 days -= isleap ? 366 : 365;
1422 else
1426 --year;
1427 bool isleap = isLeapYear(year - 1900);
1428 days += isleap ? 366 : 365;
1429 } while (days < 0);
1431 return year - 1900;
1435 void
1436 fillGnashTime(double t, GnashTime& gt)
1439 // Calculate local time by adding offset from UTC in
1440 // milliseconds to time value. Offset is in minutes.
1441 double time = t + gt.timeZoneOffset * 60000;
1443 gt.millisecond = std::fmod(time, 1000.0);
1444 time /= 1000.0;
1446 // Get the sub-day part of the time, if any and reduce time
1447 // to number of complete days.
1448 // This is a safe cast.
1449 boost::int32_t remainder =
1450 static_cast<boost::int32_t>(std::fmod(time, 86400.0));
1452 // This could overflow.
1453 boost::int32_t days;
1454 truncateDouble(days, time / 86400.0); // complete days
1456 gt.second = remainder % 60;
1457 remainder /= 60;
1459 gt.minute = remainder % 60;
1460 remainder /= 60;
1462 gt.hour = remainder % 24;
1464 if (time < 0)
1466 if (gt.millisecond < 0) { gt.millisecond += 1000; --gt.second; }
1467 if (gt.second < 0) { gt.second += 60; --gt.minute; }
1468 if (gt.minute < 0) { gt.minute += 60; --gt.hour; }
1469 if (gt.hour < 0) { gt.hour += 24; --days; }
1472 if (days >= -4) gt.weekday = (days + 4) % 7;
1473 else gt.weekday = 6 - (((-5) - days ) % 7);
1475 // default, brute force:
1476 gt.year = getYearBruteForce(days);
1478 gt.month = 0;
1479 for (int i = 0; i < 12; ++i)
1481 if (days - daysInMonth[isLeapYear(gt.year)][i] < 0)
1483 gt.month = i;
1484 break;
1486 days -= daysInMonth[isLeapYear(gt.year)][i];
1489 gt.monthday = days + 1;
1493 } // anonymous namespace
1494 } // gnash namespace