summaryrefslogtreecommitdiff
path: root/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'time.c')
-rw-r--r--time.c533
1 files changed, 286 insertions, 247 deletions
diff --git a/time.c b/time.c
index 9c4c93939e..e3f58e3645 100644
--- a/time.c
+++ b/time.c
@@ -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");