1 /**********************************************************************
6 created at: Tue Dec 28 14:31:59 JST 1993
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
10 **********************************************************************/
12 #define _DEFAULT_SOURCE
14 #include "ruby/internal/config.h"
20 #include <sys/types.h>
30 #if defined(HAVE_SYS_TIME_H)
31 # include <sys/time.h>
36 #include "internal/array.h"
37 #include "internal/compar.h"
38 #include "internal/numeric.h"
39 #include "internal/rational.h"
40 #include "internal/string.h"
41 #include "internal/time.h"
42 #include "internal/variable.h"
43 #include "ruby/encoding.h"
48 static ID id_submicro
, id_nano_num
, id_nano_den
, id_offset
, id_zone
;
49 static ID id_nanosecond
, id_microsecond
, id_millisecond
, id_nsec
, id_usec
;
50 static ID id_local_to_utc
, id_utc_to_local
, id_find_timezone
;
51 static ID id_year
, id_mon
, id_mday
, id_hour
, id_min
, id_sec
, id_isdst
;
52 static VALUE str_utc
, str_empty
;
56 #define id_divmod idDivmod
57 #define id_name idName
58 #define UTC_ZONE Qundef
64 #define NDIV(x,y) (-(-((x)+1)/(y))-1)
65 #define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
66 #define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
67 #define MOD(n,d) ((n)<0 ? NMOD((n),(d)) : (n)%(d))
68 #define VTM_WDAY_INITVAL (7)
69 #define VTM_ISDST_INITVAL (3)
74 if (FIXNUM_P(x
) && FIXNUM_P(y
)) {
77 return RTEST(rb_funcall(x
, idEq
, 1, y
));
83 if (FIXNUM_P(x
) && FIXNUM_P(y
)) {
84 if ((long)x
< (long)y
)
86 if ((long)x
> (long)y
)
90 if (RB_BIGNUM_TYPE_P(x
)) return FIX2INT(rb_big_cmp(x
, y
));
91 return rb_cmpint(rb_funcall(x
, idCmp
, 1, y
), x
, y
);
94 #define ne(x,y) (!eq((x),(y)))
95 #define lt(x,y) (cmp((x),(y)) < 0)
96 #define gt(x,y) (cmp((x),(y)) > 0)
97 #define le(x,y) (cmp((x),(y)) <= 0)
98 #define ge(x,y) (cmp((x),(y)) >= 0)
101 addv(VALUE x
, VALUE y
)
103 if (FIXNUM_P(x
) && FIXNUM_P(y
)) {
104 return LONG2NUM(FIX2LONG(x
) + FIX2LONG(y
));
106 if (RB_BIGNUM_TYPE_P(x
)) return rb_big_plus(x
, y
);
107 return rb_funcall(x
, '+', 1, y
);
111 subv(VALUE x
, VALUE y
)
113 if (FIXNUM_P(x
) && FIXNUM_P(y
)) {
114 return LONG2NUM(FIX2LONG(x
) - FIX2LONG(y
));
116 if (RB_BIGNUM_TYPE_P(x
)) return rb_big_minus(x
, y
);
117 return rb_funcall(x
, '-', 1, y
);
121 mulv(VALUE x
, VALUE y
)
123 if (FIXNUM_P(x
) && FIXNUM_P(y
)) {
124 return rb_fix_mul_fix(x
, y
);
126 if (RB_BIGNUM_TYPE_P(x
))
127 return rb_big_mul(x
, y
);
128 return rb_funcall(x
, '*', 1, y
);
132 divv(VALUE x
, VALUE y
)
134 if (FIXNUM_P(x
) && FIXNUM_P(y
)) {
135 return rb_fix_div_fix(x
, y
);
137 if (RB_BIGNUM_TYPE_P(x
))
138 return rb_big_div(x
, y
);
139 return rb_funcall(x
, id_div
, 1, y
);
143 modv(VALUE x
, VALUE y
)
146 if (FIX2LONG(y
) == 0) rb_num_zerodiv();
147 if (FIXNUM_P(x
)) return rb_fix_mod_fix(x
, y
);
149 if (RB_BIGNUM_TYPE_P(x
)) return rb_big_modulo(x
, y
);
150 return rb_funcall(x
, '%', 1, y
);
153 #define neg(x) (subv(INT2FIX(0), (x)))
156 quor(VALUE x
, VALUE y
)
158 if (FIXNUM_P(x
) && FIXNUM_P(y
)) {
162 if (b
== 0) rb_num_zerodiv();
163 if (a
== FIXNUM_MIN
&& b
== -1) return LONG2NUM(-a
);
169 return rb_numeric_quo(x
, y
);
173 quov(VALUE x
, VALUE y
)
175 VALUE ret
= quor(x
, y
);
176 if (RB_TYPE_P(ret
, T_RATIONAL
) &&
177 RRATIONAL(ret
)->den
== INT2FIX(1)) {
178 ret
= RRATIONAL(ret
)->num
;
183 #define mulquov(x,y,z) (((y) == (z)) ? (x) : quov(mulv((x),(y)),(z)))
186 divmodv(VALUE n
, VALUE d
, VALUE
*q
, VALUE
*r
)
190 if (FIX2LONG(d
) == 0) rb_num_zerodiv();
192 rb_fix_divmod_fix(n
, d
, q
, r
);
196 tmp
= rb_funcall(n
, id_divmod
, 1, d
);
197 ary
= rb_check_array_type(tmp
);
199 rb_raise(rb_eTypeError
, "unexpected divmod result: into %"PRIsVALUE
,
202 *q
= rb_ary_entry(ary
, 0);
203 *r
= rb_ary_entry(ary
, 1);
207 # define INT64toNUM(x) LONG2NUM(x)
208 #elif defined(HAVE_LONG_LONG) && SIZEOF_LONG_LONG == 8
209 # define INT64toNUM(x) LL2NUM(x)
212 #if defined(HAVE_UINT64_T) && SIZEOF_LONG*2 <= SIZEOF_UINT64_T
213 typedef uint64_t uwideint_t
;
214 typedef int64_t wideint_t
;
215 typedef uint64_t WIDEVALUE
;
216 typedef int64_t SIGNED_WIDEVALUE
;
217 # define WIDEVALUE_IS_WIDER 1
218 # define UWIDEINT_MAX UINT64_MAX
219 # define WIDEINT_MAX INT64_MAX
220 # define WIDEINT_MIN INT64_MIN
221 # define FIXWINT_P(tv) ((tv) & 1)
222 # define FIXWVtoINT64(tv) RSHIFT((SIGNED_WIDEVALUE)(tv), 1)
223 # define INT64toFIXWV(wi) ((WIDEVALUE)((SIGNED_WIDEVALUE)(wi) << 1 | FIXNUM_FLAG))
224 # define FIXWV_MAX (((int64_t)1 << 62) - 1)
225 # define FIXWV_MIN (-((int64_t)1 << 62))
226 # define FIXWVABLE(wi) (POSFIXWVABLE(wi) && NEGFIXWVABLE(wi))
227 # define WINT2FIXWV(i) WIDEVAL_WRAP(INT64toFIXWV(i))
228 # define FIXWV2WINT(w) FIXWVtoINT64(WIDEVAL_GET(w))
230 typedef unsigned long uwideint_t
;
231 typedef long wideint_t
;
232 typedef VALUE WIDEVALUE
;
233 typedef SIGNED_VALUE SIGNED_WIDEVALUE
;
234 # define WIDEVALUE_IS_WIDER 0
235 # define UWIDEINT_MAX ULONG_MAX
236 # define WIDEINT_MAX LONG_MAX
237 # define WIDEINT_MIN LONG_MIN
238 # define FIXWINT_P(v) FIXNUM_P(v)
239 # define FIXWV_MAX FIXNUM_MAX
240 # define FIXWV_MIN FIXNUM_MIN
241 # define FIXWVABLE(i) FIXABLE(i)
242 # define WINT2FIXWV(i) WIDEVAL_WRAP(LONG2FIX(i))
243 # define FIXWV2WINT(w) FIX2LONG(WIDEVAL_GET(w))
246 #define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1)
247 #define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN)
248 #define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w))
249 #define MUL_OVERFLOW_FIXWV_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXWV_MIN, FIXWV_MAX)
251 /* #define STRUCT_WIDEVAL */
252 #ifdef STRUCT_WIDEVAL
253 /* for type checking */
257 static inline wideval_t
WIDEVAL_WRAP(WIDEVALUE v
) { wideval_t w
= { v
}; return w
; }
258 # define WIDEVAL_GET(w) ((w).value)
260 typedef WIDEVALUE wideval_t
;
261 # define WIDEVAL_WRAP(v) (v)
262 # define WIDEVAL_GET(w) (w)
265 #if WIDEVALUE_IS_WIDER
266 static inline wideval_t
267 wint2wv(wideint_t wi
)
270 return WINT2FIXWV(wi
);
272 return WIDEVAL_WRAP(INT64toNUM(wi
));
274 # define WINT2WV(wi) wint2wv(wi)
276 # define WINT2WV(wi) WIDEVAL_WRAP(LONG2NUM(wi))
282 #if WIDEVALUE_IS_WIDER
284 return INT64toNUM(FIXWV2WINT(w
));
285 return (VALUE
)WIDEVAL_GET(w
);
287 return WIDEVAL_GET(w
);
291 #if WIDEVALUE_IS_WIDER
297 sign
= rb_integer_pack(v
, &u
, 1, sizeof(u
), 0,
298 INTEGER_PACK_NATIVE_BYTE_ORDER
);
300 return WINT2FIXWV(0);
301 else if (sign
== -1) {
303 return WINT2FIXWV(-(wideint_t
)u
);
305 else if (sign
== +1) {
307 return WINT2FIXWV((wideint_t
)u
);
309 return WIDEVAL_WRAP(v
);
313 static inline wideval_t
316 if (RB_TYPE_P(v
, T_RATIONAL
)) {
317 if (RRATIONAL(v
)->den
!= LONG2FIX(1))
318 return WIDEVAL_WRAP(v
);
319 v
= RRATIONAL(v
)->num
;
321 #if WIDEVALUE_IS_WIDER
323 return WIDEVAL_WRAP((WIDEVALUE
)(SIGNED_WIDEVALUE
)(long)v
);
325 else if (RB_BIGNUM_TYPE_P(v
) &&
326 rb_absint_size(v
, NULL
) <= sizeof(WIDEVALUE
)) {
327 return v2w_bignum(v
);
330 return WIDEVAL_WRAP(v
);
334 weq(wideval_t wx
, wideval_t wy
)
336 #if WIDEVALUE_IS_WIDER
337 if (FIXWV_P(wx
) && FIXWV_P(wy
)) {
338 return WIDEVAL_GET(wx
) == WIDEVAL_GET(wy
);
340 return RTEST(rb_funcall(w2v(wx
), idEq
, 1, w2v(wy
)));
342 return eq(WIDEVAL_GET(wx
), WIDEVAL_GET(wy
));
347 wcmp(wideval_t wx
, wideval_t wy
)
350 #if WIDEVALUE_IS_WIDER
351 if (FIXWV_P(wx
) && FIXWV_P(wy
)) {
367 #define wne(x,y) (!weq((x),(y)))
368 #define wlt(x,y) (wcmp((x),(y)) < 0)
369 #define wgt(x,y) (wcmp((x),(y)) > 0)
370 #define wle(x,y) (wcmp((x),(y)) <= 0)
371 #define wge(x,y) (wcmp((x),(y)) >= 0)
374 wadd(wideval_t wx
, wideval_t wy
)
376 #if WIDEVALUE_IS_WIDER
377 if (FIXWV_P(wx
) && FIXWV_P(wy
)) {
378 wideint_t r
= FIXWV2WINT(wx
) + FIXWV2WINT(wy
);
382 return v2w(addv(w2v(wx
), w2v(wy
)));
386 wsub(wideval_t wx
, wideval_t wy
)
388 #if WIDEVALUE_IS_WIDER
389 if (FIXWV_P(wx
) && FIXWV_P(wy
)) {
390 wideint_t r
= FIXWV2WINT(wx
) - FIXWV2WINT(wy
);
394 return v2w(subv(w2v(wx
), w2v(wy
)));
398 wmul(wideval_t wx
, wideval_t wy
)
400 #if WIDEVALUE_IS_WIDER
401 if (FIXWV_P(wx
) && FIXWV_P(wy
)) {
402 if (!MUL_OVERFLOW_FIXWV_P(FIXWV2WINT(wx
), FIXWV2WINT(wy
)))
403 return WINT2WV(FIXWV2WINT(wx
) * FIXWV2WINT(wy
));
406 return v2w(mulv(w2v(wx
), w2v(wy
)));
410 wquo(wideval_t wx
, wideval_t wy
)
412 #if WIDEVALUE_IS_WIDER
413 if (FIXWV_P(wx
) && FIXWV_P(wy
)) {
417 if (b
== 0) rb_num_zerodiv();
424 return v2w(quov(w2v(wx
), w2v(wy
)));
427 #define wmulquo(x,y,z) ((WIDEVAL_GET(y) == WIDEVAL_GET(z)) ? (x) : wquo(wmul((x),(y)),(z)))
428 #define wmulquoll(x,y,z) (((y) == (z)) ? (x) : wquo(wmul((x),WINT2WV(y)),WINT2WV(z)))
430 #if WIDEVALUE_IS_WIDER
432 wdivmod0(wideval_t wn
, wideval_t wd
, wideval_t
*wq
, wideval_t
*wr
)
434 if (FIXWV_P(wn
) && FIXWV_P(wd
)) {
435 wideint_t n
, d
, q
, r
;
437 if (d
== 0) rb_num_zerodiv();
444 wideint_t xneg
= -FIXWV2WINT(wn
);
457 if (d
> 0 ? r
< 0 : r
> 0) {
470 wdivmod(wideval_t wn
, wideval_t wd
, wideval_t
*wq
, wideval_t
*wr
)
473 #if WIDEVALUE_IS_WIDER
474 if (wdivmod0(wn
, wd
, wq
, wr
)) return;
476 divmodv(w2v(wn
), w2v(wd
), &vq
, &vr
);
482 wmuldivmod(wideval_t wx
, wideval_t wy
, wideval_t wz
, wideval_t
*wq
, wideval_t
*wr
)
484 if (WIDEVAL_GET(wy
) == WIDEVAL_GET(wz
)) {
489 wdivmod(wmul(wx
,wy
), wz
, wq
, wr
);
493 wdiv(wideval_t wx
, wideval_t wy
)
495 #if WIDEVALUE_IS_WIDER
497 if (wdivmod0(wx
, wy
, &q
, &dmy
)) return q
;
499 return v2w(divv(w2v(wx
), w2v(wy
)));
503 wmod(wideval_t wx
, wideval_t wy
)
505 #if WIDEVALUE_IS_WIDER
507 if (wdivmod0(wx
, wy
, &dmy
, &r
)) return r
;
509 return v2w(modv(w2v(wx
), w2v(wy
)));
523 return rb_rational_canonicalize(v
);
526 if ((tmp
= rb_check_funcall(v
, idTo_r
, 0, NULL
)) != Qundef
) {
527 /* test to_int method availability to reject non-Numeric
528 * objects such as String, Time, etc which have to_r method. */
529 if (!rb_respond_to(v
, idTo_int
)) {
532 else if (RB_INTEGER_TYPE_P(tmp
)) {
535 else if (RB_TYPE_P(tmp
, T_RATIONAL
)) {
536 return rb_rational_canonicalize(tmp
);
539 else if (!NIL_P(tmp
= rb_check_to_int(v
))) {
545 rb_raise(rb_eTypeError
, "can't convert %"PRIsVALUE
" into an exact number",
553 rb_time_magnify(wideval_t w
)
555 return wmul(w
, WINT2FIXWV(TIME_SCALE
));
559 rb_time_unmagnify_to_rational(wideval_t w
)
561 return quor(w2v(w
), INT2FIX(TIME_SCALE
));
565 rb_time_unmagnify(wideval_t w
)
567 return v2w(rb_time_unmagnify_to_rational(w
));
571 rb_time_unmagnify_to_float(wideval_t w
)
574 #if WIDEVALUE_IS_WIDER
581 return DBL2NUM((double)c
);
583 v
= DBL2NUM((double)FIXWV2WINT(w
));
584 return quov(v
, DBL2NUM(TIME_SCALE
));
588 if (RB_TYPE_P(v
, T_RATIONAL
))
589 return rb_Float(quov(v
, INT2FIX(TIME_SCALE
)));
591 return quov(v
, DBL2NUM(TIME_SCALE
));
595 split_second(wideval_t timew
, wideval_t
*timew_p
, VALUE
*subsecx_p
)
598 wdivmod(timew
, WINT2FIXWV(TIME_SCALE
), &q
, &r
);
606 #if WIDEVALUE_IS_WIDER
607 if (TIMET_MIN
== 0) {
608 uwideint_t wi
= (uwideint_t
)t
;
609 if (wi
<= FIXWV_MAX
) {
610 return WINT2FIXWV(wi
);
614 wideint_t wi
= (wideint_t
)t
;
615 if (FIXWV_MIN
<= wi
&& wi
<= FIXWV_MAX
) {
616 return WINT2FIXWV(wi
);
620 return v2w(TIMET2NUM(t
));
622 #define TIMET2WV(t) timet2wv(t)
625 wv2timet(wideval_t w
)
627 #if WIDEVALUE_IS_WIDER
629 wideint_t wi
= FIXWV2WINT(w
);
630 if (TIMET_MIN
== 0) {
632 rb_raise(rb_eRangeError
, "negative value to convert into `time_t'");
633 if (TIMET_MAX
< (uwideint_t
)wi
)
634 rb_raise(rb_eRangeError
, "too big to convert into `time_t'");
637 if (wi
< TIMET_MIN
|| TIMET_MAX
< wi
)
638 rb_raise(rb_eRangeError
, "too big to convert into `time_t'");
643 return NUM2TIMET(w2v(w
));
645 #define WV2TIMET(t) wv2timet(t)
648 static VALUE rb_cTimeTM
;
650 static int obj2int(VALUE obj
);
651 static uint32_t obj2ubits(VALUE obj
, unsigned int bits
);
652 static VALUE
obj2vint(VALUE obj
);
653 static uint32_t month_arg(VALUE arg
);
654 static VALUE
validate_utc_offset(VALUE utc_offset
);
655 static VALUE
validate_zone_name(VALUE zone_name
);
656 static void validate_vtm(struct vtm
*vtm
);
657 static uint32_t obj2subsecx(VALUE obj
, VALUE
*subsecx
);
659 static VALUE
time_gmtime(VALUE
);
660 static VALUE
time_localtime(VALUE
);
661 static VALUE
time_fixoff(VALUE
);
662 static VALUE
time_zonelocal(VALUE time
, VALUE off
);
664 static time_t timegm_noleapsecond(struct tm
*tm
);
665 static int tmcmp(struct tm
*a
, struct tm
*b
);
666 static int vtmcmp(struct vtm
*a
, struct vtm
*b
);
667 static const char *find_time_t(struct tm
*tptr
, int utc_p
, time_t *tp
);
669 static struct vtm
*localtimew(wideval_t timew
, struct vtm
*result
);
671 static int leap_year_p(long y
);
672 #define leap_year_v_p(y) leap_year_p(NUM2LONG(modv((y), INT2FIX(400))))
674 static VALUE
tm_from_time(VALUE klass
, VALUE time
);
676 bool ruby_tz_uptodate_p
;
679 ruby_reset_timezone(void)
681 ruby_tz_uptodate_p
= false;
682 ruby_reset_leap_second_info();
688 if (ruby_tz_uptodate_p
) return;
689 ruby_tz_uptodate_p
= true;
694 rb_localtime_r(const time_t *t
, struct tm
*result
)
696 #if defined __APPLE__ && defined __LP64__
697 if (*t
!= (time_t)(int)*t
) return NULL
;
701 result
= localtime_r(t
, result
);
704 struct tm
*tmp
= localtime(t
);
705 if (tmp
) *result
= *tmp
;
708 #if defined(HAVE_MKTIME) && defined(LOCALTIME_OVERFLOW_PROBLEM)
712 struct tm tmp
= *result
;
715 # if defined(HAVE_STRUCT_TM_TM_GMTOFF)
716 gmtoff1
= result
->tm_gmtoff
;
717 gmtoff2
= tmp
.tm_gmtoff
;
719 if (*t
+ gmtoff1
!= t2
+ gmtoff2
)
725 #define LOCALTIME(tm, result) rb_localtime_r((tm), &(result))
727 #ifndef HAVE_STRUCT_TM_TM_GMTOFF
729 rb_gmtime_r(const time_t *t
, struct tm
*result
)
732 result
= gmtime_r(t
, result
);
734 struct tm
*tmp
= gmtime(t
);
735 if (tmp
) *result
= *tmp
;
737 #if defined(HAVE_TIMEGM) && defined(LOCALTIME_OVERFLOW_PROBLEM)
738 if (result
&& *t
!= timegm(result
)) {
744 # define GMTIME(tm, result) rb_gmtime_r((tm), &(result))
747 static const int16_t common_year_yday_offset
[] = {
752 -1 + 31 + 28 + 31 + 30,
753 -1 + 31 + 28 + 31 + 30 + 31,
754 -1 + 31 + 28 + 31 + 30 + 31 + 30,
755 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31,
756 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
757 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
758 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
759 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
760 /* 1 2 3 4 5 6 7 8 9 10 11 */
762 static const int16_t leap_year_yday_offset
[] = {
767 -1 + 31 + 29 + 31 + 30,
768 -1 + 31 + 29 + 31 + 30 + 31,
769 -1 + 31 + 29 + 31 + 30 + 31 + 30,
770 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31,
771 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
772 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
773 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
774 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
775 /* 1 2 3 4 5 6 7 8 9 10 11 */
778 static const int8_t common_year_days_in_month
[] = {
779 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
781 static const int8_t leap_year_days_in_month
[] = {
782 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
785 #define days_in_month_of(leap) ((leap) ? leap_year_days_in_month : common_year_days_in_month)
786 #define days_in_month_in(y) days_in_month_of(leap_year_p(y))
787 #define days_in_month_in_v(y) days_in_month_of(leap_year_v_p(y))
790 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
791 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
792 (m),(m),(m),(m),(m),(m),(m),(m)
794 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
795 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
796 (m),(m),(m),(m),(m),(m),(m),(m),(m)
798 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
799 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
800 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m)
802 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
803 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
804 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), (m)
806 static const uint8_t common_year_mon_of_yday
[] = {
807 M31(1), M28(2), M31(3), M30(4), M31(5), M30(6),
808 M31(7), M31(8), M30(9), M31(10), M30(11), M31(12)
810 static const uint8_t leap_year_mon_of_yday
[] = {
811 M31(1), M29(2), M31(3), M30(4), M31(5), M30(6),
812 M31(7), M31(8), M30(9), M31(10), M30(11), M31(12)
822 10,11,12,13,14,15,16,17,18,19, \
823 20,21,22,23,24,25,26,27,28
826 10,11,12,13,14,15,16,17,18,19, \
827 20,21,22,23,24,25,26,27,28,29
830 10,11,12,13,14,15,16,17,18,19, \
831 20,21,22,23,24,25,26,27,28,29,30
834 10,11,12,13,14,15,16,17,18,19, \
835 20,21,22,23,24,25,26,27,28,29,30,31
837 static const uint8_t common_year_mday_of_yday
[] = {
838 /* 1 2 3 4 5 6 7 8 9 10 11 12 */
839 D31
, D28
, D31
, D30
, D31
, D30
, D31
, D31
, D30
, D31
, D30
, D31
841 static const uint8_t leap_year_mday_of_yday
[] = {
842 D31
, D29
, D31
, D30
, D31
, D30
, D31
, D31
, D30
, D31
, D30
, D31
851 calc_tm_yday(long tm_year
, int tm_mon
, int tm_mday
)
853 int tm_year_mod400
= (int)MOD(tm_year
, 400);
854 int tm_yday
= tm_mday
;
856 if (leap_year_p(tm_year_mod400
+ 1900))
857 tm_yday
+= leap_year_yday_offset
[tm_mon
];
859 tm_yday
+= common_year_yday_offset
[tm_mon
];
865 timegmw_noleapsecond(struct vtm
*vtm
)
875 year1900
= subv(vtm
->year
, INT2FIX(1900));
877 divmodv(year1900
, INT2FIX(400), &q400
, &r400
);
878 year_mod400
= NUM2INT(r400
);
880 yday
= calc_tm_yday(year_mod400
, vtm
->mon
-1, vtm
->mday
);
883 * `Seconds Since the Epoch' in SUSv3:
884 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
885 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
886 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
888 ret
= LONG2NUM(vtm
->sec
893 + DIV(year_mod400
- 69, 4)
894 - DIV(year_mod400
- 1, 100)
895 + (year_mod400
+ 299) / 400;
896 vdays
= LONG2NUM(days_in400
);
897 vdays
= addv(vdays
, mulv(q400
, INT2FIX(97)));
898 vdays
= addv(vdays
, mulv(year1900
, INT2FIX(365)));
899 wret
= wadd(rb_time_magnify(v2w(ret
)), wmul(rb_time_magnify(v2w(vdays
)), WINT2FIXWV(86400)));
900 wret
= wadd(wret
, v2w(vtm
->subsecx
));
906 zone_str(const char *zone
)
914 return rb_fstring_lit("(NO-TIMEZONE-ABBREVIATION)");
917 for (p
= zone
; *p
; p
++)
922 len
= p
- zone
+ strlen(p
);
924 str
= rb_usascii_str_new(zone
, len
);
927 str
= rb_enc_str_new(zone
, len
, rb_locale_encoding());
929 return rb_fstring(str
);
933 gmtimew_noleapsecond(wideval_t timew
, struct vtm
*vtm
)
939 wideval_t timew2
, w
, w2
;
944 split_second(timew
, &timew2
, &subsecx
);
945 vtm
->subsecx
= subsecx
;
947 wdivmod(timew2
, WINT2FIXWV(86400), &w2
, &w
);
951 wday
= NUM2INT(modv(timev
, INT2FIX(7)));
952 vtm
->wday
= (wday
+ 4) % 7;
955 vtm
->sec
= n
% 60; n
= n
/ 60;
956 vtm
->min
= n
% 60; n
= n
/ 60;
959 /* 97 leap days in the 400 year cycle */
960 divmodv(timev
, INT2FIX(400*365 + 97), &timev
, &v
);
961 vtm
->year
= mulv(timev
, INT2FIX(400));
963 /* n is the days in the 400 year cycle.
964 * the start of the cycle is 1970-01-01. */
969 /* 30 years including 7 leap days (1972, 1976, ... 1996),
970 * 31 days in January 2000 and
971 * 29 days in February 2000
972 * from 1970-01-01 to 2000-02-29 */
973 if (30*365+7+31+29-1 <= n
) {
974 /* 2000-02-29 or after */
976 /* 2000-02-29 to 2000-12-31 */
982 /* 2001-01-01 or after */
987 x
= n
/ (365*100 + 24);
988 n
= n
% (365*100 + 24);
990 if (30*365+7+31+29-1 <= n
) {
1000 x
= n
/ (365*4 + 1);
1001 n
= n
% (365*4 + 1);
1003 if (365*2+31+29-1 <= n
) {
1004 if (n
< 365*2+366) {
1019 vtm
->year
= addv(vtm
->year
, INT2NUM(y
));
1021 if (leap_year_p(y
)) {
1022 vtm
->mon
= leap_year_mon_of_yday
[n
];
1023 vtm
->mday
= leap_year_mday_of_yday
[n
];
1026 vtm
->mon
= common_year_mon_of_yday
[n
];
1027 vtm
->mday
= common_year_mday_of_yday
[n
];
1030 vtm
->utc_offset
= INT2FIX(0);
1031 vtm
->zone
= str_utc
;
1035 gmtime_with_leapsecond(const time_t *timep
, struct tm
*result
)
1037 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1038 /* 4.4BSD counts leap seconds only with localtime, not with gmtime. */
1041 int gmtoff_sec
, gmtoff_min
, gmtoff_hour
, gmtoff_day
;
1043 t
= LOCALTIME(timep
, *result
);
1047 /* subtract gmtoff */
1048 if (t
->tm_gmtoff
< 0) {
1050 gmtoff
= -t
->tm_gmtoff
;
1054 gmtoff
= t
->tm_gmtoff
;
1056 gmtoff_sec
= (int)(gmtoff
% 60);
1057 gmtoff
= gmtoff
/ 60;
1058 gmtoff_min
= (int)(gmtoff
% 60);
1059 gmtoff
= gmtoff
/ 60;
1060 gmtoff_hour
= (int)gmtoff
; /* <= 12 */
1064 gmtoff_hour
*= sign
;
1069 /* If gmtoff_sec == 0, don't change result->tm_sec.
1070 * It may be 60 which is a leap second. */
1071 result
->tm_sec
+= gmtoff_sec
;
1072 if (result
->tm_sec
< 0) {
1073 result
->tm_sec
+= 60;
1076 if (60 <= result
->tm_sec
) {
1077 result
->tm_sec
-= 60;
1082 result
->tm_min
+= gmtoff_min
;
1083 if (result
->tm_min
< 0) {
1084 result
->tm_min
+= 60;
1087 if (60 <= result
->tm_min
) {
1088 result
->tm_min
-= 60;
1093 result
->tm_hour
+= gmtoff_hour
;
1094 if (result
->tm_hour
< 0) {
1095 result
->tm_hour
+= 24;
1098 if (24 <= result
->tm_hour
) {
1099 result
->tm_hour
-= 24;
1105 if (gmtoff_day
< 0) {
1106 if (result
->tm_yday
== 0) {
1107 result
->tm_mday
= 31;
1108 result
->tm_mon
= 11; /* December */
1110 result
->tm_yday
= leap_year_p(result
->tm_year
+ 1900) ? 365 : 364;
1112 else if (result
->tm_mday
== 1) {
1113 const int8_t *days_in_month
= days_in_month_of(result
->tm_year
+ 1900);
1115 result
->tm_mday
= days_in_month
[result
->tm_mon
];
1122 result
->tm_wday
= (result
->tm_wday
+ 6) % 7;
1125 int leap
= leap_year_p(result
->tm_year
+ 1900);
1126 if (result
->tm_yday
== (leap
? 365 : 364)) {
1128 result
->tm_mon
= 0; /* January */
1129 result
->tm_mday
= 1;
1130 result
->tm_yday
= 0;
1132 else if (result
->tm_mday
== days_in_month_of(leap
)[result
->tm_mon
]) {
1134 result
->tm_mday
= 1;
1141 result
->tm_wday
= (result
->tm_wday
+ 1) % 7;
1144 result
->tm_isdst
= 0;
1145 result
->tm_gmtoff
= 0;
1146 #if defined(HAVE_TM_ZONE)
1147 result
->tm_zone
= (char *)"UTC";
1151 return GMTIME(timep
, *result
);
1155 static long this_year
= 0;
1156 static time_t known_leap_seconds_limit
;
1157 static int number_of_leap_seconds_known
;
1160 init_leap_second_info(void)
1163 * leap seconds are determined by IERS.
1164 * It is announced 6 months before the leap second.
1165 * So no one knows leap seconds in the future after the next year.
1167 if (this_year
== 0) {
1169 struct tm
*tm
, result
;
1173 #ifdef HAVE_GMTIME_R
1174 gmtime_r(&now
, &result
);
1178 tm
= gmtime_with_leapsecond(&now
, &result
);
1180 this_year
= tm
->tm_year
;
1182 if (TIMET_MAX
- now
< (time_t)(366*86400))
1183 known_leap_seconds_limit
= TIMET_MAX
;
1185 known_leap_seconds_limit
= now
+ (time_t)(366*86400);
1187 if (!gmtime_with_leapsecond(&known_leap_seconds_limit
, &result
))
1190 vtm
.year
= LONG2NUM(result
.tm_year
+ 1900);
1191 vtm
.mon
= result
.tm_mon
+ 1;
1192 vtm
.mday
= result
.tm_mday
;
1193 vtm
.hour
= result
.tm_hour
;
1194 vtm
.min
= result
.tm_min
;
1195 vtm
.sec
= result
.tm_sec
;
1196 vtm
.subsecx
= INT2FIX(0);
1197 vtm
.utc_offset
= INT2FIX(0);
1199 timew
= timegmw_noleapsecond(&vtm
);
1201 number_of_leap_seconds_known
= NUM2INT(w2v(wsub(TIMET2WV(known_leap_seconds_limit
), rb_time_unmagnify(timew
))));
1205 /* Use this if you want to re-run init_leap_second_info() */
1207 ruby_reset_leap_second_info(void)
1213 timegmw(struct vtm
*vtm
)
1220 /* The first leap second is 1972-06-30 23:59:60 UTC.
1221 * No leap seconds before. */
1222 if (gt(INT2FIX(1972), vtm
->year
))
1223 return timegmw_noleapsecond(vtm
);
1225 init_leap_second_info();
1227 timew
= timegmw_noleapsecond(vtm
);
1230 if (number_of_leap_seconds_known
== 0) {
1231 /* When init_leap_second_info() is executed, the timezone doesn't have
1232 * leap second information. Disable leap second for calculating gmtime.
1236 else if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit
)), timew
)) {
1237 return wadd(timew
, rb_time_magnify(WINT2WV(number_of_leap_seconds_known
)));
1240 tm
.tm_year
= rb_long2int(NUM2LONG(vtm
->year
) - 1900);
1241 tm
.tm_mon
= vtm
->mon
- 1;
1242 tm
.tm_mday
= vtm
->mday
;
1243 tm
.tm_hour
= vtm
->hour
;
1244 tm
.tm_min
= vtm
->min
;
1245 tm
.tm_sec
= vtm
->sec
;
1248 errmsg
= find_time_t(&tm
, 1, &t
);
1250 rb_raise(rb_eArgError
, "%s", errmsg
);
1251 return wadd(rb_time_magnify(TIMET2WV(t
)), v2w(vtm
->subsecx
));
1255 gmtimew(wideval_t timew
, struct vtm
*result
)
1262 if (wlt(timew
, WINT2FIXWV(0))) {
1263 gmtimew_noleapsecond(timew
, result
);
1267 init_leap_second_info();
1269 if (number_of_leap_seconds_known
== 0) {
1270 /* When init_leap_second_info() is executed, the timezone doesn't have
1271 * leap second information. Disable leap second for calculating gmtime.
1273 gmtimew_noleapsecond(timew
, result
);
1276 else if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit
)), timew
)) {
1277 timew
= wsub(timew
, rb_time_magnify(WINT2WV(number_of_leap_seconds_known
)));
1278 gmtimew_noleapsecond(timew
, result
);
1282 split_second(timew
, &timew2
, &subsecx
);
1284 t
= WV2TIMET(timew2
);
1285 if (!gmtime_with_leapsecond(&t
, &tm
))
1288 result
->year
= LONG2NUM((long)tm
.tm_year
+ 1900);
1289 result
->mon
= tm
.tm_mon
+ 1;
1290 result
->mday
= tm
.tm_mday
;
1291 result
->hour
= tm
.tm_hour
;
1292 result
->min
= tm
.tm_min
;
1293 result
->sec
= tm
.tm_sec
;
1294 result
->subsecx
= subsecx
;
1295 result
->utc_offset
= INT2FIX(0);
1296 result
->wday
= tm
.tm_wday
;
1297 result
->yday
= tm
.tm_yday
+1;
1298 result
->isdst
= tm
.tm_isdst
;
1300 result
->zone
= rb_fstring_lit("UTC");
1306 #define GMTIMEW(w, v) \
1307 (gmtimew(w, v) ? (void)0 : rb_raise(rb_eArgError, "gmtime error"))
1309 static struct tm
*localtime_with_gmtoff_zone(const time_t *t
, struct tm
*result
, long *gmtoff
, VALUE
*zone
);
1312 * The idea, extrapolate localtime() function, is borrowed from Perl:
1313 * http://web.archive.org/web/20080211114141/http://use.perl.org/articles/08/02/07/197204.shtml
1315 * compat_common_month_table is generated by the following program.
1316 * This table finds the last month which starts at the same day of a week.
1317 * The year 2037 is not used because:
1318 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=522949
1325 * 2036.downto(2010) {|y|
1327 * next if m == 2 && y % 4 == 0
1328 * d = Date.new(y,m,1)
1330 * h[m][d.wday] ||= y
1344 static const int compat_common_month_table
[12][7] = {
1345 /* Sun Mon Tue Wed Thu Fri Sat */
1346 { 2034, 2035, 2036, 2031, 2032, 2027, 2033 }, /* January */
1347 { 2026, 2027, 2033, 2034, 2035, 2030, 2031 }, /* February */
1348 { 2026, 2032, 2033, 2034, 2035, 2030, 2036 }, /* March */
1349 { 2035, 2030, 2036, 2026, 2032, 2033, 2034 }, /* April */
1350 { 2033, 2034, 2035, 2030, 2036, 2026, 2032 }, /* May */
1351 { 2036, 2026, 2032, 2033, 2034, 2035, 2030 }, /* June */
1352 { 2035, 2030, 2036, 2026, 2032, 2033, 2034 }, /* July */
1353 { 2032, 2033, 2034, 2035, 2030, 2036, 2026 }, /* August */
1354 { 2030, 2036, 2026, 2032, 2033, 2034, 2035 }, /* September */
1355 { 2034, 2035, 2030, 2036, 2026, 2032, 2033 }, /* October */
1356 { 2026, 2032, 2033, 2034, 2035, 2030, 2036 }, /* November */
1357 { 2030, 2036, 2026, 2032, 2033, 2034, 2035 }, /* December */
1361 * compat_leap_month_table is generated by following program.
1368 * 2037.downto(2010) {|y|
1370 * next unless m == 2 && y % 4 == 0
1371 * d = Date.new(y,m,1)
1373 * h[m][d.wday] ||= y
1385 static const int compat_leap_month_table
[7] = {
1386 /* Sun Mon Tue Wed Thu Fri Sat */
1387 2032, 2016, 2028, 2012, 2024, 2036, 2020, /* February */
1391 calc_wday(int year_mod400
, int month
, int day
)
1396 a
= (14 - month
) / 12;
1397 y
= year_mod400
+ 4800 - a
;
1398 m
= month
+ 12 * a
- 3;
1399 wday
= day
+ (153*m
+2)/5 + 365*y
+ y
/4 - y
/100 + y
/400 + 2;
1405 guess_local_offset(struct vtm
*vtm_utc
, int *isdst_ret
, VALUE
*zone_ret
)
1413 int year_mod400
, wday
;
1415 /* Daylight Saving Time was introduced in 1916.
1416 * So we don't need to care about DST before that. */
1417 if (lt(vtm_utc
->year
, INT2FIX(1916))) {
1418 VALUE off
= INT2FIX(0);
1420 zone
= rb_fstring_lit("UTC");
1422 # if defined(NEGATIVE_TIME_T)
1423 # if SIZEOF_TIME_T <= 4
1424 /* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t. */
1425 # define THE_TIME_OLD_ENOUGH ((time_t)0x80000000)
1427 /* Since the Royal Greenwich Observatory was commissioned in 1675,
1428 no timezone defined using GMT at 1600. */
1429 # define THE_TIME_OLD_ENOUGH ((time_t)(1600-1970)*366*24*60*60)
1431 if (localtime_with_gmtoff_zone((t
= THE_TIME_OLD_ENOUGH
, &t
), &tm
, &gmtoff
, &zone
)) {
1432 off
= LONG2FIX(gmtoff
);
1433 isdst
= tm
.tm_isdst
;
1437 /* 1970-01-01 00:00:00 UTC : The Unix epoch - the oldest time in portable time_t. */
1438 if (localtime_with_gmtoff_zone((t
= 0, &t
), &tm
, &gmtoff
, &zone
)) {
1439 off
= LONG2FIX(gmtoff
);
1440 isdst
= tm
.tm_isdst
;
1450 /* It is difficult to guess the future. */
1454 /* guess using a year before 2038. */
1455 year_mod400
= NUM2INT(modv(vtm_utc
->year
, INT2FIX(400)));
1456 wday
= calc_wday(year_mod400
, vtm_utc
->mon
, 1);
1457 if (vtm_utc
->mon
== 2 && leap_year_p(year_mod400
))
1458 vtm2
.year
= INT2FIX(compat_leap_month_table
[wday
]);
1460 vtm2
.year
= INT2FIX(compat_common_month_table
[vtm_utc
->mon
-1][wday
]);
1462 timev
= w2v(rb_time_unmagnify(timegmw(&vtm2
)));
1463 t
= NUM2TIMET(timev
);
1465 if (localtime_with_gmtoff_zone(&t
, &tm
, &gmtoff
, &zone
)) {
1467 *isdst_ret
= tm
.tm_isdst
;
1470 return LONG2FIX(gmtoff
);
1474 /* Use the current time offset as a last resort. */
1475 static time_t now
= 0;
1476 static long now_gmtoff
= 0;
1477 static int now_isdst
= 0;
1478 static VALUE now_zone
;
1482 localtime_with_gmtoff_zone(&now
, &tm
, &now_gmtoff
, &zone
);
1483 now_isdst
= tm
.tm_isdst
;
1484 zone
= rb_fstring(zone
);
1485 rb_gc_register_mark_object(zone
);
1489 *isdst_ret
= now_isdst
;
1491 *zone_ret
= now_zone
;
1492 return LONG2FIX(now_gmtoff
);
1497 small_vtm_sub(struct vtm
*vtm1
, struct vtm
*vtm2
)
1501 off
= vtm1
->sec
- vtm2
->sec
;
1502 off
+= (vtm1
->min
- vtm2
->min
) * 60;
1503 off
+= (vtm1
->hour
- vtm2
->hour
) * 3600;
1504 if (ne(vtm1
->year
, vtm2
->year
))
1505 off
+= lt(vtm1
->year
, vtm2
->year
) ? -24*3600 : 24*3600;
1506 else if (vtm1
->mon
!= vtm2
->mon
)
1507 off
+= vtm1
->mon
< vtm2
->mon
? -24*3600 : 24*3600;
1508 else if (vtm1
->mday
!= vtm2
->mday
)
1509 off
+= vtm1
->mday
< vtm2
->mday
? -24*3600 : 24*3600;
1511 return INT2FIX(off
);
1515 timelocalw(struct vtm
*vtm
)
1520 wideval_t timew1
, timew2
;
1521 struct vtm vtm1
, vtm2
;
1524 if (FIXNUM_P(vtm
->year
)) {
1525 long l
= FIX2LONG(vtm
->year
) - 1900;
1526 if (l
< INT_MIN
|| INT_MAX
< l
)
1528 tm
.tm_year
= (int)l
;
1531 v
= subv(vtm
->year
, INT2FIX(1900));
1532 if (lt(v
, INT2NUM(INT_MIN
)) || lt(INT2NUM(INT_MAX
), v
))
1534 tm
.tm_year
= NUM2INT(v
);
1537 tm
.tm_mon
= vtm
->mon
-1;
1538 tm
.tm_mday
= vtm
->mday
;
1539 tm
.tm_hour
= vtm
->hour
;
1540 tm
.tm_min
= vtm
->min
;
1541 tm
.tm_sec
= vtm
->sec
;
1542 tm
.tm_isdst
= vtm
->isdst
== VTM_ISDST_INITVAL
? -1 : vtm
->isdst
;
1544 if (find_time_t(&tm
, 0, &t
))
1546 return wadd(rb_time_magnify(TIMET2WV(t
)), v2w(vtm
->subsecx
));
1549 timew1
= timegmw(vtm
);
1551 if (!localtimew(timew1
, &vtm1
))
1552 rb_raise(rb_eArgError
, "localtimew error");
1554 n
= vtmcmp(vtm
, &vtm1
);
1556 timew1
= wsub(timew1
, rb_time_magnify(WINT2FIXWV(12*3600)));
1557 if (!localtimew(timew1
, &vtm1
))
1558 rb_raise(rb_eArgError
, "localtimew error");
1565 timew1
= wsub(timew1
, rb_time_magnify(WINT2FIXWV(24*3600)));
1566 if (!localtimew(timew1
, &vtm1
))
1567 rb_raise(rb_eArgError
, "localtimew error");
1570 timew2
= wadd(timew1
, rb_time_magnify(WINT2FIXWV(24*3600)));
1571 if (!localtimew(timew2
, &vtm2
))
1572 rb_raise(rb_eArgError
, "localtimew error");
1574 timew1
= wadd(timew1
, rb_time_magnify(v2w(small_vtm_sub(vtm
, &vtm1
))));
1575 timew2
= wadd(timew2
, rb_time_magnify(v2w(small_vtm_sub(vtm
, &vtm2
))));
1577 if (weq(timew1
, timew2
))
1580 if (!localtimew(timew1
, &vtm1
))
1581 rb_raise(rb_eArgError
, "localtimew error");
1582 if (vtm
->hour
!= vtm1
.hour
|| vtm
->min
!= vtm1
.min
|| vtm
->sec
!= vtm1
.sec
)
1585 if (!localtimew(timew2
, &vtm2
))
1586 rb_raise(rb_eArgError
, "localtimew error");
1587 if (vtm
->hour
!= vtm2
.hour
|| vtm
->min
!= vtm2
.min
|| vtm
->sec
!= vtm2
.sec
)
1591 return lt(vtm1
.utc_offset
, vtm2
.utc_offset
) ? timew2
: timew1
;
1593 return lt(vtm1
.utc_offset
, vtm2
.utc_offset
) ? timew1
: timew2
;
1597 localtime_with_gmtoff_zone(const time_t *t
, struct tm
*result
, long *gmtoff
, VALUE
*zone
)
1601 if (LOCALTIME(t
, tm
)) {
1602 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1603 *gmtoff
= tm
.tm_gmtoff
;
1609 u
= GMTIME(t
, tmbuf
);
1612 if (l
->tm_year
!= u
->tm_year
)
1613 off
= l
->tm_year
< u
->tm_year
? -1 : 1;
1614 else if (l
->tm_mon
!= u
->tm_mon
)
1615 off
= l
->tm_mon
< u
->tm_mon
? -1 : 1;
1616 else if (l
->tm_mday
!= u
->tm_mday
)
1617 off
= l
->tm_mday
< u
->tm_mday
? -1 : 1;
1620 off
= off
* 24 + l
->tm_hour
- u
->tm_hour
;
1621 off
= off
* 60 + l
->tm_min
- u
->tm_min
;
1622 off
= off
* 60 + l
->tm_sec
- u
->tm_sec
;
1627 #if defined(HAVE_TM_ZONE)
1628 *zone
= zone_str(tm
.tm_zone
);
1629 #elif defined(HAVE_TZNAME) && defined(HAVE_DAYLIGHT)
1630 # if defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 140
1631 # define tzname _tzname
1632 # define daylight _daylight
1634 /* this needs tzset or localtime, instead of localtime_r */
1635 *zone
= zone_str(tzname
[daylight
&& tm
.tm_isdst
]);
1639 strftime(buf
, sizeof(buf
), "%Z", &tm
);
1640 *zone
= zone_str(buf
);
1652 timew_out_of_timet_range(wideval_t timew
)
1655 #if WIDEVALUE_IS_WIDER && SIZEOF_TIME_T < SIZEOF_INT64_T
1656 if (FIXWV_P(timew
)) {
1657 wideint_t t
= FIXWV2WINT(timew
);
1658 if (t
< TIME_SCALE
* (wideint_t
)TIMET_MIN
||
1659 TIME_SCALE
* (1 + (wideint_t
)TIMET_MAX
) <= t
)
1664 #if SIZEOF_TIME_T == SIZEOF_INT64_T
1665 if (FIXWV_P(timew
)) {
1666 wideint_t t
= FIXWV2WINT(timew
);
1667 if (~(time_t)0 <= 0) {
1677 timexv
= w2v(timew
);
1678 if (lt(timexv
, mulv(INT2FIX(TIME_SCALE
), TIMET2NUM(TIMET_MIN
))) ||
1679 le(mulv(INT2FIX(TIME_SCALE
), addv(TIMET2NUM(TIMET_MAX
), INT2FIX(1))), timexv
))
1685 localtimew(wideval_t timew
, struct vtm
*result
)
1687 VALUE subsecx
, offset
;
1691 if (!timew_out_of_timet_range(timew
)) {
1697 split_second(timew
, &timew2
, &subsecx
);
1699 t
= WV2TIMET(timew2
);
1701 if (localtime_with_gmtoff_zone(&t
, &tm
, &gmtoff
, &zone
)) {
1702 result
->year
= LONG2NUM((long)tm
.tm_year
+ 1900);
1703 result
->mon
= tm
.tm_mon
+ 1;
1704 result
->mday
= tm
.tm_mday
;
1705 result
->hour
= tm
.tm_hour
;
1706 result
->min
= tm
.tm_min
;
1707 result
->sec
= tm
.tm_sec
;
1708 result
->subsecx
= subsecx
;
1709 result
->wday
= tm
.tm_wday
;
1710 result
->yday
= tm
.tm_yday
+1;
1711 result
->isdst
= tm
.tm_isdst
;
1712 result
->utc_offset
= LONG2NUM(gmtoff
);
1713 result
->zone
= zone
;
1718 if (!gmtimew(timew
, result
))
1721 offset
= guess_local_offset(result
, &isdst
, &zone
);
1723 if (!gmtimew(wadd(timew
, rb_time_magnify(v2w(offset
))), result
))
1726 result
->utc_offset
= offset
;
1727 result
->isdst
= isdst
;
1728 result
->zone
= zone
;
1733 #define TIME_TZMODE_LOCALTIME 0
1734 #define TIME_TZMODE_UTC 1
1735 #define TIME_TZMODE_FIXOFF 2
1736 #define TIME_TZMODE_UNINITIALIZED 3
1738 PACKED_STRUCT_UNALIGNED(struct time_object
{
1739 wideval_t timew
; /* time_t value * TIME_SCALE. possibly Rational. */
1741 unsigned int tzmode
:3; /* 0:localtime 1:utc 2:fixoff 3:uninitialized */
1742 unsigned int tm_got
:1;
1745 #define GetTimeval(obj, tobj) ((tobj) = get_timeval(obj))
1746 #define GetNewTimeval(obj, tobj) ((tobj) = get_new_timeval(obj))
1748 #define IsTimeval(obj) rb_typeddata_is_kind_of((obj), &time_data_type)
1749 #define TIME_INIT_P(tobj) ((tobj)->tzmode != TIME_TZMODE_UNINITIALIZED)
1751 #define TZMODE_UTC_P(tobj) ((tobj)->tzmode == TIME_TZMODE_UTC)
1752 #define TZMODE_SET_UTC(tobj) ((tobj)->tzmode = TIME_TZMODE_UTC)
1754 #define TZMODE_LOCALTIME_P(tobj) ((tobj)->tzmode == TIME_TZMODE_LOCALTIME)
1755 #define TZMODE_SET_LOCALTIME(tobj) ((tobj)->tzmode = TIME_TZMODE_LOCALTIME)
1757 #define TZMODE_FIXOFF_P(tobj) ((tobj)->tzmode == TIME_TZMODE_FIXOFF)
1758 #define TZMODE_SET_FIXOFF(tobj, off) \
1759 ((tobj)->tzmode = TIME_TZMODE_FIXOFF, \
1760 (tobj)->vtm.utc_offset = (off))
1762 #define TZMODE_COPY(tobj1, tobj2) \
1763 ((tobj1)->tzmode = (tobj2)->tzmode, \
1764 (tobj1)->vtm.utc_offset = (tobj2)->vtm.utc_offset, \
1765 (tobj1)->vtm.zone = (tobj2)->vtm.zone)
1767 static VALUE
time_get_tm(VALUE
, struct time_object
*);
1768 #define MAKE_TM(time, tobj) \
1770 if ((tobj)->tm_got == 0) { \
1771 time_get_tm((time), (tobj)); \
1774 #define MAKE_TM_ENSURE(time, tobj, cond) \
1776 MAKE_TM(time, tobj); \
1778 VALUE zone = (tobj)->vtm.zone; \
1779 if (!NIL_P(zone)) zone_localtime(zone, (time)); \
1784 time_mark(void *ptr
)
1786 struct time_object
*tobj
= ptr
;
1787 if (!FIXWV_P(tobj
->timew
))
1788 rb_gc_mark(w2v(tobj
->timew
));
1789 rb_gc_mark(tobj
->vtm
.year
);
1790 rb_gc_mark(tobj
->vtm
.subsecx
);
1791 rb_gc_mark(tobj
->vtm
.utc_offset
);
1792 rb_gc_mark(tobj
->vtm
.zone
);
1796 time_memsize(const void *tobj
)
1798 return sizeof(struct time_object
);
1801 static const rb_data_type_t time_data_type
= {
1803 {time_mark
, RUBY_TYPED_DEFAULT_FREE
, time_memsize
,},
1805 (RUBY_TYPED_FREE_IMMEDIATELY
| RUBY_TYPED_FROZEN_SHAREABLE
),
1809 time_s_alloc(VALUE klass
)
1812 struct time_object
*tobj
;
1814 obj
= TypedData_Make_Struct(klass
, struct time_object
, &time_data_type
, tobj
);
1815 tobj
->tzmode
= TIME_TZMODE_UNINITIALIZED
;
1817 tobj
->timew
= WINT2FIXWV(0);
1818 tobj
->vtm
.zone
= Qnil
;
1823 static struct time_object
*
1824 get_timeval(VALUE obj
)
1826 struct time_object
*tobj
;
1827 TypedData_Get_Struct(obj
, struct time_object
, &time_data_type
, tobj
);
1828 if (!TIME_INIT_P(tobj
)) {
1829 rb_raise(rb_eTypeError
, "uninitialized %"PRIsVALUE
, rb_obj_class(obj
));
1834 static struct time_object
*
1835 get_new_timeval(VALUE obj
)
1837 struct time_object
*tobj
;
1838 TypedData_Get_Struct(obj
, struct time_object
, &time_data_type
, tobj
);
1839 if (TIME_INIT_P(tobj
)) {
1840 rb_raise(rb_eTypeError
, "already initialized %"PRIsVALUE
, rb_obj_class(obj
));
1846 time_modify(VALUE time
)
1848 rb_check_frozen(time
);
1852 timenano2timew(time_t sec
, long nsec
)
1856 timew
= rb_time_magnify(TIMET2WV(sec
));
1858 timew
= wadd(timew
, wmulquoll(WINT2WV(nsec
), TIME_SCALE
, 1000000000));
1862 static struct timespec
1863 timew2timespec(wideval_t timew
)
1869 if (timew_out_of_timet_range(timew
))
1870 rb_raise(rb_eArgError
, "time out of system range");
1871 split_second(timew
, &timew2
, &subsecx
);
1872 ts
.tv_sec
= WV2TIMET(timew2
);
1873 ts
.tv_nsec
= NUM2LONG(mulquov(subsecx
, INT2FIX(1000000000), INT2FIX(TIME_SCALE
)));
1877 static struct timespec
*
1878 timew2timespec_exact(wideval_t timew
, struct timespec
*ts
)
1884 if (timew_out_of_timet_range(timew
))
1886 split_second(timew
, &timew2
, &subsecx
);
1887 ts
->tv_sec
= WV2TIMET(timew2
);
1888 nsecv
= mulquov(subsecx
, INT2FIX(1000000000), INT2FIX(TIME_SCALE
));
1889 if (!FIXNUM_P(nsecv
))
1891 ts
->tv_nsec
= NUM2LONG(nsecv
);
1896 rb_timespec_now(struct timespec
*ts
)
1898 #ifdef HAVE_CLOCK_GETTIME
1899 if (clock_gettime(CLOCK_REALTIME
, ts
) == -1) {
1900 rb_sys_fail("clock_gettime");
1905 if (gettimeofday(&tv
, 0) < 0) {
1906 rb_sys_fail("gettimeofday");
1908 ts
->tv_sec
= tv
.tv_sec
;
1909 ts
->tv_nsec
= tv
.tv_usec
* 1000;
1915 time_init_now(rb_execution_context_t
*ec
, VALUE time
, VALUE zone
)
1917 struct time_object
*tobj
;
1921 GetNewTimeval(time
, tobj
);
1922 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
1924 tobj
->timew
= WINT2FIXWV(0);
1925 rb_timespec_now(&ts
);
1926 tobj
->timew
= timenano2timew(ts
.tv_sec
, ts
.tv_nsec
);
1929 time_zonelocal(time
, zone
);
1935 time_set_utc_offset(VALUE time
, VALUE off
)
1937 struct time_object
*tobj
;
1938 off
= num_exact(off
);
1941 GetTimeval(time
, tobj
);
1944 tobj
->vtm
.zone
= Qnil
;
1945 TZMODE_SET_FIXOFF(tobj
, off
);
1951 vtm_add_offset(struct vtm
*vtm
, VALUE off
, int sign
)
1957 if (lt(off
, INT2FIX(0))) {
1961 divmodv(off
, INT2FIX(1), &off
, &subsec
);
1962 divmodv(off
, INT2FIX(60), &off
, &v
);
1964 divmodv(off
, INT2FIX(60), &off
, &v
);
1966 divmodv(off
, INT2FIX(24), &off
, &v
);
1970 subsec
= neg(subsec
);
1978 if (!rb_equal(subsec
, INT2FIX(0))) {
1979 vtm
->subsecx
= addv(vtm
->subsecx
, w2v(rb_time_magnify(v2w(subsec
))));
1980 if (lt(vtm
->subsecx
, INT2FIX(0))) {
1981 vtm
->subsecx
= addv(vtm
->subsecx
, INT2FIX(TIME_SCALE
));
1984 if (le(INT2FIX(TIME_SCALE
), vtm
->subsecx
)) {
1985 vtm
->subsecx
= subv(vtm
->subsecx
, INT2FIX(TIME_SCALE
));
1990 /* If sec + subsec == 0, don't change vtm->sec.
1991 * It may be 60 which is a leap second. */
2030 if (vtm
->mon
== 1 && vtm
->mday
== 1) {
2032 vtm
->mon
= 12; /* December */
2033 vtm
->year
= subv(vtm
->year
, INT2FIX(1));
2034 vtm
->yday
= leap_year_v_p(vtm
->year
) ? 366 : 365;
2036 else if (vtm
->mday
== 1) {
2037 const int8_t *days_in_month
= days_in_month_in_v(vtm
->year
);
2039 vtm
->mday
= days_in_month
[vtm
->mon
-1];
2046 vtm
->wday
= (vtm
->wday
+ 6) % 7;
2049 int leap
= leap_year_v_p(vtm
->year
);
2050 if (vtm
->mon
== 12 && vtm
->mday
== 31) {
2051 vtm
->year
= addv(vtm
->year
, INT2FIX(1));
2052 vtm
->mon
= 1; /* January */
2056 else if (vtm
->mday
== days_in_month_of(leap
)[vtm
->mon
-1]) {
2065 vtm
->wday
= (vtm
->wday
+ 1) % 7;
2071 maybe_tzobj_p(VALUE obj
)
2073 if (NIL_P(obj
)) return FALSE
;
2074 if (RB_INTEGER_TYPE_P(obj
)) return FALSE
;
2075 if (RB_TYPE_P(obj
, T_STRING
)) return FALSE
;
2079 NORETURN(static void invalid_utc_offset(VALUE
));
2081 invalid_utc_offset(VALUE zone
)
2083 rb_raise(rb_eArgError
, "\"+HH:MM\", \"-HH:MM\", \"UTC\" or "
2084 "\"A\"..\"I\",\"K\"..\"Z\" expected for utc_offset: %"PRIsVALUE
,
2089 utc_offset_arg(VALUE arg
)
2092 if (!NIL_P(tmp
= rb_check_string_type(arg
))) {
2094 const char *s
= RSTRING_PTR(tmp
), *min
= NULL
, *sec
= NULL
;
2095 if (!rb_enc_str_asciicompat_p(tmp
)) {
2096 goto invalid_utc_offset
;
2098 switch (RSTRING_LEN(tmp
)) {
2103 /* Military Time Zone Names */
2104 if (s
[0] >= 'A' && s
[0] <= 'I') {
2105 n
= (int)s
[0] - 'A' + 1;
2107 else if (s
[0] >= 'K' && s
[0] <= 'M') {
2108 n
= (int)s
[0] - 'A';
2110 else if (s
[0] >= 'N' && s
[0] <= 'Y') {
2111 n
= 'M' - (int)s
[0];
2114 goto invalid_utc_offset
;
2119 if (STRNCASECMP("UTC", s
, 3) == 0) {
2126 case 6: /* +HH:MM */
2129 case 7: /* +HHMMSS */
2133 case 9: /* +HH:MM:SS */
2138 goto invalid_utc_offset
;
2141 if (sec
== s
+7 && *(sec
-1) != ':') goto invalid_utc_offset
;
2142 if (!ISDIGIT(sec
[0]) || !ISDIGIT(sec
[1])) goto invalid_utc_offset
;
2143 n
+= (sec
[0] * 10 + sec
[1] - '0' * 11);
2146 if (min
== s
+4 && *(min
-1) != ':') goto invalid_utc_offset
;
2147 if (!ISDIGIT(min
[0]) || !ISDIGIT(min
[1])) goto invalid_utc_offset
;
2148 if (min
[0] > '5') goto invalid_utc_offset
;
2149 n
+= (min
[0] * 10 + min
[1] - '0' * 11) * 60;
2151 if (s
[0] != '+' && s
[0] != '-') goto invalid_utc_offset
;
2152 if (!ISDIGIT(s
[1]) || !ISDIGIT(s
[2])) goto invalid_utc_offset
;
2153 n
+= (s
[1] * 10 + s
[2] - '0' * 11) * 3600;
2155 if (n
== 0) return UTC_ZONE
;
2161 return num_exact(arg
);
2168 zone_set_offset(VALUE zone
, struct time_object
*tobj
,
2169 wideval_t tlocal
, wideval_t tutc
)
2171 /* tlocal and tutc must be unmagnified and in seconds */
2172 wideval_t w
= wsub(tlocal
, tutc
);
2174 validate_utc_offset(off
);
2175 tobj
->vtm
.utc_offset
= off
;
2176 tobj
->vtm
.zone
= zone
;
2177 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
2181 extract_time(VALUE time
)
2184 const ID id_to_i
= idTo_i
;
2186 #define EXTRACT_TIME() do { \
2187 t = v2w(rb_Integer(AREF(to_i))); \
2190 if (rb_typeddata_is_kind_of(time
, &time_data_type
)) {
2191 struct time_object
*tobj
= DATA_PTR(time
);
2193 time_gmtime(time
); /* ensure tm got */
2194 t
= rb_time_unmagnify(tobj
->timew
);
2196 else if (RB_TYPE_P(time
, T_STRUCT
)) {
2197 #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2202 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2212 extract_vtm(VALUE time
, struct vtm
*vtm
, VALUE subsecx
)
2215 const ID id_to_i
= idTo_i
;
2217 #define EXTRACT_VTM() do { \
2219 vtm->year = obj2vint(AREF(year)); \
2220 vtm->mon = month_arg(AREF(mon)); \
2221 vtm->mday = obj2ubits(AREF(mday), 5); \
2222 vtm->hour = obj2ubits(AREF(hour), 5); \
2223 vtm->min = obj2ubits(AREF(min), 6); \
2224 vtm->sec = obj2subsecx(AREF(sec), &subsecx); \
2225 vtm->isdst = RTEST(AREF(isdst)); \
2226 vtm->utc_offset = Qnil; \
2227 t = v2w(rb_Integer(AREF(to_i))); \
2230 if (rb_typeddata_is_kind_of(time
, &time_data_type
)) {
2231 struct time_object
*tobj
= DATA_PTR(time
);
2233 time_get_tm(time
, tobj
);
2235 t
= rb_time_unmagnify(tobj
->timew
);
2236 if (TZMODE_FIXOFF_P(tobj
) && vtm
->utc_offset
!= INT2FIX(0))
2237 t
= wadd(t
, v2w(vtm
->utc_offset
));
2239 else if (RB_TYPE_P(time
, T_STRUCT
)) {
2240 #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2244 else if (rb_integer_type_p(time
)) {
2246 GMTIMEW(rb_time_magnify(t
), vtm
);
2249 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2254 vtm
->subsecx
= subsecx
;
2260 zone_set_dst(VALUE zone
, struct time_object
*tobj
, VALUE tm
)
2264 CONST_ID(id_dst_p
, "dst?");
2265 dst
= rb_check_funcall(zone
, id_dst_p
, 1, &tm
);
2266 tobj
->vtm
.isdst
= (dst
!= Qundef
&& RTEST(dst
));
2270 zone_timelocal(VALUE zone
, VALUE time
)
2273 struct time_object
*tobj
= DATA_PTR(time
);
2276 t
= rb_time_unmagnify(tobj
->timew
);
2277 tm
= tm_from_time(rb_cTimeTM
, time
);
2278 utc
= rb_check_funcall(zone
, id_local_to_utc
, 1, &tm
);
2279 if (utc
== Qundef
) return 0;
2281 s
= extract_time(utc
);
2282 zone_set_offset(zone
, tobj
, t
, s
);
2283 s
= rb_time_magnify(s
);
2284 if (tobj
->vtm
.subsecx
!= INT2FIX(0)) {
2285 s
= wadd(s
, v2w(tobj
->vtm
.subsecx
));
2288 zone_set_dst(zone
, tobj
, tm
);
2293 zone_localtime(VALUE zone
, VALUE time
)
2295 VALUE local
, tm
, subsecx
;
2296 struct time_object
*tobj
= DATA_PTR(time
);
2299 split_second(tobj
->timew
, &t
, &subsecx
);
2300 tm
= tm_from_time(rb_cTimeTM
, time
);
2302 local
= rb_check_funcall(zone
, id_utc_to_local
, 1, &tm
);
2303 if (local
== Qundef
) return 0;
2305 s
= extract_vtm(local
, &tobj
->vtm
, subsecx
);
2307 zone_set_offset(zone
, tobj
, s
, t
);
2308 zone_set_dst(zone
, tobj
, tm
);
2313 find_timezone(VALUE time
, VALUE zone
)
2315 VALUE klass
= CLASS_OF(time
);
2317 return rb_check_funcall_default(klass
, id_find_timezone
, 1, &zone
, Qnil
);
2321 time_init_args(rb_execution_context_t
*ec
, VALUE time
, VALUE year
, VALUE mon
, VALUE mday
, VALUE hour
, VALUE min
, VALUE sec
, VALUE zone
)
2325 struct time_object
*tobj
;
2327 vtm
.wday
= VTM_WDAY_INITVAL
;
2329 vtm
.zone
= str_empty
;
2331 vtm
.year
= obj2vint(year
);
2333 vtm
.mon
= NIL_P(mon
) ? 1 : month_arg(mon
);
2335 vtm
.mday
= NIL_P(mday
) ? 1 : obj2ubits(mday
, 5);
2337 vtm
.hour
= NIL_P(hour
) ? 0 : obj2ubits(hour
, 5);
2339 vtm
.min
= NIL_P(min
) ? 0 : obj2ubits(min
, 6);
2343 vtm
.subsecx
= INT2FIX(0);
2347 vtm
.sec
= obj2subsecx(sec
, &subsecx
);
2348 vtm
.subsecx
= subsecx
;
2351 vtm
.isdst
= VTM_ISDST_INITVAL
;
2352 vtm
.utc_offset
= Qnil
;
2353 const VALUE arg
= zone
;
2356 if (arg
== ID2SYM(rb_intern("dst")))
2358 else if (arg
== ID2SYM(rb_intern("std")))
2360 else if (maybe_tzobj_p(arg
))
2362 else if (!NIL_P(utc
= utc_offset_arg(arg
)))
2363 vtm
.utc_offset
= utc
== UTC_ZONE
? INT2FIX(0) : utc
;
2364 else if (NIL_P(zone
= find_timezone(time
, arg
)))
2365 invalid_utc_offset(arg
);
2371 GetNewTimeval(time
, tobj
);
2374 tobj
->timew
= timegmw(&vtm
);
2377 TZMODE_SET_LOCALTIME(tobj
);
2378 if (zone_timelocal(zone
, time
)) {
2381 else if (NIL_P(vtm
.utc_offset
= utc_offset_arg(zone
))) {
2382 if (NIL_P(zone
= find_timezone(time
, zone
)) || !zone_timelocal(zone
, time
))
2383 invalid_utc_offset(arg
);
2387 if (utc
== UTC_ZONE
) {
2388 tobj
->timew
= timegmw(&vtm
);
2391 TZMODE_SET_UTC(tobj
);
2395 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
2397 tobj
->timew
= WINT2FIXWV(0);
2399 if (!NIL_P(vtm
.utc_offset
)) {
2400 VALUE off
= vtm
.utc_offset
;
2401 vtm_add_offset(&vtm
, off
, -1);
2402 vtm
.utc_offset
= Qnil
;
2403 tobj
->timew
= timegmw(&vtm
);
2404 return time_set_utc_offset(time
, off
);
2407 tobj
->timew
= timelocalw(&vtm
);
2408 return time_localtime(time
);
2413 subsec_normalize(time_t *secp
, long *subsecp
, const long maxsubsec
)
2416 long subsec
= *subsecp
;
2419 if (UNLIKELY(subsec
>= maxsubsec
)) { /* subsec positive overflow */
2420 sec2
= subsec
/ maxsubsec
;
2421 if (TIMET_MAX
- sec2
< sec
) {
2422 rb_raise(rb_eRangeError
, "out of Time range");
2424 subsec
-= sec2
* maxsubsec
;
2427 else if (UNLIKELY(subsec
< 0)) { /* subsec negative overflow */
2428 sec2
= NDIV(subsec
, maxsubsec
); /* negative div */
2429 if (sec
< TIMET_MIN
- sec2
) {
2430 rb_raise(rb_eRangeError
, "out of Time range");
2432 subsec
-= sec2
* maxsubsec
;
2435 #ifndef NEGATIVE_TIME_T
2437 rb_raise(rb_eArgError
, "time must be positive");
2443 #define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
2444 #define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000)
2447 nsec2timew(time_t sec
, long nsec
)
2449 time_nsec_normalize(&sec
, &nsec
);
2450 return timenano2timew(sec
, nsec
);
2454 time_new_timew(VALUE klass
, wideval_t timew
)
2456 VALUE time
= time_s_alloc(klass
);
2457 struct time_object
*tobj
;
2459 tobj
= DATA_PTR(time
); /* skip type check */
2460 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
2461 tobj
->timew
= timew
;
2467 rb_time_new(time_t sec
, long usec
)
2469 time_usec_normalize(&sec
, &usec
);
2470 return time_new_timew(rb_cTime
, timenano2timew(sec
, usec
* 1000));
2473 /* returns localtime time object */
2475 rb_time_nano_new(time_t sec
, long nsec
)
2477 return time_new_timew(rb_cTime
, nsec2timew(sec
, nsec
));
2481 rb_time_timespec_new(const struct timespec
*ts
, int offset
)
2483 struct time_object
*tobj
;
2484 VALUE time
= time_new_timew(rb_cTime
, nsec2timew(ts
->tv_sec
, ts
->tv_nsec
));
2486 if (-86400 < offset
&& offset
< 86400) { /* fixoff */
2487 GetTimeval(time
, tobj
);
2488 TZMODE_SET_FIXOFF(tobj
, INT2FIX(offset
));
2490 else if (offset
== INT_MAX
) { /* localtime */
2492 else if (offset
== INT_MAX
-1) { /* UTC */
2493 GetTimeval(time
, tobj
);
2494 TZMODE_SET_UTC(tobj
);
2497 rb_raise(rb_eArgError
, "utc_offset out of range");
2504 rb_time_num_new(VALUE timev
, VALUE off
)
2506 VALUE time
= time_new_timew(rb_cTime
, rb_time_magnify(v2w(timev
)));
2511 if (maybe_tzobj_p(zone
)) {
2513 if (zone_timelocal(zone
, time
)) return time
;
2515 if (NIL_P(off
= utc_offset_arg(off
))) {
2517 if (NIL_P(zone
= find_timezone(time
, off
))) invalid_utc_offset(off
);
2519 if (!zone_timelocal(zone
, time
)) invalid_utc_offset(off
);
2522 else if (off
== UTC_ZONE
) {
2523 return time_gmtime(time
);
2526 validate_utc_offset(off
);
2527 time_set_utc_offset(time
, off
);
2534 static struct timespec
2535 time_timespec(VALUE num
, int interval
)
2538 const char *const tstr
= interval
? "time interval" : "time";
2541 #ifndef NEGATIVE_TIME_T
2542 # define arg_range_check(v) \
2544 rb_raise(rb_eArgError, "%s must not be negative", tstr) : \
2547 # define arg_range_check(v) \
2548 ((interval && (v) < 0) ? \
2549 rb_raise(rb_eArgError, "time interval must not be negative") : \
2553 if (FIXNUM_P(num
)) {
2554 t
.tv_sec
= NUM2TIMET(num
);
2555 arg_range_check(t
.tv_sec
);
2558 else if (RB_FLOAT_TYPE_P(num
)) {
2559 double x
= RFLOAT_VALUE(num
);
2566 t
.tv_nsec
= (int)(d
*1e9
+0.5);
2567 if (t
.tv_nsec
>= 1000000000) {
2568 t
.tv_nsec
-= 1000000000;
2572 else if ((t
.tv_nsec
= (int)(-d
*1e9
+0.5)) > 0) {
2573 t
.tv_nsec
= 1000000000 - t
.tv_nsec
;
2576 t
.tv_sec
= (time_t)f
;
2577 if (f
!= t
.tv_sec
) {
2578 rb_raise(rb_eRangeError
, "%f out of Time range", x
);
2582 else if (RB_BIGNUM_TYPE_P(num
)) {
2583 t
.tv_sec
= NUM2TIMET(num
);
2584 arg_range_check(t
.tv_sec
);
2589 ary
= rb_check_funcall(num
, id_divmod
, 1, &i
);
2590 if (ary
!= Qundef
&& !NIL_P(ary
= rb_check_array_type(ary
))) {
2591 i
= rb_ary_entry(ary
, 0);
2592 f
= rb_ary_entry(ary
, 1);
2593 t
.tv_sec
= NUM2TIMET(i
);
2594 arg_range_check(t
.tv_sec
);
2595 f
= rb_funcall(f
, '*', 1, INT2FIX(1000000000));
2596 t
.tv_nsec
= NUM2LONG(f
);
2599 rb_raise(rb_eTypeError
, "can't convert %"PRIsVALUE
" into %s",
2600 rb_obj_class(num
), tstr
);
2604 #undef arg_range_check
2607 static struct timeval
2608 time_timeval(VALUE num
, int interval
)
2613 ts
= time_timespec(num
, interval
);
2614 tv
.tv_sec
= (TYPEOF_TIMEVAL_TV_SEC
)ts
.tv_sec
;
2615 tv
.tv_usec
= (TYPEOF_TIMEVAL_TV_USEC
)(ts
.tv_nsec
/ 1000);
2621 rb_time_interval(VALUE num
)
2623 return time_timeval(num
, TRUE
);
2627 rb_time_timeval(VALUE time
)
2629 struct time_object
*tobj
;
2633 if (IsTimeval(time
)) {
2634 GetTimeval(time
, tobj
);
2635 ts
= timew2timespec(tobj
->timew
);
2636 t
.tv_sec
= (TYPEOF_TIMEVAL_TV_SEC
)ts
.tv_sec
;
2637 t
.tv_usec
= (TYPEOF_TIMEVAL_TV_USEC
)(ts
.tv_nsec
/ 1000);
2640 return time_timeval(time
, FALSE
);
2644 rb_time_timespec(VALUE time
)
2646 struct time_object
*tobj
;
2649 if (IsTimeval(time
)) {
2650 GetTimeval(time
, tobj
);
2651 t
= timew2timespec(tobj
->timew
);
2654 return time_timespec(time
, FALSE
);
2658 rb_time_timespec_interval(VALUE num
)
2660 return time_timespec(num
, TRUE
);
2664 get_scale(VALUE unit
)
2666 if (unit
== ID2SYM(id_nanosecond
) || unit
== ID2SYM(id_nsec
)) {
2669 else if (unit
== ID2SYM(id_microsecond
) || unit
== ID2SYM(id_usec
)) {
2672 else if (unit
== ID2SYM(id_millisecond
)) {
2676 rb_raise(rb_eArgError
, "unexpected unit: %"PRIsVALUE
, unit
);
2681 time_s_at(rb_execution_context_t
*ec
, VALUE klass
, VALUE time
, VALUE subsec
, VALUE unit
, VALUE zone
)
2687 int scale
= get_scale(unit
);
2688 time
= num_exact(time
);
2689 t
= num_exact(subsec
);
2690 timew
= wadd(rb_time_magnify(v2w(time
)), wmulquoll(v2w(t
), TIME_SCALE
, scale
));
2691 t
= time_new_timew(klass
, timew
);
2693 else if (IsTimeval(time
)) {
2694 struct time_object
*tobj
, *tobj2
;
2695 GetTimeval(time
, tobj
);
2696 t
= time_new_timew(klass
, tobj
->timew
);
2697 GetTimeval(t
, tobj2
);
2698 TZMODE_COPY(tobj2
, tobj
);
2701 timew
= rb_time_magnify(v2w(num_exact(time
)));
2702 t
= time_new_timew(klass
, timew
);
2705 time_zonelocal(t
, zone
);
2712 time_s_at1(rb_execution_context_t
*ec
, VALUE klass
, VALUE time
)
2714 return time_s_at(ec
, klass
, time
, Qfalse
, ID2SYM(id_microsecond
), Qnil
);
2717 static const char months
[][4] = {
2718 "jan", "feb", "mar", "apr", "may", "jun",
2719 "jul", "aug", "sep", "oct", "nov", "dec",
2725 if (RB_TYPE_P(obj
, T_STRING
)) {
2726 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2729 return NUM2INT(obj
);
2732 /* bits should be 0 <= x <= 31 */
2734 obj2ubits(VALUE obj
, unsigned int bits
)
2736 const unsigned int usable_mask
= (1U << bits
) - 1;
2737 unsigned int rv
= (unsigned int)obj2int(obj
);
2739 if ((rv
& usable_mask
) != rv
)
2740 rb_raise(rb_eArgError
, "argument out of range");
2741 return (uint32_t)rv
;
2747 if (RB_TYPE_P(obj
, T_STRING
)) {
2748 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2751 obj
= rb_to_int(obj
);
2758 obj2subsecx(VALUE obj
, VALUE
*subsecx
)
2762 if (RB_TYPE_P(obj
, T_STRING
)) {
2763 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2764 *subsecx
= INT2FIX(0);
2767 divmodv(num_exact(obj
), INT2FIX(1), &obj
, &subsec
);
2768 *subsecx
= w2v(rb_time_magnify(v2w(subsec
)));
2770 return obj2ubits(obj
, 6); /* vtm->sec */
2774 usec2subsecx(VALUE obj
)
2776 if (RB_TYPE_P(obj
, T_STRING
)) {
2777 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2780 return mulquov(num_exact(obj
), INT2FIX(TIME_SCALE
), INT2FIX(1000000));
2784 month_arg(VALUE arg
)
2788 if (FIXNUM_P(arg
)) {
2789 return obj2ubits(arg
, 4);
2793 VALUE s
= rb_check_string_type(arg
);
2794 if (!NIL_P(s
) && RSTRING_LEN(s
) > 0) {
2796 for (i
=0; i
<12; i
++) {
2797 if (RSTRING_LEN(s
) == 3 &&
2798 STRNCASECMP(months
[i
], RSTRING_PTR(s
), 3) == 0) {
2805 mon
= obj2ubits(arg
, 4);
2811 validate_utc_offset(VALUE utc_offset
)
2813 if (le(utc_offset
, INT2FIX(-86400)) || ge(utc_offset
, INT2FIX(86400)))
2814 rb_raise(rb_eArgError
, "utc_offset out of range");
2819 validate_zone_name(VALUE zone_name
)
2821 StringValueCStr(zone_name
);
2826 validate_vtm(struct vtm
*vtm
)
2828 #define validate_vtm_range(mem, b, e) \
2829 ((vtm->mem < b || vtm->mem > e) ? \
2830 rb_raise(rb_eArgError, #mem" out of range") : (void)0)
2831 validate_vtm_range(mon
, 1, 12);
2832 validate_vtm_range(mday
, 1, 31);
2833 validate_vtm_range(hour
, 0, 24);
2834 validate_vtm_range(min
, 0, (vtm
->hour
== 24 ? 0 : 59));
2835 validate_vtm_range(sec
, 0, (vtm
->hour
== 24 ? 0 : 60));
2836 if (lt(vtm
->subsecx
, INT2FIX(0)) || ge(vtm
->subsecx
, INT2FIX(TIME_SCALE
)))
2837 rb_raise(rb_eArgError
, "subsecx out of range");
2838 if (!NIL_P(vtm
->utc_offset
)) validate_utc_offset(vtm
->utc_offset
);
2839 #undef validate_vtm_range
2843 time_arg(int argc
, const VALUE
*argv
, struct vtm
*vtm
)
2846 VALUE subsecx
= INT2FIX(0);
2848 vtm
->year
= INT2FIX(0);
2854 vtm
->subsecx
= INT2FIX(0);
2855 vtm
->utc_offset
= Qnil
;
2859 vtm
->zone
= str_empty
;
2869 vtm
->isdst
= RTEST(argv
[8]) ? 1 : 0;
2872 rb_scan_args(argc
, argv
, "17", &v
[0],&v
[1],&v
[2],&v
[3],&v
[4],&v
[5],&v
[6],&v
[7]);
2873 /* v[6] may be usec or zone (parsedate) */
2874 /* v[7] is wday (parsedate; ignored) */
2875 vtm
->wday
= VTM_WDAY_INITVAL
;
2876 vtm
->isdst
= VTM_ISDST_INITVAL
;
2879 vtm
->year
= obj2vint(v
[0]);
2885 vtm
->mon
= month_arg(v
[1]);
2892 vtm
->mday
= obj2ubits(v
[2], 5);
2895 /* normalize month-mday */
2899 /* this drops higher bits but it's not a problem to calc leap year */
2900 unsigned int mday2
= leap_year_v_p(vtm
->year
) ? 29 : 28;
2901 if (vtm
->mday
> mday2
) {
2911 if (vtm
->mday
== 31) {
2918 vtm
->hour
= NIL_P(v
[3])?0:obj2ubits(v
[3], 5);
2920 vtm
->min
= NIL_P(v
[4])?0:obj2ubits(v
[4], 6);
2922 if (!NIL_P(v
[6]) && argc
== 7) {
2923 vtm
->sec
= NIL_P(v
[5])?0:obj2ubits(v
[5],6);
2924 subsecx
= usec2subsecx(v
[6]);
2927 /* when argc == 8, v[6] is timezone, but ignored */
2932 vtm
->sec
= obj2subsecx(v
[5], &subsecx
);
2935 vtm
->subsecx
= subsecx
;
2938 RB_GC_GUARD(subsecx
);
2945 * ensure about negative years in proleptic Gregorian calendar.
2947 unsigned long uy
= (unsigned long)(LIKELY(y
>= 0) ? y
: -y
);
2949 if (LIKELY(uy
% 4 != 0)) return 0;
2951 unsigned long century
= uy
/ 100;
2952 if (LIKELY(uy
!= century
* 100)) return 1;
2953 return century
% 4 == 0;
2957 timegm_noleapsecond(struct tm
*tm
)
2959 long tm_year
= tm
->tm_year
;
2960 int tm_yday
= calc_tm_yday(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
2963 * `Seconds Since the Epoch' in SUSv3:
2964 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
2965 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
2966 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
2968 return tm
->tm_sec
+ tm
->tm_min
*60 + tm
->tm_hour
*3600 +
2972 DIV(tm_year
-1,100) +
2973 DIV(tm_year
+299,400))*86400;
2977 #define DEBUG_FIND_TIME_NUMGUESS
2978 #define DEBUG_GUESSRANGE
2981 static const bool debug_guessrange
=
2982 #ifdef DEBUG_GUESSRANGE
2988 #define DEBUG_REPORT_GUESSRANGE \
2989 (debug_guessrange ? debug_report_guessrange(guess_lo, guess_hi) : (void)0)
2992 debug_report_guessrange(time_t guess_lo
, time_t guess_hi
)
2994 unsigned_time_t guess_diff
= (unsigned_time_t
)(guess_hi
-guess_lo
);
2995 fprintf(stderr
, "find time guess range: %"PRI_TIMET_PREFIX
"d - "
2996 "%"PRI_TIMET_PREFIX
"d : %"PRI_TIMET_PREFIX
"u\n",
2997 guess_lo
, guess_hi
, guess_diff
);
3000 static const bool debug_find_time_numguess
=
3001 #ifdef DEBUG_FIND_TIME_NUMGUESS
3007 #define DEBUG_FIND_TIME_NUMGUESS_INC \
3008 (void)(debug_find_time_numguess && find_time_numguess++),
3009 static unsigned long long find_time_numguess
;
3012 find_time_numguess_getter(ID name
, VALUE
*data
)
3014 unsigned long long *numguess
= (void *)data
;
3015 return ULL2NUM(*numguess
);
3019 find_time_t(struct tm
*tptr
, int utc_p
, time_t *tp
)
3021 time_t guess
, guess0
, guess_lo
, guess_hi
;
3022 struct tm
*tm
, tm0
, tm_lo
, tm_hi
;
3029 #define GUESS(p) (DEBUG_FIND_TIME_NUMGUESS_INC (utc_p ? gmtime_with_leapsecond((p), &result) : LOCALTIME((p), result)))
3031 guess_lo
= TIMET_MIN
;
3032 guess_hi
= TIMET_MAX
;
3034 find_dst
= 0 < tptr
->tm_isdst
;
3036 /* /etc/localtime might be changed. reload it. */
3040 if (tm0
.tm_mon
< 0) {
3047 else if (11 < tm0
.tm_mon
) {
3054 else if (tm0
.tm_mday
< 1) {
3060 else if ((d
= days_in_month_in(1900 + tm0
.tm_year
)[tm0
.tm_mon
]) < tm0
.tm_mday
) {
3066 else if (tm0
.tm_hour
< 0) {
3071 else if (23 < tm0
.tm_hour
) {
3076 else if (tm0
.tm_min
< 0) {
3080 else if (59 < tm0
.tm_min
) {
3084 else if (tm0
.tm_sec
< 0) {
3087 else if (60 < tm0
.tm_sec
) {
3091 DEBUG_REPORT_GUESSRANGE
;
3092 guess0
= guess
= timegm_noleapsecond(&tm0
);
3095 d
= tmcmp(tptr
, tm
);
3096 if (d
== 0) { goto found
; }
3099 guess
-= 24 * 60 * 60;
3103 guess
+= 24 * 60 * 60;
3105 DEBUG_REPORT_GUESSRANGE
;
3106 if (guess_lo
< guess
&& guess
< guess_hi
&& (tm
= GUESS(&guess
)) != NULL
) {
3107 d
= tmcmp(tptr
, tm
);
3108 if (d
== 0) { goto found
; }
3113 DEBUG_REPORT_GUESSRANGE
;
3117 tm
= GUESS(&guess_lo
);
3118 if (!tm
) goto error
;
3119 d
= tmcmp(tptr
, tm
);
3120 if (d
< 0) goto out_of_range
;
3121 if (d
== 0) { guess
= guess_lo
; goto found
; }
3124 tm
= GUESS(&guess_hi
);
3125 if (!tm
) goto error
;
3126 d
= tmcmp(tptr
, tm
);
3127 if (d
> 0) goto out_of_range
;
3128 if (d
== 0) { guess
= guess_hi
; goto found
; }
3131 DEBUG_REPORT_GUESSRANGE
;
3135 while (guess_lo
+ 1 < guess_hi
) {
3138 guess
= guess_lo
/ 2 + guess_hi
/ 2;
3139 if (guess
<= guess_lo
)
3140 guess
= guess_lo
+ 1;
3141 else if (guess
>= guess_hi
)
3142 guess
= guess_hi
- 1;
3147 time_t guess0_hi
= timegm_noleapsecond(&tm_hi
);
3148 guess
= guess_hi
- (guess0_hi
- guess0
);
3149 if (guess
== guess_hi
) /* hh:mm:60 tends to cause this condition. */
3153 else if (status
== 2) {
3154 time_t guess0_lo
= timegm_noleapsecond(&tm_lo
);
3155 guess
= guess_lo
+ (guess0
- guess0_lo
);
3156 if (guess
== guess_lo
)
3160 if (guess
<= guess_lo
|| guess_hi
<= guess
) {
3161 /* Previous guess is invalid. try binary search. */
3162 if (debug_guessrange
) {
3163 if (guess
<= guess_lo
) {
3164 fprintf(stderr
, "too small guess: %"PRI_TIMET_PREFIX
"d"\
3165 " <= %"PRI_TIMET_PREFIX
"d\n", guess
, guess_lo
);
3167 if (guess_hi
<= guess
) {
3168 fprintf(stderr
, "too big guess: %"PRI_TIMET_PREFIX
"d"\
3169 " <= %"PRI_TIMET_PREFIX
"d\n", guess_hi
, guess
);
3178 if (!tm
) goto error
;
3180 d
= tmcmp(tptr
, tm
);
3185 DEBUG_REPORT_GUESSRANGE
;
3190 DEBUG_REPORT_GUESSRANGE
;
3197 /* Given argument has no corresponding time_t. Let's extrapolate. */
3199 * `Seconds Since the Epoch' in SUSv3:
3200 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
3201 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
3202 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
3205 tptr_tm_yday
= calc_tm_yday(tptr
->tm_year
, tptr
->tm_mon
, tptr
->tm_mday
);
3208 ((tptr
->tm_year
- tm_lo
.tm_year
) * 365 +
3209 DIV((tptr
->tm_year
-69), 4) -
3210 DIV((tptr
->tm_year
-1), 100) +
3211 DIV((tptr
->tm_year
+299), 400) -
3212 DIV((tm_lo
.tm_year
-69), 4) +
3213 DIV((tm_lo
.tm_year
-1), 100) -
3214 DIV((tm_lo
.tm_year
+299), 400) +
3216 tm_lo
.tm_yday
) * 86400 +
3217 (tptr
->tm_hour
- tm_lo
.tm_hour
) * 3600 +
3218 (tptr
->tm_min
- tm_lo
.tm_min
) * 60 +
3219 (tptr
->tm_sec
- (tm_lo
.tm_sec
== 60 ? 59 : tm_lo
.tm_sec
));
3225 /* If localtime is nonmonotonic, another result may exist. */
3228 guess2
= guess
- 2 * 60 * 60;
3229 tm
= LOCALTIME(&guess2
, result
);
3231 if (tptr
->tm_hour
!= (tm
->tm_hour
+ 2) % 24 ||
3232 tptr
->tm_min
!= tm
->tm_min
||
3233 tptr
->tm_sec
!= tm
->tm_sec
) {
3234 guess2
-= (tm
->tm_hour
- tptr
->tm_hour
) * 60 * 60 +
3235 (tm
->tm_min
- tptr
->tm_min
) * 60 +
3236 (tm
->tm_sec
- tptr
->tm_sec
);
3237 if (tptr
->tm_mday
!= tm
->tm_mday
)
3238 guess2
+= 24 * 60 * 60;
3239 if (guess
!= guess2
) {
3240 tm
= LOCALTIME(&guess2
, result
);
3241 if (tm
&& tmcmp(tptr
, tm
) == 0) {
3253 guess2
= guess
+ 2 * 60 * 60;
3254 tm
= LOCALTIME(&guess2
, result
);
3256 if ((tptr
->tm_hour
+ 2) % 24 != tm
->tm_hour
||
3257 tptr
->tm_min
!= tm
->tm_min
||
3258 tptr
->tm_sec
!= tm
->tm_sec
) {
3259 guess2
-= (tm
->tm_hour
- tptr
->tm_hour
) * 60 * 60 +
3260 (tm
->tm_min
- tptr
->tm_min
) * 60 +
3261 (tm
->tm_sec
- tptr
->tm_sec
);
3262 if (tptr
->tm_mday
!= tm
->tm_mday
)
3263 guess2
-= 24 * 60 * 60;
3264 if (guess
!= guess2
) {
3265 tm
= LOCALTIME(&guess2
, result
);
3266 if (tm
&& tmcmp(tptr
, tm
) == 0) {
3282 return "time out of range";
3285 return "gmtime/localtime error";
3289 vtmcmp(struct vtm
*a
, struct vtm
*b
)
3291 if (ne(a
->year
, b
->year
))
3292 return lt(a
->year
, b
->year
) ? -1 : 1;
3293 else if (a
->mon
!= b
->mon
)
3294 return a
->mon
< b
->mon
? -1 : 1;
3295 else if (a
->mday
!= b
->mday
)
3296 return a
->mday
< b
->mday
? -1 : 1;
3297 else if (a
->hour
!= b
->hour
)
3298 return a
->hour
< b
->hour
? -1 : 1;
3299 else if (a
->min
!= b
->min
)
3300 return a
->min
< b
->min
? -1 : 1;
3301 else if (a
->sec
!= b
->sec
)
3302 return a
->sec
< b
->sec
? -1 : 1;
3303 else if (ne(a
->subsecx
, b
->subsecx
))
3304 return lt(a
->subsecx
, b
->subsecx
) ? -1 : 1;
3310 tmcmp(struct tm
*a
, struct tm
*b
)
3312 if (a
->tm_year
!= b
->tm_year
)
3313 return a
->tm_year
< b
->tm_year
? -1 : 1;
3314 else if (a
->tm_mon
!= b
->tm_mon
)
3315 return a
->tm_mon
< b
->tm_mon
? -1 : 1;
3316 else if (a
->tm_mday
!= b
->tm_mday
)
3317 return a
->tm_mday
< b
->tm_mday
? -1 : 1;
3318 else if (a
->tm_hour
!= b
->tm_hour
)
3319 return a
->tm_hour
< b
->tm_hour
? -1 : 1;
3320 else if (a
->tm_min
!= b
->tm_min
)
3321 return a
->tm_min
< b
->tm_min
? -1 : 1;
3322 else if (a
->tm_sec
!= b
->tm_sec
)
3323 return a
->tm_sec
< b
->tm_sec
? -1 : 1;
3330 * Time.utc(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3331 * Time.utc(sec_i, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3333 * Returns a new \Time object based the on given arguments;
3334 * its timezone is UTC.
3336 * In the first form (up to seven arguments), argument +year+ is required.
3338 * Time.utc(2000) # => 2000-01-01 00:00:00 UTC
3339 * Time.utc(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 UTC
3341 * In the second form, all ten arguments are required,
3342 * though the last four are ignored.
3343 * This form is useful for creating a time from a 10-element array
3344 * such as is returned by #to_a.
3346 * array = Time.now.to_a
3347 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3349 * Time.utc(*array) # => 2000-04-24 13:26:57 UTC
3352 * :include: doc/time/year.rdoc
3353 * :include: doc/time/mon-min.rdoc
3354 * :include: doc/time/sec_i.rdoc
3355 * :include: doc/time/usec.rdoc
3359 * Related: Time.local.
3363 time_s_mkutc(int argc
, VALUE
*argv
, VALUE klass
)
3367 time_arg(argc
, argv
, &vtm
);
3368 return time_gmtime(time_new_timew(klass
, timegmw(&vtm
)));
3373 * Time.local(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3374 * Time.local(sec, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3376 * Returns a new \Time object based the on given arguments;
3377 * its timezone is the local timezone.
3379 * In the first form (up to seven arguments), argument +year+ is required.
3381 * Time.local(2000) # => 2000-01-01 00:00:00 -0600
3382 * Time.local(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 -0600
3384 * In the second form, all ten arguments are required,
3385 * though the last four are ignored.
3386 * This form is useful for creating a time from a 10-element array
3387 * such as those returned by #to_a.
3389 * array = Time.now.to_a
3390 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3392 * Time.local(*array) # => 2000-04-24 13:26:57 -0500
3395 * :include: doc/time/year.rdoc
3396 * :include: doc/time/mon-min.rdoc
3397 * :include: doc/time/sec_i.rdoc
3398 * :include: doc/time/usec.rdoc
3400 * Alias: Time.mktime.
3402 * Related: Time.utc.
3406 time_s_mktime(int argc
, VALUE
*argv
, VALUE klass
)
3410 time_arg(argc
, argv
, &vtm
);
3411 return time_localtime(time_new_timew(klass
, timelocalw(&vtm
)));
3417 * time.tv_sec -> int
3419 * Returns the value of _time_ as an integer number of seconds
3422 * If _time_ contains subsecond, they are truncated.
3424 * t = Time.now #=> 2020-07-21 01:41:29.746012609 +0900
3425 * t.to_i #=> 1595263289
3429 time_to_i(VALUE time
)
3431 struct time_object
*tobj
;
3433 GetTimeval(time
, tobj
);
3434 return w2v(wdiv(tobj
->timew
, WINT2FIXWV(TIME_SCALE
)));
3439 * time.to_f -> float
3441 * Returns the value of _time_ as a floating point number of
3442 * seconds since the Epoch.
3443 * The return value approximate the exact value in the Time object
3444 * because floating point numbers cannot represent all rational numbers
3447 * t = Time.now #=> 2020-07-20 22:00:29.38740268 +0900
3448 * t.to_f #=> 1595250029.3874028
3449 * t.to_i #=> 1595250029
3451 * Note that IEEE 754 double is not accurate enough to represent
3452 * the exact number of nanoseconds since the Epoch.
3453 * (IEEE 754 double has 53bit mantissa.
3454 * So it can represent exact number of nanoseconds only in
3455 * <tt>2 ** 53 / 1_000_000_000 / 60 / 60 / 24 = 104.2</tt> days.)
3456 * When Ruby uses a nanosecond-resolution clock function,
3457 * such as +clock_gettime+ of POSIX, to obtain the current time,
3458 * Time#to_f can lose information of a Time object created with +Time.now+.
3462 time_to_f(VALUE time
)
3464 struct time_object
*tobj
;
3466 GetTimeval(time
, tobj
);
3467 return rb_Float(rb_time_unmagnify_to_float(tobj
->timew
));
3472 * time.to_r -> a_rational
3474 * Returns the value of _time_ as a rational number of seconds
3477 * t = Time.now #=> 2020-07-20 22:03:45.212167333 +0900
3478 * t.to_r #=> (1595250225212167333/1000000000)
3480 * This method is intended to be used to get an accurate value
3481 * representing the seconds (including subsecond) since the Epoch.
3485 time_to_r(VALUE time
)
3487 struct time_object
*tobj
;
3490 GetTimeval(time
, tobj
);
3491 v
= rb_time_unmagnify_to_rational(tobj
->timew
);
3492 if (!RB_TYPE_P(v
, T_RATIONAL
)) {
3493 v
= rb_Rational1(v
);
3501 * time.tv_usec -> int
3503 * Returns the number of microseconds for the subsecond part of _time_.
3504 * The result is a non-negative integer less than 10**6.
3506 * t = Time.now #=> 2020-07-20 22:05:58.459785953 +0900
3509 * If _time_ has fraction of microsecond (such as nanoseconds),
3512 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3515 * Time#subsec can be used to obtain the subsecond part exactly.
3519 time_usec(VALUE time
)
3521 struct time_object
*tobj
;
3524 GetTimeval(time
, tobj
);
3526 w
= wmod(tobj
->timew
, WINT2WV(TIME_SCALE
));
3527 wmuldivmod(w
, WINT2FIXWV(1000000), WINT2FIXWV(TIME_SCALE
), &q
, &r
);
3528 return rb_to_int(w2v(q
));
3534 * time.tv_nsec -> int
3536 * Returns the number of nanoseconds for the subsecond part of _time_.
3537 * The result is a non-negative integer less than 10**9.
3539 * t = Time.now #=> 2020-07-20 22:07:10.963933942 +0900
3540 * t.nsec #=> 963933942
3542 * If _time_ has fraction of nanosecond (such as picoseconds),
3545 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3546 * t.nsec #=> 666777888
3548 * Time#subsec can be used to obtain the subsecond part exactly.
3552 time_nsec(VALUE time
)
3554 struct time_object
*tobj
;
3556 GetTimeval(time
, tobj
);
3557 return rb_to_int(w2v(wmulquoll(wmod(tobj
->timew
, WINT2WV(TIME_SCALE
)), 1000000000, TIME_SCALE
)));
3562 * time.subsec -> number
3564 * Returns the subsecond for _time_.
3566 * The return value can be a rational number.
3568 * t = Time.now #=> 2020-07-20 15:40:26.867462289 +0900
3569 * t.subsec #=> (867462289/1000000000)
3571 * t = Time.now #=> 2020-07-20 15:40:50.313828595 +0900
3572 * t.subsec #=> (62765719/200000000)
3574 * t = Time.new(2000,1,1,2,3,4) #=> 2000-01-01 02:03:04 +0900
3577 * Time.new(2000,1,1,0,0,1/3r,"UTC").subsec #=> (1/3)
3582 time_subsec(VALUE time
)
3584 struct time_object
*tobj
;
3586 GetTimeval(time
, tobj
);
3587 return quov(w2v(wmod(tobj
->timew
, WINT2FIXWV(TIME_SCALE
))), INT2FIX(TIME_SCALE
));
3592 * time <=> other_time -> -1, 0, +1, or nil
3594 * Compares +time+ with +other_time+.
3596 * -1, 0, +1 or nil depending on whether +time+ is less than, equal to, or
3597 * greater than +other_time+.
3599 * +nil+ is returned if the two values are incomparable.
3601 * t = Time.now #=> 2007-11-19 08:12:12 -0600
3602 * t2 = t + 2592000 #=> 2007-12-19 08:12:12 -0600
3606 * t = Time.now #=> 2007-11-19 08:13:38 -0600
3607 * t2 = t + 0.1 #=> 2007-11-19 08:13:38 -0600
3608 * t.nsec #=> 98222999
3609 * t2.nsec #=> 198222999
3616 time_cmp(VALUE time1
, VALUE time2
)
3618 struct time_object
*tobj1
, *tobj2
;
3621 GetTimeval(time1
, tobj1
);
3622 if (IsTimeval(time2
)) {
3623 GetTimeval(time2
, tobj2
);
3624 n
= wcmp(tobj1
->timew
, tobj2
->timew
);
3627 return rb_invcmp(time1
, time2
);
3629 if (n
== 0) return INT2FIX(0);
3630 if (n
> 0) return INT2FIX(1);
3636 * time.eql?(other_time)
3638 * Returns +true+ if _time_ and +other_time+ are
3639 * both Time objects with the same seconds (including subsecond) from the Epoch.
3643 time_eql(VALUE time1
, VALUE time2
)
3645 struct time_object
*tobj1
, *tobj2
;
3647 GetTimeval(time1
, tobj1
);
3648 if (IsTimeval(time2
)) {
3649 GetTimeval(time2
, tobj2
);
3650 return rb_equal(w2v(tobj1
->timew
), w2v(tobj2
->timew
));
3657 * time.utc? -> true or false
3658 * time.gmt? -> true or false
3660 * Returns +true+ if _time_ represents a time in UTC (GMT).
3662 * t = Time.now #=> 2007-11-19 08:15:23 -0600
3664 * t = Time.gm(2000,"jan",1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3667 * t = Time.now #=> 2007-11-19 08:16:03 -0600
3669 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3674 time_utc_p(VALUE time
)
3676 struct time_object
*tobj
;
3678 GetTimeval(time
, tobj
);
3679 return RBOOL(TZMODE_UTC_P(tobj
));
3684 * time.hash -> integer
3686 * Returns a hash code for this Time object.
3688 * See also Object#hash.
3692 time_hash(VALUE time
)
3694 struct time_object
*tobj
;
3696 GetTimeval(time
, tobj
);
3697 return rb_hash(w2v(tobj
->timew
));
3702 time_init_copy(VALUE copy
, VALUE time
)
3704 struct time_object
*tobj
, *tcopy
;
3706 if (!OBJ_INIT_COPY(copy
, time
)) return copy
;
3707 GetTimeval(time
, tobj
);
3708 GetNewTimeval(copy
, tcopy
);
3709 MEMCPY(tcopy
, tobj
, struct time_object
, 1);
3715 time_dup(VALUE time
)
3717 VALUE dup
= time_s_alloc(rb_obj_class(time
));
3718 time_init_copy(dup
, time
);
3723 time_localtime(VALUE time
)
3725 struct time_object
*tobj
;
3729 GetTimeval(time
, tobj
);
3730 if (TZMODE_LOCALTIME_P(tobj
)) {
3738 zone
= tobj
->vtm
.zone
;
3739 if (maybe_tzobj_p(zone
) && zone_localtime(zone
, time
)) {
3743 if (!localtimew(tobj
->timew
, &vtm
))
3744 rb_raise(rb_eArgError
, "localtime error");
3748 TZMODE_SET_LOCALTIME(tobj
);
3753 time_zonelocal(VALUE time
, VALUE off
)
3756 if (zone_localtime(zone
, time
)) return time
;
3758 if (NIL_P(off
= utc_offset_arg(off
))) {
3760 if (NIL_P(zone
= find_timezone(time
, off
))) invalid_utc_offset(off
);
3761 if (!zone_localtime(zone
, time
)) invalid_utc_offset(off
);
3764 else if (off
== UTC_ZONE
) {
3765 return time_gmtime(time
);
3767 validate_utc_offset(off
);
3769 time_set_utc_offset(time
, off
);
3770 return time_fixoff(time
);
3775 * time.localtime -> time
3776 * time.localtime(utc_offset) -> time
3778 * Converts _time_ to local time (using the local time zone in
3779 * effect at the creation time of _time_) modifying the receiver.
3781 * If +utc_offset+ is given, it is used instead of the local time.
3783 * t = Time.utc(2000, "jan", 1, 20, 15, 1) #=> 2000-01-01 20:15:01 UTC
3786 * t.localtime #=> 2000-01-01 14:15:01 -0600
3789 * t.localtime("+09:00") #=> 2000-01-02 05:15:01 +0900
3792 * If +utc_offset+ is not given and _time_ is local time, just returns
3797 time_localtime_m(int argc
, VALUE
*argv
, VALUE time
)
3801 if (rb_check_arity(argc
, 0, 1) && !NIL_P(off
= argv
[0])) {
3802 return time_zonelocal(time
, off
);
3805 return time_localtime(time
);
3810 * time.gmtime -> time
3813 * Converts _time_ to UTC (GMT), modifying the receiver.
3815 * t = Time.now #=> 2007-11-19 08:18:31 -0600
3817 * t.gmtime #=> 2007-11-19 14:18:31 UTC
3820 * t = Time.now #=> 2007-11-19 08:18:51 -0600
3822 * t.utc #=> 2007-11-19 14:18:51 UTC
3827 time_gmtime(VALUE time
)
3829 struct time_object
*tobj
;
3832 GetTimeval(time
, tobj
);
3833 if (TZMODE_UTC_P(tobj
)) {
3842 GMTIMEW(tobj
->timew
, &vtm
);
3846 TZMODE_SET_UTC(tobj
);
3851 time_fixoff(VALUE time
)
3853 struct time_object
*tobj
;
3857 GetTimeval(time
, tobj
);
3858 if (TZMODE_FIXOFF_P(tobj
)) {
3866 if (TZMODE_FIXOFF_P(tobj
))
3867 off
= tobj
->vtm
.utc_offset
;
3871 GMTIMEW(tobj
->timew
, &vtm
);
3873 zone
= tobj
->vtm
.zone
;
3875 tobj
->vtm
.zone
= zone
;
3876 vtm_add_offset(&tobj
->vtm
, off
, +1);
3879 TZMODE_SET_FIXOFF(tobj
, off
);
3885 * time.getlocal -> new_time
3886 * time.getlocal(utc_offset) -> new_time
3887 * time.getlocal(timezone) -> new_time
3889 * Returns a new Time object representing _time_ in
3890 * local time (using the local time zone in effect for this process).
3892 * If +utc_offset+ is given, it is used instead of the local time.
3893 * +utc_offset+ can be given as a human-readable string (eg. <code>"+09:00"</code>)
3894 * or as a number of seconds (eg. <code>32400</code>).
3896 * t = Time.utc(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3899 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
3903 * j = t.getlocal("+09:00") #=> 2000-01-02 05:15:01 +0900
3907 * k = t.getlocal(9*60*60) #=> 2000-01-02 05:15:01 +0900
3913 time_getlocaltime(int argc
, VALUE
*argv
, VALUE time
)
3917 if (rb_check_arity(argc
, 0, 1) && !NIL_P(off
= argv
[0])) {
3919 if (maybe_tzobj_p(zone
)) {
3920 VALUE t
= time_dup(time
);
3921 if (zone_localtime(off
, t
)) return t
;
3924 if (NIL_P(off
= utc_offset_arg(off
))) {
3926 if (NIL_P(zone
= find_timezone(time
, off
))) invalid_utc_offset(off
);
3927 time
= time_dup(time
);
3928 if (!zone_localtime(zone
, time
)) invalid_utc_offset(off
);
3931 else if (off
== UTC_ZONE
) {
3932 return time_gmtime(time_dup(time
));
3934 validate_utc_offset(off
);
3936 time
= time_dup(time
);
3937 time_set_utc_offset(time
, off
);
3938 return time_fixoff(time
);
3941 return time_localtime(time_dup(time
));
3946 * time.getgm -> new_time
3947 * time.getutc -> new_time
3949 * Returns a new Time object representing _time_ in UTC.
3951 * t = Time.local(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 -0600
3953 * y = t.getgm #=> 2000-01-02 02:15:01 UTC
3959 time_getgmtime(VALUE time
)
3961 return time_gmtime(time_dup(time
));
3965 time_get_tm(VALUE time
, struct time_object
*tobj
)
3967 if (TZMODE_UTC_P(tobj
)) return time_gmtime(time
);
3968 if (TZMODE_FIXOFF_P(tobj
)) return time_fixoff(time
);
3969 return time_localtime(time
);
3972 static VALUE
strftime_cstr(const char *fmt
, size_t len
, VALUE time
, rb_encoding
*enc
);
3973 #define strftimev(fmt, time, enc) strftime_cstr((fmt), rb_strlen_lit(fmt), (time), (enc))
3977 * time.asctime -> string
3978 * time.ctime -> string
3980 * Returns a canonical string representation of _time_.
3982 * Time.now.asctime #=> "Wed Apr 9 08:56:03 2003"
3983 * Time.now.ctime #=> "Wed Apr 9 08:56:03 2003"
3987 time_asctime(VALUE time
)
3989 return strftimev("%a %b %e %T %Y", time
, rb_usascii_encoding());
3994 * time.to_s -> string
3996 * Returns a string representing _time_. Equivalent to calling
3997 * #strftime with the appropriate format string.
4000 * t.to_s #=> "2012-11-10 18:16:12 +0100"
4001 * t.strftime "%Y-%m-%d %H:%M:%S %z" #=> "2012-11-10 18:16:12 +0100"
4003 * t.utc.to_s #=> "2012-11-10 17:16:12 UTC"
4004 * t.strftime "%Y-%m-%d %H:%M:%S UTC" #=> "2012-11-10 17:16:12 UTC"
4008 time_to_s(VALUE time
)
4010 struct time_object
*tobj
;
4012 GetTimeval(time
, tobj
);
4013 if (TZMODE_UTC_P(tobj
))
4014 return strftimev("%Y-%m-%d %H:%M:%S UTC", time
, rb_usascii_encoding());
4016 return strftimev("%Y-%m-%d %H:%M:%S %z", time
, rb_usascii_encoding());
4021 * time.inspect -> string
4023 * Returns a detailed string representing _time_. Unlike to_s,
4024 * preserves subsecond in the representation for easier debugging.
4027 * t.inspect #=> "2012-11-10 18:16:12.261257655 +0100"
4028 * t.strftime "%Y-%m-%d %H:%M:%S.%N %z" #=> "2012-11-10 18:16:12.261257655 +0100"
4030 * t.utc.inspect #=> "2012-11-10 17:16:12.261257655 UTC"
4031 * t.strftime "%Y-%m-%d %H:%M:%S.%N UTC" #=> "2012-11-10 17:16:12.261257655 UTC"
4035 time_inspect(VALUE time
)
4037 struct time_object
*tobj
;
4040 GetTimeval(time
, tobj
);
4041 str
= strftimev("%Y-%m-%d %H:%M:%S", time
, rb_usascii_encoding());
4042 subsec
= w2v(wmod(tobj
->timew
, WINT2FIXWV(TIME_SCALE
)));
4043 if (FIXNUM_P(subsec
) && FIX2LONG(subsec
) == 0) {
4045 else if (FIXNUM_P(subsec
) && FIX2LONG(subsec
) < TIME_SCALE
) {
4047 rb_str_catf(str
, ".%09ld", FIX2LONG(subsec
));
4048 for (len
=RSTRING_LEN(str
); RSTRING_PTR(str
)[len
-1] == '0' && len
> 0; len
--)
4050 rb_str_resize(str
, len
);
4053 rb_str_cat_cstr(str
, " ");
4054 subsec
= quov(subsec
, INT2FIX(TIME_SCALE
));
4055 rb_str_concat(str
, rb_obj_as_string(subsec
));
4057 if (TZMODE_UTC_P(tobj
)) {
4058 rb_str_cat_cstr(str
, " UTC");
4061 /* ?TODO: subsecond offset */
4062 long off
= NUM2LONG(rb_funcall(tobj
->vtm
.utc_offset
, rb_intern("round"), 0));
4063 char sign
= (off
< 0) ? (off
= -off
, '-') : '+';
4065 int min
= (off
/= 60) % 60;
4067 rb_str_catf(str
, " %c%.2d%.2d", sign
, (int)off
, min
);
4068 if (sec
) rb_str_catf(str
, "%.2d", sec
);
4074 time_add0(VALUE klass
, const struct time_object
*tobj
, VALUE torig
, VALUE offset
, int sign
)
4077 struct time_object
*result_tobj
;
4079 offset
= num_exact(offset
);
4081 result
= time_new_timew(klass
, wsub(tobj
->timew
, rb_time_magnify(v2w(offset
))));
4083 result
= time_new_timew(klass
, wadd(tobj
->timew
, rb_time_magnify(v2w(offset
))));
4084 GetTimeval(result
, result_tobj
);
4085 TZMODE_COPY(result_tobj
, tobj
);
4091 time_add(const struct time_object
*tobj
, VALUE torig
, VALUE offset
, int sign
)
4093 return time_add0(rb_cTime
, tobj
, torig
, offset
, sign
);
4098 * time + numeric -> time
4100 * Adds some number of seconds (possibly including subsecond) to
4101 * _time_ and returns that value as a new Time object.
4103 * t = Time.now #=> 2020-07-20 22:14:43.170490982 +0900
4104 * t + (60 * 60 * 24) #=> 2020-07-21 22:14:43.170490982 +0900
4108 time_plus(VALUE time1
, VALUE time2
)
4110 struct time_object
*tobj
;
4111 GetTimeval(time1
, tobj
);
4113 if (IsTimeval(time2
)) {
4114 rb_raise(rb_eTypeError
, "time + time?");
4116 return time_add(tobj
, time1
, time2
, 1);
4121 * time - other_time -> float
4122 * time - numeric -> time
4124 * Returns a difference in seconds as a Float
4125 * between _time_ and +other_time+, or subtracts the given number
4126 * of seconds in +numeric+ from _time_.
4128 * t = Time.now #=> 2020-07-20 22:15:49.302766336 +0900
4129 * t2 = t + 2592000 #=> 2020-08-19 22:15:49.302766336 +0900
4130 * t2 - t #=> 2592000.0
4131 * t2 - 2592000 #=> 2020-07-20 22:15:49.302766336 +0900
4135 time_minus(VALUE time1
, VALUE time2
)
4137 struct time_object
*tobj
;
4139 GetTimeval(time1
, tobj
);
4140 if (IsTimeval(time2
)) {
4141 struct time_object
*tobj2
;
4143 GetTimeval(time2
, tobj2
);
4144 return rb_Float(rb_time_unmagnify_to_float(wsub(tobj
->timew
, tobj2
->timew
)));
4146 return time_add(tobj
, time1
, time2
, -1);
4150 ndigits_denominator(VALUE ndigits
)
4152 long nd
= NUM2LONG(ndigits
);
4155 rb_raise(rb_eArgError
, "negative ndigits given");
4160 return rb_rational_new(INT2FIX(1),
4161 rb_int_positive_pow(10, (unsigned long)nd
));
4166 * time.round([ndigits]) -> new_time
4168 * Rounds subsecond to a given precision in decimal digits (0 digits by default).
4169 * It returns a new Time object.
4170 * +ndigits+ should be zero or a positive integer.
4172 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4173 * t #=> 2010-03-30 05:43:25.123456789 UTC
4174 * t.round #=> 2010-03-30 05:43:25 UTC
4175 * t.round(0) #=> 2010-03-30 05:43:25 UTC
4176 * t.round(1) #=> 2010-03-30 05:43:25.1 UTC
4177 * t.round(2) #=> 2010-03-30 05:43:25.12 UTC
4178 * t.round(3) #=> 2010-03-30 05:43:25.123 UTC
4179 * t.round(4) #=> 2010-03-30 05:43:25.1235 UTC
4181 * t = Time.utc(1999,12,31, 23,59,59)
4182 * (t + 0.4).round #=> 1999-12-31 23:59:59 UTC
4183 * (t + 0.49).round #=> 1999-12-31 23:59:59 UTC
4184 * (t + 0.5).round #=> 2000-01-01 00:00:00 UTC
4185 * (t + 1.4).round #=> 2000-01-01 00:00:00 UTC
4186 * (t + 1.49).round #=> 2000-01-01 00:00:00 UTC
4187 * (t + 1.5).round #=> 2000-01-01 00:00:01 UTC
4189 * t = Time.utc(1999,12,31, 23,59,59) #=> 1999-12-31 23:59:59 UTC
4190 * (t + 0.123456789).round(4).iso8601(6) #=> 1999-12-31 23:59:59.1235 UTC
4194 time_round(int argc
, VALUE
*argv
, VALUE time
)
4196 VALUE ndigits
, v
, den
;
4197 struct time_object
*tobj
;
4199 if (!rb_check_arity(argc
, 0, 1) || NIL_P(ndigits
= argv
[0]))
4202 den
= ndigits_denominator(ndigits
);
4204 GetTimeval(time
, tobj
);
4205 v
= w2v(rb_time_unmagnify(tobj
->timew
));
4208 if (lt(v
, quov(den
, INT2FIX(2))))
4209 return time_add(tobj
, time
, v
, -1);
4211 return time_add(tobj
, time
, subv(den
, v
), 1);
4216 * time.floor([ndigits]) -> new_time
4218 * Floors subsecond to a given precision in decimal digits (0 digits by default).
4219 * It returns a new Time object.
4220 * +ndigits+ should be zero or a positive integer.
4222 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4223 * t #=> 2010-03-30 05:43:25.123456789 UTC
4224 * t.floor #=> 2010-03-30 05:43:25 UTC
4225 * t.floor(0) #=> 2010-03-30 05:43:25 UTC
4226 * t.floor(1) #=> 2010-03-30 05:43:25.1 UTC
4227 * t.floor(2) #=> 2010-03-30 05:43:25.12 UTC
4228 * t.floor(3) #=> 2010-03-30 05:43:25.123 UTC
4229 * t.floor(4) #=> 2010-03-30 05:43:25.1234 UTC
4231 * t = Time.utc(1999,12,31, 23,59,59)
4232 * (t + 0.4).floor #=> 1999-12-31 23:59:59 UTC
4233 * (t + 0.9).floor #=> 1999-12-31 23:59:59 UTC
4234 * (t + 1.4).floor #=> 2000-01-01 00:00:00 UTC
4235 * (t + 1.9).floor #=> 2000-01-01 00:00:00 UTC
4237 * t = Time.utc(1999,12,31, 23,59,59)
4238 * (t + 0.123456789).floor(4) #=> 1999-12-31 23:59:59.1234 UTC
4242 time_floor(int argc
, VALUE
*argv
, VALUE time
)
4244 VALUE ndigits
, v
, den
;
4245 struct time_object
*tobj
;
4247 if (!rb_check_arity(argc
, 0, 1) || NIL_P(ndigits
= argv
[0]))
4250 den
= ndigits_denominator(ndigits
);
4252 GetTimeval(time
, tobj
);
4253 v
= w2v(rb_time_unmagnify(tobj
->timew
));
4256 return time_add(tobj
, time
, v
, -1);
4261 * time.ceil([ndigits]) -> new_time
4263 * Ceils subsecond to a given precision in decimal digits (0 digits by default).
4264 * It returns a new Time object.
4265 * +ndigits+ should be zero or a positive integer.
4267 * t = Time.utc(2010,3,30, 5,43,25.0123456789r)
4268 * t #=> 2010-03-30 05:43:25 123456789/10000000000 UTC
4269 * t.ceil #=> 2010-03-30 05:43:26 UTC
4270 * t.ceil(0) #=> 2010-03-30 05:43:26 UTC
4271 * t.ceil(1) #=> 2010-03-30 05:43:25.1 UTC
4272 * t.ceil(2) #=> 2010-03-30 05:43:25.02 UTC
4273 * t.ceil(3) #=> 2010-03-30 05:43:25.013 UTC
4274 * t.ceil(4) #=> 2010-03-30 05:43:25.0124 UTC
4276 * t = Time.utc(1999,12,31, 23,59,59)
4277 * (t + 0.4).ceil #=> 2000-01-01 00:00:00 UTC
4278 * (t + 0.9).ceil #=> 2000-01-01 00:00:00 UTC
4279 * (t + 1.4).ceil #=> 2000-01-01 00:00:01 UTC
4280 * (t + 1.9).ceil #=> 2000-01-01 00:00:01 UTC
4282 * t = Time.utc(1999,12,31, 23,59,59)
4283 * (t + 0.123456789).ceil(4) #=> 1999-12-31 23:59:59.1235 UTC
4287 time_ceil(int argc
, VALUE
*argv
, VALUE time
)
4289 VALUE ndigits
, v
, den
;
4290 struct time_object
*tobj
;
4292 if (!rb_check_arity(argc
, 0, 1) || NIL_P(ndigits
= argv
[0]))
4295 den
= ndigits_denominator(ndigits
);
4297 GetTimeval(time
, tobj
);
4298 v
= w2v(rb_time_unmagnify(tobj
->timew
));
4301 if (!rb_equal(v
, INT2FIX(0))) {
4304 return time_add(tobj
, time
, v
, 1);
4309 * time.sec -> integer
4311 * Returns the second of the minute (0..60) for _time_.
4313 * *Note:* Seconds range from zero to 60 to allow the system to inject
4314 * leap seconds. See https://en.wikipedia.org/wiki/Leap_second for further
4317 * t = Time.now #=> 2007-11-19 08:25:02 -0600
4322 time_sec(VALUE time
)
4324 struct time_object
*tobj
;
4326 GetTimeval(time
, tobj
);
4327 MAKE_TM(time
, tobj
);
4328 return INT2FIX(tobj
->vtm
.sec
);
4333 * time.min -> integer
4335 * Returns the minute of the hour (0..59) for _time_.
4337 * t = Time.now #=> 2007-11-19 08:25:51 -0600
4342 time_min(VALUE time
)
4344 struct time_object
*tobj
;
4346 GetTimeval(time
, tobj
);
4347 MAKE_TM(time
, tobj
);
4348 return INT2FIX(tobj
->vtm
.min
);
4353 * time.hour -> integer
4355 * Returns the hour of the day (0..23) for _time_.
4357 * t = Time.now #=> 2007-11-19 08:26:20 -0600
4362 time_hour(VALUE time
)
4364 struct time_object
*tobj
;
4366 GetTimeval(time
, tobj
);
4367 MAKE_TM(time
, tobj
);
4368 return INT2FIX(tobj
->vtm
.hour
);
4373 * time.day -> integer
4374 * time.mday -> integer
4376 * Returns the day of the month (1..31) for _time_.
4378 * t = Time.now #=> 2007-11-19 08:27:03 -0600
4384 time_mday(VALUE time
)
4386 struct time_object
*tobj
;
4388 GetTimeval(time
, tobj
);
4389 MAKE_TM(time
, tobj
);
4390 return INT2FIX(tobj
->vtm
.mday
);
4395 * time.mon -> integer
4396 * time.month -> integer
4398 * Returns the month of the year (1..12) for _time_.
4400 * t = Time.now #=> 2007-11-19 08:27:30 -0600
4406 time_mon(VALUE time
)
4408 struct time_object
*tobj
;
4410 GetTimeval(time
, tobj
);
4411 MAKE_TM(time
, tobj
);
4412 return INT2FIX(tobj
->vtm
.mon
);
4417 * time.year -> integer
4419 * Returns the year for _time_ (including the century).
4421 * t = Time.now #=> 2007-11-19 08:27:51 -0600
4426 time_year(VALUE time
)
4428 struct time_object
*tobj
;
4430 GetTimeval(time
, tobj
);
4431 MAKE_TM(time
, tobj
);
4432 return tobj
->vtm
.year
;
4437 * time.wday -> integer
4439 * Returns an integer representing the day of the week, 0..6, with
4442 * t = Time.now #=> 2007-11-20 02:35:35 -0600
4444 * t.sunday? #=> false
4445 * t.monday? #=> false
4446 * t.tuesday? #=> true
4447 * t.wednesday? #=> false
4448 * t.thursday? #=> false
4449 * t.friday? #=> false
4450 * t.saturday? #=> false
4454 time_wday(VALUE time
)
4456 struct time_object
*tobj
;
4458 GetTimeval(time
, tobj
);
4459 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.wday
!= VTM_WDAY_INITVAL
);
4460 return INT2FIX((int)tobj
->vtm
.wday
);
4463 #define wday_p(n) {\
4464 return RBOOL(time_wday(time) == INT2FIX(n)); \
4469 * time.sunday? -> true or false
4471 * Returns +true+ if _time_ represents Sunday.
4473 * t = Time.local(1990, 4, 1) #=> 1990-04-01 00:00:00 -0600
4474 * t.sunday? #=> true
4478 time_sunday(VALUE time
)
4485 * time.monday? -> true or false
4487 * Returns +true+ if _time_ represents Monday.
4489 * t = Time.local(2003, 8, 4) #=> 2003-08-04 00:00:00 -0500
4490 * t.monday? #=> true
4494 time_monday(VALUE time
)
4501 * time.tuesday? -> true or false
4503 * Returns +true+ if _time_ represents Tuesday.
4505 * t = Time.local(1991, 2, 19) #=> 1991-02-19 00:00:00 -0600
4506 * t.tuesday? #=> true
4510 time_tuesday(VALUE time
)
4517 * time.wednesday? -> true or false
4519 * Returns +true+ if _time_ represents Wednesday.
4521 * t = Time.local(1993, 2, 24) #=> 1993-02-24 00:00:00 -0600
4522 * t.wednesday? #=> true
4526 time_wednesday(VALUE time
)
4533 * time.thursday? -> true or false
4535 * Returns +true+ if _time_ represents Thursday.
4537 * t = Time.local(1995, 12, 21) #=> 1995-12-21 00:00:00 -0600
4538 * t.thursday? #=> true
4542 time_thursday(VALUE time
)
4549 * time.friday? -> true or false
4551 * Returns +true+ if _time_ represents Friday.
4553 * t = Time.local(1987, 12, 18) #=> 1987-12-18 00:00:00 -0600
4554 * t.friday? #=> true
4558 time_friday(VALUE time
)
4565 * time.saturday? -> true or false
4567 * Returns +true+ if _time_ represents Saturday.
4569 * t = Time.local(2006, 6, 10) #=> 2006-06-10 00:00:00 -0500
4570 * t.saturday? #=> true
4574 time_saturday(VALUE time
)
4581 * time.yday -> integer
4583 * Returns an integer representing the day of the year, 1..366.
4585 * t = Time.now #=> 2007-11-19 08:32:31 -0600
4590 time_yday(VALUE time
)
4592 struct time_object
*tobj
;
4594 GetTimeval(time
, tobj
);
4595 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.yday
!= 0);
4596 return INT2FIX(tobj
->vtm
.yday
);
4601 * time.isdst -> true or false
4602 * time.dst? -> true or false
4604 * Returns +true+ if _time_ occurs during Daylight
4605 * Saving Time in its time zone.
4608 * Time.local(2000, 1, 1).zone #=> "CST"
4609 * Time.local(2000, 1, 1).isdst #=> false
4610 * Time.local(2000, 1, 1).dst? #=> false
4611 * Time.local(2000, 7, 1).zone #=> "CDT"
4612 * Time.local(2000, 7, 1).isdst #=> true
4613 * Time.local(2000, 7, 1).dst? #=> true
4616 * Time.local(2000, 1, 1).zone #=> "JST"
4617 * Time.local(2000, 1, 1).isdst #=> false
4618 * Time.local(2000, 1, 1).dst? #=> false
4619 * Time.local(2000, 7, 1).zone #=> "JST"
4620 * Time.local(2000, 7, 1).isdst #=> false
4621 * Time.local(2000, 7, 1).dst? #=> false
4625 time_isdst(VALUE time
)
4627 struct time_object
*tobj
;
4629 GetTimeval(time
, tobj
);
4630 MAKE_TM(time
, tobj
);
4631 if (tobj
->vtm
.isdst
== VTM_ISDST_INITVAL
) {
4632 rb_raise(rb_eRuntimeError
, "isdst is not set yet");
4634 return RBOOL(tobj
->vtm
.isdst
);
4639 * time.zone -> string or timezone
4641 * Returns the name of the time zone used for _time_. As of Ruby
4642 * 1.8, returns ``UTC'' rather than ``GMT'' for UTC times.
4644 * t = Time.gm(2000, "jan", 1, 20, 15, 1)
4646 * t = Time.local(2000, "jan", 1, 20, 15, 1)
4651 time_zone(VALUE time
)
4653 struct time_object
*tobj
;
4656 GetTimeval(time
, tobj
);
4657 MAKE_TM(time
, tobj
);
4659 if (TZMODE_UTC_P(tobj
)) {
4660 return rb_usascii_str_new_cstr("UTC");
4662 zone
= tobj
->vtm
.zone
;
4666 if (RB_TYPE_P(zone
, T_STRING
))
4667 zone
= rb_str_dup(zone
);
4673 * time.gmt_offset -> integer
4674 * time.gmtoff -> integer
4675 * time.utc_offset -> integer
4677 * Returns the offset in seconds between the timezone of _time_
4680 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
4681 * t.gmt_offset #=> 0
4682 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
4683 * l.gmt_offset #=> -21600
4687 rb_time_utc_offset(VALUE time
)
4689 struct time_object
*tobj
;
4691 GetTimeval(time
, tobj
);
4693 if (TZMODE_UTC_P(tobj
)) {
4697 MAKE_TM(time
, tobj
);
4698 return tobj
->vtm
.utc_offset
;
4704 * time.to_a -> array
4706 * Returns a ten-element _array_ of values for _time_:
4708 * [sec, min, hour, day, month, year, wday, yday, isdst, zone]
4710 * See the individual methods for an explanation of the
4711 * valid ranges of each value. The ten elements can be passed directly
4712 * to Time.utc or Time.local to create a
4715 * t = Time.now #=> 2007-11-19 08:36:01 -0600
4716 * now = t.to_a #=> [1, 36, 8, 19, 11, 2007, 1, 323, false, "CST"]
4720 time_to_a(VALUE time
)
4722 struct time_object
*tobj
;
4724 GetTimeval(time
, tobj
);
4725 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.yday
!= 0);
4726 return rb_ary_new3(10,
4727 INT2FIX(tobj
->vtm
.sec
),
4728 INT2FIX(tobj
->vtm
.min
),
4729 INT2FIX(tobj
->vtm
.hour
),
4730 INT2FIX(tobj
->vtm
.mday
),
4731 INT2FIX(tobj
->vtm
.mon
),
4733 INT2FIX(tobj
->vtm
.wday
),
4734 INT2FIX(tobj
->vtm
.yday
),
4735 RBOOL(tobj
->vtm
.isdst
),
4740 rb_strftime_alloc(const char *format
, size_t format_len
, rb_encoding
*enc
,
4741 VALUE time
, struct vtm
*vtm
, wideval_t timew
, int gmt
)
4746 if (!timew2timespec_exact(timew
, &ts
))
4747 timev
= w2v(rb_time_unmagnify(timew
));
4750 return rb_strftime_timespec(format
, format_len
, enc
, time
, vtm
, &ts
, gmt
);
4753 return rb_strftime(format
, format_len
, enc
, time
, vtm
, timev
, gmt
);
4758 strftime_cstr(const char *fmt
, size_t len
, VALUE time
, rb_encoding
*enc
)
4760 struct time_object
*tobj
;
4763 GetTimeval(time
, tobj
);
4764 MAKE_TM(time
, tobj
);
4765 str
= rb_strftime_alloc(fmt
, len
, enc
, time
, &tobj
->vtm
, tobj
->timew
, TZMODE_UTC_P(tobj
));
4766 if (!str
) rb_raise(rb_eArgError
, "invalid format: %s", fmt
);
4772 * time.strftime( string ) -> string
4774 * Formats _time_ according to the directives in the given format string.
4776 * The directives begin with a percent (%) character.
4777 * Any text not listed as a directive will be passed through to the
4780 * The directive consists of a percent (%) character,
4781 * zero or more flags, optional minimum field width,
4782 * optional modifier and a conversion specifier
4785 * %<flags><width><modifier><conversion>
4788 * - don't pad a numerical output
4789 * _ use spaces for padding
4790 * 0 use zeros for padding
4791 * ^ upcase the result string
4793 * : use colons for %z
4795 * The minimum field width specifies the minimum width.
4797 * The modifiers are "E" and "O".
4800 * Format directives:
4802 * Date (Year, Month, Day):
4803 * %Y - Year with century if provided, will pad result at least 4 digits.
4804 * -0001, 0000, 1995, 2009, 14292, etc.
4805 * %C - year / 100 (rounded down such as 20 in 2009)
4806 * %y - year % 100 (00..99)
4808 * %m - Month of the year, zero-padded (01..12)
4809 * %_m blank-padded ( 1..12)
4810 * %-m no-padded (1..12)
4811 * %B - The full month name (``January'')
4812 * %^B uppercased (``JANUARY'')
4813 * %b - The abbreviated month name (``Jan'')
4814 * %^b uppercased (``JAN'')
4815 * %h - Equivalent to %b
4817 * %d - Day of the month, zero-padded (01..31)
4818 * %-d no-padded (1..31)
4819 * %e - Day of the month, blank-padded ( 1..31)
4821 * %j - Day of the year (001..366)
4823 * Time (Hour, Minute, Second, Subsecond):
4824 * %H - Hour of the day, 24-hour clock, zero-padded (00..23)
4825 * %k - Hour of the day, 24-hour clock, blank-padded ( 0..23)
4826 * %I - Hour of the day, 12-hour clock, zero-padded (01..12)
4827 * %l - Hour of the day, 12-hour clock, blank-padded ( 1..12)
4828 * %P - Meridian indicator, lowercase (``am'' or ``pm'')
4829 * %p - Meridian indicator, uppercase (``AM'' or ``PM'')
4831 * %M - Minute of the hour (00..59)
4833 * %S - Second of the minute (00..60)
4835 * %L - Millisecond of the second (000..999)
4836 * The digits under millisecond are truncated to not produce 1000.
4837 * %N - Fractional seconds digits, default is 9 digits (nanosecond)
4838 * %3N millisecond (3 digits)
4839 * %6N microsecond (6 digits)
4840 * %9N nanosecond (9 digits)
4841 * %12N picosecond (12 digits)
4842 * %15N femtosecond (15 digits)
4843 * %18N attosecond (18 digits)
4844 * %21N zeptosecond (21 digits)
4845 * %24N yoctosecond (24 digits)
4846 * The digits under the specified length are truncated to avoid
4850 * %z - Time zone as hour and minute offset from UTC (e.g. +0900)
4851 * %:z - hour and minute offset from UTC with a colon (e.g. +09:00)
4852 * %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
4853 * %Z - Abbreviated time zone name or similar information. (OS dependent)
4856 * %A - The full weekday name (``Sunday'')
4857 * %^A uppercased (``SUNDAY'')
4858 * %a - The abbreviated name (``Sun'')
4859 * %^a uppercased (``SUN'')
4860 * %u - Day of the week (Monday is 1, 1..7)
4861 * %w - Day of the week (Sunday is 0, 0..6)
4863 * ISO 8601 week-based year and week number:
4864 * The first week of YYYY starts with a Monday and includes YYYY-01-04.
4865 * The days in the year before the first week are in the last week of
4866 * the previous year.
4867 * %G - The week-based year
4868 * %g - The last 2 digits of the week-based year (00..99)
4869 * %V - Week number of the week-based year (01..53)
4872 * The first week of YYYY that starts with a Sunday or Monday (according to %U
4873 * or %W). The days in the year before the first week are in week 0.
4874 * %U - Week number of the year. The week starts with Sunday. (00..53)
4875 * %W - Week number of the year. The week starts with Monday. (00..53)
4877 * Seconds since the Epoch:
4878 * %s - Number of seconds since 1970-01-01 00:00:00 UTC.
4881 * %n - Newline character (\n)
4882 * %t - Tab character (\t)
4883 * %% - Literal ``%'' character
4886 * %c - date and time (%a %b %e %T %Y)
4887 * %D - Date (%m/%d/%y)
4888 * %F - The ISO 8601 date format (%Y-%m-%d)
4889 * %v - VMS date (%e-%^b-%4Y)
4892 * %r - 12-hour time (%I:%M:%S %p)
4893 * %R - 24-hour time (%H:%M)
4894 * %T - 24-hour time (%H:%M:%S)
4896 * This method is similar to strftime() function defined in ISO C and POSIX.
4898 * While all directives are locale independent since Ruby 1.9, %Z is platform
4900 * So, the result may differ even if the same format string is used in other
4901 * systems such as C.
4903 * %z is recommended over %Z.
4904 * %Z doesn't identify the timezone.
4905 * For example, "CST" is used at America/Chicago (-06:00),
4906 * America/Havana (-05:00), Asia/Harbin (+08:00), Australia/Darwin (+09:30)
4907 * and Australia/Adelaide (+10:30).
4908 * Also, %Z is highly dependent on the operating system.
4909 * For example, it may generate a non ASCII string on Japanese Windows,
4910 * i.e. the result can be different to "JST".
4911 * So the numeric time zone offset, %z, is recommended.
4915 * t = Time.new(2007,11,19,8,37,48,"-06:00") #=> 2007-11-19 08:37:48 -0600
4916 * t.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007"
4917 * t.strftime("at %I:%M %p") #=> "at 08:37 AM"
4919 * Various ISO 8601 formats:
4920 * %Y%m%d => 20071119 Calendar date (basic)
4921 * %F => 2007-11-19 Calendar date (extended)
4922 * %Y-%m => 2007-11 Calendar date, reduced accuracy, specific month
4923 * %Y => 2007 Calendar date, reduced accuracy, specific year
4924 * %C => 20 Calendar date, reduced accuracy, specific century
4925 * %Y%j => 2007323 Ordinal date (basic)
4926 * %Y-%j => 2007-323 Ordinal date (extended)
4927 * %GW%V%u => 2007W471 Week date (basic)
4928 * %G-W%V-%u => 2007-W47-1 Week date (extended)
4929 * %GW%V => 2007W47 Week date, reduced accuracy, specific week (basic)
4930 * %G-W%V => 2007-W47 Week date, reduced accuracy, specific week (extended)
4931 * %H%M%S => 083748 Local time (basic)
4932 * %T => 08:37:48 Local time (extended)
4933 * %H%M => 0837 Local time, reduced accuracy, specific minute (basic)
4934 * %H:%M => 08:37 Local time, reduced accuracy, specific minute (extended)
4935 * %H => 08 Local time, reduced accuracy, specific hour
4936 * %H%M%S,%L => 083748,000 Local time with decimal fraction, comma as decimal sign (basic)
4937 * %T,%L => 08:37:48,000 Local time with decimal fraction, comma as decimal sign (extended)
4938 * %H%M%S.%L => 083748.000 Local time with decimal fraction, full stop as decimal sign (basic)
4939 * %T.%L => 08:37:48.000 Local time with decimal fraction, full stop as decimal sign (extended)
4940 * %H%M%S%z => 083748-0600 Local time and the difference from UTC (basic)
4941 * %T%:z => 08:37:48-06:00 Local time and the difference from UTC (extended)
4942 * %Y%m%dT%H%M%S%z => 20071119T083748-0600 Date and time of day for calendar date (basic)
4943 * %FT%T%:z => 2007-11-19T08:37:48-06:00 Date and time of day for calendar date (extended)
4944 * %Y%jT%H%M%S%z => 2007323T083748-0600 Date and time of day for ordinal date (basic)
4945 * %Y-%jT%T%:z => 2007-323T08:37:48-06:00 Date and time of day for ordinal date (extended)
4946 * %GW%V%uT%H%M%S%z => 2007W471T083748-0600 Date and time of day for week date (basic)
4947 * %G-W%V-%uT%T%:z => 2007-W47-1T08:37:48-06:00 Date and time of day for week date (extended)
4948 * %Y%m%dT%H%M => 20071119T0837 Calendar date and local time (basic)
4949 * %FT%R => 2007-11-19T08:37 Calendar date and local time (extended)
4950 * %Y%jT%H%MZ => 2007323T0837Z Ordinal date and UTC of day (basic)
4951 * %Y-%jT%RZ => 2007-323T08:37Z Ordinal date and UTC of day (extended)
4952 * %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic)
4953 * %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended)
4958 time_strftime(VALUE time
, VALUE format
)
4960 struct time_object
*tobj
;
4966 GetTimeval(time
, tobj
);
4967 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.yday
!= 0);
4968 StringValue(format
);
4969 if (!rb_enc_str_asciicompat_p(format
)) {
4970 rb_raise(rb_eArgError
, "format should have ASCII compatible encoding");
4972 tmp
= rb_str_tmp_frozen_acquire(format
);
4973 fmt
= RSTRING_PTR(tmp
);
4974 len
= RSTRING_LEN(tmp
);
4975 enc
= rb_enc_get(format
);
4977 rb_warning("strftime called with empty format string");
4978 return rb_enc_str_new(0, 0, enc
);
4981 VALUE str
= rb_strftime_alloc(fmt
, len
, enc
, time
, &tobj
->vtm
, tobj
->timew
,
4982 TZMODE_UTC_P(tobj
));
4983 rb_str_tmp_frozen_release(format
, tmp
);
4984 if (!str
) rb_raise(rb_eArgError
, "invalid format: %"PRIsVALUE
, format
);
4989 int ruby_marshal_write_long(long x
, char *buf
);
4991 enum {base_dump_size
= 8};
4995 time_mdump(VALUE time
)
4997 struct time_object
*tobj
;
4999 char buf
[base_dump_size
+ sizeof(long) + 1];
5006 VALUE subsecx
, nano
, subnano
, v
, zone
;
5008 VALUE year_extend
= Qnil
;
5009 const int max_year
= 1900+0xffff;
5011 GetTimeval(time
, tobj
);
5013 gmtimew(tobj
->timew
, &vtm
);
5015 if (FIXNUM_P(vtm
.year
)) {
5016 year
= FIX2LONG(vtm
.year
);
5017 if (year
> max_year
) {
5018 year_extend
= INT2FIX(year
- max_year
);
5021 else if (year
< 1900) {
5022 year_extend
= LONG2NUM(1900 - year
);
5027 if (rb_int_positive_p(vtm
.year
)) {
5028 year_extend
= rb_int_minus(vtm
.year
, INT2FIX(max_year
));
5032 year_extend
= rb_int_minus(INT2FIX(1900), vtm
.year
);
5037 subsecx
= vtm
.subsecx
;
5039 nano
= mulquov(subsecx
, INT2FIX(1000000000), INT2FIX(TIME_SCALE
));
5040 divmodv(nano
, INT2FIX(1), &v
, &subnano
);
5045 nano
= addv(LONG2FIX(nsec
), subnano
);
5047 p
= 0x1UL
<< 31 | /* 1 */
5048 TZMODE_UTC_P(tobj
) << 30 | /* 1 */
5049 (year
-1900) << 14 | /* 16 */
5050 (vtm
.mon
-1) << 10 | /* 4 */
5051 vtm
.mday
<< 5 | /* 5 */
5053 s
= (unsigned long)vtm
.min
<< 26 | /* 6 */
5054 vtm
.sec
<< 20 | /* 6 */
5057 for (i
=0; i
<4; i
++) {
5058 buf
[i
] = (unsigned char)p
;
5061 for (i
=4; i
<8; i
++) {
5062 buf
[i
] = (unsigned char)s
;
5066 if (!NIL_P(year_extend
)) {
5068 * Append extended year distance from 1900..(1900+0xffff). In
5069 * each cases, there is no sign as the value is positive. The
5070 * format is length (marshaled long) + little endian packed
5071 * binary (like as Integer).
5073 size_t ysize
= rb_absint_size(year_extend
, NULL
);
5074 char *p
, *const buf_year_extend
= buf
+ base_dump_size
;
5075 if (ysize
> LONG_MAX
||
5076 (i
= ruby_marshal_write_long((long)ysize
, buf_year_extend
)) < 0) {
5077 rb_raise(rb_eArgError
, "year too %s to marshal: %"PRIsVALUE
" UTC",
5078 (year
== 1900 ? "small" : "big"), vtm
.year
);
5080 i
+= base_dump_size
;
5081 str
= rb_str_new(NULL
, i
+ ysize
);
5082 p
= RSTRING_PTR(str
);
5085 rb_integer_pack(year_extend
, p
, ysize
, 1, 0, INTEGER_PACK_LITTLE_ENDIAN
);
5088 str
= rb_str_new(buf
, base_dump_size
);
5090 rb_copy_generic_ivar(str
, time
);
5091 if (!rb_equal(nano
, INT2FIX(0))) {
5092 if (RB_TYPE_P(nano
, T_RATIONAL
)) {
5093 rb_ivar_set(str
, id_nano_num
, RRATIONAL(nano
)->num
);
5094 rb_ivar_set(str
, id_nano_den
, RRATIONAL(nano
)->den
);
5097 rb_ivar_set(str
, id_nano_num
, nano
);
5098 rb_ivar_set(str
, id_nano_den
, INT2FIX(1));
5101 if (nsec
) { /* submicro is only for Ruby 1.9.1 compatibility */
5103 * submicro is formatted in fixed-point packed BCD (without sign).
5104 * It represent digits under microsecond.
5105 * For nanosecond resolution, 3 digits (2 bytes) are used.
5106 * However it can be longer.
5107 * Extra digits are ignored for loading.
5110 int len
= (int)sizeof(buf
);
5111 buf
[1] = (char)((nsec
% 10) << 4);
5113 buf
[0] = (char)(nsec
% 10);
5115 buf
[0] |= (char)((nsec
% 10) << 4);
5118 rb_ivar_set(str
, id_submicro
, rb_str_new(buf
, len
));
5120 if (!TZMODE_UTC_P(tobj
)) {
5121 VALUE off
= rb_time_utc_offset(time
), div
, mod
;
5122 divmodv(off
, INT2FIX(1), &div
, &mod
);
5123 if (rb_equal(mod
, INT2FIX(0)))
5124 off
= rb_Integer(div
);
5125 rb_ivar_set(str
, id_offset
, off
);
5127 zone
= tobj
->vtm
.zone
;
5128 if (maybe_tzobj_p(zone
)) {
5129 zone
= rb_funcallv(zone
, id_name
, 0, 0);
5131 rb_ivar_set(str
, id_zone
, zone
);
5137 time_dump(int argc
, VALUE
*argv
, VALUE time
)
5141 rb_check_arity(argc
, 0, 1);
5142 str
= time_mdump(time
);
5148 mload_findzone(VALUE arg
)
5150 VALUE
*argp
= (VALUE
*)arg
;
5151 VALUE time
= argp
[0], zone
= argp
[1];
5152 return find_timezone(time
, zone
);
5156 mload_zone(VALUE time
, VALUE zone
)
5161 z
= rb_rescue(mload_findzone
, (VALUE
)args
, 0, Qnil
);
5162 if (NIL_P(z
)) return rb_fstring(zone
);
5163 if (RB_TYPE_P(z
, T_STRING
)) return rb_fstring(z
);
5167 long ruby_marshal_read_long(const char **buf
, long len
);
5171 time_mload(VALUE time
, VALUE str
)
5173 struct time_object
*tobj
;
5181 VALUE submicro
, nano_num
, nano_den
, offset
, zone
, year
;
5186 #define get_attr(attr, iffound) \
5187 attr = rb_attr_delete(str, id_##attr); \
5188 if (!NIL_P(attr)) { \
5192 get_attr(nano_num
, {});
5193 get_attr(nano_den
, {});
5194 get_attr(submicro
, {});
5195 get_attr(offset
, (offset
= rb_rescue(validate_utc_offset
, offset
, 0, Qnil
)));
5196 get_attr(zone
, (zone
= rb_rescue(validate_zone_name
, zone
, 0, Qnil
)));
5201 rb_copy_generic_ivar(time
, str
);
5204 buf
= (unsigned char *)RSTRING_PTR(str
);
5205 if (RSTRING_LEN(str
) < base_dump_size
) {
5206 goto invalid_format
;
5210 for (i
=0; i
<4; i
++) {
5211 p
|= (unsigned long)buf
[i
]<<(8*i
);
5213 for (i
=4; i
<8; i
++) {
5214 s
|= (unsigned long)buf
[i
]<<(8*(i
-4));
5217 if ((p
& (1UL<<31)) == 0) {
5223 timew
= wadd(rb_time_magnify(TIMET2WV(sec
)), wmulquoll(WINT2FIXWV(usec
), TIME_SCALE
, 1000000));
5227 gmt
= (int)((p
>> 30) & 0x1);
5230 year
= INT2FIX(((int)(p
>> 14) & 0xffff) + 1900);
5232 if (RSTRING_LEN(str
) > base_dump_size
) {
5233 long len
= RSTRING_LEN(str
) - base_dump_size
;
5236 const char *ybuf
= (const char *)(buf
+= base_dump_size
);
5237 ysize
= ruby_marshal_read_long(&ybuf
, len
);
5238 len
-= ybuf
- (const char *)buf
;
5239 if (ysize
< 0 || ysize
> len
) goto invalid_format
;
5240 year_extend
= rb_integer_unpack(ybuf
, ysize
, 1, 0, INTEGER_PACK_LITTLE_ENDIAN
);
5241 if (year
== INT2FIX(1900)) {
5242 year
= rb_int_minus(year
, year_extend
);
5245 year
= rb_int_plus(year
, year_extend
);
5248 unsigned int mon
= ((int)(p
>> 10) & 0xf); /* 0...12 */
5251 year
= addv(year
, LONG2FIX(1));
5255 vtm
.mday
= (int)(p
>> 5) & 0x1f;
5256 vtm
.hour
= (int) p
& 0x1f;
5257 vtm
.min
= (int)(s
>> 26) & 0x3f;
5258 vtm
.sec
= (int)(s
>> 20) & 0x3f;
5259 vtm
.utc_offset
= INT2FIX(0);
5260 vtm
.yday
= vtm
.wday
= 0;
5262 vtm
.zone
= str_empty
;
5264 usec
= (long)(s
& 0xfffff);
5268 vtm
.subsecx
= mulquov(LONG2FIX(nsec
), INT2FIX(TIME_SCALE
), LONG2FIX(1000000000));
5269 if (nano_num
!= Qnil
) {
5270 VALUE nano
= quov(num_exact(nano_num
), num_exact(nano_den
));
5271 vtm
.subsecx
= addv(vtm
.subsecx
, mulquov(nano
, INT2FIX(TIME_SCALE
), LONG2FIX(1000000000)));
5273 else if (submicro
!= Qnil
) { /* for Ruby 1.9.1 compatibility */
5277 ptr
= (unsigned char*)StringValuePtr(submicro
);
5278 len
= RSTRING_LEN(submicro
);
5281 if (10 <= (digit
= ptr
[0] >> 4)) goto end_submicro
;
5282 nsec
+= digit
* 100;
5283 if (10 <= (digit
= ptr
[0] & 0xf)) goto end_submicro
;
5287 if (10 <= (digit
= ptr
[1] >> 4)) goto end_submicro
;
5290 vtm
.subsecx
= addv(vtm
.subsecx
, mulquov(LONG2FIX(nsec
), INT2FIX(TIME_SCALE
), LONG2FIX(1000000000)));
5293 timew
= timegmw(&vtm
);
5296 GetNewTimeval(time
, tobj
);
5297 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
5299 tobj
->timew
= timew
;
5301 TZMODE_SET_UTC(tobj
);
5303 else if (!NIL_P(offset
)) {
5304 time_set_utc_offset(time
, offset
);
5308 zone
= mload_zone(time
, zone
);
5309 tobj
->vtm
.zone
= zone
;
5310 zone_localtime(zone
, time
);
5316 rb_raise(rb_eTypeError
, "marshaled time format differ");
5317 UNREACHABLE_RETURN(Qundef
);
5322 time_load(VALUE klass
, VALUE str
)
5324 VALUE time
= time_s_alloc(klass
);
5326 time_mload(time
, str
);
5331 /* Document-class: Time::tm
5333 * A container class for timezone conversion.
5339 * Time::tm.from_time(t) -> tm
5341 * Creates new Time::tm object from a Time object.
5345 tm_from_time(VALUE klass
, VALUE time
)
5347 struct time_object
*tobj
;
5351 struct time_object
*ttm
;
5353 GetTimeval(time
, tobj
);
5354 tm
= time_s_alloc(klass
);
5357 GMTIMEW(ttm
->timew
= tobj
->timew
, v
);
5358 ttm
->timew
= wsub(ttm
->timew
, v
->subsecx
);
5359 v
->subsecx
= INT2FIX(0);
5363 TZMODE_SET_UTC(ttm
);
5369 GetTimeval(time
, tobj
);
5370 if (tobj
->tm_got
&& TZMODE_UTC_P(tobj
))
5373 GMTIMEW(tobj
->timew
, v
= &vtm
);
5374 args
[i
++] = v
->year
;
5375 args
[i
++] = INT2FIX(v
->mon
);
5376 args
[i
++] = INT2FIX(v
->mday
);
5377 args
[i
++] = INT2FIX(v
->hour
);
5378 args
[i
++] = INT2FIX(v
->min
);
5379 args
[i
++] = INT2FIX(v
->sec
);
5381 case 0: args
[i
++] = Qfalse
; break;
5382 case 1: args
[i
++] = Qtrue
; break;
5383 default: args
[i
++] = Qnil
; break;
5385 args
[i
++] = w2v(rb_time_unmagnify(tobj
->timew
));
5386 return rb_class_new_instance(i
, args
, klass
);
5393 * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm
5395 * Creates new Time::tm object.
5399 tm_initialize(int argc
, VALUE
*argv
, VALUE tm
)
5404 if (rb_check_arity(argc
, 1, 7) > 6) argc
= 6;
5405 time_arg(argc
, argv
, &vtm
);
5409 struct time_object
*tobj
= DATA_PTR(tm
);
5410 tobj
->tzmode
= TIME_TZMODE_UTC
;
5415 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.sec
));
5416 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.min
));
5417 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.hour
));
5418 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.mday
));
5419 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.mon
));
5420 RSTRUCT_SET(tm
, i
++, vtm
.year
);
5421 RSTRUCT_SET(tm
, i
++, w2v(rb_time_unmagnify(t
)));
5429 * tm.to_time -> time
5431 * Returns a new Time object.
5435 tm_to_time(VALUE tm
)
5438 struct time_object
*torig
= get_timeval(tm
);
5439 VALUE dup
= time_s_alloc(rb_cTime
);
5440 struct time_object
*tobj
= DATA_PTR(dup
);
5445 const VALUE
*p
= RSTRUCT_CONST_PTR(tm
);
5448 for (i
= 0; i
< numberof(t
); ++i
) {
5449 t
[i
] = p
[numberof(t
) - 1 - i
];
5451 return time_s_mkutc(numberof(t
), t
, rb_cTime
);
5462 #define tm_subsec tm_zero
5463 #define tm_utc_offset tm_zero
5474 const VALUE
*p
= RSTRUCT_CONST_PTR(tm
);
5476 return rb_sprintf("%.4"PRIsVALUE
"-%.2"PRIsVALUE
"-%.2"PRIsVALUE
" "
5477 "%.2"PRIsVALUE
":%.2"PRIsVALUE
":%.2"PRIsVALUE
" "
5479 p
[5], p
[4], p
[3], p
[2], p
[1], p
[0]);
5483 tm_plus(VALUE tm
, VALUE offset
)
5485 return time_add0(rb_obj_class(tm
), get_timeval(tm
), tm
, offset
, +1);
5489 tm_minus(VALUE tm
, VALUE offset
)
5491 return time_add0(rb_obj_class(tm
), get_timeval(tm
), tm
, offset
, -1);
5496 Init_tm(VALUE outer
, const char *name
)
5501 tm
= rb_define_class_under(outer
, name
, rb_cObject
);
5502 rb_define_alloc_func(tm
, time_s_alloc
);
5503 rb_define_method(tm
, "sec", time_sec
, 0);
5504 rb_define_method(tm
, "min", time_min
, 0);
5505 rb_define_method(tm
, "hour", time_hour
, 0);
5506 rb_define_method(tm
, "mday", time_mday
, 0);
5507 rb_define_method(tm
, "day", time_mday
, 0);
5508 rb_define_method(tm
, "mon", time_mon
, 0);
5509 rb_define_method(tm
, "month", time_mon
, 0);
5510 rb_define_method(tm
, "year", time_year
, 0);
5511 rb_define_method(tm
, "isdst", time_isdst
, 0);
5512 rb_define_method(tm
, "dst?", time_isdst
, 0);
5513 rb_define_method(tm
, "zone", time_zone
, 0);
5514 rb_define_method(tm
, "gmtoff", rb_time_utc_offset
, 0);
5515 rb_define_method(tm
, "gmt_offset", rb_time_utc_offset
, 0);
5516 rb_define_method(tm
, "utc_offset", rb_time_utc_offset
, 0);
5517 rb_define_method(tm
, "utc?", time_utc_p
, 0);
5518 rb_define_method(tm
, "gmt?", time_utc_p
, 0);
5519 rb_define_method(tm
, "to_s", time_to_s
, 0);
5520 rb_define_method(tm
, "inspect", time_inspect
, 0);
5521 rb_define_method(tm
, "to_a", time_to_a
, 0);
5522 rb_define_method(tm
, "tv_sec", time_to_i
, 0);
5523 rb_define_method(tm
, "tv_usec", time_usec
, 0);
5524 rb_define_method(tm
, "usec", time_usec
, 0);
5525 rb_define_method(tm
, "tv_nsec", time_nsec
, 0);
5526 rb_define_method(tm
, "nsec", time_nsec
, 0);
5527 rb_define_method(tm
, "subsec", time_subsec
, 0);
5528 rb_define_method(tm
, "to_i", time_to_i
, 0);
5529 rb_define_method(tm
, "to_f", time_to_f
, 0);
5530 rb_define_method(tm
, "to_r", time_to_r
, 0);
5531 rb_define_method(tm
, "+", tm_plus
, 1);
5532 rb_define_method(tm
, "-", tm_minus
, 1);
5534 tm
= rb_struct_define_under(outer
, "tm",
5535 "sec", "min", "hour",
5536 "mday", "mon", "year",
5538 rb_define_method(tm
, "subsec", tm_subsec
, 0);
5539 rb_define_method(tm
, "utc_offset", tm_utc_offset
, 0);
5540 rb_define_method(tm
, "to_s", tm_to_s
, 0);
5541 rb_define_method(tm
, "inspect", tm_to_s
, 0);
5542 rb_define_method(tm
, "isdst", tm_isdst
, 0);
5543 rb_define_method(tm
, "dst?", tm_isdst
, 0);
5545 rb_define_method(tm
, "initialize", tm_initialize
, -1);
5546 rb_define_method(tm
, "utc", tm_to_time
, 0);
5547 rb_alias(tm
, rb_intern_const("to_time"), rb_intern_const("utc"));
5548 rb_define_singleton_method(tm
, "from_time", tm_from_time
, 1);
5555 rb_time_zone_abbreviation(VALUE zone
, VALUE time
)
5557 VALUE tm
, abbr
, strftime_args
[2];
5559 abbr
= rb_check_string_type(zone
);
5560 if (!NIL_P(abbr
)) return abbr
;
5562 tm
= tm_from_time(rb_cTimeTM
, time
);
5563 abbr
= rb_check_funcall(zone
, rb_intern("abbr"), 1, &tm
);
5564 if (abbr
!= Qundef
) {
5567 #ifdef SUPPORT_TZINFO_ZONE_ABBREVIATION
5568 abbr
= rb_check_funcall(zone
, rb_intern("period_for_utc"), 1, &tm
);
5569 if (abbr
!= Qundef
) {
5570 abbr
= rb_funcallv(abbr
, rb_intern("abbreviation"), 0, 0);
5574 strftime_args
[0] = rb_fstring_lit("%Z");
5575 strftime_args
[1] = tm
;
5576 abbr
= rb_check_funcall(zone
, rb_intern("strftime"), 2, strftime_args
);
5577 if (abbr
!= Qundef
) {
5580 abbr
= rb_check_funcall_default(zone
, idName
, 0, 0, Qnil
);
5582 return rb_obj_as_string(abbr
);
5585 /* Internal Details:
5587 * Since Ruby 1.9.2, Time implementation uses a signed 63 bit integer or
5588 * Integer(T_BIGNUM), Rational.
5589 * The integer is a number of nanoseconds since the _Epoch_ which can
5590 * represent 1823-11-12 to 2116-02-20.
5591 * When Integer(T_BIGNUM) or Rational is used (before 1823, after 2116, under
5592 * nanosecond), Time works slower than when integer is used.
5599 id_submicro
= rb_intern_const("submicro");
5600 id_nano_num
= rb_intern_const("nano_num");
5601 id_nano_den
= rb_intern_const("nano_den");
5602 id_offset
= rb_intern_const("offset");
5603 id_zone
= rb_intern_const("zone");
5604 id_nanosecond
= rb_intern_const("nanosecond");
5605 id_microsecond
= rb_intern_const("microsecond");
5606 id_millisecond
= rb_intern_const("millisecond");
5607 id_nsec
= rb_intern_const("nsec");
5608 id_usec
= rb_intern_const("usec");
5609 id_local_to_utc
= rb_intern_const("local_to_utc");
5610 id_utc_to_local
= rb_intern_const("utc_to_local");
5611 id_year
= rb_intern_const("year");
5612 id_mon
= rb_intern_const("mon");
5613 id_mday
= rb_intern_const("mday");
5614 id_hour
= rb_intern_const("hour");
5615 id_min
= rb_intern_const("min");
5616 id_sec
= rb_intern_const("sec");
5617 id_isdst
= rb_intern_const("isdst");
5618 id_find_timezone
= rb_intern_const("find_timezone");
5620 str_utc
= rb_fstring_lit("UTC");
5621 rb_gc_register_mark_object(str_utc
);
5622 str_empty
= rb_fstring_lit("");
5623 rb_gc_register_mark_object(str_empty
);
5625 rb_cTime
= rb_define_class("Time", rb_cObject
);
5626 rb_include_module(rb_cTime
, rb_mComparable
);
5628 rb_define_alloc_func(rb_cTime
, time_s_alloc
);
5629 rb_define_singleton_method(rb_cTime
, "utc", time_s_mkutc
, -1);
5630 rb_define_singleton_method(rb_cTime
, "local", time_s_mktime
, -1);
5631 rb_define_alias(rb_singleton_class(rb_cTime
), "gm", "utc");
5632 rb_define_alias(rb_singleton_class(rb_cTime
), "mktime", "local");
5634 rb_define_method(rb_cTime
, "to_i", time_to_i
, 0);
5635 rb_define_method(rb_cTime
, "to_f", time_to_f
, 0);
5636 rb_define_method(rb_cTime
, "to_r", time_to_r
, 0);
5637 rb_define_method(rb_cTime
, "<=>", time_cmp
, 1);
5638 rb_define_method(rb_cTime
, "eql?", time_eql
, 1);
5639 rb_define_method(rb_cTime
, "hash", time_hash
, 0);
5640 rb_define_method(rb_cTime
, "initialize_copy", time_init_copy
, 1);
5642 rb_define_method(rb_cTime
, "localtime", time_localtime_m
, -1);
5643 rb_define_method(rb_cTime
, "gmtime", time_gmtime
, 0);
5644 rb_define_method(rb_cTime
, "utc", time_gmtime
, 0);
5645 rb_define_method(rb_cTime
, "getlocal", time_getlocaltime
, -1);
5646 rb_define_method(rb_cTime
, "getgm", time_getgmtime
, 0);
5647 rb_define_method(rb_cTime
, "getutc", time_getgmtime
, 0);
5649 rb_define_method(rb_cTime
, "ctime", time_asctime
, 0);
5650 rb_define_method(rb_cTime
, "asctime", time_asctime
, 0);
5651 rb_define_method(rb_cTime
, "to_s", time_to_s
, 0);
5652 rb_define_method(rb_cTime
, "inspect", time_inspect
, 0);
5653 rb_define_method(rb_cTime
, "to_a", time_to_a
, 0);
5655 rb_define_method(rb_cTime
, "+", time_plus
, 1);
5656 rb_define_method(rb_cTime
, "-", time_minus
, 1);
5658 rb_define_method(rb_cTime
, "round", time_round
, -1);
5659 rb_define_method(rb_cTime
, "floor", time_floor
, -1);
5660 rb_define_method(rb_cTime
, "ceil", time_ceil
, -1);
5662 rb_define_method(rb_cTime
, "sec", time_sec
, 0);
5663 rb_define_method(rb_cTime
, "min", time_min
, 0);
5664 rb_define_method(rb_cTime
, "hour", time_hour
, 0);
5665 rb_define_method(rb_cTime
, "mday", time_mday
, 0);
5666 rb_define_method(rb_cTime
, "day", time_mday
, 0);
5667 rb_define_method(rb_cTime
, "mon", time_mon
, 0);
5668 rb_define_method(rb_cTime
, "month", time_mon
, 0);
5669 rb_define_method(rb_cTime
, "year", time_year
, 0);
5670 rb_define_method(rb_cTime
, "wday", time_wday
, 0);
5671 rb_define_method(rb_cTime
, "yday", time_yday
, 0);
5672 rb_define_method(rb_cTime
, "isdst", time_isdst
, 0);
5673 rb_define_method(rb_cTime
, "dst?", time_isdst
, 0);
5674 rb_define_method(rb_cTime
, "zone", time_zone
, 0);
5675 rb_define_method(rb_cTime
, "gmtoff", rb_time_utc_offset
, 0);
5676 rb_define_method(rb_cTime
, "gmt_offset", rb_time_utc_offset
, 0);
5677 rb_define_method(rb_cTime
, "utc_offset", rb_time_utc_offset
, 0);
5679 rb_define_method(rb_cTime
, "utc?", time_utc_p
, 0);
5680 rb_define_method(rb_cTime
, "gmt?", time_utc_p
, 0);
5682 rb_define_method(rb_cTime
, "sunday?", time_sunday
, 0);
5683 rb_define_method(rb_cTime
, "monday?", time_monday
, 0);
5684 rb_define_method(rb_cTime
, "tuesday?", time_tuesday
, 0);
5685 rb_define_method(rb_cTime
, "wednesday?", time_wednesday
, 0);
5686 rb_define_method(rb_cTime
, "thursday?", time_thursday
, 0);
5687 rb_define_method(rb_cTime
, "friday?", time_friday
, 0);
5688 rb_define_method(rb_cTime
, "saturday?", time_saturday
, 0);
5690 rb_define_method(rb_cTime
, "tv_sec", time_to_i
, 0);
5691 rb_define_method(rb_cTime
, "tv_usec", time_usec
, 0);
5692 rb_define_method(rb_cTime
, "usec", time_usec
, 0);
5693 rb_define_method(rb_cTime
, "tv_nsec", time_nsec
, 0);
5694 rb_define_method(rb_cTime
, "nsec", time_nsec
, 0);
5695 rb_define_method(rb_cTime
, "subsec", time_subsec
, 0);
5697 rb_define_method(rb_cTime
, "strftime", time_strftime
, 1);
5699 /* methods for marshaling */
5700 rb_define_private_method(rb_cTime
, "_dump", time_dump
, -1);
5701 rb_define_private_method(rb_singleton_class(rb_cTime
), "_load", time_load
, 1);
5703 /* Time will support marshal_dump and marshal_load in the future (1.9 maybe) */
5704 rb_define_private_method(rb_cTime
, "marshal_dump", time_mdump
, 0);
5705 rb_define_private_method(rb_cTime
, "marshal_load", time_mload
, 1);
5708 if (debug_find_time_numguess
) {
5709 rb_define_hooked_variable("$find_time_numguess", (VALUE
*)&find_time_numguess
,
5710 find_time_numguess_getter
, NULL
);
5713 rb_cTimeTM
= Init_tm(rb_cTime
, "tm");
5716 #include "timev.rbinc"