diff --git a/CHANGES b/CHANGES index 76e64c2f1a6c63fef494ae598c47579dbcb90c92..cdda85c350f41bbcb52c46c6f4a30235694eadbc 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,10 @@ o SSL.File Support query_fd() and set_buffer_mode() methods. +o mktime/System.TM + + Considerable speedup and a reduction in codesize. + Bug fixes --------- @@ -85,6 +89,10 @@ o Sql.pgsql - Repair fetch_row_array(). +o mktime/System.TM + + Make timezone management consistent (especially UTC handling). + o Standards.IIM Disabled debug output for unknown segment markers. diff --git a/src/builtin.cmod b/src/builtin.cmod index b13ab4c83bf2de0c07c419df49991ef59ad29db1..f4ade9ac3bd690ca2a40eff291ec264b6cb9e3ce 100644 --- a/src/builtin.cmod +++ b/src/builtin.cmod @@ -43,8 +43,29 @@ #define DEFAULT_CMOD_STORAGE -DECLARATIONS +#ifdef STRUCT_TM_HAS___TM_GMTOFF + struct tm_extra { }; +#define tm_zone __tm_zone +#define tm_gmtoff __tm_gmtoff +#define GET_GMTOFF(TM) ((TM)->tm_gmtoff) +#define GET_ZONE(this) ((this)->t.tm_zone) +#define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) +#define SET_ZONE(this, VAL) ((this)->t.tm_zone = (VAL)) +#elif defined(STRUCT_TM_HAS_GMTOFF) + struct tm_extra { }; +#define GET_GMTOFF(TM) ((TM)->tm_gmtoff) +#define GET_ZONE(this) ((this)->t.tm_zone) +#define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) +#define SET_ZONE(this, VAL) ((this)->t.tm_zone = (VAL)) +#else + struct tm_extra { const char*tm_zone; }; +#define GET_GMTOFF(TM) 0 +#define GET_ZONE(this) ((this)->extra.tm_zone) +#define SET_GMTOFF(TM, VAL) (VAL) +#define SET_ZONE(this, VAL) ((this)->extra.tm_zone = (VAL)) +#endif +DECLARATIONS /*! @module System */ @@ -59,28 +80,9 @@ PIKECLASS TM CVAR time_t unix_time; CVAR int modified; CVAR struct pike_string *set_zone; - -#ifdef STRUCT_TM_HAS___TM_GMTOFF -#define tm_zone __tm_zone -#define tm_gmtoff __tm_gmtoff -#define GET_GMTOFF(TM) ((TM)->tm_gmtoff) -#define GET_ZONE(TM) ((TM)->tm_zone) -#define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) -#define SET_ZONE(TM, VAL) (((TM)->tm_zone) = (VAL)) -#elif defined(STRUCT_TM_HAS_GMTOFF) -#define GET_GMTOFF(TM) ((TM)->tm_gmtoff) -#define GET_ZONE(TM) ((TM)->tm_zone) -#define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) -#define SET_ZONE(TM, VAL) (((TM)->tm_zone) = (VAL)) -#else -#define GET_GMTOFF(TM) 0 -#define GET_ZONE(TM) ((char*)NULL) -#define SET_GMTOFF(TM, VAL) (VAL) -#define SET_ZONE(TM, VAL) (VAL) -#endif + CVAR struct tm_extra extra; #define strftime_zone strftime -#define mktime_zone mktime #define strptime_zone strptime #define asctime_zone asctime #define localtime_zone(X,Y) localtime(X) @@ -89,13 +91,21 @@ PIKECLASS TM #endif #define MODIFY(X) do{ THIS->modified = 1;THIS->t.X; }while(0) -#define FIX_THIS() do { \ - if(THIS->modified){ \ - THIS->unix_time = mktime_zone( &THIS->t ); \ - THIS->modified = 0; \ - } \ +#define FIX_THIS(fname) do { \ + if(THIS->modified) \ + fix_tm(fname, args, THIS); \ } while(0) +static void fix_tm(const char*fname, int args, struct TM_struct*this) +{ + const char*tm_zone = GET_ZONE(this); + int is_utc_zone = tm_zone && !strcmp(tm_zone, "UTC"); + if (is_utc_zone) + this->t.tm_isdst = 0; + this->unix_time = mktime_zone(fname, args, &this->t, is_utc_zone, 0); + this->modified = 0; +} + #ifdef HAVE_STRPTIME /*! @decl int(0..1) strptime( string(1..255) format, string(1..255) data ) *! @@ -356,13 +366,13 @@ PIKECLASS TM *! Unlike the system struct tm the 'year' field is not year-1900, *! instead it is the actual year. */ - PIKEFUN int(0..60) `sec() { FIX_THIS();RETURN THIS->t.tm_sec; } - PIKEFUN int(0..59) `min() { FIX_THIS();RETURN THIS->t.tm_min; } - PIKEFUN int(0..23) `hour() { FIX_THIS();RETURN THIS->t.tm_hour; } - PIKEFUN int(1..31) `mday() { FIX_THIS();RETURN THIS->t.tm_mday; } - PIKEFUN int(0..11) `mon() { FIX_THIS();RETURN THIS->t.tm_mon; } + PIKEFUN int(0..60) `sec() { FIX_THIS("sec");RETURN THIS->t.tm_sec; } + PIKEFUN int(0..59) `min() { FIX_THIS("min");RETURN THIS->t.tm_min; } + PIKEFUN int(0..23) `hour() { FIX_THIS("hour");RETURN THIS->t.tm_hour; } + PIKEFUN int(1..31) `mday() { FIX_THIS("mday");RETURN THIS->t.tm_mday; } + PIKEFUN int(0..11) `mon() { FIX_THIS("mon");RETURN THIS->t.tm_mon; } - PIKEFUN int `year() { FIX_THIS();RETURN THIS->t.tm_year+1900; } + PIKEFUN int `year() { FIX_THIS("year");RETURN THIS->t.tm_year+1900; } PIKEFUN int `sec=(int a) { MODIFY(tm_sec=a); } PIKEFUN int `min=(int a) { MODIFY(tm_min=a); } PIKEFUN int `hour=(int a){ MODIFY(tm_hour=a); } @@ -377,7 +387,7 @@ PIKECLASS TM *! automatically using the timezone rules. */ PIKEFUN int(-1..1) `isdst() { - FIX_THIS(); + FIX_THIS("isdst"); RETURN THIS->t.tm_isdst; } @@ -385,13 +395,13 @@ PIKECLASS TM *! The day of the week, sunday is 0, saturday is 6. *! This is calculated from the other fields and can not be changed directly. */ - PIKEFUN int(0..6) `wday() { FIX_THIS(); RETURN THIS->t.tm_wday; } + PIKEFUN int(0..6) `wday() { FIX_THIS("wday"); RETURN THIS->t.tm_wday; } /*! @decl int yday *! The day of the year, from 0 (the first day) to 365 *! This is calculated from the other fields and can not be changed directly. */ - PIKEFUN int(0..365) `yday() { FIX_THIS(); RETURN THIS->t.tm_yday; } + PIKEFUN int(0..365) `yday() { FIX_THIS("yday"); RETURN THIS->t.tm_yday; } /*! @decl int unix_time() *! Return the unix time corresponding to this time_t. If no time @@ -399,7 +409,7 @@ PIKECLASS TM */ PIKEFUN int unix_time() { - FIX_THIS(); + FIX_THIS("unix_time"); RETURN THIS->unix_time; } @@ -410,19 +420,20 @@ PIKECLASS TM */ PIKEFUN string asctime() { - FIX_THIS(); + FIX_THIS("asctime"); { - char *tval = asctime_zone( &THIS->t ); - if( tval ) - push_text( tval ); - else - push_undefined(); +#define STRFTIME_MAXSIZE 26 + char s[STRFTIME_MAXSIZE]; + if( !strftime(s, STRFTIME_MAXSIZE, "%c\n", &THIS->t) ) + push_undefined(); + else + push_text(s); } } PIKEFUN void _sprintf( int flag, mapping options ) { - int post_sum = 1; + int post_sum = 0; switch( flag ) { case 'O': @@ -432,10 +443,10 @@ PIKECLASS TM case 's': f_TM_asctime(0); push_text("\n"); - if( GET_ZONE(&(THIS->t)) ) + if( GET_ZONE(THIS) ) { push_text(" "); - push_text( GET_ZONE(&(THIS->t)) ); + push_text( GET_ZONE(THIS) ); f_add( 2 ); } else @@ -484,9 +495,9 @@ PIKECLASS TM *! The timezone of this structure */ PIKEFUN string `zone() { - FIX_THIS(); - if( GET_ZONE(&(THIS->t)) ) - push_text( GET_ZONE(&(THIS->t)) ); + FIX_THIS("zone"); + if( GET_ZONE(THIS) ) + push_text( GET_ZONE(THIS) ); else push_undefined(); } @@ -495,7 +506,7 @@ PIKECLASS TM *! The offset from GMT for the time in this tm-struct */ PIKEFUN int `gmtoff() { - FIX_THIS(); + FIX_THIS("gmtoff"); push_int( GET_GMTOFF(&(THIS->t)) ); } @@ -520,10 +531,6 @@ PIKECLASS TM if( !res ) RETURN 0; - /* These are supposedly correctly by localtime_zone. */ - SET_GMTOFF(res, GET_GMTOFF(&(THIS->t))); - SET_ZONE(res, GET_ZONE(&(THIS->t))); - THIS->t = *res; THIS->modified = 1; RETURN 1; @@ -543,6 +550,7 @@ PIKECLASS TM RETURN 0; THIS->t = *res; + SET_ZONE(THIS, "UTC"); /* Override timezone */ THIS->modified = 1; RETURN 1; } @@ -581,26 +589,30 @@ PIKECLASS TM string|void timezone ) { struct tm *t = &THIS->t; - t->tm_isdst = -1; + int use_utc; + t->tm_isdst = use_utc ? 0 : -1; t->tm_year = year - 1900; t->tm_mon = mon; t->tm_mday = mday; t->tm_hour = hour; t->tm_min = min; t->tm_sec = sec; + use_utc = 0; + if (timezone) { + if (strcmp(timezone->str, "UTC")) + Pike_error("Timezone must either be UTC or omitted.\n"); + use_utc = 1; + } if (THIS->set_zone) { free_string(THIS->set_zone); THIS->set_zone = NULL; } - if( !timezone ) /* gmtime. */ - SET_ZONE(t, "UTC"); - else - { - add_ref(timezone); - THIS->set_zone = timezone; - SET_ZONE(t, timezone->str); - } - THIS->unix_time = mktime_zone( t ); + if (use_utc) + t->tm_isdst = 0; + THIS->unix_time = mktime_zone("TM", args, &THIS->t, use_utc, 0); + /* Setting it to other timezones than UTC is not supported (yet) */ + if (use_utc) + SET_ZONE(THIS, "UTC"); } INIT { diff --git a/src/builtin_functions.c b/src/builtin_functions.c index 621a347433fb9e688e034550b43cbe0e741830ef..dc86b6a8c07254bea99283433fa4e646e99d19e9 100644 --- a/src/builtin_functions.c +++ b/src/builtin_functions.c @@ -5733,224 +5733,52 @@ PMOD_EXPORT void f_localtime(INT32 args) f_aggregate_mapping(20); } -#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) - -static const int mon_lengths[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} -}; - -static void normalize_date (struct tm *t) -/* Normalizes t->tm_mday and t->tm_mon. */ +time_t mktime_zone(const char*fname, int args, + struct tm*date, int other_timezone, int tz) { - int q, year, mon, mday, leap; - - q = t->tm_mon / 12; - if (t->tm_mon < 0) q--; - t->tm_mon -= q * 12; - t->tm_year += q; - - year = t->tm_year + 1900; - leap = isleap (year); - mon = t->tm_mon; - mday = t->tm_mday; - - if (mday > 0) { - int mon_len = mon_lengths[leap][mon]; - if (mday <= mon_len) return; - do { - mday -= mon_len; - if (++mon == 12) mon = 0, year++, leap = isleap (year); - } while (mday > (mon_len = mon_lengths[leap][mon])); - } - - else - do { - if (mon == 0) mon = 11, year--, leap = isleap (year); - else mon--; - mday += mon_lengths[leap][mon]; - } while (mday < 1); - - t->tm_year = year - 1900; - t->tm_mon = mon; - t->tm_mday = mday; -} - -#define CHECKED_DIFF_MULT(RES, A, B, MULT, OVERFLOW) do { \ - RES = (A - B) * (MULT); \ - if ((A > B) != (RES > 0)) {OVERFLOW;} \ - } while (0) - -#define CHECKED_ADD(ACC, DIFF, OVERFLOW) do { \ - time_t res_ = ACC + DIFF; \ - if ((ACC > 0) == (DIFF > 0) && (ACC > 0) != (res_ > 0)) \ - {OVERFLOW;} \ - else \ - ACC = res_; \ - } while (0) - -/* Returns the approximate difference in seconds between the - * two struct tm's. - */ -static time_t my_tm_diff(const struct tm *t1, const struct tm *t2) -{ - time_t base, diff; - - /* Win32 localtime() returns NULL for all dates before Jan 01, 1970. */ - if (!t2) return -1; - - CHECKED_DIFF_MULT (base, t1->tm_year, t2->tm_year, 60*60*24*31*12, - return base < 0 ? MAX_TIME_T : MIN_TIME_T); - - /* Overflow detection not necessary on these fields since we can - * assume they're all in the valid ranges here. */ - diff = - (t1->tm_mon - t2->tm_mon) * (60*60*24*31) + - (t1->tm_mday - t2->tm_mday) * (60*60*24) + - (t1->tm_hour - t2->tm_hour) * (60*60) + - (t1->tm_min - t2->tm_min) * 60 + - (t1->tm_sec - t2->tm_sec); - - CHECKED_ADD (base, diff, - return diff < 0 ? MIN_TIME_T : MAX_TIME_T); - - return base; -} - -typedef struct tm *time_fn (const time_t *); - -/* Inverse operation of gmtime or localtime. Unlike mktime(3), this - * doesn't fill in a normalized time in target_tm. - */ -static int my_time_inverse (struct tm *target_tm, time_t *result, time_fn timefn) -{ - struct tm norm_tm = *target_tm; - time_t current_ts = 0; - time_t displacement; - time_t diff_ts, old_diff_ts = 0; - int loop_cnt, tried_dst_displacement = 0; - -#ifdef DEBUG_MY_TIME_INVERSE - fprintf (stderr, "target: y %d m %d d %d h %d m %d isdst %d\n", - target_tm->tm_year, target_tm->tm_mon, target_tm->tm_mday, - target_tm->tm_hour, target_tm->tm_min, target_tm->tm_isdst); -#endif - - /* An hour, minute or second value outside the valid range is - * treated as a displacement rather than an absolute time spec. We - * therefore zero them in the target time spec and add the - * displacement seconds back to the time_t afterwards. This way we - * don't need to worry about them in the date normalization. */ - - /* It's quicker to always move the seconds to the displacement. It - * works just as well and we don't need to consider leap seconds. */ - displacement = norm_tm.tm_sec; - norm_tm.tm_sec = 0; - - /* Bug: The following conversions to seconds ought to compensate for - * leap seconds. That should only happen if timefn takes leap - * seconds into account however, which it might not do. */ - if (norm_tm.tm_min < 0 || norm_tm.tm_min >= 60) { - time_t d; - CHECKED_DIFF_MULT (d, norm_tm.tm_min, 0, 60, return 0); - CHECKED_ADD (displacement, d, return 0); - norm_tm.tm_min = 0; - } - if (norm_tm.tm_hour < 0 || norm_tm.tm_hour >= 60) { - time_t d; - CHECKED_DIFF_MULT (d, norm_tm.tm_hour, 0, 60*60, return 0); - CHECKED_ADD (displacement, d, return 0); - norm_tm.tm_hour = 0; - } - - /* Normalize the date. This is necessary since the simplistic diff - * calculation in my_tm_diff doesn't work on invalid dates like - * November 100th or March -10th. (Can't use the displacement - * variable for an invalid tm_mday since the number of seconds per - * day isn't constant.) */ - normalize_date (&norm_tm); -#ifdef DEBUG_MY_TIME_INVERSE - fprintf (stderr, "normalized: y %d m %d d %d h %d m %d isdst %d\n" - "displacement: %ld\n", - norm_tm.tm_year, norm_tm.tm_mon, norm_tm.tm_mday, - norm_tm.tm_hour, norm_tm.tm_min, norm_tm.tm_isdst, - (long) displacement); -#endif - - /* This loop seems stable, and usually converges in two passes. - * The loop counter is for paranoia reasons. - */ - for (loop_cnt = 0; loop_cnt < 20; loop_cnt++, old_diff_ts = diff_ts) { - struct tm *current_tm = timefn(¤t_ts); -#ifdef DEBUG_MY_TIME_INVERSE - fprintf (stderr, "curr: y %d m %d d %d h %d m %d isdst %d\n", - current_tm->tm_year, current_tm->tm_mon, current_tm->tm_mday, - current_tm->tm_hour, current_tm->tm_min, current_tm->tm_isdst); -#endif + time_t retval; + int normalised_time; - diff_ts = my_tm_diff (&norm_tm, current_tm); -#ifdef DEBUG_MY_TIME_INVERSE - fprintf (stderr, "diff: %ld\n", (long) diff_ts); -#endif + date->tm_wday = -1; /* flag to determine failure */ - if (!current_tm) { -#ifdef DEBUG_MY_TIME_INVERSE - fprintf (stderr, "outside range for timefn().\n"); -#endif - return 0; - } - - if (!diff_ts) { - /* Got a satisfactory time, but if norm_tm has an opinion on - * DST we should check if we can return an alternative in the - * same DST zone, to cope with the overlapping DST adjustment at - * fall. */ - if (norm_tm.tm_isdst >= 0 && - norm_tm.tm_isdst != current_tm->tm_isdst && - !tried_dst_displacement) { - /* Offset the time a day and iterate some more (only once - * more, really), so that we approach the target time from the - * right direction. */ - if (norm_tm.tm_isdst) - current_ts -= 24 * 3600; - else - current_ts += 24 * 3600; - tried_dst_displacement = 1; -#ifdef DEBUG_MY_TIME_INVERSE - fprintf (stderr, "dst displacement\n"); -#endif - continue; - } - break; - } - - if (diff_ts == -old_diff_ts) { - /* We're oscillating. Shouldn't happen since norm_tm ought to be - * valid. */ -#ifdef DEBUG_MY_TIME_INVERSE - fprintf (stderr, "oscillation detected: %ld <-> %ld\n", - (long) old_diff_ts, (long) diff_ts); + { + int sec, min, hour; + sec = date->tm_sec; + min = date->tm_min; + hour = date->tm_hour; + + min += sec / 60; + if ((sec %= 60) < 0) + min--, sec += 60; + hour += min / 60; + if ((min %= 60) < 0) + hour--, min += 60; + if ((hour %= 24) < 0) + hour += 24; + normalised_time = ((hour * 60) + min) * 60 + sec; + } + + retval = mktime(date); + if (date->tm_wday < 0) + PIKE_ERROR("mktime", "Time conversion unsuccessful.\n", Pike_sp, args); + + if(other_timezone) + { + normalised_time -= ((date->tm_hour * 60) + date->tm_min) * 60 + date->tm_sec; + if (normalised_time < -12*60*60) + normalised_time += 24*60*60; + else if (normalised_time > 12*60*60) + normalised_time -= 24*60*60; +#ifdef STRUCT_TM_HAS___TM_GMTOFF + retval += date->__tm_gmtoff; +#elif defined(STRUCT_TM_HAS_GMTOFF) + retval += date->tm_gmtoff; +#else + normalised_time = retval - mktime(gmtime(&retval)); #endif - return 0; - } - - /* It's ok to not add the full diff here since we're looping. Do - * this since the diff calculation can overshoot the target - * time. */ - CHECKED_ADD (current_ts, diff_ts, { - if (diff_ts > 0 && current_ts < MAX_TIME_T) - current_ts = MAX_TIME_T; - else if (diff_ts < 0 && current_ts > MIN_TIME_T) - current_ts = MIN_TIME_T; - else - return 0; - }); + retval += normalised_time + tz; } - - CHECKED_ADD (current_ts, displacement, return 0); - - *result = current_ts; - return 1; + return retval; } /*! @decl int mktime(mapping(string:int) tm) @@ -6000,6 +5828,7 @@ PMOD_EXPORT void f_mktime (INT32 args) INT_TYPE isdst = -1, tz = 0; struct tm date; time_t retval; + int normalised_time; if (args<1) SIMPLE_TOO_FEW_ARGS_ERROR("mktime", 1); @@ -6034,30 +5863,11 @@ PMOD_EXPORT void f_mktime (INT32 args) date.tm_mon=mon; date.tm_year=year; date.tm_isdst=isdst; - /* date.tm_zone = NULL; */ - if((args > 7) && (SUBTYPEOF(Pike_sp[7-args]) == NUMBER_NUMBER)) - { - /* UTC-relative time. Use gmtime. */ - if (!my_time_inverse (&date, &retval, gmtime)) - PIKE_ERROR("mktime", "Time conversion failed.\n", Pike_sp, args); - retval += tz; - } else - - { - retval = mktime(&date); - if (retval == -1) - { - /* mktime might fail on dates before 1970 (e.g. GNU libc 2.3.2), - * so try our own inverse function with localtime. - * - * Note that localtime on Win32 will also fail for dates before 1970. - */ - if (!my_time_inverse (&date, &retval, localtime)) - PIKE_ERROR("mktime", "Time conversion unsuccessful.\n", Pike_sp, args); - } - } + retval = mktime_zone("mktime", args, &date, + args > 7 && SUBTYPEOF(Pike_sp[7-args]) == NUMBER_NUMBER, + tz); pop_n_elems(args); #if SIZEOF_TIME_T > SIZEOF_INT_TYPE diff --git a/src/builtin_functions.h b/src/builtin_functions.h index e8a84aff9c3e8695887c7f90bdf62d19da0d4d86..decb02ade0de976ffc285a8d8e00319da9f6b065 100644 --- a/src/builtin_functions.h +++ b/src/builtin_functions.h @@ -124,6 +124,8 @@ PMOD_EXPORT void f__assembler_debug(INT32 args); PMOD_EXPORT void f__compiler_trace(INT32 args); PMOD_EXPORT void f_gmtime(INT32 args); PMOD_EXPORT void f_localtime(INT32 args); +time_t mktime_zone(const char*fname, int args, + struct tm*date, int other_timezone, int tz); PMOD_EXPORT void f_mktime (INT32 args); PMOD_EXPORT void f_glob(INT32 args); PMOD_EXPORT void f_permute(INT32 args); diff --git a/src/modules/system/testsuite.in b/src/modules/system/testsuite.in index b76189260a6092f3a3fce9b60f4b50241fc7ecdc..7d8ee57074dd2f4be42bf81ddc06d48d84687424 100644 --- a/src/modules/system/testsuite.in +++ b/src/modules/system/testsuite.in @@ -278,4 +278,39 @@ cond_begin([[ System["__MMAP__"] ]]) cond_end // System["__MMAP__"] +test_equal(sort(indices(System.Time())), ({ "sec","usec","usec_full" })) +test_eq(abs(time()-System.Time()->sec)<2, 1) +test_eq(System.Time()->usec-System.Time()->usec<=0, 1) + +test_do(System.TM()) +test_do((int)System.TM(0), 0) +test_do((int)System.TM(1<<31), 1<<31) +test_do((int)System.TM(1<<32), 1<<32) +test_do((int)System.TM(1<<33), 1<<33) +test_do(System.TM(2017,11,21,16,15,01,"UTC")) +test_do(System.TM(2017,6,21,16,15,01,"UTC")) +test_do(System.TM(2017,6,21,16,15,01)) + +test_eq(System.TM(1513871300)->year, 2017) +test_eq(System.TM(1513871300)->mon, 11) +test_eq(System.TM(1513871300)->mday, 21) +test_eq(System.TM(1513871300)->wday, 4) +test_eq(System.TM(1513871300)->yday, 354) +test_eq(System.TM(1513871300)->hour, 15) +test_eq(System.TM(1513871300)->min, 48) +test_eq(System.TM(1513871300)->sec, 20) +test_eq(System.TM(1513871300)->isdst, 0) +test_eq(intp(System.TM(1513871300)->gmtoff), 1) +test_eq(stringp(System.TM(1513871300)->zone), 1) +test_eq(stringp(System.TM(1513871300)->asctime()), 1) +test_eq(System.TM(1513871300)->unix_time(), 1513871300) +test_equal(map("%d%D%e%F%G%g%H%I%k%l%M%m%p%r%R%T%u%U%V%w"/2, + System.TM(1513871300)->strftime), + ({ "21", "12/21/17", "21", "2017-12-21", "2017", "17", + "15", "03", "15", " 3", "48", "12", "PM", + "03:48:20 PM", "15:48", "15:48:20", "4", "51", "51", "4" })) + +test_eq(stringp((string)System.TM(1513871300)), 1) +test_eq((int)System.TM(1513871300), 1513871300) + END_MARKER diff --git a/src/testsuite.in b/src/testsuite.in index 39953b7ee29a43d147c8a05e395ce9bf503aa577..367809deb44b89393651811ec548245fbb68c2aa 100644 --- a/src/testsuite.in +++ b/src/testsuite.in @@ -11298,6 +11298,19 @@ test_equal(mkmultiset(indices(class{constant a="a"; constant b="b";}())), // - localtime cond([[all_constants()->localtime]],[[ test_true(mappingp(localtime(0))) + test_eq(localtime((1<<31)-1)->year, 138) + test_equal(sort(indices(localtime(0))),({ + "hour", + "isdst", + "mday", + "min", + "mon", + "sec", + "timezone", + "wday", + "yday", + "year" + })) test_do([[int t = -1; catch(localtime(t));]]) ]]) cond([[all_constants()->localtime && all_constants()->mktime]], @@ -11492,7 +11505,8 @@ cond([[all_constants()->mktime]], test_any([[foreach(({1075550400,94691300,220921700,347152100,473382500, 599612900,725843300,852073700,978304100,1104534500, 1230764900,1356995300,1483225700,1609456100,1735686500, - 1861916900,1988147300,2114377700,1500033813 + 1861916900,1988147300,2114377700,1500033813, + 0,1,1<<31-1 }),int t) { int res = mktime (gmtime (t)); if(res!=t) return ({t, res}); @@ -11506,7 +11520,7 @@ cond([[all_constants()->mktime]], test_eq(mktime (-200, -200, -200, 200, 1, 107, 0, 0), 1186749400); test_eq(mktime (33, 3, 12, 14, 6, 117, 0, 0), 1500033813); test_eq(mktime ((["sec":33, "min":3, "hour":12, - "mday":14, "mon":6, "year":117, "timezone":0])), 1500033813); + "mday":14, "mon":6, "year":117, "isdst":0, "timezone":0])), 1500033813); test_any( [[ // bug [2861] ------------------------------------------------------------