diff options
Diffstat (limited to 'time.c')
| -rw-r--r-- | time.c | 404 |
1 files changed, 308 insertions, 96 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; @@ -244,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)) @@ -331,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) { @@ -570,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) { @@ -650,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; } @@ -697,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(); } @@ -929,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 @@ -1436,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 @@ -1501,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) @@ -1645,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 @@ -1830,26 +1889,27 @@ force_make_tm(VALUE time, struct time_object *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); + 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, - NULL, // No external memory to report, + .wrap_struct_name = "time", + .function = { + .dmark = time_mark_and_move, + .dfree = RUBY_TYPED_DEFAULT_FREE, + .dsize = NULL, + .dcompact = time_mark_and_move, }, - 0, 0, - (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE), + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE, }; static VALUE @@ -1896,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; @@ -1958,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) { @@ -2145,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) { @@ -2199,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; @@ -2226,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); } @@ -2244,7 +2312,7 @@ 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)) { @@ -2287,7 +2355,7 @@ extract_vtm(VALUE time, VALUE orig_time, struct time_object *orig_tobj, VALUE su 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)) { @@ -2342,13 +2410,13 @@ zone_timelocal(VALUE zone, VALUE 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)); @@ -2377,7 +2445,7 @@ zone_localtime(VALUE zone, VALUE time) s = extract_vtm(local, time, tobj, subsecx); tobj->vtm.tm_got = 1; - zone_set_offset(zone, tobj, s, t); + zone_set_offset(zone, tobj, s, t, time); zone_set_dst(zone, tobj, tm); RB_GC_GUARD(time); @@ -2408,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) @@ -2516,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 @@ -2537,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)) { @@ -2631,15 +2706,11 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, } 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); } @@ -2660,19 +2731,19 @@ only_year: .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; @@ -2680,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; } @@ -2697,13 +2764,6 @@ 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) { @@ -2717,25 +2777,39 @@ time_new_timew(VALUE klass, wideval_t 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); @@ -3947,10 +4021,21 @@ 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+ 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. */ @@ -3990,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; } @@ -5203,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}; @@ -5524,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); } @@ -5553,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. @@ -5571,8 +5787,10 @@ tm_from_time(VALUE klass, VALUE time) tm = time_s_alloc(klass); 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; time_set_vtm(tm, ttm, *v); @@ -5584,7 +5802,6 @@ tm_from_time(VALUE klass, VALUE time) /* * call-seq: - * * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm * * Creates new Time::tm object. @@ -5608,7 +5825,6 @@ tm_initialize(int argc, VALUE *argv, VALUE time) } /* call-seq: - * * tm.to_time -> time * * Returns a new Time object. @@ -5713,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"); @@ -5761,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); @@ -5840,6 +6050,8 @@ 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); |
