diff options
Diffstat (limited to 'time.c')
-rw-r--r-- | time.c | 533 |
1 files changed, 286 insertions, 247 deletions
@@ -42,6 +42,7 @@ #include "internal/time.h" #include "internal/variable.h" #include "ruby/encoding.h" +#include "ruby/util.h" #include "timev.h" #include "builtin.h" @@ -62,10 +63,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)) @@ -574,6 +571,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 +654,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; } @@ -1322,9 +1322,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; } @@ -1508,7 +1505,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) @@ -1761,32 +1758,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 +1790,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 +1802,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,7 +1829,7 @@ 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); } @@ -1829,17 +1845,15 @@ time_mark(void *ptr) rb_gc_mark(tobj->vtm.zone); } -static size_t -time_memsize(const void *tobj) -{ - return sizeof(struct time_object); -} - static const rb_data_type_t time_data_type = { "time", - {time_mark, RUBY_TYPED_DEFAULT_FREE, time_memsize,}, + { + time_mark, + RUBY_TYPED_DEFAULT_FREE, + NULL, // No external memory to report, + }, 0, 0, - (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE), + (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE), }; static VALUE @@ -1849,9 +1863,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; @@ -1948,6 +1962,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 +1975,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 +2001,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; } @@ -2239,10 +2256,12 @@ extract_time(VALUE time) } 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 +2279,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; \ @@ -2279,13 +2299,15 @@ extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx) } 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 +2316,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 +2326,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,10 +2347,10 @@ 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; @@ -2335,8 +2361,12 @@ zone_timelocal(VALUE zone, VALUE time) 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 +2374,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 +2383,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; + s = extract_vtm(local, time, tobj, subsecx); + tobj->vtm.tm_got = 1; zone_set_offset(zone, tobj, s, t); zone_set_dst(zone, tobj, tm); + + RB_GC_GUARD(time); + return 1; } @@ -2383,6 +2416,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 +2482,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 +2497,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); } } @@ -2494,7 +2532,7 @@ two_digits(const char *ptr, const char *end, const char **endp, const char *name ((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)); @@ -2511,8 +2549,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 +2569,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 +2579,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,12 +2642,11 @@ 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) { VALUE mul = rb_int_positive_pow(10, TIME_SCALE_NUMDIGITS - ndigits); subsec = rb_int_mul(subsec, mul); @@ -2611,6 +2657,9 @@ time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, } } +only_year: + ; + struct vtm vtm = { .wday = VTM_WDAY_INITVAL, .yday = 0, @@ -2623,7 +2672,7 @@ 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 @@ -2673,9 +2722,9 @@ 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; } @@ -2702,7 +2751,7 @@ rb_time_timespec_new(const struct timespec *ts, int offset) 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 +3596,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 +3657,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 +3674,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 +3688,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 +3705,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 +3739,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 +3818,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 +3845,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 +3934,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 @@ -3922,8 +3963,6 @@ time_eql(VALUE time1, VALUE time2) * # => 2000-01-01 20:15:01 UTC * utc.utc? # => true * - * Time#gmt? is an alias for Time#utc?. - * * Related: Time.utc. */ @@ -3985,7 +4024,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 +4038,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 +4074,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 +4114,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 +4125,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 +4134,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 +4150,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 +4165,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); + + time_set_vtm(time, tobj, vtm); + RB_OBJ_WRITE_UNALIGNED(time, &tobj->vtm.zone, zone); - tobj->tm_got = 1; - TZMODE_SET_FIXOFF(tobj, off); + tobj->vtm.tm_got = 1; + TZMODE_SET_FIXOFF(time, tobj, off); return time; } @@ -4141,7 +4179,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 +4188,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 +4228,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 +4237,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 +4270,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 +4393,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 +4421,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 +4430,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 +4471,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 +4520,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 +4565,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 +4695,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 +4719,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 +4943,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 +4998,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 +5027,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 +5215,115 @@ time_strftime(VALUE time, VALUE format) } } +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 +5633,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); } @@ -5546,45 +5685,22 @@ 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); 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 } /* @@ -5596,7 +5712,7 @@ tm_from_time(VALUE klass, VALUE time) */ 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,24 +5720,12 @@ 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: @@ -5634,52 +5738,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 +5756,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 +5794,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,16 +5834,6 @@ 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) @@ -5830,9 +5872,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 +5951,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"); |