1 // Date_as.cpp: ActionScript class for date and time, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free SoftwareFoundation, Inc
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.
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
52 // * OS portability is done by libboost, not here;
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).
68 #include "GnashNumeric.h"
71 #include "Global_as.h"
72 #include "GnashException.h"
73 #include "NativeFunction.h"
74 #include "ClockTime.h"
77 #include "namedStrings.h"
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.
89 // A time struct to contain the broken-down time.
92 std::int32_t millisecond
;
96 std::int32_t monthday
;
100 std::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
)
166 Date_as::toString() const
168 const char* monthname
[12] = { "Jan", "Feb", "Mar",
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
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();
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
);
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);
282 // Helpers for calendar algorithms
284 isLeapYear(std::int32_t year
)
286 return !((year
+ 1900) % 400) ||
287 ( !((year
+ 1900) % 4) && ((year
+ 1900) % 100));
292 countLeapYears(std::int32_t year
)
294 return year
/ 4 - year
/ 100 + year
/ 400;
298 /// Return the broken-down time as a local time.
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
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();
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.
336 attachDateInterface(as_object
& 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")));
383 attachDateStaticInterface(as_object
& 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
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
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.
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
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();
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
425 if (( foo
= rogue_date_args(fn
, 7)) != 0.0) {
426 obj
->setRelay(new Date_as(foo
));
430 if (fn
.nargs
< 1 || fn
.arg(0).is_undefined()) {
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
))));
439 // Create a time from the supplied (at least 2) arguments.
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;
462 IF_VERBOSE_ASCODING_ERRORS(
463 log_aserror(_("Date constructor called with more than 7 "
468 // fractions of milliseconds are ignored
469 gt
.millisecond
= toInt(fn
.arg(6), getVM(fn
));
471 gt
.second
= toInt(fn
.arg(5), getVM(fn
));
473 gt
.minute
= toInt(fn
.arg(4), getVM(fn
));
475 gt
.hour
= toInt(fn
.arg(3), getVM(fn
));
477 gt
.monthday
= toInt(fn
.arg(2), getVM(fn
));
483 // The arguments are in local time: subtract the local time offset
484 // at the desired time to get UTC. This may not be completely correct
485 // due to shortcomings in the timezoneoffset calculation, but should
486 // be internally consistent.
487 double localTime
= makeTimeValue(gt
);
488 obj
->setRelay(new Date_as(
489 localTime
- clocktime::getTimeZoneOffset(localTime
) * 60000));
496 // ========= Functions to get dates in various ways ========
499 // Date.getTime() is implemented by Date.valueOf()
501 /// Return true if the date is invalid.
503 bool invalidDate(double timeValue
)
505 return (isNaN(timeValue
) || isInf(timeValue
));
508 /// Returns an element of the Date object as an as_value
510 /// An invalid date value is returned as NaN (this is probably not correct,
511 /// as the pp returns something weird in this case).
513 /// @param dateFunc The date function (either localTime or universalTime)
514 /// to use to break the time value into elements.
515 /// @param element A pointer-to-data-member of the GnashTime struct,
516 /// specifying which element to return.
517 /// @param timeValue The time value to break into elements.
518 /// @param adjustment Adjust the result by this amount (used for full year).
520 inline as_value
timeElement(T dateFunc
, std::int32_t GnashTime::* element
,
521 double timeValue
, int adjustment
= 0)
523 if (invalidDate(timeValue
)) return as_value();
525 dateFunc(timeValue
, gt
);
526 return as_value(gt
.*element
+ adjustment
);
532 /// Returns a Date's Gregorian year minus 1900 according to local time.
534 date_getYear(const fn_call
& fn
)
536 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
537 return timeElement(localTime
, &GnashTime::year
, date
->getTimeValue());
540 /// \brief Date.getFullYear
541 /// returns a Date's Gregorian year according to local time.
543 date_getFullYear(const fn_call
& fn
)
545 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
547 localTime
, &GnashTime::year
, date
->getTimeValue(), 1900);
550 /// \brief Date.getMonth
551 /// returns a Date's month in the range 0 to 11.
553 date_getMonth(const fn_call
& fn
)
555 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
556 return timeElement(localTime
, &GnashTime::month
, date
->getTimeValue());
559 /// \brief Date.getDate
560 /// returns a Date's day-of-month, from 1 to 31 according to local time.
562 date_getDate(const fn_call
& fn
)
564 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
565 return timeElement(localTime
, &GnashTime::monthday
, date
->getTimeValue());
568 /// \brief Date.getDay
569 /// returns the day of the week for a Date according to local time,
570 /// where 0 is Sunday and 6 is Saturday.
572 date_getDay(const fn_call
& fn
)
574 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
575 return timeElement(localTime
, &GnashTime::weekday
, date
->getTimeValue());
579 /// \brief Date.getHours
580 /// Returns the hour number for a Date, from 0 to 23, according to local time.
582 date_getHours(const fn_call
& fn
)
584 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
585 return timeElement(localTime
, &GnashTime::hour
, date
->getTimeValue());
588 /// \brief Date.getMinutes
589 /// returns a Date's minutes, from 0-59, according to localtime.
590 /// (Yes, some places do have a fractions of an hour's timezone offset
591 /// or daylight saving time!)
593 date_getMinutes(const fn_call
& fn
)
595 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
596 return timeElement(localTime
, &GnashTime::minute
, date
->getTimeValue());
599 /// \brief Date.getSeconds
600 /// returns a Date's seconds, from 0-59.
601 /// Localtime should be irrelevant.
603 date_getSeconds(const fn_call
& fn
)
605 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
606 return timeElement(localTime
, &GnashTime::second
, date
->getTimeValue());
609 /// \brief Date.getMilliseconds
610 /// returns a Date's millisecond component as an integer from 0 to 999.
611 /// Localtime is irrelevant!
613 // Also implements Date.getUTCMilliseconds
615 date_getMilliseconds(const fn_call
& fn
)
617 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
619 localTime
, &GnashTime::millisecond
, date
->getTimeValue());
623 // The same functions for universal time.
626 date_getUTCFullYear(const fn_call
& fn
)
628 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
629 return timeElement(universalTime
, &GnashTime::year
,
630 date
->getTimeValue(), 1900);
634 date_getUTCYear(const fn_call
& fn
)
636 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
637 return timeElement(universalTime
, &GnashTime::year
, date
->getTimeValue());
641 date_getUTCMonth(const fn_call
& fn
)
643 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
644 return timeElement(universalTime
, &GnashTime::month
, date
->getTimeValue());
648 date_getutcdate(const fn_call
& fn
)
650 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
652 universalTime
, &GnashTime::monthday
, date
->getTimeValue());
657 date_getUTCDay(const fn_call
& fn
)
659 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
661 universalTime
, &GnashTime::weekday
, date
->getTimeValue());
665 date_getUTCHours(const fn_call
& fn
)
667 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
669 universalTime
, &GnashTime::hour
, date
->getTimeValue());
673 date_getUTCMinutes(const fn_call
& fn
)
675 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
676 return timeElement(universalTime
, &GnashTime::minute
, date
->getTimeValue());
680 // Return the difference between UTC and localtime in minutes.
682 localTimeZoneOffset(double time
)
684 // This simply has to return the difference in minutes
685 // between UTC (Greenwich Mean Time, GMT) and the localtime.
686 // Obviously, this includes Daylight Saving Time if it applies.
687 return clocktime::getTimeZoneOffset(time
);
691 /// \brief Date.getTimezoneOffset
692 /// returns the difference between localtime and UTC that was in effect at the
693 /// time specified by a Date object, according to local timezone and DST.
694 /// For example, if you are in GMT+0100, the offset is -60
696 date_getTimezoneOffset(const fn_call
& fn
)
698 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
699 return as_value(-localTimeZoneOffset(date
->getTimeValue()));
704 // ========= Functions to set dates in various ways ========
709 /// sets a Date in milliseconds after January 1, 1970 00:00 UTC.
710 /// The return value is the same as the parameter.
712 /// If no arguments are passed or the first argument is undefined, the time
713 /// value is set to NaN.
715 /// Partial milliseconds are just ignored. The permissible range
716 /// is +/- 8.64+e15 (magic numbers).
718 date_setTime(const fn_call
& fn
)
720 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
722 if (fn
.nargs
< 1 || fn
.arg(0).is_undefined()) {
723 IF_VERBOSE_ASCODING_ERRORS(
724 log_aserror(_("Date.setTime needs one argument"));
726 date
->setTimeValue(NaN
);
730 const double magicMaxValue
= 8.64e+15;
731 double d
= toNumber(fn
.arg(0), getVM(fn
));
733 if (!isFinite(d
) || std::abs(d
) > magicMaxValue
) {
734 date
->setTimeValue(NaN
);
737 // Knock off the decimal part.
738 date
->setTimeValue(d
< 0 ? std::ceil(d
) : std::floor(d
));
743 IF_VERBOSE_ASCODING_ERRORS(
744 log_aserror(_("Date.setTime was called with more than one "
749 return as_value(date
->getTimeValue());
753 // Functions to set just some components of a Date.
755 // We do this by exploding the datestamp into the calendar components,
756 // setting the fields that are to be changed, then converting back.
759 // The Adobe player 9 behaves strangely. e.g., after "new date = Date(0)":
760 // date.setYear(1970); date.setMonth(1); date.setDate(29); gives Mar 1 but
761 // date.setYear(1970); date.setDate(29); date.setMonth(1); gives Feb 28
763 // We need two sets of the same functions: those that take localtime values
764 // and those that take UTC (GMT) values.
765 // Since there are a lot of them and they are hairy, we write one set that,
766 // if an additional extra parameter is passed, switch to working in UTC
767 // instead. Apart from the bottom-level conversions they are identical.
769 // If the Date object has been set to NaN previously (see date_setTime()),
770 // then all functions that set Date components are no-ops.
773 gnashTimeToDate(GnashTime
& gt
, Date_as
& date
, bool utc
)
776 if (utc
) date
.setTimeValue(makeTimeValue(gt
));
779 double localTime
= makeTimeValue(gt
);
780 date
.setTimeValue(localTime
-
781 clocktime::getTimeZoneOffset(localTime
) * 60000);
786 dateToGnashTime(Date_as
& date
, GnashTime
& gt
, bool utc
)
789 if (utc
) universalTime(date
.getTimeValue(), gt
);
790 else localTime(date
.getTimeValue(), gt
);
794 // Compound functions that can set one, two, three or four fields at once.
796 // There are two flavours: those that work with localtime and those that do so
797 // in UTC (except for setYear, which has no UTC version). We avoid duplication
798 // by passing an extra parameter "utc": if true, we use the UTC conversion
799 // functions, otherwise the localtime ones.
801 // All non-UTC functions take dates/times to be in local time and their return
802 // value is the new date in UTC milliseconds after 1/1/1970 00:00 UTC.
805 /// \brief Date.setFullYear(year[,month[,day]])
807 /// If the month and date parameters are specified, they are set in local time.
808 /// year: A four-digit number specifying a year.
809 /// Two-digit numbers do not represent four-digit years;
810 /// for example, 99 is not the year 1999, but the year 99.
811 /// month: An integer from 0 (January) to 11 (December). [optional]
812 /// day: An integer from 1 to 31. [optional]
814 /// If the month and/or day are omitted, they are left at their current values.
815 /// If changing the year or month results in an impossible date, it is
816 /// normalised: 29 Feb becomes 1 Mar, 31 April becomes 1 May etc.
818 // When changing the year/month/date from a date in Daylight Saving Time to a
819 // date not in DST or vice versa, with setYear and setFullYear the hour of day
820 // remains the same in *local time* not in UTC.
821 // So if a date object is set to midnight in January and you change the date
822 // to June, it will still be midnight localtime.
824 // When using setUTCFullYear instead, the time of day remains the same *in UTC*
825 // so, in the northern hemisphere, changing midnight from Jan to June gives
828 // Heaven knows what happens if it is 1.30 localtime and you change the date
829 // to the day the clocks go forward.
832 date_setfullyear(const fn_call
& fn
)
834 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
837 IF_VERBOSE_ASCODING_ERRORS(
838 log_aserror(_("Date.setFullYear needs one argument"));
840 date
->setTimeValue(NaN
);
842 else if (rogue_date_args(fn
, 3) != 0.0) {
843 date
->setTimeValue(NaN
);
845 else if (!isNaN(date
->getTimeValue())) {
847 dateToGnashTime(*date
, gt
, utc
);
848 gt
.year
= toInt(fn
.arg(0), getVM(fn
)) - 1900;
849 if (fn
.nargs
>= 2) gt
.month
= toInt(fn
.arg(1), getVM(fn
));
850 if (fn
.nargs
>= 3) gt
.monthday
= toInt(fn
.arg(2), getVM(fn
));
851 gnashTimeToDate(gt
, *date
, utc
);
853 return as_value(date
->getTimeValue());
856 /// \brief Date.setYear(year[,month[,day]])
857 /// if year is 0-99, this means 1900-1999, otherwise it is a Gregorian year.
858 /// Negative values for year set negative years (years BC).
859 /// This means that you cannot set a Date to the years 0-99 AD using setYear().
860 /// "month" is 0 - 11 and day 1 - 31 as usual.
862 /// If month and/or day are omitted, they are left unchanged except:
863 /// - when the day is 29, 30 or 31 and changing to a month that has less days,
864 /// the month gets set to the following one and the date should wrap,
865 /// becoming 1, 2 or 3.
866 /// - when changing from 29 Feb in a leap year to a non-leap year, the date
867 /// should end up at March 1st of the same year.
869 // There is no setUTCYear() function.
871 date_setYear(const fn_call
& fn
)
873 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
875 // assert(fn.nargs == 1);
877 IF_VERBOSE_ASCODING_ERRORS(
878 log_aserror(_("Date.setYear needs one argument"));
880 date
->setTimeValue(NaN
);
882 else if (rogue_date_args(fn
, 3) != 0.0) {
883 date
->setTimeValue(NaN
);
885 else if (!isNaN(date
->getTimeValue())) {
888 dateToGnashTime(*date
, gt
, false);
890 // TODO: Should truncation be done before or after subtracting 1900?
892 double year
= toNumber(fn
.arg(0), getVM(fn
));
893 if (year
< 0 || year
> 100) year
-= 1900;
895 truncateDouble(gt
.year
, year
);
897 if (fn
.nargs
>= 2) gt
.month
= toInt(fn
.arg(1), getVM(fn
));
898 if (fn
.nargs
>= 3) gt
.monthday
= toInt(fn
.arg(2), getVM(fn
));
900 IF_VERBOSE_ASCODING_ERRORS(
901 log_aserror(_("Date.setYear was called with more than three "
905 gnashTimeToDate(gt
, *date
, false); // utc=false: use localtime
907 return as_value(date
->getTimeValue());
910 /// \brief Date.setMonth(month[,day])
911 /// sets the month (0-11) and day-of-month (1-31) components of a Date.
913 /// If the day argument is omitted, the new month has less days than the
914 /// old one and the new day is beyond the end of the month,
915 /// the day should be set to the last day of the specified month.
916 /// This implementation currently wraps it into the next month, which is wrong.
918 // If no arguments are given or if an invalid type is given,
919 // the commercial player sets the month to January in the same year.
920 // Only if the second parameter is present and has a non-numeric value,
921 // the result is NaN.
922 // We do not do the same because it's a bugger to code.
925 date_setmonth(const fn_call
& fn
)
927 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
929 // assert(fn.nargs >= 1 && fn.nargs <= 2);
931 IF_VERBOSE_ASCODING_ERRORS(
932 log_aserror(_("Date.set%sMonth needs one argument"),
935 date
->setTimeValue(NaN
);
937 else if (rogue_date_args(fn
, 2) != 0.0) {
938 date
->setTimeValue(NaN
);
940 else if (!isNaN(date
->getTimeValue())) {
944 dateToGnashTime(*date
, gt
, utc
);
946 // It seems odd, but FlashPlayer takes all bad month values to mean
948 double monthvalue
= toNumber(fn
.arg(0), getVM(fn
));
949 if (isNaN(monthvalue
) || isInf(monthvalue
)) monthvalue
= 0.0;
950 truncateDouble(gt
.month
, monthvalue
);
952 // If the day-of-month value is invalid instead, the result is NaN.
954 double mdayvalue
= toNumber(fn
.arg(1), getVM(fn
));
955 if (isNaN(mdayvalue
) || isInf(mdayvalue
)) {
956 date
->setTimeValue(NaN
);
957 return as_value(date
->getTimeValue());
960 truncateDouble(gt
.monthday
, mdayvalue
);
964 IF_VERBOSE_ASCODING_ERRORS(
965 log_aserror(_("Date.set%sMonth was called with more than three "
966 "arguments"), utc
? "UTC" : "");
969 gnashTimeToDate(gt
, *date
, utc
);
971 return as_value(date
->getTimeValue());
974 /// \brief Date.setDate(day)
975 /// Set the day-of-month (1-31) for a Date object.
976 /// If the day-of-month is beyond the end of the current month, it wraps into
977 /// the first days of the following month. This also happens if you set the
978 /// day > 31. Example: setting the 35th in January results in Feb 4th.
981 date_setDate(const fn_call
& fn
)
983 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
986 IF_VERBOSE_ASCODING_ERRORS(
987 log_aserror(_("Date.set%sDate needs one argument"), utc
? "UTC" : "");
989 date
->setTimeValue(NaN
); // Is what FlashPlayer sets
990 } else if (rogue_date_args(fn
, 1) != 0.0) {
991 date
->setTimeValue(NaN
);
992 } else if (!isNaN(date
->getTimeValue())) {
995 dateToGnashTime(*date
, gt
, utc
);
996 gt
.monthday
= toInt(fn
.arg(0), getVM(fn
));
997 gnashTimeToDate(gt
, *date
, utc
);
1000 IF_VERBOSE_ASCODING_ERRORS(
1001 log_aserror(_("Date.set%sDate was called with more than one argument"),
1005 return as_value(date
->getTimeValue());
1008 /// \brief Date.setHours(hour[,min[,sec[,millisec]]])
1009 /// change the time-of-day in a Date object. If optional fields are omitted,
1010 /// their values in the Date object are left the same as they were.
1012 /// If hour>23 or min/sec>59, these are accepted and wrap into the following
1013 /// minute, hour or calendar day.
1014 /// Similarly, negative values carry you back into the previous minute/hour/day.
1016 /// Only the integer part of millisec is used, truncating it, not rounding it.
1017 /// The only way to set a fractional number of milliseconds is to use
1018 /// setTime(n) or call the constructor with one argument.
1021 date_setHours(const fn_call
& fn
)
1023 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
1025 // assert(fn.nargs >= 1 && fn.nargs <= 4);
1027 IF_VERBOSE_ASCODING_ERRORS(
1028 log_aserror(_("Date.set%sHours needs one argument"),
1031 date
->setTimeValue(NaN
); // Is what FlashPlayer sets
1033 else if (rogue_date_args(fn
, 4) != 0.0) {
1034 date
->setTimeValue(NaN
);
1036 else if (!isNaN(date
->getTimeValue())) {
1040 dateToGnashTime(*date
, gt
, utc
);
1041 gt
.hour
= toInt(fn
.arg(0), getVM(fn
));
1042 if (fn
.nargs
>= 2) gt
.minute
= toInt(fn
.arg(1), getVM(fn
));
1043 if (fn
.nargs
>= 3) gt
.second
= toInt(fn
.arg(2), getVM(fn
));
1044 if (fn
.nargs
>= 4) gt
.millisecond
= toInt(fn
.arg(3), getVM(fn
));
1046 IF_VERBOSE_ASCODING_ERRORS(
1047 log_aserror(_("Date.set%sHours was called with more than "
1048 "four arguments"), utc
? "UTC" : "");
1052 gnashTimeToDate(gt
, *date
, utc
);
1054 return as_value(date
->getTimeValue());
1057 /// \brief Date.setMinutes(minutes[,secs[,millisecs]])
1058 /// change the time-of-day in a Date object. If optional fields are omitted,
1059 /// their values in the Date object are left the same as they were.
1061 /// If min/sec>59, these are accepted and wrap into the following minute, hour
1062 /// or calendar day.
1063 /// Similarly, negative values carry you back into the previous minute/hour/day.
1066 date_setMinutes(const fn_call
& fn
)
1068 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
1070 //assert(fn.nargs >= 1 && fn.nargs <= 3);
1072 IF_VERBOSE_ASCODING_ERRORS(
1073 log_aserror(_("Date.set%sMinutes needs one argument"),
1076 date
->setTimeValue(NaN
); // FlashPlayer instead leaves the date set to
1077 // a random value such as 9th December 2077 BC
1079 else if (rogue_date_args(fn
, 3) != 0.0) {
1080 date
->setTimeValue(NaN
);
1082 else if (!isNaN(date
->getTimeValue())) {
1085 dateToGnashTime(*date
, gt
, utc
);
1086 gt
.minute
= toInt(fn
.arg(0), getVM(fn
));
1087 if (fn
.nargs
>= 2) gt
.second
= toInt(fn
.arg(1), getVM(fn
));
1088 if (fn
.nargs
>= 3) gt
.millisecond
= toInt(fn
.arg(2), getVM(fn
));
1090 IF_VERBOSE_ASCODING_ERRORS(
1091 log_aserror(_("Date.set%sMinutes was called with more than "
1092 "three arguments"), utc
? "UTC" : "");
1095 gnashTimeToDate(gt
, *date
, utc
);
1097 return as_value(date
->getTimeValue());
1100 /// \brief Date.setSeconds(secs[,millisecs])
1101 /// set the "seconds" component in a date object.
1103 /// Values <0, >59 for secs or >999 for millisecs take the date back to the
1104 /// previous minute (or hour or calendar day) or on to the following ones.
1107 date_setSeconds(const fn_call
& fn
)
1109 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
1111 // assert(fn.nargs >= 1 && fn.nargs <= 2);
1113 IF_VERBOSE_ASCODING_ERRORS(
1114 log_aserror(_("Date.set%sSeconds needs one argument"),
1117 date
->setTimeValue(NaN
); // Same as commercial player
1119 else if (rogue_date_args(fn
, 2) != 0.0) {
1120 date
->setTimeValue(NaN
);
1122 else if (!isNaN(date
->getTimeValue())) {
1123 // We *could* set seconds [and milliseconds] without breaking the
1124 // structure out and reasembling it. We do it the same way as the
1125 // rest for simplicity and in case anyone's date routines ever
1126 // take account of leap seconds.
1129 dateToGnashTime(*date
, gt
, utc
);
1130 gt
.second
= toInt(fn
.arg(0), getVM(fn
));
1131 if (fn
.nargs
>= 2) gt
.millisecond
= toInt(fn
.arg(1), getVM(fn
));
1133 IF_VERBOSE_ASCODING_ERRORS(
1134 log_aserror(_("Date.set%sMinutes was called with more than "
1135 "three arguments"), utc
? "UTC" : "");
1139 gnashTimeToDate(gt
, *date
, utc
);
1141 return as_value(date
->getTimeValue());
1146 date_setMilliseconds(const fn_call
& fn
)
1148 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
1151 IF_VERBOSE_ASCODING_ERRORS(
1152 log_aserror(_("Date.set%sMilliseconds needs one argument"),
1155 date
->setTimeValue(NaN
);
1157 else if (rogue_date_args(fn
, 1) != 0.0) {
1158 date
->setTimeValue(NaN
);
1160 else if (!isNaN(date
->getTimeValue())) {
1164 dateToGnashTime(*date
, gt
, utc
);
1165 truncateDouble(gt
.millisecond
, toNumber(fn
.arg(0), getVM(fn
)));
1168 IF_VERBOSE_ASCODING_ERRORS(
1169 log_aserror(_("Date.set%sMilliseconds was called with more "
1170 "than one argument"), utc
? "UTC" : "");
1174 // This is both setMilliseconds and setUTCMilliseconds.
1175 // Use utc to avoid needless worrying about timezones.
1176 gnashTimeToDate(gt
, *date
, utc
);
1179 return as_value(date
->getTimeValue());
1183 /// \brief Date.toString()
1184 /// convert a Date to a printable string.
1185 /// The format is "Thu Jan 1 00:00:00 GMT+0000 1970" and it is displayed in
1188 date_tostring(const fn_call
& fn
)
1190 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
1191 return as_value(date
->toString());
1194 // Date.UTC(year:Number,month[,day[,hour[,minute[,second[,millisecond]]]]]
1196 // Convert a UTC date/time specification to number of milliseconds since
1197 // 1 Jan 1970 00:00 UTC.
1199 // unspecified optional arguments default to 0 except for day-of-month,
1200 // which defaults to 1.
1202 // year is a Gregorian year; special values 0 to 99 mean 1900 to 1999 so it is
1203 // impossible to specify the year 55 AD using this interface.
1205 // Any fractional part in the number of milliseconds is ignored (truncated)
1207 // If 0 or 1 argument are passed, the result is the "undefined" value.
1209 // This probably doesn't handle exceptional cases such as NaNs and infinities
1210 // the same as the commercial player. What that does is:
1211 // - if any argument is NaN, the result is NaN
1212 // - if one or more of the optional arguments are +Infinity,
1213 // the result is +Infinity
1214 // - if one or more of the optional arguments are -Infinity,
1215 // the result is -Infinity
1216 // - if both +Infinity and -Infinity are present in the optional args,
1217 // or if one of the first two arguments is not numeric (including Inf),
1218 // the result is NaN.
1219 // Actually, given a first parameter of Infinity,-Infinity or NaN,
1220 // it returns -6.77681005679712e+19 but that's just crazy.
1222 // We test for < 2 parameters and return undefined, but given any other
1223 // non-numeric arguments we give NaN.
1225 date_UTC(const fn_call
& fn
) {
1227 GnashTime gt
; // Date structure for values down to milliseconds
1230 IF_VERBOSE_ASCODING_ERRORS(
1231 log_aserror(_("Date.UTC needs one argument"));
1233 return as_value(); // undefined
1238 // Check for presence of NaNs and Infinities in the arguments
1239 // and return the appropriate value if so.
1240 if ( (result
= rogue_date_args(fn
, 7)) != 0.0) {
1241 return as_value(NaN
);
1244 // Preset default values
1245 // Year and month are always given explicitly
1253 default: // More than 7
1254 IF_VERBOSE_ASCODING_ERRORS(
1255 log_aserror(_("Date.UTC was called with more than 7 arguments"));
1259 // millisecs is double, but fractions of millisecs are ignored.
1260 gt
.millisecond
= toInt(fn
.arg(6), getVM(fn
));
1262 gt
.second
= toInt(fn
.arg(5), getVM(fn
));
1264 gt
.minute
= toInt(fn
.arg(4), getVM(fn
));
1266 gt
.hour
= toInt(fn
.arg(3), getVM(fn
));
1268 gt
.monthday
= toInt(fn
.arg(2), getVM(fn
));
1269 case 2: // these last two are always performed
1270 gt
.month
= toInt(fn
.arg(1), getVM(fn
));
1272 std::int32_t year
= 0;
1273 truncateDouble(year
, toNumber(fn
.arg(0), getVM(fn
)));
1274 if (year
< 100) gt
.year
= year
;
1275 else gt
.year
= year
- 1900;
1279 result
= makeTimeValue(gt
);
1280 return as_value(result
);
1283 // Auxillary function checks for Infinities and NaN in a function's args and
1284 // returns 0.0 if there are none,
1285 // plus (or minus) infinity if positive (or negative) infinites are present,
1286 // NaN is there are NaNs present, or a mixture of positive and negative infs.
1288 rogue_date_args(const fn_call
& fn
, unsigned maxargs
)
1290 // Two flags: Did we find any +Infinity (or -Infinity) values in the
1291 // argument list? If so, "infinity" must be set to the kind that we
1293 bool plusinf
= false;
1294 bool minusinf
= false;
1295 double infinity
= 0.0; // The kind of infinity we found.
1298 // Only check the present parameters, up to the stated maximum number
1299 if (fn
.nargs
< maxargs
) maxargs
= fn
.nargs
;
1301 for (unsigned int i
= 0; i
< maxargs
; i
++) {
1302 double arg
= toNumber(fn
.arg(i
), getVM(fn
));
1304 if (isNaN(arg
)) return(NaN
);
1307 if (arg
> 0) { // Plus infinity
1310 else { // Minus infinity
1313 // Remember the kind of infinity we found
1317 // If both kinds of infinity were present in the args,
1318 // the result is NaN.
1319 if (plusinf
&& minusinf
) return(NaN
);
1321 // If only one kind of infinity was in the args, return that.
1322 if (plusinf
|| minusinf
) return(infinity
);
1324 // Otherwise indicate that the function arguments contained
1329 /// \brief Date.getTime() returns the number of milliseconds since midnight
1330 /// January 1, 1970 00:00 UTC, for a Date. The return value can be a fractional
1331 /// number of milliseconds.
1332 as_value
date_getTime(const fn_call
& fn
)
1334 Date_as
* date
= ensure
<ThisIsNative
<Date_as
> >(fn
);
1335 return as_value(date
->getTimeValue());
1340 /// Date conversion functions
1344 // Converts a time struct into a swf timestamp. Similar to
1345 // mktime, but not limited by the size of time_t. The mathematical
1346 // algorithm looks nicer, but does not cope with large dates.
1347 // Bumping up the int size or using doubles more might help - I
1348 // haven't really looked at it.
1349 // The first algorithm appears to mimic flash behaviour for
1350 // all dates, though it's a bit ugly.
1352 makeTimeValue(GnashTime
& t
)
1355 // First, adjust years to deal with strange month
1358 // Add or substract more than 12 months from the year
1359 // and adjust months to a valid value.
1360 t
.year
+= t
.month
/ 12;
1363 // Any negative remainder rolls back to the previous year.
1369 // Now work out the years from 1970 in days.
1371 // Use a temporary 1970-based year for clarity.
1372 const std::int32_t ouryear
= t
.year
- 70;
1374 // Count the leap years between 1970-1-1 and the beginning of our year.
1375 // 1970 - 1972: no leap years
1376 // 1970 - 1968: one leap year
1377 // Adding one less than the required year gives this behaviour.
1378 std::int32_t day
= countLeapYears(ouryear
+ 1969) - countLeapYears(1970);
1379 day
+= ouryear
* 365;
1381 /// The year 0 was a leap year, but countLeapYears won't calculate it.
1382 if (ouryear
<= -1970) --day
;
1384 // Add days for each month. Month must be 0 - 11;
1385 for (int i
= 0; i
< t
.month
; i
++)
1387 assert (t
.month
< 12);
1388 day
+= daysInMonth
[isLeapYear(t
.year
)][i
];
1391 // Add the days of the month
1392 day
+= t
.monthday
- 1;
1394 /// Work out the timestamp
1395 double ret
= day
* 86400000.0;
1396 ret
+= t
.hour
* 3600000.0;
1397 ret
+= t
.minute
* 60000.0;
1398 ret
+= t
.second
* 1000.0;
1399 ret
+= t
.millisecond
;
1404 // The brute force way of converting days into years since the epoch.
1405 // This also reduces the number of days accurately. Its disadvantage is,
1406 // of course, that it iterates; its advantage that it's always correct.
1408 getYearBruteForce(std::int32_t& days
)
1410 std::int32_t year
= 1970;
1412 // Handle 400-year blocks - which always have the same
1413 // number of days (14097) - to cut down on iterations.
1414 year
+= (days
/ 146097) * 400;
1421 bool isleap
= isLeapYear(year
- 1900);
1422 if (days
< (isleap
? 366 : 365)) break;
1424 days
-= isleap
? 366 : 365;
1432 bool isleap
= isLeapYear(year
- 1900);
1433 days
+= isleap
? 366 : 365;
1441 fillGnashTime(double t
, GnashTime
& gt
)
1444 // Calculate local time by adding offset from UTC in
1445 // milliseconds to time value. Offset is in minutes.
1446 double time
= t
+ gt
.timeZoneOffset
* 60000;
1448 gt
.millisecond
= std::fmod(time
, 1000.0);
1451 // Get the sub-day part of the time, if any and reduce time
1452 // to number of complete days.
1453 // This is a safe cast.
1454 std::int32_t remainder
=
1455 static_cast<std::int32_t>(std::fmod(time
, 86400.0));
1457 // This could overflow.
1459 truncateDouble(days
, time
/ 86400.0); // complete days
1461 gt
.second
= remainder
% 60;
1464 gt
.minute
= remainder
% 60;
1467 gt
.hour
= remainder
% 24;
1471 if (gt
.millisecond
< 0) { gt
.millisecond
+= 1000; --gt
.second
; }
1472 if (gt
.second
< 0) { gt
.second
+= 60; --gt
.minute
; }
1473 if (gt
.minute
< 0) { gt
.minute
+= 60; --gt
.hour
; }
1474 if (gt
.hour
< 0) { gt
.hour
+= 24; --days
; }
1477 if (days
>= -4) gt
.weekday
= (days
+ 4) % 7;
1478 else gt
.weekday
= 6 - (((-5) - days
) % 7);
1480 // default, brute force:
1481 gt
.year
= getYearBruteForce(days
);
1484 for (int i
= 0; i
< 12; ++i
)
1486 if (days
- daysInMonth
[isLeapYear(gt
.year
)][i
] < 0)
1491 days
-= daysInMonth
[isLeapYear(gt
.year
)][i
];
1494 gt
.monthday
= days
+ 1;
1498 } // anonymous namespace
1499 } // gnash namespace