[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] gnash ChangeLog server/asobj/Date.cpp
From: |
Benjamin Wolsey |
Subject: |
[Gnash-commit] gnash ChangeLog server/asobj/Date.cpp |
Date: |
Thu, 03 Apr 2008 10:41:28 +0000 |
CVSROOT: /sources/gnash
Module name: gnash
Changes by: Benjamin Wolsey <bwy> 08/04/03 10:41:28
Modified files:
. : ChangeLog
server/asobj : Date.cpp
Log message:
* server/asobj/Date.cpp: add several algorithms for working out
the
year from the epoch day; the most accurate but slowest is
enabled.
Comments explaining the advantages and disadvantages of the
implementations. Fixes most date tests.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&r1=1.6168&r2=1.6169
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/Date.cpp?cvsroot=gnash&r1=1.57&r2=1.58
Patches:
Index: ChangeLog
===================================================================
RCS file: /sources/gnash/gnash/ChangeLog,v
retrieving revision 1.6168
retrieving revision 1.6169
diff -u -b -r1.6168 -r1.6169
--- ChangeLog 3 Apr 2008 10:34:49 -0000 1.6168
+++ ChangeLog 3 Apr 2008 10:41:27 -0000 1.6169
@@ -1,3 +1,10 @@
+2008-04-03 Benjamin Wolsey <address@hidden>
+
+ * server/asobj/Date.cpp: add several algorithms for working out the
+ year from the epoch day; the most accurate but slowest is enabled.
+ Comments explaining the advantages and disadvantages of the
+ implementations. Fixes most date tests.
+
2008-04-03 Sandro Santilli <address@hidden>
* testsuite/actionscript.all/Object.as: test that null is a valid
Index: server/asobj/Date.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/Date.cpp,v
retrieving revision 1.57
retrieving revision 1.58
diff -u -b -r1.57 -r1.58
--- server/asobj/Date.cpp 2 Apr 2008 21:13:58 -0000 1.57
+++ server/asobj/Date.cpp 3 Apr 2008 10:41:28 -0000 1.58
@@ -21,19 +21,17 @@
//
// Implements methods of the ActionScript "Date" class for Gnash
//
-// TODO:
-// What does Flash setTime() do/return if you hand it 0 parameters?
+// TODO: What does Flash setTime() do/return if you hand it 0 parameters?
//
-// BUGS:
// Flash player handles a huge range of dates, including
-// thousands of years BC, but the localtime code here only works for
-// valid Unix dates Dec 13 20:45:52 1901 - Jan 19 03:14:07 2038.
-// Our UTC code could be hacked into working outside this range but
-// this is not worth doing unless we also implement full-range localtime
-// operations too.
+// thousands of years BC. The timestamp value is correspondingly
+// large: it is a double, which has a minimum size of 8 bytes
+// in the C++ standard. Methods provided by ctime and sys/time.h
+// generally rely on time_t whose size varies according to platform.
+// It is not big enough to deal with all valid flash timestamps,
+// so this class uses its own methods to convert to and from
+// a time struct and the time stamp.
//
-// We probably get negative datestamps (1901-1969) wrong sometimes but
-// don't really care that much.
//
// FEATURES:
// Flash Player does not seem to respect TZ or the zoneinfo database;
@@ -43,36 +41,29 @@
// Flash player does bizarre things for some argument combinations,
// returning datestamps of /6.*e+19 We don't bother doing this...
//
-// It may be useful to convert this to use libboost's date_time stuff
-// http://www.boost.org/doc/html/date_time.html
+// Boost date-time handles a larger range of correct dates than
+// the usual C and system functions. However, it is still limited
+// to POSIX to 1 Jan 1400 - 31 Dec 9999 and will not handle
+// dates at all outside this range. Flash isn't really that
+// bothered by correctness; rather, it needs to handle a vast
+// range of dates consistently. See
http://www.boost.org/doc/html/date_time.html
+//
// Pros:
+//
// * OS portability is done by libboost, not here;
-// * it extends correct date handling from POSIX to 1 Jan 1400 - 31 Dec 9999
+//
// Cons:
-// * it doesn't handle fractions of milliseconds (and who cares?);
-// * mapping between boost's coherent date_time methods and Flash's
-// ideosyncratic ones to implement this class' methods is more tricky;
-// * it brings the need to handle all boundary cases and exceptions
+//
+// * It doesn't handle fractions of milliseconds (and who cares?);
+// * Mapping between boost's coherent date_time methods and Flash's
+// idiosyncratic ones to implement this class' methods is more tricky;
+// * It brings the need to handle all boundary cases and exceptions
// explicitly (e.g. mapping of 38 Nov to 8 Dec, mapping negative
// month/day-of-month/hours/min/secs/millisecs into the previous
// year/month/day/hour/min/sec and so on).
-//
-// To probe FlashPlayer functionality put something like:
-// class Test {
-// }
-//
-// static function main(mc) {
-// var now = new Date();
-// var s:String;
-// s = now.toString(); // or whatever
-// _root.createTextField("tf",0,0,0,320,200);
-// _root.tf.text = now.toString(); // output in flash player
-// trace(s); // output in gnash -v
-// }
-// }
-// in test.as, compile with
-// mtasc -swf test.swf -main -header 320:200:10 test.as
-// and get someone to open test.swf for you in the commercial Flash Player.
+// * It doesn't do what ActionScript wants. This is the best reason
+// not to use it for time and date functions (though for portable
+// timing it might well be useful).
#ifdef HAVE_CONFIG_H
#include "gnashconfig.h"
@@ -117,13 +108,7 @@
namespace gnash {
-// We have our own UTC time converter that works with Flash timestamps
-// (double milliseconds after 1970) rather than the time-limited time_t.
-//
-// Currently, without this, setting times in UTC to a moment when DST is active
-// gets the hour and datestamp wrong, and changing the date into/out of a
-// DST period changes the UTC time of day (it shouldn't).
-
+// A time struct to contain the broken-down time.
struct GnashTime
{
boost::int32_t millisecond;
@@ -134,20 +119,23 @@
boost::int32_t weekday;
boost::int32_t month;
boost::int32_t year;
+ boost::int32_t timezoneOffset;
+};
+
+static const int daysInMonth[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
// forward declarations
static void fillGnashTime(double time, GnashTime& gt);
static double makeTimeValue(GnashTime& gt);
-static double rogue_date_args(const fn_call& fn, unsigned maxargs);
+static void getLocalTime(const double& time, GnashTime& gt);
+static void getUniversalTime(const double& time, GnashTime& gt);
+static double rogue_date_args(const fn_call& fn, unsigned maxargs);
static int minutes_east_of_gmt(struct tm &tm);
-static const int daysInMonth[2][12] = {
- {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
- {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
-};
-
// Helper macros for calendar algorithms
#define IS_LEAP_YEAR(n) ( !((n + 1900) % 400) || ( !((n + 1900) % 4) && ((n +
1900) % 100)) )
@@ -155,91 +143,6 @@
// to get the actual number
#define COUNT_LEAP_YEARS(n) ( (n - 70) / 4 - (n - 70) / 100 + (n - 70) / 400
)
-// A portable replacement for mktime (the latter uses time_t - generally 8
-// bytes on 64-bit but only 4 on 32-bit platforms). ActionScript
-// time functions need an 8 byte (double) return values. This
-// function provides that in a way consistent with the ActionScript
-// Date class.
-// Returns the number of milliseconds since the epoch from
-// a time struct.
-static double
-makeTimeValue(GnashTime& t)
-{
-
-#if 1
-
- // First, adjust years to deal with strange month
- // values.
-
- // Add or substract more than 12 months from the year
- // and adjust months to a valid value.
- t.year += t.month / 12;
- t.month %= 12;
-
- // Any negative remainder rolls back to the previous year.
- if (t.month < 0) {
- t.year--;
- t.month += 12;
- }
-
- // Now work out the years from 1970 in days.
- boost::int32_t day = t.monthday;
-
- // This works but is a bit clunky.
- if (t.year < 70) {
- day = COUNT_LEAP_YEARS(t.year - 2) + ((t.year - 70) * 365);
- // Adds an extra leap year for the year 0.
- if (t.year <= 0) day++;
- }
- else {
- day = COUNT_LEAP_YEARS(t.year + 1) + ((t.year - 70) * 365);
- }
-
- // Add days for each month. Month must be 0 - 11;
- // December is 0.
- for (int i = 0; i < t.month; i++)
- {
- assert (t.month < 12);
- day += daysInMonth[IS_LEAP_YEAR (t.year)][i];
- }
-
- // Add the days of the month
- day += t.monthday - 1;
-
- /// Work out the timestamp
- double ret = static_cast<double>(day) * 86400000.0;
- ret += t.hour * 3600000.0;
- ret += t.minute * 60000.0;
- ret += t.second * 1000.0;
- ret += t.millisecond;
- return ret;
-#else
-
- boost::int32_t d = t.monthday;
- boost::int32_t m = t.month + 1;
- boost::int32_t ya = t.year; /* Years since 1900 */
- boost::int32_t k; /* day number since 1 Jan 1900 */
-
- // For calculation, convert to a year starting on 1 March
- if (m > 2) m -= 3;
- else {
- m += 9;
- ya--;
- }
-
- k = (1461 * ya) / 4 + (153 * m + 2) / 5 + d + 58;
-
- /* K is now the day number since 1 Jan 1900.
- * Convert to minutes since 1 Jan 1970 */
- /* 25567 is the number of days from 1 Jan 1900 to 1 Jan 1970 */
- k = ((k - 25567) * 24 + t.hour) * 60 + t.minute;
-
- // Converting to double after minutes allows for +/- 4082 years with
- // 32-bit signed integers.
- return (k * 60.0 + t.second) * 1000.0 + t.millisecond;
-#endif
-}
-
static void
getLocalTime(const double& time, GnashTime& gt)
{
@@ -254,54 +157,29 @@
fillGnashTime(time, gt);
}
-// A modified version of mktime that works in localtime on struct tm
-// without you having to set tm_isdst.
-// If the real mktime() sees isdst==0 with a DST date, it sets
-// t_isdst and modifies the hour fields, but we need to set the
-// specified hour in the localtime in force at that time.
-//
-// To do this we set tm_isdst to the correct value for that moment in time
-// by doing an initial conversion of the time to find out is_dst for that
-// moment without DST, then do the real conversion.
-// This may still get things wrong around the hour when the clocks go back
-// or forth.
-//static time_t
-//makeTimeValue(struct tm *tmp)
-//{
-// struct tm tm2 = *tmp;
-// time_t t2;
-
-// tm2.tm_isdst = 0;
-// t2 = mktime(&tm2); // Convert the time without DST,
-// log_debug ("t2: %d", t2);
-
-// _localtime_r(&t2, &tm2); // find out whether DST was in force
-// tmp->tm_isdst = tm2.tm_isdst; // and apply that to the given time
-// return(mktime(tmp));
-//}
-
+// Seconds and milliseconds should be exactly the same whether in UTC
+// or in localtime, so we always use localtime.
// forward declarations
static as_value date_new(const fn_call& fn);
+static as_value date_gettime(const fn_call& fn);
+static as_value date_settime(const fn_call& fn);
+static as_value date_gettimezoneoffset(const fn_call& fn);
+static as_value date_getyear(const fn_call& fn);
+static as_value date_getfullyear(const fn_call& fn);
+static as_value date_getmonth(const fn_call& fn);
static as_value date_getdate(const fn_call& fn);
static as_value date_getday(const fn_call& fn);
-static as_value date_getfullyear(const fn_call& fn);
static as_value date_gethours(const fn_call& fn);
-static as_value date_getmilliseconds(const fn_call& fn);
static as_value date_getminutes(const fn_call& fn);
-static as_value date_getmonth(const fn_call& fn);
static as_value date_getseconds(const fn_call& fn);
-static as_value date_gettime(const fn_call& fn);
-static as_value date_gettimezoneoffset(const fn_call& fn);
+static as_value date_getmilliseconds(const fn_call& fn);
+static as_value date_getutcfullyear(const fn_call& fn);
+static as_value date_getutcmonth(const fn_call& fn);
static as_value date_getutcdate(const fn_call& fn);
static as_value date_getutcday(const fn_call& fn);
-static as_value date_getutcfullyear(const fn_call& fn);
static as_value date_getutchours(const fn_call& fn);
static as_value date_getutcminutes(const fn_call& fn);
-static as_value date_getutcmonth(const fn_call& fn);
-static as_value date_getutcseconds(const fn_call& fn);
-//static as_value date_getutcmilliseconds(const fn_call& fn); // ==
getmilliseconds
-static as_value date_getyear(const fn_call& fn);
static as_value date_setdate(const fn_call& fn);
static as_value date_setfullyear(const fn_call& fn);
static as_value date_sethours(const fn_call& fn);
@@ -309,14 +187,11 @@
static as_value date_setminutes(const fn_call& fn);
static as_value date_setmonth(const fn_call& fn);
static as_value date_setseconds(const fn_call& fn);
-static as_value date_settime(const fn_call& fn);
static as_value date_setutcdate(const fn_call& fn);
static as_value date_setutcfullyear(const fn_call& fn);
static as_value date_setutchours(const fn_call& fn);
-//static as_value date_setutcmilliseconds(const fn_call& fn); // ==
setmilliseconds
static as_value date_setutcminutes(const fn_call& fn);
static as_value date_setutcmonth(const fn_call& fn);
-static as_value date_setutcseconds(const fn_call& fn);
static as_value date_setyear(const fn_call& fn);
static as_value date_tostring(const fn_call& fn);
static as_value date_valueof(const fn_call& fn);
@@ -348,7 +223,7 @@
o.init_member("getUTCMilliseconds", new
builtin_function(date_getmilliseconds)); // same
o.init_member("getUTCMinutes", new builtin_function(date_getutcminutes));
o.init_member("getUTCMonth", new builtin_function(date_getutcmonth));
- o.init_member("getUTCSeconds", new builtin_function(date_getutcseconds));
+ o.init_member("getUTCSeconds", new builtin_function(date_getseconds));
o.init_member("getYear", new builtin_function(date_getyear));
o.init_member("setDate", new builtin_function(date_setdate));
o.init_member("setFullYear", new builtin_function(date_setfullyear));
@@ -364,7 +239,7 @@
o.init_member("setUTCMilliseconds", new
builtin_function(date_setmilliseconds)); // same
o.init_member("setUTCMinutes", new builtin_function(date_setutcminutes));
o.init_member("setUTCMonth", new builtin_function(date_setutcmonth));
- o.init_member("setUTCSeconds", new builtin_function(date_setutcseconds));
+ o.init_member("setUTCSeconds", new builtin_function(date_setseconds));
o.init_member("setYear", new builtin_function(date_setyear));
o.init_member("toString", new builtin_function(date_tostring));
o.init_member("valueOf", new builtin_function(date_valueof));
@@ -515,14 +390,14 @@
// Poo! Use old time() to get seconds only
date->value = time((time_t *) 0) * 1000.0;
#endif
- } else if (fn.nargs == 1) {
+ }
+ else if (fn.nargs == 1) {
// Set the value in milliseconds since 1970 UTC
date->value = fn.arg(0).to_number();
- } else {
+ }
+ else {
+ // Create a time from the supplied (at least 2) arguments.
GnashTime gt;
-// struct tm tm; // time constructed from localtime components
-// time_t utcsecs; // Seconds since 1970 as returned by mktime()
-// double millisecs = 0; // milliseconds if specified by caller
gt.millisecond = 0;
gt.second = 0;
@@ -537,7 +412,8 @@
// negative value is a year before 1900
// A year between 0 and 99 is the year since 1900
if (year < 100) gt.year = year;
- // A value of 100 or more is a full year.
+ // A value of 100 or more is a full year and must be
+ // converted to years since 1900
else gt.year = year - 1900;
switch (fn.nargs) {
@@ -657,8 +533,6 @@
date_get_proto(date_getutcday, getUniversalTime, weekday)
date_get_proto(date_getutchours, getUniversalTime, hour)
date_get_proto(date_getutcminutes, getUniversalTime, minute)
-date_get_proto(date_getutcseconds, getUniversalTime, second)
-// date_getutcmilliseconds is implemented by date_getmilliseconds.
// Return the difference between UTC and localtime+DST for a given date/time
@@ -803,22 +677,13 @@
// The Adobe player 9 behaves strangely. e.g., after "new date = Date(0)":
// date.setYear(1970); date.setMonth(1); date.setDate(29); gives Mar 1 but
// date.setYear(1970); date.setDate(29); date.setMonth(1); gives Feb 28
-// For now, just let mktime do as it pleases with rogue values.
-
+//
// We need two sets of the same functions: those that take localtime values
// and those that take UTC (GMT) values.
// Since there are a lot of them and they are hairy, we write one set that,
// if an additional extra parameter is passed, switch to working in UTC
// instead. Apart from the bottom-level conversions they are identical.
-// Until we find a better algorithm, we can use mktime which, by
-// experiment, seems to flip timezone at midnight, not at 2 in the morning,
-// so we use that to do year/month/day and put the unadjusted hours/mins/secs
-// in by hand. It's probably not right but it'll do for the moment.
-
-// Now the generic version of these two functions that switch according to
-// what the customer asked for
-
static void
gnashTimeToDate(GnashTime& gt, date_as_object& date, bool utc)
{
@@ -983,7 +848,7 @@
dateToGnashTime(*date, gt, utc);
// It seems odd, but FlashPlayer takes all bad month values to mean
- // January.
+ // January
double monthvalue = fn.arg(0).to_number();
if (isnan(monthvalue) || isinf(monthvalue)) monthvalue = 0.0;
gt.month = (int) monthvalue;
@@ -1211,7 +1076,6 @@
utc_proto(date)
utc_proto(hours)
utc_proto(minutes)
-utc_proto(seconds)
#undef utc_proto
@@ -1308,10 +1172,9 @@
case 2: // these last two are always performed
gt.month = fn.arg(1).to_int();
{
- int y = fn.arg(0).to_int();
- if (y < 100 && y >= 0) y += 1900;
- // y is now the Gregorian year number
- gt.year = y - 1900;
+ int year = static_cast<int>(fn.arg(0).to_number());
+ if (year < 100) gt.year = year;
+ else gt.year = year - 1900;
}
}
@@ -1399,104 +1262,243 @@
}
-/*
- * This routine converts time as follows.
- * The epoch is 00:00 Jan 1 1970 GMT.
- * The argument time is in seconds since then.
- * fillGnashTime() fills the structure pointed to by tmp as follows:
- *
- * tm_sec seconds (0-59)
- * tm_min minutes (0-59)
- * tm_hour hours (0-23)
- * tm_mday day of month (1-31)
- * tm_mon month (0-11)
- * tm_year year - 1900 (70- )
- * tm_wday weekday (0-6, Sun is 0)
- * tm_yday day of the year (0-364/5)
- * tm_isdst is daylight saving time in force? (always 0 = GMT)
- *
- * Reference: Algorithm 199 by Robert G. Tantzen
- * from "Collected Algorithms from ACM" Volume 1
- * Published by the Association for Computing Machinery, 1980.
- * See http://portal.acm.org/citation.cfm?id=390020 (pay-to-know site)
- * See also http://ftp.unicamp.br/pub/unix-c/calendars/jday-jdate.c
- *
- * When munging this, bear in mind that for calculation
- * the year starts on March the 1st (yday == 0)
- *
- * These routines have been tested exhaustively against gmtime() and
- * may work for dates outside the range that gmtime() can handle
- * (Dec 13 20:45:52 1901 - Jan 19 03:14:07 2038)
- * Dates up to 2100 should work; ones before 1900 I doubt it.
- */
+// Converts a time struct into a flash timestamp. Similar to
+// mktime, but not limited by the size of time_t. The mathematical
+// algorithm looks nicer, but does not cope with large dates.
+// Bumping up the int size or using doubles more might help - I
+// haven't really looked at it.
+// The first algorithm appears to mimic flash behaviour for
+// all dates, though it's a bit ugly.
+static double
+makeTimeValue(GnashTime& t)
+{
+
+#if 1
-// Uses milliseconds since the epoch (date->value) to fill
-// a Gnash time struct.
-static void
-fillGnashTime(double time, GnashTime& gt)
+ // First, adjust years to deal with strange month
+ // values.
+
+ // Add or substract more than 12 months from the year
+ // and adjust months to a valid value.
+ t.year += t.month / 12;
+ t.month %= 12;
+
+ // Any negative remainder rolls back to the previous year.
+ if (t.month < 0) {
+ t.year--;
+ t.month += 12;
+ }
+
+ // Now work out the years from 1970 in days.
+ boost::int32_t day = t.monthday;
+
+ // This works but is a bit clunky.
+ if (t.year < 70) {
+ day = COUNT_LEAP_YEARS(t.year - 2) + ((t.year - 70) * 365);
+ // Adds an extra leap year for the year 0.
+ if (t.year <= 0) day++;
+ }
+ else {
+ day = COUNT_LEAP_YEARS(t.year + 1) + ((t.year - 70) * 365);
+ }
+
+ // Add days for each month. Month must be 0 - 11;
+ for (int i = 0; i < t.month; i++)
+ {
+ assert (t.month < 12);
+ day += daysInMonth[IS_LEAP_YEAR (t.year)][i];
+ }
+
+ // Add the days of the month
+ day += t.monthday - 1;
+
+ /// Work out the timestamp
+ double ret = static_cast<double>(day) * 86400000.0;
+ ret += t.hour * 3600000.0;
+ ret += t.minute * 60000.0;
+ ret += t.second * 1000.0;
+ ret += t.millisecond;
+ return ret;
+#else
+
+ boost::int32_t d = t.monthday;
+ boost::int32_t m = t.month + 1;
+ boost::int32_t ya = t.year; /* Years since 1900 */
+ boost::int32_t k; /* day number since 1 Jan 1900 */
+
+ // For calculation, convert to a year starting on 1 March
+ if (m > 2) m -= 3;
+ else {
+ m += 9;
+ ya--;
+ }
+
+ k = (1461 * ya) / 4 + (153 * m + 2) / 5 + d + 58;
+
+ /* K is now the day number since 1 Jan 1900.
+ * Convert to minutes since 1 Jan 1970 */
+ /* 25567 is the number of days from 1 Jan 1900 to 1 Jan 1970 */
+ k = ((k - 25567) * 24 + t.hour) * 60 + t.minute;
+
+ // Converting to double after minutes allows for +/- 4082 years with
+ // 32-bit signed integers.
+ return (k * 60.0 + t.second) * 1000.0 + t.millisecond;
+#endif
+}
+
+/// Helper function for getYearMathematical
+static double
+daysSinceUTCForYear(double year)
+{
+ return (
+ 365 * (year - 1970) +
+ std::floor ((year - 1969) / 4.0f) -
+ std::floor ((year - 1901) / 100.0f) +
+ std::floor ((year - 1601) / 400.0f)
+ );
+}
+
+// The algorithm used by swfdec. It iterates only a small number of
+// times and is reliable to within a few milliseconds in
+// +- 100000 years. However, it appears to get the year wrong for
+// midnight on January 1 of some years (as well as a few milliseconds
+// before the end of other years, though that seems less serious).
+static boost::int32_t
+getYearMathematical(double days)
{
- int d; /* workhorse variable */
- int m, a;
- /*
- * This routine is good until year 2100.
- */
+ boost::int32_t low = std::floor ((days >= 0 ? days / 366.0 : days /
365.0)) + 1970;
+ boost::int32_t high = std::ceil ((days >= 0 ? days / 365.0 : days /
366.0)) + 1970;
- gt.millisecond = std::fmod(time, 1000.0);
- time = trunc(time / 1000.0);
- gt.second = (d = static_cast<int>(std::fmod(time, 86400.0))) % 60;
- gt.minute = (d /= 60) % 60;
- gt.hour = d / 60;
+ while (low < high) {
+ boost::int32_t pivot = (low + high) / 2;
+
+ if (daysSinceUTCForYear (pivot) <= days) {
+ if (daysSinceUTCForYear (pivot + 1) > days) {
+ return pivot;
+ }
+ else {
+ low = pivot + 1;
+ }
+ }
+ else {
+ high = pivot - 1;
+ }
+ }
- d = static_cast<int>(trunc(time / 86400.0)); /* no of days after 1 Jan 1970
*/
+ return low;
+}
- /* Make time of day positive when time is negative */
- if (gt.millisecond < 0) { gt.millisecond += 1000; gt.second--; }
- if (gt.second < 0) { gt.second += 60; gt.minute--; }
- if (gt.minute < 0) { gt.minute += 60; gt.hour--; }
- if (gt.hour < 0) { gt.hour += 24; d--; }
+// Another mathematical way of working out the year, which
+// appears to be less reliable than swfdec's way. Adjusts
+// days as well as returning the year.
+static boost::int32_t
+getYearApproximate(boost::int32_t& days)
+{
+ boost::int32_t year = ((days - 16) - COUNT_LEAP_YEARS((days - 16) / 365))
/ 365 + 70;
+ if (time < 0) year--;
+ days -= (year - 70) * 365;
+ if (year < 70) {
+ days -= COUNT_LEAP_YEARS(year - 2);
+ if (year <= 0) days--;
+ }
+ else {
+ days -= COUNT_LEAP_YEARS(year + 1);
+ }
+
+ return year;
+}
+
+// The brute force way of converting days into years since the epoch.
+// This also reduces the number of days accurately. Its disadvantage is,
+// of course, that it iterates; its advantage that it's always correct.
+boost::int32_t
+getYearBruteForce(boost::int32_t& days)
+{
+ boost::int32_t year = 1970;
+
+ // Handle 400-year blocks - which always have the same
+ // number of days (14097) - to cut down on iterations.
+ year += (days / 146097) * 400;
+ days %= 146097;
+
+ if (days >= 0)
+ {
+ for (;;)
+ {
+ bool isleap = IS_LEAP_YEAR(year - 1900);
+ if (days < (isleap ? 366 : 365)) break;
+ year++;
+ days -= isleap ? 366 : 365;
+ }
+ }
+ else
+ {
+ do
+ {
+ --year;
+ bool isleap = IS_LEAP_YEAR(year - 1900);
+ days += isleap ? 366 : 365;
+ } while (days < 0);
+ }
+ return year - 1900;
+}
- /*
- * d is the day number after 1 Jan 1970. Generate day of the week.
- * The addend is 4 mod 7 (1/1/1970 was Thursday)
- */
+void fillGnashTime (double time, GnashTime& gt)
+{
- if (d >= -4) gt.weekday = (d + 4) % 7;
- else gt.weekday = 6 - (((-5) - d ) % 7);
+ gt.millisecond = std::fmod(time, 1000.0);
+ time /= 1000.0;
- // 693902 is the days from 1st March 0000 to 1st Jan 1900
- // 25567 is the days from 1st Jan 1900 to 1st Jan 1970
- // 10957 is the days from 1st Jan 1970 to 1st Jan 2000
- // 1461 is the number of days in 4 years
+ // Get the sub-day part of the time, if any and reduce time
+ // to number of complete days.
+ boost::int32_t remainder = static_cast<boost::int32_t>(std::fmod(time,
86400.0));
+ boost::int32_t days = static_cast<boost::int32_t>(time / 86400.0); //
complete days
- int year;
+ gt.second = remainder % 60;
+ remainder /= 60;
- /* deal with years 2000-2099 */
- if ( d > 10957+59 /* 29 Feb 2000 */ ) year = 100;
- else year = 0;
+ gt.minute = remainder % 60;
+ remainder /= 60;
- year += ( d = ( 4 * ( d + 693902 + 25567 ) - 1 ) % 146097 | 3 ) / 1461;
- a = ( d = ( d % 1461 ) / 4 + 1 ) - 307;
- m = ( ( d *= 5 ) - 3 ) / 153;
- gt.monthday = ( d + 2 - 153 * m ) / 5;
+ gt.hour = remainder % 24;
- /* adjust for fact that day==0 is 1st Mar */
- if ((m += 2) > 11) {
- m -= 12; year++;
+ if (time < 0)
+ {
+ if (gt.millisecond < 0) { gt.millisecond += 1000; gt.second--; }
+ if (gt.second < 0) { gt.second += 60; gt.minute--; }
+ if (gt.minute < 0) { gt.minute += 60; gt.hour--; }
+ if (gt.hour < 0) { gt.hour += 24; days--; }
}
- if (m < 0) {
- year--;
- m += 12;
+ if (days >= -4) gt.weekday = (days + 4) % 7;
+ else gt.weekday = 6 - (((-5) - days ) % 7);
+
+ // approximate way:
+ //gt.year = getYearApproximate(days);
+
+ /// swfdec way:
+ //gt.year = getYearMathematical(static_cast<double>(days));
+ //days -= 365 * (gt.year - 1970) + COUNT_LEAP_YEARS(gt.year - 1900);
+ //gt.year -= 1900;
+
+ // brute force way:
+ gt.year = getYearBruteForce(days);
+
+ gt.month = 0;
+ for (int i = 0; i < 12; ++i)
+ {
+ if (days - daysInMonth[IS_LEAP_YEAR (gt.year)][i] < 0)
+ {
+ gt.month = i;
+ break;
}
- //tmp->tm_yday = ( a >= 0 ? a : a+365 );
- //if ( (year & 3) == 0 && a < 0) tmp->tm_++;
- gt.month = m;
- gt.year = year;
+ days -= daysInMonth[IS_LEAP_YEAR (gt.year)][i];
+ }
+
+ gt.monthday = days + 1;
- assert (gt.month >= 0);
- //assert (gt.millisecond >= 0);
}
} // end of gnash namespace