diff options
Diffstat (limited to 'time.c')
| -rw-r--r-- | time.c | 764 |
1 files changed, 451 insertions, 313 deletions
@@ -42,8 +42,13 @@ #include "internal/time.h" #include "internal/variable.h" #include "ruby/encoding.h" +#include "ruby/util.h" #include "timev.h" +#if defined(_WIN32) +# include <timezoneapi.h> /* DYNAMIC_TIME_ZONE_INFORMATION */ +#endif + #include "builtin.h" static ID id_submicro, id_nano_num, id_nano_den, id_offset, id_zone; @@ -62,10 +67,6 @@ static VALUE sym_hour, sym_min, sym_sec, sym_subsec, sym_dst, sym_zone; #define id_name idName #define UTC_ZONE Qundef -#ifndef TM_IS_TIME -#define TM_IS_TIME 1 -#endif - #define NDIV(x,y) (-(-((x)+1)/(y))-1) #define NMOD(x,y) ((y)-(-((x)+1)%(y))-1) #define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d)) @@ -248,6 +249,7 @@ divmodv(VALUE n, VALUE d, VALUE *q, VALUE *r) # define FIXWV2WINT(w) FIX2LONG(WIDEVAL_GET(w)) #endif +#define SIZEOF_WIDEINT SIZEOF_INT64_T #define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1) #define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN) #define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w)) @@ -335,6 +337,8 @@ v2w(VALUE v) return WIDEVAL_WRAP(v); } +#define NUM2WV(v) v2w(rb_Integer(v)) + static int weq(wideval_t wx, wideval_t wy) { @@ -574,6 +578,9 @@ num_exact(VALUE v) /* time_t */ +/* TIME_SCALE should be 10000... */ +static const int TIME_SCALE_NUMDIGITS = rb_strlen_lit(STRINGIZE(TIME_SCALE)) - 1; + static wideval_t rb_time_magnify(wideval_t w) { @@ -654,13 +661,13 @@ wv2timet(wideval_t w) wideint_t wi = FIXWV2WINT(w); if (TIMET_MIN == 0) { if (wi < 0) - rb_raise(rb_eRangeError, "negative value to convert into `time_t'"); + rb_raise(rb_eRangeError, "negative value to convert into 'time_t'"); if (TIMET_MAX < (uwideint_t)wi) - rb_raise(rb_eRangeError, "too big to convert into `time_t'"); + rb_raise(rb_eRangeError, "too big to convert into 'time_t'"); } else { if (wi < TIMET_MIN || TIMET_MAX < wi) - rb_raise(rb_eRangeError, "too big to convert into `time_t'"); + rb_raise(rb_eRangeError, "too big to convert into 'time_t'"); } return (time_t)wi; } @@ -701,10 +708,53 @@ static VALUE tm_from_time(VALUE klass, VALUE time); bool ruby_tz_uptodate_p; +#ifdef _WIN32 +enum {tzkey_max = numberof(((DYNAMIC_TIME_ZONE_INFORMATION *)NULL)->TimeZoneKeyName)}; +static struct { + char use_tzkey; + char name[tzkey_max * 4 + 1]; +} w32_tz; + +static char * +get_tzname(int dst) +{ + if (w32_tz.use_tzkey) { + if (w32_tz.name[0]) { + return w32_tz.name; + } + else { + /* + * Use GetDynamicTimeZoneInformation::TimeZoneKeyName, Windows + * time zone ID, which is not localized because it is the key + * for "Dynamic DST" keys under the "Time Zones" registry. + * Available since Windows Vista and Windows Server 2008. + */ + DYNAMIC_TIME_ZONE_INFORMATION tzi; + WCHAR *const wtzkey = tzi.TimeZoneKeyName; + DWORD tzret = GetDynamicTimeZoneInformation(&tzi); + if (tzret != TIME_ZONE_ID_INVALID && *wtzkey) { + int wlen = (int)wcsnlen(wtzkey, tzkey_max); + int clen = WideCharToMultiByte(CP_UTF8, 0, wtzkey, wlen, + w32_tz.name, sizeof(w32_tz.name) - 1, + NULL, NULL); + w32_tz.name[clen] = '\0'; + return w32_tz.name; + } + } + } + return _tzname[_daylight && dst]; +} +#endif + +static void ruby_reset_leap_second_info(void); + void -ruby_reset_timezone(void) +ruby_reset_timezone(const char *val) { ruby_tz_uptodate_p = false; +#ifdef _WIN32 + w32_tz.use_tzkey = !val || !*val; +#endif ruby_reset_leap_second_info(); } @@ -933,26 +983,33 @@ zone_str(const char *zone) { const char *p; int ascii_only = 1; - VALUE str; size_t len; if (zone == NULL) { return rb_fstring_lit("(NO-TIMEZONE-ABBREVIATION)"); } - for (p = zone; *p; p++) + for (p = zone; *p; p++) { if (!ISASCII(*p)) { ascii_only = 0; + p += strlen(p); break; } - len = p - zone + strlen(p); + } + len = p - zone; if (ascii_only) { - str = rb_usascii_str_new(zone, len); + return rb_enc_interned_str(zone, len, rb_usascii_encoding()); } else { - str = rb_enc_str_new(zone, len, rb_locale_encoding()); +#ifdef _WIN32 + VALUE str = rb_utf8_str_new(zone, len); + /* until we move to UTF-8 on Windows completely */ + str = rb_str_export_locale(str); + return rb_fstring(str); +#else + return rb_enc_interned_str(zone, len, rb_locale_encoding()); +#endif } - return rb_fstring(str); } static void @@ -1322,9 +1379,6 @@ gmtimew(wideval_t timew, struct vtm *result) result->wday = tm.tm_wday; result->yday = tm.tm_yday+1; result->isdst = tm.tm_isdst; -#if 0 - result->zone = rb_fstring_lit("UTC"); -#endif return result; } @@ -1443,7 +1497,7 @@ guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, VALUE *zone_ret) if (lt(vtm_utc->year, INT2FIX(1916))) { VALUE off = INT2FIX(0); int isdst = 0; - zone = rb_fstring_lit("UTC"); + zone = str_utc; # if defined(NEGATIVE_TIME_T) # if SIZEOF_TIME_T <= 4 @@ -1508,7 +1562,7 @@ guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, VALUE *zone_ret) localtime_with_gmtoff_zone(&now, &tm, &now_gmtoff, &zone); now_isdst = tm.tm_isdst; zone = rb_fstring(zone); - rb_gc_register_mark_object(zone); + rb_vm_register_global_object(zone); now_zone = zone; } if (isdst_ret) @@ -1652,11 +1706,9 @@ localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, VAL if (zone) { #if defined(HAVE_TM_ZONE) *zone = zone_str(tm.tm_zone); +#elif defined(_WIN32) + *zone = zone_str(get_tzname(tm.tm_isdst)); #elif defined(HAVE_TZNAME) && defined(HAVE_DAYLIGHT) -# if defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 140 -# define tzname _tzname -# define daylight _daylight -# endif /* this needs tzset or localtime, instead of localtime_r */ *zone = zone_str(tzname[daylight && tm.tm_isdst]); #else @@ -1761,32 +1813,31 @@ localtimew(wideval_t timew, struct vtm *result) #define TIME_TZMODE_FIXOFF 2 #define TIME_TZMODE_UNINITIALIZED 3 -PACKED_STRUCT_UNALIGNED(struct time_object { +struct time_object { wideval_t timew; /* time_t value * TIME_SCALE. possibly Rational. */ struct vtm vtm; - unsigned int tzmode:3; /* 0:localtime 1:utc 2:fixoff 3:uninitialized */ - unsigned int tm_got:1; -}); +}; #define GetTimeval(obj, tobj) ((tobj) = get_timeval(obj)) #define GetNewTimeval(obj, tobj) ((tobj) = get_new_timeval(obj)) #define IsTimeval(obj) rb_typeddata_is_kind_of((obj), &time_data_type) -#define TIME_INIT_P(tobj) ((tobj)->tzmode != TIME_TZMODE_UNINITIALIZED) +#define TIME_INIT_P(tobj) ((tobj)->vtm.tzmode != TIME_TZMODE_UNINITIALIZED) -#define TZMODE_UTC_P(tobj) ((tobj)->tzmode == TIME_TZMODE_UTC) -#define TZMODE_SET_UTC(tobj) ((tobj)->tzmode = TIME_TZMODE_UTC) +#define TZMODE_UTC_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_UTC) +#define TZMODE_SET_UTC(tobj) ((tobj)->vtm.tzmode = TIME_TZMODE_UTC) -#define TZMODE_LOCALTIME_P(tobj) ((tobj)->tzmode == TIME_TZMODE_LOCALTIME) -#define TZMODE_SET_LOCALTIME(tobj) ((tobj)->tzmode = TIME_TZMODE_LOCALTIME) +#define TZMODE_LOCALTIME_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_LOCALTIME) +#define TZMODE_SET_LOCALTIME(tobj) ((tobj)->vtm.tzmode = TIME_TZMODE_LOCALTIME) -#define TZMODE_FIXOFF_P(tobj) ((tobj)->tzmode == TIME_TZMODE_FIXOFF) -#define TZMODE_SET_FIXOFF(tobj, off) \ - ((tobj)->tzmode = TIME_TZMODE_FIXOFF, \ - (tobj)->vtm.utc_offset = (off)) +#define TZMODE_FIXOFF_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_FIXOFF) +#define TZMODE_SET_FIXOFF(time, tobj, off) do { \ + (tobj)->vtm.tzmode = TIME_TZMODE_FIXOFF; \ + RB_OBJ_WRITE_UNALIGNED(time, &(tobj)->vtm.utc_offset, off); \ +} while (0) #define TZMODE_COPY(tobj1, tobj2) \ - ((tobj1)->tzmode = (tobj2)->tzmode, \ + ((tobj1)->vtm.tzmode = (tobj2)->vtm.tzmode, \ (tobj1)->vtm.utc_offset = (tobj2)->vtm.utc_offset, \ (tobj1)->vtm.zone = (tobj2)->vtm.zone) @@ -1794,7 +1845,7 @@ static int zone_localtime(VALUE zone, VALUE time); static VALUE time_get_tm(VALUE, struct time_object *); #define MAKE_TM(time, tobj) \ do { \ - if ((tobj)->tm_got == 0) { \ + if ((tobj)->vtm.tm_got == 0) { \ time_get_tm((time), (tobj)); \ } \ } while (0) @@ -1806,6 +1857,26 @@ static VALUE time_get_tm(VALUE, struct time_object *); } \ } while (0) +static void +time_set_timew(VALUE time, struct time_object *tobj, wideval_t timew) +{ + tobj->timew = timew; + if (!FIXWV_P(timew)) { + RB_OBJ_WRITTEN(time, Qnil, w2v(timew)); + } +} + +static void +time_set_vtm(VALUE time, struct time_object *tobj, struct vtm vtm) +{ + tobj->vtm = vtm; + + RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.year); + RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.subsecx); + RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.utc_offset); + RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.zone); +} + static inline void force_make_tm(VALUE time, struct time_object *tobj) { @@ -1813,33 +1884,32 @@ force_make_tm(VALUE time, struct time_object *tobj) if (!NIL_P(zone) && zone != str_empty && zone != str_utc) { if (zone_localtime(zone, time)) return; } - tobj->tm_got = 0; + tobj->vtm.tm_got = 0; time_get_tm(time, tobj); } static void -time_mark(void *ptr) +time_mark_and_move(void *ptr) { struct time_object *tobj = ptr; - if (!FIXWV_P(tobj->timew)) - rb_gc_mark(w2v(tobj->timew)); - rb_gc_mark(tobj->vtm.year); - rb_gc_mark(tobj->vtm.subsecx); - rb_gc_mark(tobj->vtm.utc_offset); - rb_gc_mark(tobj->vtm.zone); -} - -static size_t -time_memsize(const void *tobj) -{ - return sizeof(struct time_object); + if (!WIDEVALUE_IS_WIDER || !FIXWV_P(tobj->timew)) { + rb_gc_mark_and_move((VALUE *)&WIDEVAL_GET(tobj->timew)); + } + rb_gc_mark_and_move(&tobj->vtm.year); + rb_gc_mark_and_move(&tobj->vtm.subsecx); + rb_gc_mark_and_move(&tobj->vtm.utc_offset); + rb_gc_mark_and_move(&tobj->vtm.zone); } static const rb_data_type_t time_data_type = { - "time", - {time_mark, RUBY_TYPED_DEFAULT_FREE, time_memsize,}, - 0, 0, - (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE), + .wrap_struct_name = "time", + .function = { + .dmark = time_mark_and_move, + .dfree = RUBY_TYPED_DEFAULT_FREE, + .dsize = NULL, + .dcompact = time_mark_and_move, + }, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE, }; static VALUE @@ -1849,9 +1919,9 @@ time_s_alloc(VALUE klass) struct time_object *tobj; obj = TypedData_Make_Struct(klass, struct time_object, &time_data_type, tobj); - tobj->tzmode = TIME_TZMODE_UNINITIALIZED; - tobj->tm_got=0; - tobj->timew = WINT2FIXWV(0); + tobj->vtm.tzmode = TIME_TZMODE_UNINITIALIZED; + tobj->vtm.tm_got = 0; + time_set_timew(obj, tobj, WINT2FIXWV(0)); tobj->vtm.zone = Qnil; return obj; @@ -1886,11 +1956,11 @@ time_modify(VALUE time) } static wideval_t -timenano2timew(time_t sec, long nsec) +timenano2timew(wideint_t sec, long nsec) { wideval_t timew; - timew = rb_time_magnify(TIMET2WV(sec)); + timew = rb_time_magnify(WINT2WV(sec)); if (nsec) timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000)); return timew; @@ -1948,6 +2018,10 @@ rb_timespec_now(struct timespec *ts) #endif } +/* + * Sets the current time information into _time_. + * Returns _time_. + */ static VALUE time_init_now(rb_execution_context_t *ec, VALUE time, VALUE zone) { @@ -1957,10 +2031,9 @@ time_init_now(rb_execution_context_t *ec, VALUE time, VALUE zone) time_modify(time); GetNewTimeval(time, tobj); TZMODE_SET_LOCALTIME(tobj); - tobj->tm_got=0; - tobj->timew = WINT2FIXWV(0); + tobj->vtm.tm_got=0; rb_timespec_now(&ts); - tobj->timew = timenano2timew(ts.tv_sec, ts.tv_nsec); + time_set_timew(time, tobj, timenano2timew(ts.tv_sec, ts.tv_nsec)); if (!NIL_P(zone)) { time_zonelocal(time, zone); @@ -1984,9 +2057,9 @@ time_set_utc_offset(VALUE time, VALUE off) time_modify(time); GetTimeval(time, tobj); - tobj->tm_got = 0; + tobj->vtm.tm_got = 0; tobj->vtm.zone = Qnil; - TZMODE_SET_FIXOFF(tobj, off); + TZMODE_SET_FIXOFF(time, tobj, off); return time; } @@ -2136,6 +2209,9 @@ invalid_utc_offset(VALUE zone) zone); } +#define have_2digits(ptr) (ISDIGIT((ptr)[0]) && ISDIGIT((ptr)[1])) +#define num_from_2digits(ptr) ((ptr)[0] * 10 + (ptr)[1] - '0' * 11) + static VALUE utc_offset_arg(VALUE arg) { @@ -2190,18 +2266,19 @@ utc_offset_arg(VALUE arg) goto invalid_utc_offset; } if (sec) { - if (!ISDIGIT(sec[0]) || !ISDIGIT(sec[1])) goto invalid_utc_offset; - n += (sec[0] * 10 + sec[1] - '0' * 11); + if (!have_2digits(sec)) goto invalid_utc_offset; + if (sec[0] > '5') goto invalid_utc_offset; + n += num_from_2digits(sec); ASSUME(min); } if (min) { - if (!ISDIGIT(min[0]) || !ISDIGIT(min[1])) goto invalid_utc_offset; + if (!have_2digits(min)) goto invalid_utc_offset; if (min[0] > '5') goto invalid_utc_offset; - n += (min[0] * 10 + min[1] - '0' * 11) * 60; + n += num_from_2digits(min) * 60; } if (s[0] != '+' && s[0] != '-') goto invalid_utc_offset; - if (!ISDIGIT(s[1]) || !ISDIGIT(s[2])) goto invalid_utc_offset; - n += (s[1] * 10 + s[2] - '0' * 11) * 3600; + if (!have_2digits(s+1)) goto invalid_utc_offset; + n += num_from_2digits(s+1) * 3600; if (s[0] == '-') { if (n == 0) return UTC_ZONE; n = -n; @@ -2217,14 +2294,14 @@ utc_offset_arg(VALUE arg) static void zone_set_offset(VALUE zone, struct time_object *tobj, - wideval_t tlocal, wideval_t tutc) + wideval_t tlocal, wideval_t tutc, VALUE time) { /* tlocal and tutc must be unmagnified and in seconds */ wideval_t w = wsub(tlocal, tutc); VALUE off = w2v(w); validate_utc_offset(off); - tobj->vtm.utc_offset = off; - tobj->vtm.zone = zone; + RB_OBJ_WRITE(time, &tobj->vtm.utc_offset, off); + RB_OBJ_WRITE(time, &tobj->vtm.zone, zone); TZMODE_SET_LOCALTIME(tobj); } @@ -2235,14 +2312,16 @@ extract_time(VALUE time) const ID id_to_i = idTo_i; #define EXTRACT_TIME() do { \ - t = v2w(rb_Integer(AREF(to_i))); \ + t = NUM2WV(AREF(to_i)); \ } while (0) if (rb_typeddata_is_kind_of(time, &time_data_type)) { - struct time_object *tobj = DATA_PTR(time); + struct time_object *tobj = RTYPEDDATA_GET_DATA(time); time_gmtime(time); /* ensure tm got */ t = rb_time_unmagnify(tobj->timew); + + RB_GC_GUARD(time); } else if (RB_TYPE_P(time, T_STRUCT)) { #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x)) @@ -2260,10 +2339,11 @@ extract_time(VALUE time) } static wideval_t -extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx) +extract_vtm(VALUE time, VALUE orig_time, struct time_object *orig_tobj, VALUE subsecx) { wideval_t t; const ID id_to_i = idTo_i; + struct vtm *vtm = &orig_tobj->vtm; #define EXTRACT_VTM() do { \ VALUE subsecx; \ @@ -2275,17 +2355,19 @@ extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx) vtm->sec = obj2subsecx(AREF(sec), &subsecx); \ vtm->isdst = RTEST(AREF(isdst)); \ vtm->utc_offset = Qnil; \ - t = v2w(rb_Integer(AREF(to_i))); \ + t = NUM2WV(AREF(to_i)); \ } while (0) if (rb_typeddata_is_kind_of(time, &time_data_type)) { - struct time_object *tobj = DATA_PTR(time); + struct time_object *tobj = RTYPEDDATA_GET_DATA(time); time_get_tm(time, tobj); - *vtm = tobj->vtm; + time_set_vtm(orig_time, orig_tobj, tobj->vtm); t = rb_time_unmagnify(tobj->timew); if (TZMODE_FIXOFF_P(tobj) && vtm->utc_offset != INT2FIX(0)) t = wadd(t, v2w(vtm->utc_offset)); + + RB_GC_GUARD(time); } else if (RB_TYPE_P(time, T_STRUCT)) { #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x)) @@ -2294,7 +2376,9 @@ extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx) } else if (rb_integer_type_p(time)) { t = v2w(time); - GMTIMEW(rb_time_magnify(t), vtm); + struct vtm temp_vtm = *vtm; + GMTIMEW(rb_time_magnify(t), &temp_vtm); + time_set_vtm(orig_time, orig_tobj, temp_vtm); } else { #define AREF(x) rb_funcallv(time, id_##x, 0, 0) @@ -2302,7 +2386,9 @@ extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx) #undef AREF } #undef EXTRACT_VTM - vtm->subsecx = subsecx; + + RB_OBJ_WRITE_UNALIGNED(orig_time, &vtm->subsecx, subsecx); + validate_vtm(vtm); return t; } @@ -2321,22 +2407,26 @@ static int zone_timelocal(VALUE zone, VALUE time) { VALUE utc, tm; - struct time_object *tobj = DATA_PTR(time); + struct time_object *tobj = RTYPEDDATA_GET_DATA(time); wideval_t t, s; - t = rb_time_unmagnify(tobj->timew); + wdivmod(tobj->timew, WINT2FIXWV(TIME_SCALE), &t, &s); tm = tm_from_time(rb_cTimeTM, time); utc = rb_check_funcall(zone, id_local_to_utc, 1, &tm); if (UNDEF_P(utc)) return 0; s = extract_time(utc); - zone_set_offset(zone, tobj, t, s); + zone_set_offset(zone, tobj, t, s, time); s = rb_time_magnify(s); if (tobj->vtm.subsecx != INT2FIX(0)) { s = wadd(s, v2w(tobj->vtm.subsecx)); } - tobj->timew = s; + time_set_timew(time, tobj, s); + zone_set_dst(zone, tobj, tm); + + RB_GC_GUARD(time); + return 1; } @@ -2344,7 +2434,7 @@ static int zone_localtime(VALUE zone, VALUE time) { VALUE local, tm, subsecx; - struct time_object *tobj = DATA_PTR(time); + struct time_object *tobj = RTYPEDDATA_GET_DATA(time); wideval_t t, s; split_second(tobj->timew, &t, &subsecx); @@ -2353,10 +2443,13 @@ zone_localtime(VALUE zone, VALUE time) local = rb_check_funcall(zone, id_utc_to_local, 1, &tm); if (UNDEF_P(local)) return 0; - s = extract_vtm(local, &tobj->vtm, subsecx); - tobj->tm_got = 1; - zone_set_offset(zone, tobj, s, t); + s = extract_vtm(local, time, tobj, subsecx); + tobj->vtm.tm_got = 1; + zone_set_offset(zone, tobj, s, t, time); zone_set_dst(zone, tobj, tm); + + RB_GC_GUARD(time); + return 1; } @@ -2383,6 +2476,10 @@ vtm_day_wraparound(struct vtm *vtm) static VALUE time_init_vtm(VALUE time, struct vtm vtm, VALUE zone); +/* + * Sets the broken-out time information into _time_. + * Returns _time_. + */ static VALUE time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday, VALUE hour, VALUE min, VALUE sec, VALUE zone) @@ -2445,10 +2542,10 @@ time_init_vtm(VALUE time, struct vtm vtm, VALUE zone) GetNewTimeval(time, tobj); if (!NIL_P(zone)) { - tobj->timew = timegmw(&vtm); + time_set_timew(time, tobj, timegmw(&vtm)); vtm_day_wraparound(&vtm); - tobj->vtm = vtm; - tobj->tm_got = 1; + time_set_vtm(time, tobj, vtm); + tobj->vtm.tm_got = 1; TZMODE_SET_LOCALTIME(tobj); if (zone_timelocal(zone, time)) { return time; @@ -2460,28 +2557,29 @@ time_init_vtm(VALUE time, struct vtm vtm, VALUE zone) } if (utc == UTC_ZONE) { - tobj->timew = timegmw(&vtm); + time_set_timew(time, tobj, timegmw(&vtm)); vtm.isdst = 0; /* No DST in UTC */ vtm_day_wraparound(&vtm); - tobj->vtm = vtm; - tobj->tm_got = 1; + time_set_vtm(time, tobj, vtm); + tobj->vtm.tm_got = 1; TZMODE_SET_UTC(tobj); return time; } TZMODE_SET_LOCALTIME(tobj); - tobj->tm_got=0; - tobj->timew = WINT2FIXWV(0); + tobj->vtm.tm_got=0; if (!NIL_P(vtm.utc_offset)) { VALUE off = vtm.utc_offset; vtm_add_offset(&vtm, off, -1); vtm.utc_offset = Qnil; - tobj->timew = timegmw(&vtm); + time_set_timew(time, tobj, timegmw(&vtm)); + return time_set_utc_offset(time, off); } else { - tobj->timew = timelocalw(&vtm); + time_set_timew(time, tobj, timelocalw(&vtm)); + return time_localtime(time); } } @@ -2490,17 +2588,16 @@ static int two_digits(const char *ptr, const char *end, const char **endp, const char *name) { ssize_t len = end - ptr; - if (len < 2 || (!ISDIGIT(ptr[0]) || !ISDIGIT(ptr[1])) || - ((len > 2) && ISDIGIT(ptr[2]))) { + if (len < 2 || !have_2digits(ptr) || ((len > 2) && ISDIGIT(ptr[2]))) { VALUE mesg = rb_sprintf("two digits %s is expected", name); if (ptr[-1] == '-' || ptr[-1] == ':') { - rb_str_catf(mesg, " after `%c'", ptr[-1]); + rb_str_catf(mesg, " after '%c'", ptr[-1]); } rb_str_catf(mesg, ": %.*s", ((len > 10) ? 10 : (int)(end - ptr)) + 1, ptr - 1); rb_exc_raise(rb_exc_new_str(rb_eArgError, mesg)); } *endp = ptr + 2; - return (ptr[0] - '0') * 10 + (ptr[1] - '0'); + return num_from_2digits(ptr); } static VALUE @@ -2511,8 +2608,12 @@ parse_int(const char *ptr, const char *end, const char **endp, size_t *ndigits, return rb_int_parse_cstr(ptr, len, (char **)endp, ndigits, 10, flags); } +/* + * Parses _str_ and sets the broken-out time information into _time_. + * If _str_ is not a String, returns +nil+, otherwise returns _time_. + */ static VALUE -time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, VALUE precision) +time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone, VALUE precision) { if (NIL_P(str = rb_check_string_type(str))) return Qnil; if (!rb_enc_str_asciicompat_p(str)) { @@ -2527,7 +2628,9 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, size_t ndigits; size_t prec = NIL_P(precision) ? SIZE_MAX : NUM2SIZET(precision); - while ((ptr < end) && ISSPACE(*ptr)) ptr++; + if ((ptr < end) && (ISSPACE(*ptr) || ISSPACE(*(end-1)))) { + rb_raise(rb_eArgError, "can't parse: %+"PRIsVALUE, str); + } year = parse_int(ptr, end, &ptr, &ndigits, true); if (NIL_P(year)) { rb_raise(rb_eArgError, "can't parse: %+"PRIsVALUE, str); @@ -2535,6 +2638,9 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, else if (ndigits < 4) { rb_raise(rb_eArgError, "year must be 4 or more digits: %.*s", (int)ndigits, ptr - ndigits); } + else if (ptr == end) { + goto only_year; + } do { #define peekable_p(n) ((ptrdiff_t)(n) < (end - ptr)) #define peek_n(c, n) (peekable_p(n) && ((unsigned char)ptr[n] == (c))) @@ -2595,22 +2701,24 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, if (zend > zstr) { zone = rb_str_subseq(str, zstr - begin, zend - zstr); } + else if (hour == -1) { + rb_raise(rb_eArgError, "no time information"); + } if (!NIL_P(subsec)) { /* subseconds is the last using ndigits */ - static const size_t TIME_SCALE_NUMDIGITS = - /* TIME_SCALE should be 10000... */ - rb_strlen_lit(STRINGIZE(TIME_SCALE)) - 1; - - if (ndigits < TIME_SCALE_NUMDIGITS) { + if (ndigits < (size_t)TIME_SCALE_NUMDIGITS) { VALUE mul = rb_int_positive_pow(10, TIME_SCALE_NUMDIGITS - ndigits); subsec = rb_int_mul(subsec, mul); } - else if (ndigits > TIME_SCALE_NUMDIGITS) { + else if (ndigits > (size_t)TIME_SCALE_NUMDIGITS) { VALUE num = rb_int_positive_pow(10, ndigits - TIME_SCALE_NUMDIGITS); subsec = rb_rational_new(subsec, num); } } +only_year: + ; + struct vtm vtm = { .wday = VTM_WDAY_INITVAL, .yday = 0, @@ -2623,19 +2731,19 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, .sec = (sec < 0) ? 0 : sec, .subsecx = NIL_P(subsec) ? INT2FIX(0) : subsec, }; - return time_init_vtm(klass, vtm, zone); + return time_init_vtm(time, vtm, zone); } static void -subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec) +subsec_normalize(wideint_t *secp, long *subsecp, const long maxsubsec) { - time_t sec = *secp; + wideint_t sec = *secp; long subsec = *subsecp; long sec2; if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */ sec2 = subsec / maxsubsec; - if (TIMET_MAX - sec2 < sec) { + if (WIDEINT_MAX - sec2 < sec) { rb_raise(rb_eRangeError, "out of Time range"); } subsec -= sec2 * maxsubsec; @@ -2643,16 +2751,12 @@ subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec) } else if (UNLIKELY(subsec < 0)) { /* subsec negative overflow */ sec2 = NDIV(subsec, maxsubsec); /* negative div */ - if (sec < TIMET_MIN - sec2) { + if (sec < WIDEINT_MIN - sec2) { rb_raise(rb_eRangeError, "out of Time range"); } subsec -= sec2 * maxsubsec; sec += sec2; } -#ifndef NEGATIVE_TIME_T - if (sec < 0) - rb_raise(rb_eArgError, "time must be positive"); -#endif *secp = sec; *subsecp = subsec; } @@ -2660,49 +2764,56 @@ subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec) #define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000) #define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000) -static wideval_t -nsec2timew(time_t sec, long nsec) -{ - time_nsec_normalize(&sec, &nsec); - return timenano2timew(sec, nsec); -} - static VALUE time_new_timew(VALUE klass, wideval_t timew) { VALUE time = time_s_alloc(klass); struct time_object *tobj; - tobj = DATA_PTR(time); /* skip type check */ + tobj = RTYPEDDATA_GET_DATA(time); /* skip type check */ TZMODE_SET_LOCALTIME(tobj); - tobj->timew = timew; + time_set_timew(time, tobj, timew); return time; } +static wideint_t +TIMETtoWIDEINT(time_t t) +{ +#if SIZEOF_TIME_T * CHAR_BIT - (SIGNEDNESS_OF_TIME_T < 0) > \ + SIZEOF_WIDEINT * CHAR_BIT - 1 + /* compare in bit size without sign bit */ + if (t > WIDEINT_MAX) rb_raise(rb_eArgError, "out of Time range"); +#endif + return (wideint_t)t; +} + VALUE rb_time_new(time_t sec, long usec) { - time_usec_normalize(&sec, &usec); - return time_new_timew(rb_cTime, timenano2timew(sec, usec * 1000)); + wideint_t isec = TIMETtoWIDEINT(sec); + time_usec_normalize(&isec, &usec); + return time_new_timew(rb_cTime, timenano2timew(isec, usec * 1000)); } /* returns localtime time object */ VALUE rb_time_nano_new(time_t sec, long nsec) { - return time_new_timew(rb_cTime, nsec2timew(sec, nsec)); + wideint_t isec = TIMETtoWIDEINT(sec); + time_nsec_normalize(&isec, &nsec); + return time_new_timew(rb_cTime, timenano2timew(isec, nsec)); } VALUE rb_time_timespec_new(const struct timespec *ts, int offset) { struct time_object *tobj; - VALUE time = time_new_timew(rb_cTime, nsec2timew(ts->tv_sec, ts->tv_nsec)); + VALUE time = rb_time_nano_new(ts->tv_sec, ts->tv_nsec); if (-86400 < offset && offset < 86400) { /* fixoff */ GetTimeval(time, tobj); - TZMODE_SET_FIXOFF(tobj, INT2FIX(offset)); + TZMODE_SET_FIXOFF(time, tobj, INT2FIX(offset)); } else if (offset == INT_MAX) { /* localtime */ } @@ -3547,7 +3658,7 @@ tmcmp(struct tm *a, struct tm *b) * Time.utc(year, month = 1, mday = 1, hour = 0, min = 0, sec = 0, usec = 0) -> new_time * Time.utc(sec, min, hour, mday, month, year, dummy, dummy, dummy, dummy) -> new_time * - * Returns a new \Time object based the on given arguments, + * Returns a new +Time+ object based the on given arguments, * in the UTC timezone. * * With one to seven arguments given, @@ -3608,7 +3719,7 @@ tmcmp(struct tm *a, struct tm *b) * Time.utc(Float(0.0), Rational(1, 1), 1.0, 0.0, 0.0, 0.0, 0.0) * # => 0000-01-01 00:00:00 UTC * - * - \String integers: + * - String integers: * * a = %w[0 1 1 0 0 0 0 0] * # => ["0", "1", "1", "0", "0", "0", "0", "0"] @@ -3625,7 +3736,7 @@ tmcmp(struct tm *a, struct tm *b) * # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] * Time.utc(*a) # => 0005-04-03 02:01:00 UTC * - * This form is useful for creating a \Time object from a 10-element + * This form is useful for creating a +Time+ object from a 10-element * array returned by Time.to_a: * * t = Time.new(2000, 1, 2, 3, 4, 5, 6) # => 2000-01-02 03:04:05 +000006 @@ -3639,8 +3750,6 @@ tmcmp(struct tm *a, struct tm *b) * Raises an exception if the number of arguments is eight, nine, * or greater than ten. * - * Time.gm is an alias for Time.utc. - * * Related: Time.local. * */ @@ -3658,7 +3767,7 @@ time_s_mkutc(int argc, VALUE *argv, VALUE klass) * Time.local(year, month = 1, mday = 1, hour = 0, min = 0, sec = 0, usec = 0) -> new_time * Time.local(sec, min, hour, mday, month, year, dummy, dummy, dummy, dummy) -> new_time * - * Like Time.utc, except that the returned \Time object + * Like Time.utc, except that the returned +Time+ object * has the local timezone, not the UTC timezone: * * # With seven arguments. @@ -3692,8 +3801,6 @@ time_s_mktime(int argc, VALUE *argv, VALUE klass) * Time.utc(1950, 1, 1, 0, 0, 0).to_i # => -631152000 * Time.utc(1990, 1, 1, 0, 0, 0).to_i # => 631152000 * - * Time#tv_sec is an alias for Time#to_i. - * * Related: Time#to_f Time#to_r. */ @@ -3773,8 +3880,6 @@ time_to_r(VALUE time) * t.usec # => 548469 * * Related: Time#subsec (returns exact subseconds). - * - * Time#tv_usec is an alias for Time#usec. */ static VALUE @@ -3802,8 +3907,6 @@ time_usec(VALUE time) * t.nsec # => 321963700 * * Related: Time#subsec (returns exact subseconds). - * - * Time#tv_nsec is an alias for Time#usec. */ static VALUE @@ -3893,7 +3996,7 @@ time_cmp(VALUE time1, VALUE time2) * eql?(other_time) * * Returns +true+ if +self+ and +other_time+ are - * both \Time objects with the exact same time value. + * both +Time+ objects with the exact same time value. */ static VALUE @@ -3918,11 +4021,20 @@ time_eql(VALUE time1, VALUE time2) * now = Time.now * # => 2022-08-18 10:24:13.5398485 -0500 * now.utc? # => false + * now.getutc.utc? # => true * utc = Time.utc(2000, 1, 1, 20, 15, 1) * # => 2000-01-01 20:15:01 UTC * utc.utc? # => true * - * Time#gmt? is an alias for Time#utc?. + * +Time+ objects created with these methods are considered to be in + * UTC: + * + * * Time.utc + * * Time#utc + * * Time#getutc + * + * Objects created in other ways will not be treated as UTC even if + * the environment variable "TZ" is "UTC". * * Related: Time.utc. */ @@ -3963,7 +4075,9 @@ time_init_copy(VALUE copy, VALUE time) if (!OBJ_INIT_COPY(copy, time)) return copy; GetTimeval(time, tobj); GetNewTimeval(copy, tcopy); - MEMCPY(tcopy, tobj, struct time_object, 1); + + time_set_timew(copy, tcopy, tobj->timew); + time_set_vtm(copy, tcopy, tobj->vtm); return copy; } @@ -3985,7 +4099,7 @@ time_localtime(VALUE time) GetTimeval(time, tobj); if (TZMODE_LOCALTIME_P(tobj)) { - if (tobj->tm_got) + if (tobj->vtm.tm_got) return time; } else { @@ -3999,9 +4113,9 @@ time_localtime(VALUE time) if (!localtimew(tobj->timew, &vtm)) rb_raise(rb_eArgError, "localtime error"); - tobj->vtm = vtm; + time_set_vtm(time, tobj, vtm); - tobj->tm_got = 1; + tobj->vtm.tm_got = 1; TZMODE_SET_LOCALTIME(tobj); return time; } @@ -4035,20 +4149,20 @@ time_zonelocal(VALUE time, VALUE off) * With no argument given: * * - Returns +self+ if +self+ is a local time. - * - Otherwise returns a new \Time in the user's local timezone: + * - Otherwise returns a new +Time+ in the user's local timezone: * * t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC * t.localtime # => 2000-01-01 14:15:01 -0600 * * With argument +zone+ given, - * returns the new \Time object created by converting + * returns the new +Time+ object created by converting * +self+ to the given time zone: * * t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC * t.localtime("-09:00") # => 2000-01-01 11:15:01 -0900 * * For forms of argument +zone+, see - * {Timezone Specifiers}[rdoc-ref:timezones.rdoc]. + * {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers]. * */ @@ -4075,9 +4189,7 @@ time_localtime_m(int argc, VALUE *argv, VALUE time) * t.utc # => 2000-01-01 06:00:00 UTC * t.utc? # => true * - * Time#gmtime is an alias for Time#utc. - * - * Related: Time#getutc (returns a new converted \Time object). + * Related: Time#getutc (returns a new converted +Time+ object). */ static VALUE @@ -4088,7 +4200,7 @@ time_gmtime(VALUE time) GetTimeval(time, tobj); if (TZMODE_UTC_P(tobj)) { - if (tobj->tm_got) + if (tobj->vtm.tm_got) return time; } else { @@ -4097,9 +4209,9 @@ time_gmtime(VALUE time) vtm.zone = str_utc; GMTIMEW(tobj->timew, &vtm); - tobj->vtm = vtm; + time_set_vtm(time, tobj, vtm); - tobj->tm_got = 1; + tobj->vtm.tm_got = 1; TZMODE_SET_UTC(tobj); return time; } @@ -4113,7 +4225,7 @@ time_fixoff(VALUE time) GetTimeval(time, tobj); if (TZMODE_FIXOFF_P(tobj)) { - if (tobj->tm_got) + if (tobj->vtm.tm_got) return time; } else { @@ -4128,12 +4240,13 @@ time_fixoff(VALUE time) GMTIMEW(tobj->timew, &vtm); zone = tobj->vtm.zone; - tobj->vtm = vtm; - tobj->vtm.zone = zone; - vtm_add_offset(&tobj->vtm, off, +1); + vtm_add_offset(&vtm, off, +1); - tobj->tm_got = 1; - TZMODE_SET_FIXOFF(tobj, off); + time_set_vtm(time, tobj, vtm); + RB_OBJ_WRITE_UNALIGNED(time, &tobj->vtm.zone, zone); + + tobj->vtm.tm_got = 1; + TZMODE_SET_FIXOFF(time, tobj, off); return time; } @@ -4141,7 +4254,7 @@ time_fixoff(VALUE time) * call-seq: * getlocal(zone = nil) -> new_time * - * Returns a new \Time object representing the value of +self+ + * Returns a new +Time+ object representing the value of +self+ * converted to a given timezone; * if +zone+ is +nil+, the local timezone is used: * @@ -4150,7 +4263,7 @@ time_fixoff(VALUE time) * t.getlocal('+12:00') # => 2000-01-01 12:00:00 +1200 * * For forms of argument +zone+, see - * {Timezone Specifiers}[rdoc-ref:timezones.rdoc]. + * {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers]. * */ @@ -4190,7 +4303,7 @@ time_getlocaltime(int argc, VALUE *argv, VALUE time) * call-seq: * getutc -> new_time * - * Returns a new \Time object representing the value of +self+ + * Returns a new +Time+ object representing the value of +self+ * converted to the UTC timezone: * * local = Time.local(2000) # => 2000-01-01 00:00:00 -0600 @@ -4199,7 +4312,6 @@ time_getlocaltime(int argc, VALUE *argv, VALUE time) * utc.utc? # => true * utc == local # => true * - * Time#getgm is an alias for Time#getutc. */ static VALUE @@ -4233,8 +4345,6 @@ static VALUE strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding * t.strftime('%a %b %e %T %Y') # => "Sun Dec 31 23:59:59 2000" * t.strftime('%c') # => "Sun Dec 31 23:59:59 2000" * - * Time#asctime is an alias for Time#ctime. - * * Related: Time#to_s, Time#inspect: * * t.inspect # => "2000-12-31 23:59:59.5 +000001" @@ -4358,7 +4468,7 @@ time_add(const struct time_object *tobj, VALUE torig, VALUE offset, int sign) * call-seq: * self + numeric -> new_time * - * Returns a new \Time object whose value is the sum of the numeric value + * Returns a new +Time+ object whose value is the sum of the numeric value * of +self+ and the given +numeric+: * * t = Time.new(2000) # => 2000-01-01 00:00:00 -0600 @@ -4386,7 +4496,7 @@ time_plus(VALUE time1, VALUE time2) * self - other_time -> float * * When +numeric+ is given, - * returns a new \Time object whose value is the difference + * returns a new +Time+ object whose value is the difference * of the numeric value of +self+ and +numeric+: * * t = Time.new(2000) # => 2000-01-01 00:00:00 -0600 @@ -4395,7 +4505,7 @@ time_plus(VALUE time1, VALUE time2) * * When +other_time+ is given, * returns a Float whose value is the difference - * of the numeric values of +self+ and +other_time+: + * of the numeric values of +self+ and +other_time+ in seconds: * * t - t # => 0.0 * @@ -4436,7 +4546,7 @@ ndigits_denominator(VALUE ndigits) * call-seq: * round(ndigits = 0) -> new_time * - * Returns a new \Time object whose numeric value is that of +self+, + * Returns a new +Time+ object whose numeric value is that of +self+, * with its seconds value rounded to precision +ndigits+: * * t = Time.utc(2010, 3, 30, 5, 43, 25.123456789r) @@ -4485,7 +4595,7 @@ time_round(int argc, VALUE *argv, VALUE time) * call-seq: * floor(ndigits = 0) -> new_time * - * Returns a new \Time object whose numerical value + * Returns a new +Time+ object whose numerical value * is less than or equal to +self+ with its seconds * truncated to precision +ndigits+: * @@ -4530,7 +4640,7 @@ time_floor(int argc, VALUE *argv, VALUE time) * call-seq: * ceil(ndigits = 0) -> new_time * - * Returns a new \Time object whose numerical value + * Returns a new +Time+ object whose numerical value * is greater than or equal to +self+ with its seconds * truncated to precision +ndigits+: * @@ -4660,8 +4770,6 @@ time_hour(VALUE time) * # => 2000-01-02 03:04:05 +000006 * t.mday # => 2 * - * Time#day is an alias for Time#mday. - * * Related: Time#year, Time#hour, Time#min. */ @@ -4686,8 +4794,6 @@ time_mday(VALUE time) * # => 2000-01-02 03:04:05 +000006 * t.mon # => 1 * - * Time#month is an alias for Time#mday. - * * Related: Time#year, Time#hour, Time#min. */ @@ -4912,7 +5018,6 @@ time_yday(VALUE time) * t.zone # => "Central Daylight Time" * t.dst? # => true * - * Time#isdst is an alias for Time#dst?. */ static VALUE @@ -4968,7 +5073,6 @@ time_zone(VALUE time) * Time.utc(2000, 1, 1).utc_offset # => 0 * Time.local(2000, 1, 1).utc_offset # => -21600 # -6*3600, or minus six hours. * - * Time#gmt_offset and Time#gmtoff are aliases for Time#utc_offset. */ VALUE @@ -4998,7 +5102,7 @@ rb_time_utc_offset(VALUE time) * # [sec, min, hour, day, mon, year, wday, yday, dst?, zone] * * The returned array is suitable for use as an argument to Time.utc or Time.local - * to create a new \Time object. + * to create a new +Time+ object. * */ @@ -5186,6 +5290,136 @@ time_strftime(VALUE time, VALUE format) } } +/* + * call-seq: + * xmlschema(fraction_digits=0) -> string + * + * Returns a string which represents the time as a dateTime defined by XML + * Schema: + * + * CCYY-MM-DDThh:mm:ssTZD + * CCYY-MM-DDThh:mm:ss.sssTZD + * + * where TZD is Z or [+-]hh:mm. + * + * If self is a UTC time, Z is used as TZD. [+-]hh:mm is used otherwise. + * + * +fraction_digits+ specifies a number of digits to use for fractional + * seconds. Its default value is 0. + * + * t = Time.now + * t.xmlschema # => "2011-10-05T22:26:12-04:00" + */ + +static VALUE +time_xmlschema(int argc, VALUE *argv, VALUE time) +{ + long fraction_digits = 0; + rb_check_arity(argc, 0, 1); + if (argc > 0) { + fraction_digits = NUM2LONG(argv[0]); + if (fraction_digits < 0) { + fraction_digits = 0; + } + } + + struct time_object *tobj; + + GetTimeval(time, tobj); + MAKE_TM(time, tobj); + + const long size_after_year = sizeof("-MM-DDTHH:MM:SS+ZH:ZM") + fraction_digits + + (fraction_digits > 0); + VALUE str; + char *ptr; + +# define fill_digits_long(len, prec, n) \ + for (int fill_it = 1, written = snprintf(ptr, len, "%0*ld", prec, n); \ + fill_it; ptr += written, fill_it = 0) + + if (FIXNUM_P(tobj->vtm.year)) { + long year = FIX2LONG(tobj->vtm.year); + int year_width = (year < 0) + rb_strlen_lit("YYYY"); + int w = (year >= -9999 && year <= 9999 ? year_width : (year < 0) + (int)DECIMAL_SIZE_OF(year)); + str = rb_usascii_str_new(0, w + size_after_year); + ptr = RSTRING_PTR(str); + fill_digits_long(w + 1, year_width, year) { + if (year >= -9999 && year <= 9999) { + RUBY_ASSERT(written == year_width); + } + else { + RUBY_ASSERT(written >= year_width); + RUBY_ASSERT(written <= w); + } + } + } + else { + str = rb_int2str(tobj->vtm.year, 10); + rb_str_modify_expand(str, size_after_year); + ptr = RSTRING_END(str); + } + +# define fill_2(c, n) (*ptr++ = c, *ptr++ = '0' + (n) / 10, *ptr++ = '0' + (n) % 10) + fill_2('-', tobj->vtm.mon); + fill_2('-', tobj->vtm.mday); + fill_2('T', tobj->vtm.hour); + fill_2(':', tobj->vtm.min); + fill_2(':', tobj->vtm.sec); + + if (fraction_digits > 0) { + VALUE subsecx = tobj->vtm.subsecx; + long subsec; + int digits = -1; + *ptr++ = '.'; + if (fraction_digits <= TIME_SCALE_NUMDIGITS) { + digits = TIME_SCALE_NUMDIGITS - (int)fraction_digits; + } + else { + long w = fraction_digits - TIME_SCALE_NUMDIGITS; /* > 0 */ + subsecx = mulv(subsecx, rb_int_positive_pow(10, (unsigned long)w)); + if (!RB_INTEGER_TYPE_P(subsecx)) { /* maybe Rational */ + subsecx = rb_Integer(subsecx); + } + if (FIXNUM_P(subsecx)) digits = 0; + } + if (digits >= 0 && fraction_digits < INT_MAX) { + subsec = NUM2LONG(subsecx); + if (digits > 0) subsec /= (long)pow(10, digits); + fill_digits_long(fraction_digits + 1, (int)fraction_digits, subsec) { + RUBY_ASSERT(written == (int)fraction_digits); + } + } + else { + subsecx = rb_int2str(subsecx, 10); + long len = RSTRING_LEN(subsecx); + if (fraction_digits > len) { + memset(ptr, '0', fraction_digits - len); + } + else { + len = fraction_digits; + } + ptr += fraction_digits; + memcpy(ptr - len, RSTRING_PTR(subsecx), len); + } + } + + if (TZMODE_UTC_P(tobj)) { + *ptr = 'Z'; + ptr++; + } + else { + long offset = NUM2LONG(rb_time_utc_offset(time)); + char sign = offset < 0 ? '-' : '+'; + if (offset < 0) offset = -offset; + offset /= 60; + fill_2(sign, offset / 60); + fill_2(':', offset % 60); + } + const char *const start = RSTRING_PTR(str); + rb_str_set_len(str, ptr - start); // We could skip coderange scanning as we know it's full ASCII. + return str; +} + int ruby_marshal_write_long(long x, char *buf); enum {base_dump_size = 8}; @@ -5495,8 +5729,9 @@ end_submicro: ; GetNewTimeval(time, tobj); TZMODE_SET_LOCALTIME(tobj); - tobj->tm_got = 0; - tobj->timew = timew; + tobj->vtm.tm_got = 0; + time_set_timew(time, tobj, timew); + if (gmt) { TZMODE_SET_UTC(tobj); } @@ -5506,7 +5741,7 @@ end_submicro: ; } if (!NIL_P(zone)) { zone = mload_zone(time, zone); - tobj->vtm.zone = zone; + RB_OBJ_WRITE(time, &tobj->vtm.zone, zone); zone_localtime(zone, time); } @@ -5535,7 +5770,6 @@ time_load(VALUE klass, VALUE str) /* * call-seq: - * * Time::tm.from_time(t) -> tm * * Creates new Time::tm object from a Time object. @@ -5546,57 +5780,35 @@ tm_from_time(VALUE klass, VALUE time) { struct time_object *tobj; struct vtm vtm, *v; -#if TM_IS_TIME VALUE tm; struct time_object *ttm; GetTimeval(time, tobj); tm = time_s_alloc(klass); - ttm = DATA_PTR(tm); + ttm = RTYPEDDATA_GET_DATA(tm); v = &vtm; - GMTIMEW(ttm->timew = tobj->timew, v); - ttm->timew = wsub(ttm->timew, v->subsecx); + + WIDEVALUE timew = tobj->timew; + GMTIMEW(timew, v); + time_set_timew(tm, ttm, wsub(timew, v->subsecx)); v->subsecx = INT2FIX(0); v->zone = Qnil; - ttm->vtm = *v; - ttm->tm_got = 1; + time_set_vtm(tm, ttm, *v); + + ttm->vtm.tm_got = 1; TZMODE_SET_UTC(ttm); return tm; -#else - VALUE args[8]; - int i = 0; - - GetTimeval(time, tobj); - if (tobj->tm_got && TZMODE_UTC_P(tobj)) - v = &tobj->vtm; - else - GMTIMEW(tobj->timew, v = &vtm); - args[i++] = v->year; - args[i++] = INT2FIX(v->mon); - args[i++] = INT2FIX(v->mday); - args[i++] = INT2FIX(v->hour); - args[i++] = INT2FIX(v->min); - args[i++] = INT2FIX(v->sec); - switch (v->isdst) { - case 0: args[i++] = Qfalse; break; - case 1: args[i++] = Qtrue; break; - default: args[i++] = Qnil; break; - } - args[i++] = w2v(rb_time_unmagnify(tobj->timew)); - return rb_class_new_instance(i, args, klass); -#endif } /* * call-seq: - * * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm * * Creates new Time::tm object. */ static VALUE -tm_initialize(int argc, VALUE *argv, VALUE tm) +tm_initialize(int argc, VALUE *argv, VALUE time) { struct vtm vtm; wideval_t t; @@ -5604,28 +5816,15 @@ tm_initialize(int argc, VALUE *argv, VALUE tm) if (rb_check_arity(argc, 1, 7) > 6) argc = 6; time_arg(argc, argv, &vtm); t = timegmw(&vtm); - { -#if TM_IS_TIME - struct time_object *tobj = DATA_PTR(tm); - TZMODE_SET_UTC(tobj); - tobj->timew = t; - tobj->vtm = vtm; -#else - int i = 0; - RSTRUCT_SET(tm, i++, INT2FIX(vtm.sec)); - RSTRUCT_SET(tm, i++, INT2FIX(vtm.min)); - RSTRUCT_SET(tm, i++, INT2FIX(vtm.hour)); - RSTRUCT_SET(tm, i++, INT2FIX(vtm.mday)); - RSTRUCT_SET(tm, i++, INT2FIX(vtm.mon)); - RSTRUCT_SET(tm, i++, vtm.year); - RSTRUCT_SET(tm, i++, w2v(rb_time_unmagnify(t))); -#endif - } - return tm; + struct time_object *tobj = RTYPEDDATA_GET_DATA(time); + TZMODE_SET_UTC(tobj); + time_set_timew(time, tobj, t); + time_set_vtm(time, tobj, vtm); + + return time; } /* call-seq: - * * tm.to_time -> time * * Returns a new Time object. @@ -5634,52 +5833,14 @@ tm_initialize(int argc, VALUE *argv, VALUE tm) static VALUE tm_to_time(VALUE tm) { -#if TM_IS_TIME struct time_object *torig = get_timeval(tm); VALUE dup = time_s_alloc(rb_cTime); - struct time_object *tobj = DATA_PTR(dup); + struct time_object *tobj = RTYPEDDATA_GET_DATA(dup); *tobj = *torig; return dup; -#else - VALUE t[6]; - const VALUE *p = RSTRUCT_CONST_PTR(tm); - int i; - - for (i = 0; i < numberof(t); ++i) { - t[i] = p[numberof(t) - 1 - i]; - } - return time_s_mkutc(numberof(t), t, rb_cTime); -#endif -} - -#if !TM_IS_TIME -static VALUE -tm_zero(VALUE tm) -{ - return INT2FIX(0); -} - -#define tm_subsec tm_zero -#define tm_utc_offset tm_zero - -static VALUE -tm_isdst(VALUE tm) -{ - return Qfalse; } static VALUE -tm_to_s(VALUE tm) -{ - const VALUE *p = RSTRUCT_CONST_PTR(tm); - - return rb_sprintf("%.4"PRIsVALUE"-%.2"PRIsVALUE"-%.2"PRIsVALUE" " - "%.2"PRIsVALUE":%.2"PRIsVALUE":%.2"PRIsVALUE" " - "UTC", - p[5], p[4], p[3], p[2], p[1], p[0]); -} -#else -static VALUE tm_plus(VALUE tm, VALUE offset) { return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, +1); @@ -5690,14 +5851,12 @@ tm_minus(VALUE tm, VALUE offset) { return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, -1); } -#endif static VALUE Init_tm(VALUE outer, const char *name) { /* :stopdoc:*/ VALUE tm; -#if TM_IS_TIME tm = rb_define_class_under(outer, name, rb_cObject); rb_define_alloc_func(tm, time_s_alloc); rb_define_method(tm, "sec", time_sec, 0); @@ -5730,18 +5889,6 @@ Init_tm(VALUE outer, const char *name) rb_define_method(tm, "to_r", time_to_r, 0); rb_define_method(tm, "+", tm_plus, 1); rb_define_method(tm, "-", tm_minus, 1); -#else - tm = rb_struct_define_under(outer, "tm", - "sec", "min", "hour", - "mday", "mon", "year", - "to_i", NULL); - rb_define_method(tm, "subsec", tm_subsec, 0); - rb_define_method(tm, "utc_offset", tm_utc_offset, 0); - rb_define_method(tm, "to_s", tm_to_s, 0); - rb_define_method(tm, "inspect", tm_to_s, 0); - rb_define_method(tm, "isdst", tm_isdst, 0); - rb_define_method(tm, "dst?", tm_isdst, 0); -#endif rb_define_method(tm, "initialize", tm_initialize, -1); rb_define_method(tm, "utc", tm_to_time, 0); rb_alias(tm, rb_intern_const("to_time"), rb_intern_const("utc")); @@ -5782,20 +5929,14 @@ rb_time_zone_abbreviation(VALUE zone, VALUE time) return rb_obj_as_string(abbr); } -/* Internal Details: - * - * Since Ruby 1.9.2, Time implementation uses a signed 63 bit integer or - * Integer(T_BIGNUM), Rational. - * The integer is a number of nanoseconds since the _Epoch_ which can - * represent 1823-11-12 to 2116-02-20. - * When Integer(T_BIGNUM) or Rational is used (before 1823, after 2116, under - * nanosecond), Time works slower than when integer is used. - */ - // void Init_Time(void) { +#ifdef _WIN32 + ruby_reset_timezone(getenv("TZ")); +#endif + id_submicro = rb_intern_const("submicro"); id_nano_num = rb_intern_const("nano_num"); id_nano_den = rb_intern_const("nano_den"); @@ -5830,9 +5971,9 @@ Init_Time(void) sym_zone = ID2SYM(rb_intern_const("zone")); str_utc = rb_fstring_lit("UTC"); - rb_gc_register_mark_object(str_utc); + rb_vm_register_global_object(str_utc); str_empty = rb_fstring_lit(""); - rb_gc_register_mark_object(str_empty); + rb_vm_register_global_object(str_empty); rb_cTime = rb_define_class("Time", rb_cObject); VALUE scTime = rb_singleton_class(rb_cTime); @@ -5909,19 +6050,16 @@ Init_Time(void) rb_define_method(rb_cTime, "subsec", time_subsec, 0); rb_define_method(rb_cTime, "strftime", time_strftime, 1); + rb_define_method(rb_cTime, "xmlschema", time_xmlschema, -1); + rb_define_alias(rb_cTime, "iso8601", "xmlschema"); /* methods for marshaling */ rb_define_private_method(rb_cTime, "_dump", time_dump, -1); rb_define_private_method(scTime, "_load", time_load, 1); -#if 0 - /* Time will support marshal_dump and marshal_load in the future (1.9 maybe) */ - rb_define_private_method(rb_cTime, "marshal_dump", time_mdump, 0); - rb_define_private_method(rb_cTime, "marshal_load", time_mload, 1); -#endif if (debug_find_time_numguess) { rb_define_hooked_variable("$find_time_numguess", (VALUE *)&find_time_numguess, - find_time_numguess_getter, NULL); + find_time_numguess_getter, 0); } rb_cTimeTM = Init_tm(rb_cTime, "tm"); |
