[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] gnash configure.ac ChangeLog server/asobj/Date.cpp
From: |
Martin Guy |
Subject: |
[Gnash-commit] gnash configure.ac ChangeLog server/asobj/Date.cpp |
Date: |
Sat, 03 Feb 2007 21:15:00 +0000 |
CVSROOT: /sources/gnash
Module name: gnash
Changes by: Martin Guy <martinwguy> 07/02/03 21:15:00
Modified files:
. : configure.ac ChangeLog
server/asobj : Date.cpp
Log message:
Rewrote Date.cpp to change internal representation; extra tests in
configure.ac
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/configure.ac?cvsroot=gnash&r1=1.249&r2=1.250
http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&r1=1.2239&r2=1.2240
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/Date.cpp?cvsroot=gnash&r1=1.16&r2=1.17
Patches:
Index: configure.ac
===================================================================
RCS file: /sources/gnash/gnash/configure.ac,v
retrieving revision 1.249
retrieving revision 1.250
diff -u -b -r1.249 -r1.250
--- configure.ac 2 Feb 2007 01:57:57 -0000 1.249
+++ configure.ac 3 Feb 2007 21:15:00 -0000 1.250
@@ -15,7 +15,7 @@
dnl Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
dnl
-dnl $Id: configure.ac,v 1.249 2007/02/02 01:57:57 rsavoye Exp $
+dnl $Id: configure.ac,v 1.250 2007/02/03 21:15:00 martinwguy Exp $
AC_PREREQ(2.50)
AC_INIT(gnash, cvs)
@@ -385,6 +385,8 @@
AC_CHECK_FUNCS(sysconf)
AC_CHECK_FUNCS(shmget shmat shmdt mmap)
AC_CHECK_FUNCS(gettimeofday)
+AC_CHECK_FUNCS(tzset)
+AC_CHECK_FUNCS(localtime_r)
AC_CHECK_FUNCS(strlcpy, AC_DEFINE(HAVE_STRLCPY_PROTO, [1],[Define if you have
the strlcpy prototype]))
AC_CHECK_FUNCS(strlcat, AC_DEFINE(HAVE_STRLCAT_PROTO, [1],[Define if you have
the strlcat prototype]))
dnl Look for Win32 networking stuff
Index: ChangeLog
===================================================================
RCS file: /sources/gnash/gnash/ChangeLog,v
retrieving revision 1.2239
retrieving revision 1.2240
diff -u -b -r1.2239 -r1.2240
--- ChangeLog 3 Feb 2007 12:25:38 -0000 1.2239
+++ ChangeLog 3 Feb 2007 21:15:00 -0000 1.2240
@@ -1,3 +1,10 @@
+2007-02-03 Martin Guy <address@hidden>
+
+ * server/asobj/Data.cpp: Pretty much complete rewrite of nonworking
+ module forced by change of internal representation from many little
+ date fields to milliseconds since epoch. Not fully working yet.
+ * configure.ac: include tests for localtime_r and tzset.
+
2007-02-02 Markus Gothe <address@hidden>
* server/asobj/Data.cpp: Fixed fmod issue.
Index: server/asobj/Date.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/Date.cpp,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -b -r1.16 -r1.17
--- server/asobj/Date.cpp 3 Feb 2007 12:25:38 -0000 1.16
+++ server/asobj/Date.cpp 3 Feb 2007 21:15:00 -0000 1.17
@@ -15,6 +15,28 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+// Date.cpp
+//
+// Implements methods of the ActionScript "Date" class for Gnash
+//
+// TODO:
+// implement static method Date.UTC
+//
+// To probe FlashPlayer functionality put something like:
+// class Test {
+// }
+//
+// static function main(mc) {
+// var now = new Date();
+// _root.createTextField("tf",0,0,0,320,200);
+// _root.tf.text = now.toString();
+// }
+// }
+// in test.as, compile with
+// mtasc -swf test.swf -main -header 320:200:10 test.as
+// and open test.swf in the commercial Flash Player.
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -34,10 +56,44 @@
# include <sys/time.h>
#endif
-#include <sys/timeb.h>
+// Declaration for replacement timezone functions
+// In the absence of gettimeofday() we use ftime() to get milliseconds,
+// but not for timezone offset bcos ftime's TZ stuff is unreliable.
+// For that we use tzset()/timezone if it is available
+
+#ifndef HAVE_GETTIMEOFDAY
+# include <sys/timeb.h> // for ftime()
+# ifdef HAVE_TZSET
+ extern long timezone; // for tzset()/timezone
+# endif
+#endif
namespace gnash {
+#ifdef HAVE_LOCALTIME_R
+ // Use the library functions
+# define _gmtime_r gmtime_r
+# define _localtime_r localtime_r
+#else
+// Roll our own compatible versions rather checking the ifdef everywhere
+static struct tm *
+_localtime_r(time_t *t, struct tm *tm)
+{
+ struct tm *tmp;
+ tmp = localtime(t);
+ memcpy(tm, tmp, sizeof(struct tm));
+ return(tm);
+}
+static struct tm *
+_gmtime_r(time_t *t, struct tm *tm)
+{
+ struct tm *tmp;
+ tmp = gmtime(t);
+ memcpy(tm, tmp, sizeof(struct tm));
+ return(tm);
+}
+#endif
+
// forwrd declarations
static void date_new(const fn_call& fn);
static void date_getdate(const fn_call& fn);
@@ -48,16 +104,16 @@
static void date_getminutes(const fn_call& fn);
static void date_getmonth(const fn_call& fn);
static void date_getseconds(const fn_call& fn);
-static void date_gettime(const fn_call& fn);
+// static void date_gettime(const fn_call& fn); == date_valueof()
static void date_gettimezoneoffset(const fn_call& fn);
static void date_getutcdate(const fn_call& fn);
static void date_getutcday(const fn_call& fn);
static void date_getutcfullyear(const fn_call& fn);
static void date_getutchours(const fn_call& fn);
-static void date_getutcmilliseconds(const fn_call& fn);
static void date_getutcminutes(const fn_call& fn);
static void date_getutcmonth(const fn_call& fn);
static void date_getutcseconds(const fn_call& fn);
+//static void date_getutcmilliseconds(const fn_call& fn); // == getmilliseconds
static void date_getyear(const fn_call& fn);
static void date_setdate(const fn_call& fn);
static void date_setfullyear(const fn_call& fn);
@@ -70,12 +126,14 @@
static void date_setutcdate(const fn_call& fn);
static void date_setutcfullyear(const fn_call& fn);
static void date_setutchours(const fn_call& fn);
-static void date_setutcmilliseconds(const fn_call& fn);
+//static void date_setutcmilliseconds(const fn_call& fn); // == setmilliseconds
static void date_setutcminutes(const fn_call& fn);
static void date_setutcmonth(const fn_call& fn);
static void date_setutcseconds(const fn_call& fn);
static void date_setyear(const fn_call& fn);
static void date_tostring(const fn_call& fn);
+static void date_valueof(const fn_call& fn);
+
static void date_utc(const fn_call& fn);
static as_object* getDateInterface();
static void attachDateInterface(as_object& o);
@@ -92,13 +150,13 @@
o.init_member("getMinutes", &date_getminutes);
o.init_member("getMonth", &date_getmonth);
o.init_member("getSeconds", &date_getseconds);
- o.init_member("getTime", &date_gettime);
+ o.init_member("getTime", &date_valueof);
o.init_member("getTimezoneOffset", &date_gettimezoneoffset);
o.init_member("getUTCDate", &date_getutcdate);
o.init_member("getUTCDay", &date_getutcday);
o.init_member("getUTCFullYear", &date_getutcfullyear);
o.init_member("getUTCHours", &date_getutchours);
- o.init_member("getUTCMilliseconds", &date_getutcmilliseconds);
+ o.init_member("getUTCMilliseconds", &date_getmilliseconds); // same
o.init_member("getUTCMinutes", &date_getutcminutes);
o.init_member("getUTCMonth", &date_getutcmonth);
o.init_member("getUTCSeconds", &date_getutcseconds);
@@ -114,18 +172,20 @@
o.init_member("setUTCDate", &date_setutcdate);
o.init_member("setUTCFullYear", &date_setutcfullyear);
o.init_member("setUTCHours", &date_setutchours);
- o.init_member("setUTCMilliseconds", &date_setutcmilliseconds);
+ o.init_member("setUTCMilliseconds", &date_setmilliseconds); // same
o.init_member("setUTCMinutes", &date_setutcminutes);
o.init_member("setUTCMonth", &date_setutcmonth);
o.init_member("setUTCSeconds", &date_setutcseconds);
o.init_member("setYear", &date_setyear);
o.init_member("toString", &date_tostring);
+ o.init_member("valueOf", &date_valueof);
}
static void
attachDateStaticInterface(as_object& o)
{
// TODO: This should *only* be available when SWF version is > 6
+ // Are you sure? the references say it's in from v5 -martin
o.init_member("UTC", &date_utc);
}
@@ -144,26 +204,10 @@
class date_as_object : public as_object
{
public:
- // Use double to avoid overflow
- double getTime();
- void UTC();
-
- // These store the local time
- bool isDST;
- long year,month,date,hour,minute,second,millisecond;
- int minutesEast;
- int dayWeek,dayYear;
-
- // This returns a tm struct representing this date in UTC time
- tm convertUTC();
- // This returns a tm struct representing this date in local time
- tm convertTM();
- // This sets the values in the date object to those in the tm struct
- // And ignores any values not stored in the tm struct
- void setFromTM(const tm newtime);
- // This function normalizes the time - for example, if we set the date
- // to Jan-32, 1:61:60, after normalize the time will be Feb-1, 2:02:00
- void Normalize();
+ // value is the master field and the Date's value, and holds the
+ // date as the number of milliseconds since midnight 1 Jan 1970.
+ // All other "fields" are calculated from this.
+ double value; // milliseconds UTC since the epoch
date_as_object()
:
@@ -174,88 +218,16 @@
private:
};
-// Return time as number of milliseconds since 1 Jan 1970 UTC
-double
-date_as_object::getTime()
-{
- tm result = convertTM();
- time_t count = mktime(&result);
- return double(count) * 1000.0;
-}
-
-tm
-date_as_object::convertUTC()
-{
- tm utctime;
-
- utctime.tm_sec = second;
- utctime.tm_min = minute;
- utctime.tm_hour = hour;
- utctime.tm_mday = date;
- utctime.tm_mon = month;
- utctime.tm_year = year;
- utctime.tm_wday = dayWeek;
- utctime.tm_yday = dayYear;
- utctime.tm_isdst = isDST;
-
- time_t normalized;
-
- normalized = mktime(&utctime);
-
- tm *result = gmtime(&normalized);
-
- return *result;
-}
-
-tm
-date_as_object::convertTM()
-{
- tm thistime;
-
- thistime.tm_sec = second;
- thistime.tm_min = minute;
- thistime.tm_hour = hour;
- thistime.tm_mday = date;
- thistime.tm_mon = month;
- thistime.tm_year = year;
- thistime.tm_wday = dayWeek;
- thistime.tm_yday = dayYear;
- thistime.tm_isdst = isDST;
-
- time_t normalized;
-
- normalized = mktime(&thistime);
-
- tm *result = localtime(&normalized);
-
- return *result;
-}
-
-void
-date_as_object::setFromTM(const tm newtime)
-{
- second = newtime.tm_sec;
- minute = newtime.tm_min;
- hour = newtime.tm_hour;
- date = newtime.tm_mday;
- month = newtime.tm_mon;
- year = newtime.tm_year;
- dayWeek = newtime.tm_wday;
- dayYear = newtime.tm_yday;
- isDST = newtime.tm_isdst;
-}
-
-
-void
-date_as_object::Normalize()
-{
- second += (millisecond / 1000);
- millisecond = millisecond % 1000;
-
- tm thistime = convertTM();
- time_t newtime = mktime(&thistime);
- setFromTM(*(localtime(&newtime)));
-}
+/// \brief Date constructor
+//
+/// The constructor has three forms: 0 args, 1 arg and 2-7 args.
+/// new Date() sets the Date to the current time of day
+/// new Date(timeValue:Number) sets the date to a number of milliseconds since
+/// 1 Jan 1970 UTC
+/// new Date(year:Number, month:Number [, date:Number [, hour:Number
+/// [, minute:Number [, second:Number [, millisecond:Number ]]]]])
+/// sets the date object to a specified year/month etc in local time
+/// Defaults are 0 except for date (day of month) whose default it 1.
void
date_new(const fn_call& fn)
@@ -264,43 +236,84 @@
// register the exported interface, don't
// replicate all functions !!
- date_as_object *date_obj = new date_as_object;
-
+ date_as_object *date = new date_as_object;
// TODO: move this to date_as_object constructor
- struct tm *ti;
- if (fn.nargs == 0) {
-#ifndef HAVE_GETTIMEOFDAY
+ if (fn.nargs < 1) {
+ // Set from system clock
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday(&tv,&tz);
+ date->value = (double)tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
+#else
struct timeb tb;
ftime (&tb);
- ti = localtime(&tb.time);
- log_error("date_new constructor doesn't set timezone or
milliseconds on your system - using defaults\n");
- date_obj->millisecond = 0;
- date_obj->minutesEast = 0;
-#else
- struct timeval tEnd;
- struct timezone tZone;
- gettimeofday(&tEnd,&tZone);
- date_obj->millisecond = tEnd.tv_usec;
- date_obj->minutesEast = -tZone.tz_minuteswest;
- time_t t = time(&t);
- ti = localtime(&t);
+ date->value = (double)tb.time * 1000.0 + tb.millitm;
#endif
- date_obj->second = ti->tm_sec;
- date_obj->minute = ti->tm_min;
- date_obj->hour = ti->tm_hour;
- date_obj->date = ti->tm_mday;
- date_obj->month = ti->tm_mon;
- date_obj->year = ti->tm_year;
- date_obj->dayWeek = ti->tm_wday;
- date_obj->dayYear = ti->tm_yday;
- date_obj->isDST = ti->tm_isdst;
+ } else if (fn.nargs == 1) {
+ // Set the value in milliseconds since 1970 UTC
+ date->value = fn.arg(0).to_number();
+ } else {
+ 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
+
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+ tm.tm_mday = 1;
+ tm.tm_mon = (int) fn.arg(1).to_number();
+ tm.tm_year = (int) fn.arg(0).to_number();
+ // We need to know whether DST was in effect at the date they
+ // mention to know how to interpret the hour they specify.
+ // In fact this is impossible bcos on the day that the
+ // clocks go back there are two hours of time with the same
+ // local year-month-day-hour-minute values.
+ // Example: 1995 Apr 2 01:00-59 and 02:00-59 give the same UTC,
+ // both in GMT (which is wrong: BST started 26 March!)
+ // We'll do our best...
+
+ switch (fn.nargs) {
+ default:
+ IF_VERBOSE_ASCODING_ERRORS(
+ log_aserror("Date constructor called with more than 7
arguments");
+ )
+ case 7:
+ millisecs = fn.arg(6).to_number();
+ case 6:
+ tm.tm_sec = (int)fn.arg(5).to_number();
+ case 5:
+ tm.tm_min = (int)fn.arg(4).to_number();
+ case 4:
+ tm.tm_hour = (int)fn.arg(3).to_number();
+ case 3:
+ tm.tm_mday = (int)fn.arg(2).to_number();
+ case 2:
+ tm.tm_mon = (int)fn.arg(1).to_number();
+ tm.tm_year = (int)fn.arg(0).to_number();
+ // 0-99 means years since 1900
+ // 100- means full year
+ // negative values are so much before 1900
+ // (so 55AC is only expressible as -1845)
+ // Fractional part of a year is ignored.
+ if (tm.tm_year >= 100) tm.tm_year -= 1900;
+ }
+ // Experimentally, glibc mktime ignores and tm_isdst and sets
+ // it from the date, applying it at midnight (not at 2am)
+ utcsecs = mktime(&tm); // mktime converts from local time
+ if (utcsecs == -1) {
+ // mktime could not represent the time
+ log_error("Date() failed to initialise from arguments");
+ date->value = 0;
+ } else {
+ date->value = (double)utcsecs * 1000.0 + millisecs;
+ }
}
- else
- log_error("date_new constructor with %d arguments
unimplemented!", fn.nargs);
- fn.result->set_as_object(date_obj);
+ fn.result->set_as_object(date);
}
// Wrapper around dynamic_cast to implement user warning.
@@ -316,541 +329,664 @@
return ret;
}
-static void date_getdate(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->date);
-}
-static void date_getday(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->dayWeek);
-}
-static void date_getfullyear(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->year + 1900);
-}
-static void date_gethours(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->hour);
-}
-static void date_getmilliseconds(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->millisecond);
-}
-static void date_getminutes(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->minute);
-}
-static void date_getmonth(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->month);
-}
-static void date_getseconds(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->second);
-}
-static void date_gettime(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_double(date->getTime());
-}
-static void date_gettimezoneoffset(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->minutesEast);
-}
-static void date_getutcdate(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- tm result = date->convertUTC();
+//
+// ========= Functions to get dates in various ways ========
+//
+
+// Date.getTime() is implemented by Date.valueOf()
+
+// Functions to return broken-out elements of the date and time.
+
+// We use a prototype macro to generate the function bodies because the many
+// individual functions are almost identical.
+//
+// localtime() and gmtime() return pointers to static structures, which is
+// not thread-safe, so we use the local-storage variants localtime_r gmtime_r
+// if they are available, hence two versions of the macro here.
+
+#if HAVE_LOCALTIME_R
+#define date_get_proto(function, timefn, element) \
+ static void function(const fn_call& fn) { \
+ date_as_object* date = ensure_date_object(fn.this_ptr); \
+ time_t t = (time_t)(date->value / 1000.0); \
+ struct tm tm; \
+ fn.result->set_int(timefn##_r(&t, &tm)->element); \
+ }
+#else
+#define date_get_proto(function, timefn, element) \
+ static void function(const fn_call& fn) { \
+ date_as_object* date = ensure_date_object(fn.this_ptr); \
+ time_t t = (time_t)(date->value / 1000.0); \
+ fn.result->set_int(timefn(&t)->element); \
+ }
+#endif
- fn.result->set_int(int(result.tm_mday));
-}
-static void date_getutcday(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- tm result = date->convertUTC();
+/// \brief Date.getYear returns the year of the specified Date object
+/// according to local time. The year is the full year minus 1900.
+/// For example, the year 2000 is represented as 100.
- fn.result->set_int(int(result.tm_wday));
-}
-static void date_getutcfullyear(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- tm result = date->convertUTC();
+date_get_proto(date_getyear, localtime, tm_year);
- fn.result->set_int(int(result.tm_year)+1900);
-}
-static void date_getutchours(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- tm result = date->convertUTC();
+/// \brief Date.getFullYear returns the full year (a four-digit number,
+/// such as 2000) of the specified Date object, according to local time.
- fn.result->set_int(int(result.tm_hour));
-}
-static void date_getutcmilliseconds(const fn_call& fn) {
+date_get_proto(date_getfullyear, localtime, tm_year + 1900)
+
+/// \brief Date.getMonth returns the month (0 for January, 1 for February,
+/// and so on) of the specified Date object, according to local time
+
+date_get_proto(date_getmonth, localtime, tm_mon)
+
+/// \brief Date.getDate returns the day of the month (an integer from 1 to 31)
+/// of the specified Date object according to local time.
+
+date_get_proto(date_getdate, localtime, tm_mday)
+
+/// \brief Date.getDay returns the day of the week (0 for Sunday, 1 for Monday,
+/// and so on) of the specified Date object according to local time.
+
+date_get_proto(date_getday, localtime, tm_wday)
+
+/// \brief Date.getHours returns the hour (an integer from 0 to 23)
+/// of the specified Date object, according to local time
+
+date_get_proto(date_gethours, localtime, tm_hour)
+
+/// \brief Date.getMinutes returns the minutes (an integer from 0 to 59)
+/// of the specified Date object, according to local time
+
+date_get_proto(date_getminutes, localtime, tm_min)
+
+/// \brief Date.getSeconds returns the seconds (an integer from 0 to 59)
+/// of the specified Date object, according to local time
+
+date_get_proto(date_getseconds, localtime, tm_sec)
+
+/// \brief Date.getMilliseconds returns the milliseconds (an integer
+/// from 0 to 999) of the specified Date object. Localtime is irrelevant!
+//
+// Also implements Date.getUTCMilliseconds
+
+static void date_getmilliseconds(const fn_call& fn) {
date_as_object* date = ensure_date_object(fn.this_ptr);
- // Milliseconds (value between 0 and 999) won't be affected by timezone
- fn.result->set_int(int(date->millisecond));
+ fn.result->set_int((int) fmod(date->value, 1000.0));
}
-static void date_getutcminutes(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- tm result = date->convertUTC();
- fn.result->set_int(int(result.tm_min));
-}
-static void date_getutcmonth(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- tm result = date->convertUTC();
+// The same functions for universal time.
+//
+date_get_proto(date_getutcfullyear, gmtime, tm_year + 1900)
+date_get_proto(date_getutcmonth, gmtime, tm_mon)
+date_get_proto(date_getutcdate, gmtime, tm_mday)
+date_get_proto(date_getutcday, gmtime, tm_wday)
+date_get_proto(date_getutchours, gmtime, tm_hour)
+date_get_proto(date_getutcminutes, gmtime, tm_min)
+date_get_proto(date_getutcseconds, gmtime, tm_sec)
+// date_getutcmilliseconds is implemented by date_getmilliseconds.
+
+
+/// \brief Date.getTimezoneOffset returns the difference in minutes between
+/// the computer's local time and universal time as minutes east of GMT.
+//
+/// In fact this has nothing to do with the Date object, since it depends on
+/// the system timezone settings, not on the value in a Date.
+
+// Return number of minutes east of GMT, also used in toString()
+
+// Yet another implementation option is suggested by localtime(3):
+// "The glibc version of struct tm has additional fields
+// long tm_gmtoff; /* Seconds east of UTC */
+// defined when _BSD_SOURCE was set before including <time.h>"
+
+static int minutes_east_of_gmt()
+{
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+ struct timezone tz;
+ gettimeofday(&tv,&tz);
+ return(-tz.tz_minuteswest);
+#elif defined(HAVE_TZSET)
+ tzset();
+ return(-timezone/60); // timezone is seconds west of GMT
+#else
+ // ftime(3): "These days the contents of the timezone and dstflag
+ // fields are undefined."
+ // In practice, timezone is -120 in Italy when it should be -60.
+ // Still, mancansa d'asu, t'acuma i buoi.
+ struct timeb tb;
- fn.result->set_int(int(result.tm_mon));
-}
-static void date_getutcseconds(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- // Seconds (value between 0 and 59) won't be affected by timezone
- fn.result->set_int(int(date->second));
+ ftime (&tb);
+ // tb.timezone is number of minutes west of GMT
+ return(-tb.timezone);
+#endif
}
-static void date_getyear(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- fn.result->set_int(date->year);
+
+static void date_gettimezoneoffset(const fn_call& fn) {
+ fn.result->set_int(minutes_east_of_gmt());
}
-// TODO: for all these "set" functions, what do we do if sent illegal values?
-// Clamp them to a proper range? Ignore and keep previous value? Throw an
error?
-// For now I'm "normalizing" them e.g. Jan-33 25:60:60 -> Feb-3 2:01:00
-// TODO: Also, confirm this is the appropriate behavior for the setUTC()
-// functions. Right now, we convert the time to UTC, set the variable,
-// then convert back to local time. We should confirm the official behavior!
+//
+// ========= Functions to set dates in various ways ========
+//
-static void date_setdate(const fn_call& fn) {
+/// \brief Date.setTime sets the date for the specified Date object
+/// in milliseconds since midnight on January 1, 1970, and returns
+/// the new time in milliseconds.
+static void date_settime(const fn_call& fn) {
date_as_object* date = ensure_date_object(fn.this_ptr);
+ // assert(fn.nargs == 1);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setDate needs one argument");
+ log_aserror("Date.setTime needs one argument");
)
- } else {
- date->date = (long int)(fn.arg(0).to_number());
- date->Normalize();
- }
+ } else
+ date->value = fn.arg(0).to_number();
if (fn.nargs > 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setDate has more than one argument");
+ log_aserror("Date.setTime was called with more than one
argument");
)
}
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-/// \brief Set year [, month [, date]]
+//
+// Functions to set just some components of a Date.
+//
+// We do this by exploding the datestamp into the calendar components,
+// setting the fields that are to be changed, then converting back.
+//
-static void date_setfullyear(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
+// 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
+// I doubt we can reproduce its exact operation, so just let mktime do what
+// it wants with rogue values.
- // assert(fn.nargs >= 1 && fn.nargs <= 3);
- if (fn.nargs < 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setFullYear needs one argument");
- )
- } else {
- date->year = (long int)(fn.arg(0).to_number() - 1900);
- if (fn.nargs >= 2)
- date->month = (long int)(fn.arg(1).to_number());
- if (fn.nargs >= 3)
- date->date = (long int)(fn.arg(2).to_number());
- if (fn.nargs > 3) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setFullYear has more than three
arguments");
- )
- }
- date->Normalize();
- }
- fn.result->set_double(date->getTime());
+// 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.
+
+
+// Two low-level functions to convert between datestamps and time structures
+// whose contents are in local time
+
+// convert flash datestamp (number of milliseconds since the epoch as a double)
+// to time structure and remaining milliseconds expressed in localtime.
+static void
+local_date_to_tm_msec(date_as_object* &date, struct tm &tm, double &msec)
+{
+ time_t t = (time_t)(date->value / 1000.0);
+ msec = fmod(date->value, 1000.0);
+ _localtime_r(&t, &tm); // break out date/time elements
}
-static void date_sethours(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs >= 1 && fn.nargs <= 4);
- if (fn.nargs < 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setHours needs one argument");
- )
+// convert Unix time structure and the remaining milliseconds to
+// Flash datestamp
+static void
+local_tm_msec_to_date(struct tm &tm, double &msec, date_as_object* &date)
+{
+ time_t t = mktime(&tm);
+
+ // Recostruct the time value and put the milliseconds back in.
+ // If mktime fails to reconstruct the date, change nothing.
+ if (t == (time_t)(-1)) {
+ log_error("Failed to set a date.\n");
} else {
- date->hour = (long int)(fn.arg(0).to_number());
- if (fn.nargs >= 2)
- date->minute = (long int)(fn.arg(1).to_number());
- if (fn.nargs >= 3)
- date->second = (long int)(fn.arg(2).to_number());
- if (fn.nargs >= 4)
- date->millisecond = (long int)(fn.arg(3).to_number());
- if (fn.nargs > 4) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setHours has more than four arguments");
- )
+ date->value = t * 1000.0 + msec;
}
- date->Normalize();
- }
- fn.result->set_double(date->getTime());
}
-static void date_setmilliseconds(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs == 1);
- if (fn.nargs < 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setMilliseconds needs one argument");
- )
+// Two low-level functions to convert between datestamps and time structures
+// whose contents are in UTC
+//
+// Unfortunately, mktime() only works in localtime.
+// gmtime() will split it for us, but how do we put it back together again?
+
+static void
+utc_date_to_tm_msec(date_as_object* &date, struct tm &tm, double &msec)
+{
+ msec = fmod(date->value, 1000.0);
+ time_t t = (time_t)(date->value / 1000.0);
+
+ _gmtime_r(&t, &tm);
+}
+
+// TODO:
+// Until we find the correct 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.
+
+static void
+utc_tm_msec_to_date(struct tm &tm, double &msec, date_as_object* &date)
+{
+ time_t t = mktime(&tm);
+ if (t == (time_t)(-1)) {
+ log_error("utc_tm_msec_to_date failed to convert back to Date");
} else {
- date->millisecond = (long int)(fn.arg(0).to_number());
- if (fn.nargs > 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setMilliseconds has more than one
argument");
- )
- }
- date->Normalize();
+ // Restore the UTC time-of-day
+ t = (t % 86400) + tm.tm_sec + 60 * (tm.tm_min + 60 * tm.tm_hour);
}
- fn.result->set_double(date->getTime());
+
+ date->value = t * 1000.0 + msec;
}
-static void date_setminutes(const fn_call& fn) {
+
+// Now the generic version of these two functions that switch according to
+// what the customer asked for
+
+static void
+tm_msec_to_date(struct tm &tm, double &msec, date_as_object* &date, bool utc)
+{
+ if (utc)
+ utc_tm_msec_to_date(tm, msec, date);
+ else
+ local_tm_msec_to_date(tm, msec, date);
+}
+
+static void
+date_to_tm_msec(date_as_object* &date, struct tm &tm, double &msec, bool utc)
+{
+ if (utc)
+ utc_date_to_tm_msec(date, tm, msec);
+ else
+ local_date_to_tm_msec(date, tm, msec);
+}
+
+//
+// Compound functions that can set one, two, three or four fields at once.
+//
+// There are two flavours: those that work with localtime and those that do so
+// in UTC (except for setYear, which has no UTC version). We avoid duplication
+// by passing an optional variable "utc": if present and true, we use the
+// UTC conversion funtions. Otherwise the localtime ones.
+//
+
+/// \brief Date.setFullYear(year[,month[,day]])
+/// sets the year of the specified Date object according to local time
+/// and returns the new time in milliseconds.
+//
+/// If the month and date parameters are specified, they are set in local time.
+/// year: A four-digit number specifying a year.
+/// Two-digit numbers do not represent four-digit years;
+/// for example, 99 is not the year 1999, but the year 99.
+/// month: An integer from 0 (January) to 11 (December). [optional]
+/// day: An integer from 1 to 31. [optional]
+///
+/// If the month and/or day are omitted, they are left at their current values.
+/// If changing the year or month results in an impossible date, it is
+/// normalised: 29 Feb becomes 1 Mar, 31 April becomes 1 May etc.
+
+static void _date_setfullyear(const fn_call& fn, bool utc=false) {
date_as_object* date = ensure_date_object(fn.this_ptr);
- //assert(fn.nargs >= 1 && fn.nargs <= 3);
+ // assert(fn.nargs >= 1 && fn.nargs <= 3);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setMinutes needs one argument");
+ log_aserror("Date.setFullYear needs one argument");
)
} else {
- // Seconds (value between 0 and 59) won't be affected by timezone
- date->minute = (long int)(fn.arg(0).to_number());
- if (fn.nargs >= 2) date->second = (long int)(fn.arg(1).to_number());
- if (fn.nargs >= 3) date->millisecond = (long
int)(fn.arg(2).to_number());
+ struct tm tm; double msec;
+
+ date_to_tm_msec(date, tm, msec, utc);
+ tm.tm_year = (int) fn.arg(0).to_number() - 1900;
+ if (fn.nargs >= 2)
+ tm.tm_mon = (int) fn.arg(1).to_number();
+ if (fn.nargs >= 3)
+ tm.tm_mday = (int) fn.arg(2).to_number();
if (fn.nargs > 3) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setMinutes has more than three
arguments");
+ log_aserror("Date.setFullYear was called with more than
three arguments");
)
}
- date->Normalize();
+ tm_msec_to_date(tm, msec, date, utc);
}
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-static void date_setmonth(const fn_call& fn) {
+
+/// \brief Date.setYear(year[,month[,day]])
+/// sets the year for the specified Date object in local time
+/// and returns the new time in milliseconds
+/// If year is an integer between 0-99, setYear sets the year at 1900 + year;
+/// otherwise, the year is a four-digit one.
+//
+/// Contrary to the spec at sephiroth.it, this takes and acts on optional
+/// parameters month and day; if they are unspecified their values are not
+/// changed.
+///
+/// There is no setUTCYear() function.
+
+static void date_setyear(const fn_call& fn) {
date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs >= 1 && fn.nargs <= 2);
+ // assert(fn.nargs == 1);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setMonth needs one argument");
+ log_aserror("Date.setYear needs one argument");
)
} else {
- date->month = (long int)(fn.arg(0).to_number());
+ struct tm tm; double msec;
+
+ date_to_tm_msec(date, tm, msec, false);
+ tm.tm_year = (int) fn.arg(0).to_number();
+ if (tm.tm_year < 100) tm.tm_year += 1900;
if (fn.nargs >= 2)
- date->date = (long int)(fn.arg(1).to_number());
- if (fn.nargs > 2) {
+ tm.tm_mon = (int) fn.arg(1).to_number();
+ if (fn.nargs >= 3)
+ tm.tm_mday = (int) fn.arg(2).to_number();
+ if (fn.nargs > 3) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setMonth has more than two arguments");
+ log_aserror("Date.setYear was called with more than three
arguments");
)
}
-
- date->Normalize();
+ tm_msec_to_date(tm, msec, date, false);
}
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-static void date_setseconds(const fn_call& fn) {
+
+/// \brief Date.setMonth(month[,date]) sets the month and optionally the day
+/// of the month for the specified Date object in local time and returns
+/// the new time in milliseconds
+//
+/// month: An integer from 0 (January) to 11 (December).
+/// date: An integer from 1 to 31. [optional]
+
+static void _date_setmonth(const fn_call& fn, bool utc=false) {
date_as_object* date = ensure_date_object(fn.this_ptr);
// assert(fn.nargs >= 1 && fn.nargs <= 2);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setSeconds needs one argument");
+ log_aserror("Date.setMonth needs one argument");
)
} else {
- // Seconds (value between 0 and 59) won't be affected by timezone
- date->second = (long int)(fn.arg(0).to_number());
+ struct tm tm; double msec;
+
+ date_to_tm_msec(date, tm, msec, utc);
+ tm.tm_mon = (int) fn.arg(0).to_number();
if (fn.nargs >= 2)
- date->millisecond = (long int)(fn.arg(1).to_number());
+ tm.tm_mday = (int) fn.arg(2).to_number();
if (fn.nargs > 2) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setSeconds has more than two arguments");
+ log_aserror("Date.setMonth was called with more than three
arguments");
)
}
-
- date->Normalize();
+ tm_msec_to_date(tm, msec, date, utc);
}
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-static void date_settime(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
-
- // assert(fn.nargs == 1);
- if (fn.nargs < 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setTime needs one argument");
- )
- } else {
- double millitime = fn.arg(0).to_number();
- date->millisecond = (long) std::fmod(millitime, 1000.0);
- time_t sectime = (time_t) (millitime / 1000.0);
- tm *tm = gmtime(§ime);
- date->second = tm->tm_sec;
- date->minute = tm->tm_min;
- date->hour = tm->tm_hour;
- date->date = tm->tm_mday;
- date->month = tm->tm_mon;
- date->year = tm->tm_year; // No of years since 1900, the same.
- date->Normalize();
- }
- if (fn.nargs > 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setTime has more than one argument");
- )
- }
+/// \brief Date.setDate(date) sets the day of the month for the specified Date
+/// object according to local time, and returns the new time in milliseconds.
+//
+/// date: An integer from 1 to 31
- fn.result->set_double(date->getTime());
-}
-static void date_setutcdate(const fn_call& fn) {
+static void _date_setdate(const fn_call& fn, bool utc=false) {
date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs == 1);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCDate needs one argument");
+ log_aserror("Date.setDate needs one argument");
)
} else {
- tm utctime = date->convertUTC();
- // Set mday to our new UTC date (yday and wday don't need to be set)
- utctime.tm_mday = int(fn.arg(0).to_number());
-
- // Convert back from UTC to local time
- utctime.tm_min += date->minutesEast;
-
- // Normalize the time, then set as this object's new time
- time_t newtime = mktime(&utctime);
- date->setFromTM(*(localtime(&newtime)));
+ struct tm tm; double msec;
+ date_to_tm_msec(date, tm, msec, utc);
+ tm.tm_mday = (int)(fn.arg(0).to_number());
+ tm_msec_to_date(tm, msec, date, utc);
+ }
if (fn.nargs > 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCDate has more than one argument");
+ log_aserror("Date.setDate was called with more than one
argument");
)
}
- }
-
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-static void date_setutcfullyear(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs >= 1 && fn.nargs <= 3);
- if (fn.nargs < 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCFullYear needs one argument");
- )
- } else {
- tm utctime = date->convertUTC();
- // Set year to our new UTC date
- utctime.tm_year = int(fn.arg(0).to_number()-1900);
- if (fn.nargs >= 2)
- utctime.tm_mon = (long int)(fn.arg(1).to_number());
- if (fn.nargs >= 3)
- utctime.tm_mday = (long int)(fn.arg(2).to_number());
- if (fn.nargs > 3) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCFullYear has more than three
arguments");
- )
- }
-
- // Convert back from UTC to local time
- utctime.tm_min += date->minutesEast;
+/// \brief Date.setHours(hour[,min[,sec[,millisec]]])
+/// sets the components of the specified Date object according to local time
+/// and returns the new time in milliseconds
+//
+/// hour: An integer from 0 (midnight) to 23 (11 p.m.).
+/// min: An integer from 0 to 59. [optional]
+/// sec: An integer from 0 to 59. [optional]
+/// millisec: An integer from 0 to 999. [optional]
+///
+/// If optional fields are omitted, their values in the Date object
+/// are left the same as they were.
+///
+/// Contrary to the spec at sephiroth.it, this takes and acts on optional
+/// parameters min, sec and millisec.
- // Normalize the time, then set as this object's new time
- time_t newtime = mktime(&utctime);
- date->setFromTM(*(localtime(&newtime)));
- }
-
- fn.result->set_double(date->getTime());
-}
-static void date_setutchours(const fn_call& fn) {
+static void _date_sethours(const fn_call& fn, bool utc=false) {
date_as_object* date = ensure_date_object(fn.this_ptr);
// assert(fn.nargs >= 1 && fn.nargs <= 4);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCHours needs one argument");
+ log_aserror("Date.setHours needs one argument");
)
} else {
+ struct tm tm; double msec;
- if (fn.nargs >= 4)
- {
- date->millisecond = (long int)(fn.arg(3).to_number());
- date->Normalize();
- }
-
- tm utctime = date->convertUTC();
- // Set year to our new UTC date
- utctime.tm_hour = int(fn.arg(0).to_number());
+ date_to_tm_msec(date, tm, msec, utc);
+ tm.tm_hour = (int) fn.arg(0).to_number();
if (fn.nargs >= 2)
- utctime.tm_min = (long int)(fn.arg(1).to_number());
+ tm.tm_min = (int) fn.arg(1).to_number();
if (fn.nargs >= 3)
- utctime.tm_sec = (long int)(fn.arg(2).to_number());
+ tm.tm_sec = (int) fn.arg(2).to_number();
+ if (fn.nargs >= 4)
+ msec = fn.arg(3).to_number();
if (fn.nargs > 4) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCHours has more than four
arguments");
+ log_aserror("Date.setHours was called with more than four
arguments");
)
}
-
- // Convert back from UTC to local time
- utctime.tm_min += date->minutesEast;
-
- // Normalize the time, then set as this object's new time
- time_t newtime = mktime(&utctime);
- date->setFromTM(*(localtime(&newtime)));
+ tm_msec_to_date(tm, msec, date, utc);
}
-
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-static void date_setutcmilliseconds(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs == 1);
- if (fn.nargs < 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCMilliseconds needs one argument");
- )
- } else {
- date->millisecond = (long int)(fn.arg(0).to_number());
- if (fn.nargs > 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCMilliseconds has more than one
argument");
- )
- }
+/// \brief Date.setMinutes(minutes[,secs[,millisecs]]) sets the given fields
+/// for a specified Date object according to local time and returns the new
+/// time in milliseconds.
+//
+/// Contrary to the spec at sephiroth.it, this takes and acts on optional
+/// extra parameters secs and millisecs.
- date->Normalize();
- }
- fn.result->set_double(date->getTime());
-}
-static void date_setutcminutes(const fn_call& fn) {
+static void _date_setminutes(const fn_call& fn, bool utc=false) {
date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs >= 1 && fn.nargs <= 3);
+ //assert(fn.nargs >= 1 && fn.nargs <= 3);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCMinutes needs one argument");
+ log_aserror("Date.setMinutes needs one argument");
)
} else {
- // Seconds (value between 0 and 59) won't be affected by timezone
- date->minute = (long int)(fn.arg(0).to_number());
- if (fn.nargs >= 2) date->second = (long int)(fn.arg(1).to_number());
- if (fn.nargs >= 3) date->millisecond = (long
int)(fn.arg(2).to_number());
+ struct tm tm; double msec;
+
+ date_to_tm_msec(date, tm, msec, utc);
+ tm.tm_min = (int) fn.arg(0).to_number();
+ if (fn.nargs >= 2)
+ tm.tm_sec = (int) fn.arg(1).to_number();
+ if (fn.nargs >= 3)
+ msec = fn.arg(2).to_number();
if (fn.nargs > 3) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCMinutes has more than three
arguments");
+ log_aserror("Date.setMinutes was called with more than
three arguments");
)
}
-
- // Setting milliseconds to less than 0 or more than 999 affects
seconds
-
- // TODO: both of these lines are wrong for negative values
- date->second += date->millisecond / 1000;
- date->millisecond = date->millisecond % 1000;
-
- date->Normalize();
+ tm_msec_to_date(tm, msec, date, utc);
}
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-static void date_setutcmonth(const fn_call& fn) {
- date_as_object* date = ensure_date_object(fn.this_ptr);
- // assert(fn.nargs >= 1 && fn.nargs <= 2);
- if (fn.nargs < 1) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCMonth needs one argument");
- )
- } else {
- tm utctime = date->convertUTC();
- // Set year to our new UTC date
- utctime.tm_mon = int(fn.arg(0).to_number());
- if (fn.nargs >= 2)
- utctime.tm_mday = (long int)(fn.arg(1).to_number());
- if (fn.nargs > 2) {
- IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCMonth has more than two arguments");
- )
- }
+/// \brief Date.setSeconds(secs[,millisecs]) sets the seconds and optionally
+/// the milliseconds for the specified Date object in local time
+/// and returns the new time in milliseconds.
- // Convert back from UTC to local time
- utctime.tm_min += date->minutesEast;
-
- // Normalize the time, then set as this object's new time
- time_t newtime = mktime(&utctime);
- date->setFromTM(*(localtime(&newtime)));
- }
- fn.result->set_double(date->getTime());
-}
-static void date_setutcseconds(const fn_call& fn) {
+static void _date_setseconds(const fn_call& fn, bool utc=false) {
date_as_object* date = ensure_date_object(fn.this_ptr);
// assert(fn.nargs >= 1 && fn.nargs <= 2);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCSeconds needs one argument");
+ log_aserror("Date.setSeconds needs one argument");
)
} else {
- // Seconds (value between 0 and 59) won't be affected by timezone
- date->second = (long int)(fn.arg(0).to_number());
+ // We *could* set seconds [and milliseconds] without breaking the
+ // structure out and reasembling it. We do it the same way as the
+ // rest for simplicity and in case anyone's date routines ever
+ // take account of leap seconds.
+ struct tm tm; double msec;
+
+ date_to_tm_msec(date, tm, msec, utc);
+ tm.tm_sec = (int) fn.arg(0).to_number();
if (fn.nargs >= 2)
- date->millisecond = (long int)(fn.arg(1).to_number());
+ msec = fn.arg(1).to_number();
if (fn.nargs > 2) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setUTCSeconds has more than two
arguments");
+ log_aserror("Date.setMinutes was called with more than
three arguments");
)
}
-
- date->Normalize();
+ tm_msec_to_date(tm, msec, date, utc);
}
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
-// If year is an integer between 0-99, setYear sets the year at 1900 + year;
-// otherwise, the year is the value of the year parameter.
-static void date_setyear(const fn_call& fn) {
+static void date_setmilliseconds(const fn_call& fn) {
date_as_object* date = ensure_date_object(fn.this_ptr);
// assert(fn.nargs == 1);
if (fn.nargs < 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setYear needs one argument");
+ log_aserror("Date.setMilliseconds needs one argument");
)
} else {
- date->year = (long int)(fn.arg(0).to_number());
- if (date->year < 100) date->year += 1900;
+ // Zero the milliseconds and set them from the argument.
+ date->value = fmod(date->value, 1000.0) + fn.arg(0).to_number();
if (fn.nargs > 1) {
IF_VERBOSE_ASCODING_ERRORS(
- log_aserror("Date.setYear has more than two arguments");
+ log_aserror("Date.setMilliseconds was called with more than
one argument");
)
}
-
- date->Normalize();
}
- fn.result->set_double(date->getTime());
+ fn.result->set_double(date->value);
}
+// Bindings for localtime versions
+#define local_proto(item) \
+ static void date_set##item(const fn_call& fn) { \
+ _date_set##item(fn); \
+ }
+local_proto(fullyear)
+local_proto(month)
+local_proto(date)
+local_proto(hours)
+local_proto(minutes)
+local_proto(seconds)
+
+// The same things for UTC.
+#define utc_proto(item) \
+ static void date_setutc##item(const fn_call& fn) { \
+ _date_set##item(fn, true); \
+ }
+utc_proto(fullyear)
+utc_proto(month)
+utc_proto(date)
+utc_proto(hours)
+utc_proto(minutes)
+utc_proto(seconds)
+
+
+/// \brief Date.toString()
+/// returns a string value for the specified date object in a readable format
+//
+/// The format is "Thu Jan 1 00:00:00 GMT+0000 1970" and it is displayed in
+/// local time.
+
static void date_tostring(const fn_call& fn) {
- // TODO: I have no idea what the real flash player does, but at least
this
- // gives something functional for now. Tried to mimic the "date" command
- char buffer[128];
+ char buffer[40]; // 32 chars + slop
char* monthname[12] =
{"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
char* dayweekname[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
date_as_object* date = ensure_date_object(fn.this_ptr);
- snprintf((char *)&buffer,128,"%s %s %2ld %.2ld:%.2ld:%.2ld %ld",
- dayweekname[date->dayWeek],monthname[date->month],
- date->date,date->hour,date->minute,date->second,
- 1900+date->year);
+ time_t t = (time_t) (date->value / 1000.0);
+ struct tm tm;
+ int tzhours, tzminutes;
+
+ _localtime_r(&t, &tm);
+
+ // At the meridian we need to print "GMT+0100" when Daylight Saving
+ // Time is in force, "GMT+0000" when it isn't, and other values for
+ // other places around the globe when DST is/isn't in force there.
+ //
+ // For now, just take time East of GMT and add an hour if DST is
+ // active. To get it right I guess we should call both gmtime()
+ // and localtime() and look at the difference.
+ //
+ // According to http://www.timeanddate.com/time/, the only place that
+ // uses DST != +1 hour is Lord Howe Island with half an hour. Tough.
+
+ tzhours = (tzminutes = minutes_east_of_gmt()) / 60, tzminutes %= 60;
+
+ if (tm.tm_isdst == 0) {
+ // DST exists and is not in effect
+ } else if (tm.tm_isdst > 0) {
+ // DST exists and is in effect
+ tzhours++;
+ // The range of standard time is GMT-11 to GMT+14.
+ // The most extreme with DST is Chatham Island GMT+12:45 +1DST
+ } else {
+ // tm_isdst is negative: cannot get TZ info.
+ // Convert and print in UTC instead.
+ _gmtime_r(&t, &tm);
+ }
+
+ // If timezone is negative, both hours and minutes will be negative
+ // but for the purpose of printing a string, only the hour needs to
+ // produce a minus sign.
+ if (tzminutes < 0) tzminutes = -tzminutes;
+
+ snprintf((char *)&buffer, sizeof(buffer),
+ "%s %s %d %02d:%02d:%02d GMT%+03d%02d %d",
+ dayweekname[tm.tm_wday], monthname[tm.tm_mon],
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tzhours, tzminutes, tm.tm_year+1900);
fn.result->set_string((char *)&buffer);
}
-// This should be a static method - not quite sure what that means...
+// Date.UTC(year:Number,month[,date[,hour[,minute[,second[,millisecond]]]]]
+// returns the number of milliseconds between midnight on January 1, 1970,
+// universal time, and the time specified in the parameters.
+//
+// This is a static method that is invoked through the Date object constructor,
+// not through a specific Date object. This method lets you create a Date
object
+// that assumes universal time, whereas the Date constructor assumes local
time.
+//
+// year: A four-digit integer that represents the year (for example, 2000).
+//
+// e.g. var maryBirthday_date:Date = new Date(Date.UTC(1974, 7, 12));
+
static void date_utc(const fn_call& fn) {
date_as_object* date = ensure_date_object(fn.this_ptr);
UNUSED(date);
- log_msg("Date.UTC unimplemented\n");
+ log_msg("Date.UTC is unimplemented\n");
+}
+
+/// \brief Date.valueOf() returns the number of milliseconds since midnight
+/// January 1, 1970, universal time, for this Date.
+/// Also used for Date.getTime()
+static void date_valueof(const fn_call& fn) {
+ date_as_object* date = ensure_date_object(fn.this_ptr);
+ fn.result->set_double(date->value);
}
// extern (used by Global.cpp)
- [Gnash-commit] gnash configure.ac ChangeLog server/asobj/Date.cpp,
Martin Guy <=