summaryrefslogtreecommitdiff
path: root/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'time.c')
-rw-r--r--time.c404
1 files changed, 308 insertions, 96 deletions
diff --git a/time.c b/time.c
index 7023414bfd..b2eed2daf3 100644
--- a/time.c
+++ b/time.c
@@ -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);