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_s_now(rb_execution_context_t
*ec
, VALUE klass
, VALUE zone
)
1937 VALUE t
= time_s_alloc(klass
);
1938 return time_init_now(ec
, t
, zone
);
1942 time_set_utc_offset(VALUE time
, VALUE off
)
1944 struct time_object
*tobj
;
1945 off
= num_exact(off
);
1948 GetTimeval(time
, tobj
);
1951 tobj
->vtm
.zone
= Qnil
;
1952 TZMODE_SET_FIXOFF(tobj
, off
);
1958 vtm_add_offset(struct vtm
*vtm
, VALUE off
, int sign
)
1964 if (lt(off
, INT2FIX(0))) {
1968 divmodv(off
, INT2FIX(1), &off
, &subsec
);
1969 divmodv(off
, INT2FIX(60), &off
, &v
);
1971 divmodv(off
, INT2FIX(60), &off
, &v
);
1973 divmodv(off
, INT2FIX(24), &off
, &v
);
1977 subsec
= neg(subsec
);
1985 if (!rb_equal(subsec
, INT2FIX(0))) {
1986 vtm
->subsecx
= addv(vtm
->subsecx
, w2v(rb_time_magnify(v2w(subsec
))));
1987 if (lt(vtm
->subsecx
, INT2FIX(0))) {
1988 vtm
->subsecx
= addv(vtm
->subsecx
, INT2FIX(TIME_SCALE
));
1991 if (le(INT2FIX(TIME_SCALE
), vtm
->subsecx
)) {
1992 vtm
->subsecx
= subv(vtm
->subsecx
, INT2FIX(TIME_SCALE
));
1997 /* If sec + subsec == 0, don't change vtm->sec.
1998 * It may be 60 which is a leap second. */
2037 if (vtm
->mon
== 1 && vtm
->mday
== 1) {
2039 vtm
->mon
= 12; /* December */
2040 vtm
->year
= subv(vtm
->year
, INT2FIX(1));
2041 vtm
->yday
= leap_year_v_p(vtm
->year
) ? 366 : 365;
2043 else if (vtm
->mday
== 1) {
2044 const int8_t *days_in_month
= days_in_month_in_v(vtm
->year
);
2046 vtm
->mday
= days_in_month
[vtm
->mon
-1];
2053 vtm
->wday
= (vtm
->wday
+ 6) % 7;
2056 int leap
= leap_year_v_p(vtm
->year
);
2057 if (vtm
->mon
== 12 && vtm
->mday
== 31) {
2058 vtm
->year
= addv(vtm
->year
, INT2FIX(1));
2059 vtm
->mon
= 1; /* January */
2063 else if (vtm
->mday
== days_in_month_of(leap
)[vtm
->mon
-1]) {
2072 vtm
->wday
= (vtm
->wday
+ 1) % 7;
2078 maybe_tzobj_p(VALUE obj
)
2080 if (NIL_P(obj
)) return FALSE
;
2081 if (RB_INTEGER_TYPE_P(obj
)) return FALSE
;
2082 if (RB_TYPE_P(obj
, T_STRING
)) return FALSE
;
2086 NORETURN(static void invalid_utc_offset(VALUE
));
2088 invalid_utc_offset(VALUE zone
)
2090 rb_raise(rb_eArgError
, "\"+HH:MM\", \"-HH:MM\", \"UTC\" or "
2091 "\"A\"..\"I\",\"K\"..\"Z\" expected for utc_offset: %"PRIsVALUE
,
2096 utc_offset_arg(VALUE arg
)
2099 if (!NIL_P(tmp
= rb_check_string_type(arg
))) {
2101 const char *s
= RSTRING_PTR(tmp
), *min
= NULL
, *sec
= NULL
;
2102 if (!rb_enc_str_asciicompat_p(tmp
)) {
2103 goto invalid_utc_offset
;
2105 switch (RSTRING_LEN(tmp
)) {
2110 /* Military Time Zone Names */
2111 if (s
[0] >= 'A' && s
[0] <= 'I') {
2112 n
= (int)s
[0] - 'A' + 1;
2114 else if (s
[0] >= 'K' && s
[0] <= 'M') {
2115 n
= (int)s
[0] - 'A';
2117 else if (s
[0] >= 'N' && s
[0] <= 'Y') {
2118 n
= 'M' - (int)s
[0];
2121 goto invalid_utc_offset
;
2126 if (STRNCASECMP("UTC", s
, 3) == 0) {
2133 case 6: /* +HH:MM */
2136 case 7: /* +HHMMSS */
2140 case 9: /* +HH:MM:SS */
2145 goto invalid_utc_offset
;
2148 if (sec
== s
+7 && *(sec
-1) != ':') goto invalid_utc_offset
;
2149 if (!ISDIGIT(sec
[0]) || !ISDIGIT(sec
[1])) goto invalid_utc_offset
;
2150 n
+= (sec
[0] * 10 + sec
[1] - '0' * 11);
2153 if (min
== s
+4 && *(min
-1) != ':') goto invalid_utc_offset
;
2154 if (!ISDIGIT(min
[0]) || !ISDIGIT(min
[1])) goto invalid_utc_offset
;
2155 if (min
[0] > '5') goto invalid_utc_offset
;
2156 n
+= (min
[0] * 10 + min
[1] - '0' * 11) * 60;
2158 if (s
[0] != '+' && s
[0] != '-') goto invalid_utc_offset
;
2159 if (!ISDIGIT(s
[1]) || !ISDIGIT(s
[2])) goto invalid_utc_offset
;
2160 n
+= (s
[1] * 10 + s
[2] - '0' * 11) * 3600;
2162 if (n
== 0) return UTC_ZONE
;
2168 return num_exact(arg
);
2175 zone_set_offset(VALUE zone
, struct time_object
*tobj
,
2176 wideval_t tlocal
, wideval_t tutc
)
2178 /* tlocal and tutc must be unmagnified and in seconds */
2179 wideval_t w
= wsub(tlocal
, tutc
);
2181 validate_utc_offset(off
);
2182 tobj
->vtm
.utc_offset
= off
;
2183 tobj
->vtm
.zone
= zone
;
2184 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
2188 extract_time(VALUE time
)
2191 const ID id_to_i
= idTo_i
;
2193 #define EXTRACT_TIME() do { \
2194 t = v2w(rb_Integer(AREF(to_i))); \
2197 if (rb_typeddata_is_kind_of(time
, &time_data_type
)) {
2198 struct time_object
*tobj
= DATA_PTR(time
);
2200 time_gmtime(time
); /* ensure tm got */
2201 t
= rb_time_unmagnify(tobj
->timew
);
2203 else if (RB_TYPE_P(time
, T_STRUCT
)) {
2204 #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2209 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2219 extract_vtm(VALUE time
, struct vtm
*vtm
, VALUE subsecx
)
2222 const ID id_to_i
= idTo_i
;
2224 #define EXTRACT_VTM() do { \
2226 vtm->year = obj2vint(AREF(year)); \
2227 vtm->mon = month_arg(AREF(mon)); \
2228 vtm->mday = obj2ubits(AREF(mday), 5); \
2229 vtm->hour = obj2ubits(AREF(hour), 5); \
2230 vtm->min = obj2ubits(AREF(min), 6); \
2231 vtm->sec = obj2subsecx(AREF(sec), &subsecx); \
2232 vtm->isdst = RTEST(AREF(isdst)); \
2233 vtm->utc_offset = Qnil; \
2234 t = v2w(rb_Integer(AREF(to_i))); \
2237 if (rb_typeddata_is_kind_of(time
, &time_data_type
)) {
2238 struct time_object
*tobj
= DATA_PTR(time
);
2240 time_get_tm(time
, tobj
);
2242 t
= rb_time_unmagnify(tobj
->timew
);
2243 if (TZMODE_FIXOFF_P(tobj
) && vtm
->utc_offset
!= INT2FIX(0))
2244 t
= wadd(t
, v2w(vtm
->utc_offset
));
2246 else if (RB_TYPE_P(time
, T_STRUCT
)) {
2247 #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2251 else if (rb_integer_type_p(time
)) {
2253 GMTIMEW(rb_time_magnify(t
), vtm
);
2256 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2261 vtm
->subsecx
= subsecx
;
2267 zone_set_dst(VALUE zone
, struct time_object
*tobj
, VALUE tm
)
2271 CONST_ID(id_dst_p
, "dst?");
2272 dst
= rb_check_funcall(zone
, id_dst_p
, 1, &tm
);
2273 tobj
->vtm
.isdst
= (dst
!= Qundef
&& RTEST(dst
));
2277 zone_timelocal(VALUE zone
, VALUE time
)
2280 struct time_object
*tobj
= DATA_PTR(time
);
2283 t
= rb_time_unmagnify(tobj
->timew
);
2284 tm
= tm_from_time(rb_cTimeTM
, time
);
2285 utc
= rb_check_funcall(zone
, id_local_to_utc
, 1, &tm
);
2286 if (utc
== Qundef
) return 0;
2288 s
= extract_time(utc
);
2289 zone_set_offset(zone
, tobj
, t
, s
);
2290 s
= rb_time_magnify(s
);
2291 if (tobj
->vtm
.subsecx
!= INT2FIX(0)) {
2292 s
= wadd(s
, v2w(tobj
->vtm
.subsecx
));
2295 zone_set_dst(zone
, tobj
, tm
);
2300 zone_localtime(VALUE zone
, VALUE time
)
2302 VALUE local
, tm
, subsecx
;
2303 struct time_object
*tobj
= DATA_PTR(time
);
2306 split_second(tobj
->timew
, &t
, &subsecx
);
2307 tm
= tm_from_time(rb_cTimeTM
, time
);
2309 local
= rb_check_funcall(zone
, id_utc_to_local
, 1, &tm
);
2310 if (local
== Qundef
) return 0;
2312 s
= extract_vtm(local
, &tobj
->vtm
, subsecx
);
2314 zone_set_offset(zone
, tobj
, s
, t
);
2315 zone_set_dst(zone
, tobj
, tm
);
2320 find_timezone(VALUE time
, VALUE zone
)
2322 VALUE klass
= CLASS_OF(time
);
2324 return rb_check_funcall_default(klass
, id_find_timezone
, 1, &zone
, Qnil
);
2328 time_init_args(rb_execution_context_t
*ec
, VALUE time
, VALUE year
, VALUE mon
, VALUE mday
, VALUE hour
, VALUE min
, VALUE sec
, VALUE zone
)
2332 struct time_object
*tobj
;
2334 vtm
.wday
= VTM_WDAY_INITVAL
;
2336 vtm
.zone
= str_empty
;
2338 vtm
.year
= obj2vint(year
);
2340 vtm
.mon
= NIL_P(mon
) ? 1 : month_arg(mon
);
2342 vtm
.mday
= NIL_P(mday
) ? 1 : obj2ubits(mday
, 5);
2344 vtm
.hour
= NIL_P(hour
) ? 0 : obj2ubits(hour
, 5);
2346 vtm
.min
= NIL_P(min
) ? 0 : obj2ubits(min
, 6);
2350 vtm
.subsecx
= INT2FIX(0);
2354 vtm
.sec
= obj2subsecx(sec
, &subsecx
);
2355 vtm
.subsecx
= subsecx
;
2358 vtm
.isdst
= VTM_ISDST_INITVAL
;
2359 vtm
.utc_offset
= Qnil
;
2360 const VALUE arg
= zone
;
2363 if (arg
== ID2SYM(rb_intern("dst")))
2365 else if (arg
== ID2SYM(rb_intern("std")))
2367 else if (maybe_tzobj_p(arg
))
2369 else if (!NIL_P(utc
= utc_offset_arg(arg
)))
2370 vtm
.utc_offset
= utc
== UTC_ZONE
? INT2FIX(0) : utc
;
2371 else if (NIL_P(zone
= find_timezone(time
, arg
)))
2372 invalid_utc_offset(arg
);
2378 GetNewTimeval(time
, tobj
);
2381 tobj
->timew
= timegmw(&vtm
);
2384 TZMODE_SET_LOCALTIME(tobj
);
2385 if (zone_timelocal(zone
, time
)) {
2388 else if (NIL_P(vtm
.utc_offset
= utc_offset_arg(zone
))) {
2389 if (NIL_P(zone
= find_timezone(time
, zone
)) || !zone_timelocal(zone
, time
))
2390 invalid_utc_offset(arg
);
2394 if (utc
== UTC_ZONE
) {
2395 tobj
->timew
= timegmw(&vtm
);
2398 TZMODE_SET_UTC(tobj
);
2402 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
2404 tobj
->timew
= WINT2FIXWV(0);
2406 if (!NIL_P(vtm
.utc_offset
)) {
2407 VALUE off
= vtm
.utc_offset
;
2408 vtm_add_offset(&vtm
, off
, -1);
2409 vtm
.utc_offset
= Qnil
;
2410 tobj
->timew
= timegmw(&vtm
);
2411 return time_set_utc_offset(time
, off
);
2414 tobj
->timew
= timelocalw(&vtm
);
2415 return time_localtime(time
);
2420 subsec_normalize(time_t *secp
, long *subsecp
, const long maxsubsec
)
2423 long subsec
= *subsecp
;
2426 if (UNLIKELY(subsec
>= maxsubsec
)) { /* subsec positive overflow */
2427 sec2
= subsec
/ maxsubsec
;
2428 if (TIMET_MAX
- sec2
< sec
) {
2429 rb_raise(rb_eRangeError
, "out of Time range");
2431 subsec
-= sec2
* maxsubsec
;
2434 else if (UNLIKELY(subsec
< 0)) { /* subsec negative overflow */
2435 sec2
= NDIV(subsec
, maxsubsec
); /* negative div */
2436 if (sec
< TIMET_MIN
- sec2
) {
2437 rb_raise(rb_eRangeError
, "out of Time range");
2439 subsec
-= sec2
* maxsubsec
;
2442 #ifndef NEGATIVE_TIME_T
2444 rb_raise(rb_eArgError
, "time must be positive");
2450 #define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
2451 #define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000)
2454 nsec2timew(time_t sec
, long nsec
)
2456 time_nsec_normalize(&sec
, &nsec
);
2457 return timenano2timew(sec
, nsec
);
2461 time_new_timew(VALUE klass
, wideval_t timew
)
2463 VALUE time
= time_s_alloc(klass
);
2464 struct time_object
*tobj
;
2466 tobj
= DATA_PTR(time
); /* skip type check */
2467 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
2468 tobj
->timew
= timew
;
2474 rb_time_new(time_t sec
, long usec
)
2476 time_usec_normalize(&sec
, &usec
);
2477 return time_new_timew(rb_cTime
, timenano2timew(sec
, usec
* 1000));
2480 /* returns localtime time object */
2482 rb_time_nano_new(time_t sec
, long nsec
)
2484 return time_new_timew(rb_cTime
, nsec2timew(sec
, nsec
));
2488 rb_time_timespec_new(const struct timespec
*ts
, int offset
)
2490 struct time_object
*tobj
;
2491 VALUE time
= time_new_timew(rb_cTime
, nsec2timew(ts
->tv_sec
, ts
->tv_nsec
));
2493 if (-86400 < offset
&& offset
< 86400) { /* fixoff */
2494 GetTimeval(time
, tobj
);
2495 TZMODE_SET_FIXOFF(tobj
, INT2FIX(offset
));
2497 else if (offset
== INT_MAX
) { /* localtime */
2499 else if (offset
== INT_MAX
-1) { /* UTC */
2500 GetTimeval(time
, tobj
);
2501 TZMODE_SET_UTC(tobj
);
2504 rb_raise(rb_eArgError
, "utc_offset out of range");
2511 rb_time_num_new(VALUE timev
, VALUE off
)
2513 VALUE time
= time_new_timew(rb_cTime
, rb_time_magnify(v2w(timev
)));
2518 if (maybe_tzobj_p(zone
)) {
2520 if (zone_timelocal(zone
, time
)) return time
;
2522 if (NIL_P(off
= utc_offset_arg(off
))) {
2524 if (NIL_P(zone
= find_timezone(time
, off
))) invalid_utc_offset(off
);
2526 if (!zone_timelocal(zone
, time
)) invalid_utc_offset(off
);
2529 else if (off
== UTC_ZONE
) {
2530 return time_gmtime(time
);
2533 validate_utc_offset(off
);
2534 time_set_utc_offset(time
, off
);
2541 static struct timespec
2542 time_timespec(VALUE num
, int interval
)
2545 const char *const tstr
= interval
? "time interval" : "time";
2548 #ifndef NEGATIVE_TIME_T
2549 # define arg_range_check(v) \
2551 rb_raise(rb_eArgError, "%s must not be negative", tstr) : \
2554 # define arg_range_check(v) \
2555 ((interval && (v) < 0) ? \
2556 rb_raise(rb_eArgError, "time interval must not be negative") : \
2560 if (FIXNUM_P(num
)) {
2561 t
.tv_sec
= NUM2TIMET(num
);
2562 arg_range_check(t
.tv_sec
);
2565 else if (RB_FLOAT_TYPE_P(num
)) {
2566 double x
= RFLOAT_VALUE(num
);
2573 t
.tv_nsec
= (int)(d
*1e9
+0.5);
2574 if (t
.tv_nsec
>= 1000000000) {
2575 t
.tv_nsec
-= 1000000000;
2579 else if ((t
.tv_nsec
= (int)(-d
*1e9
+0.5)) > 0) {
2580 t
.tv_nsec
= 1000000000 - t
.tv_nsec
;
2583 t
.tv_sec
= (time_t)f
;
2584 if (f
!= t
.tv_sec
) {
2585 rb_raise(rb_eRangeError
, "%f out of Time range", x
);
2589 else if (RB_BIGNUM_TYPE_P(num
)) {
2590 t
.tv_sec
= NUM2TIMET(num
);
2591 arg_range_check(t
.tv_sec
);
2596 ary
= rb_check_funcall(num
, id_divmod
, 1, &i
);
2597 if (ary
!= Qundef
&& !NIL_P(ary
= rb_check_array_type(ary
))) {
2598 i
= rb_ary_entry(ary
, 0);
2599 f
= rb_ary_entry(ary
, 1);
2600 t
.tv_sec
= NUM2TIMET(i
);
2601 arg_range_check(t
.tv_sec
);
2602 f
= rb_funcall(f
, '*', 1, INT2FIX(1000000000));
2603 t
.tv_nsec
= NUM2LONG(f
);
2606 rb_raise(rb_eTypeError
, "can't convert %"PRIsVALUE
" into %s",
2607 rb_obj_class(num
), tstr
);
2611 #undef arg_range_check
2614 static struct timeval
2615 time_timeval(VALUE num
, int interval
)
2620 ts
= time_timespec(num
, interval
);
2621 tv
.tv_sec
= (TYPEOF_TIMEVAL_TV_SEC
)ts
.tv_sec
;
2622 tv
.tv_usec
= (TYPEOF_TIMEVAL_TV_USEC
)(ts
.tv_nsec
/ 1000);
2628 rb_time_interval(VALUE num
)
2630 return time_timeval(num
, TRUE
);
2634 rb_time_timeval(VALUE time
)
2636 struct time_object
*tobj
;
2640 if (IsTimeval(time
)) {
2641 GetTimeval(time
, tobj
);
2642 ts
= timew2timespec(tobj
->timew
);
2643 t
.tv_sec
= (TYPEOF_TIMEVAL_TV_SEC
)ts
.tv_sec
;
2644 t
.tv_usec
= (TYPEOF_TIMEVAL_TV_USEC
)(ts
.tv_nsec
/ 1000);
2647 return time_timeval(time
, FALSE
);
2651 rb_time_timespec(VALUE time
)
2653 struct time_object
*tobj
;
2656 if (IsTimeval(time
)) {
2657 GetTimeval(time
, tobj
);
2658 t
= timew2timespec(tobj
->timew
);
2661 return time_timespec(time
, FALSE
);
2665 rb_time_timespec_interval(VALUE num
)
2667 return time_timespec(num
, TRUE
);
2671 get_scale(VALUE unit
)
2673 if (unit
== ID2SYM(id_nanosecond
) || unit
== ID2SYM(id_nsec
)) {
2676 else if (unit
== ID2SYM(id_microsecond
) || unit
== ID2SYM(id_usec
)) {
2679 else if (unit
== ID2SYM(id_millisecond
)) {
2683 rb_raise(rb_eArgError
, "unexpected unit: %"PRIsVALUE
, unit
);
2688 time_s_at(rb_execution_context_t
*ec
, VALUE klass
, VALUE time
, VALUE subsec
, VALUE unit
, VALUE zone
)
2694 int scale
= get_scale(unit
);
2695 time
= num_exact(time
);
2696 t
= num_exact(subsec
);
2697 timew
= wadd(rb_time_magnify(v2w(time
)), wmulquoll(v2w(t
), TIME_SCALE
, scale
));
2698 t
= time_new_timew(klass
, timew
);
2700 else if (IsTimeval(time
)) {
2701 struct time_object
*tobj
, *tobj2
;
2702 GetTimeval(time
, tobj
);
2703 t
= time_new_timew(klass
, tobj
->timew
);
2704 GetTimeval(t
, tobj2
);
2705 TZMODE_COPY(tobj2
, tobj
);
2708 timew
= rb_time_magnify(v2w(num_exact(time
)));
2709 t
= time_new_timew(klass
, timew
);
2712 time_zonelocal(t
, zone
);
2719 time_s_at1(rb_execution_context_t
*ec
, VALUE klass
, VALUE time
)
2721 return time_s_at(ec
, klass
, time
, Qfalse
, ID2SYM(id_microsecond
), Qnil
);
2724 static const char months
[][4] = {
2725 "jan", "feb", "mar", "apr", "may", "jun",
2726 "jul", "aug", "sep", "oct", "nov", "dec",
2732 if (RB_TYPE_P(obj
, T_STRING
)) {
2733 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2736 return NUM2INT(obj
);
2739 /* bits should be 0 <= x <= 31 */
2741 obj2ubits(VALUE obj
, unsigned int bits
)
2743 const unsigned int usable_mask
= (1U << bits
) - 1;
2744 unsigned int rv
= (unsigned int)obj2int(obj
);
2746 if ((rv
& usable_mask
) != rv
)
2747 rb_raise(rb_eArgError
, "argument out of range");
2748 return (uint32_t)rv
;
2754 if (RB_TYPE_P(obj
, T_STRING
)) {
2755 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2758 obj
= rb_to_int(obj
);
2765 obj2subsecx(VALUE obj
, VALUE
*subsecx
)
2769 if (RB_TYPE_P(obj
, T_STRING
)) {
2770 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2771 *subsecx
= INT2FIX(0);
2774 divmodv(num_exact(obj
), INT2FIX(1), &obj
, &subsec
);
2775 *subsecx
= w2v(rb_time_magnify(v2w(subsec
)));
2777 return obj2ubits(obj
, 6); /* vtm->sec */
2781 usec2subsecx(VALUE obj
)
2783 if (RB_TYPE_P(obj
, T_STRING
)) {
2784 obj
= rb_str_to_inum(obj
, 10, TRUE
);
2787 return mulquov(num_exact(obj
), INT2FIX(TIME_SCALE
), INT2FIX(1000000));
2791 month_arg(VALUE arg
)
2795 if (FIXNUM_P(arg
)) {
2796 return obj2ubits(arg
, 4);
2800 VALUE s
= rb_check_string_type(arg
);
2801 if (!NIL_P(s
) && RSTRING_LEN(s
) > 0) {
2803 for (i
=0; i
<12; i
++) {
2804 if (RSTRING_LEN(s
) == 3 &&
2805 STRNCASECMP(months
[i
], RSTRING_PTR(s
), 3) == 0) {
2812 mon
= obj2ubits(arg
, 4);
2818 validate_utc_offset(VALUE utc_offset
)
2820 if (le(utc_offset
, INT2FIX(-86400)) || ge(utc_offset
, INT2FIX(86400)))
2821 rb_raise(rb_eArgError
, "utc_offset out of range");
2826 validate_zone_name(VALUE zone_name
)
2828 StringValueCStr(zone_name
);
2833 validate_vtm(struct vtm
*vtm
)
2835 #define validate_vtm_range(mem, b, e) \
2836 ((vtm->mem < b || vtm->mem > e) ? \
2837 rb_raise(rb_eArgError, #mem" out of range") : (void)0)
2838 validate_vtm_range(mon
, 1, 12);
2839 validate_vtm_range(mday
, 1, 31);
2840 validate_vtm_range(hour
, 0, 24);
2841 validate_vtm_range(min
, 0, (vtm
->hour
== 24 ? 0 : 59));
2842 validate_vtm_range(sec
, 0, (vtm
->hour
== 24 ? 0 : 60));
2843 if (lt(vtm
->subsecx
, INT2FIX(0)) || ge(vtm
->subsecx
, INT2FIX(TIME_SCALE
)))
2844 rb_raise(rb_eArgError
, "subsecx out of range");
2845 if (!NIL_P(vtm
->utc_offset
)) validate_utc_offset(vtm
->utc_offset
);
2846 #undef validate_vtm_range
2850 time_arg(int argc
, const VALUE
*argv
, struct vtm
*vtm
)
2853 VALUE subsecx
= INT2FIX(0);
2855 vtm
->year
= INT2FIX(0);
2861 vtm
->subsecx
= INT2FIX(0);
2862 vtm
->utc_offset
= Qnil
;
2866 vtm
->zone
= str_empty
;
2876 vtm
->isdst
= RTEST(argv
[8]) ? 1 : 0;
2879 rb_scan_args(argc
, argv
, "17", &v
[0],&v
[1],&v
[2],&v
[3],&v
[4],&v
[5],&v
[6],&v
[7]);
2880 /* v[6] may be usec or zone (parsedate) */
2881 /* v[7] is wday (parsedate; ignored) */
2882 vtm
->wday
= VTM_WDAY_INITVAL
;
2883 vtm
->isdst
= VTM_ISDST_INITVAL
;
2886 vtm
->year
= obj2vint(v
[0]);
2892 vtm
->mon
= month_arg(v
[1]);
2899 vtm
->mday
= obj2ubits(v
[2], 5);
2902 /* normalize month-mday */
2906 /* this drops higher bits but it's not a problem to calc leap year */
2907 unsigned int mday2
= leap_year_v_p(vtm
->year
) ? 29 : 28;
2908 if (vtm
->mday
> mday2
) {
2918 if (vtm
->mday
== 31) {
2925 vtm
->hour
= NIL_P(v
[3])?0:obj2ubits(v
[3], 5);
2927 vtm
->min
= NIL_P(v
[4])?0:obj2ubits(v
[4], 6);
2929 if (!NIL_P(v
[6]) && argc
== 7) {
2930 vtm
->sec
= NIL_P(v
[5])?0:obj2ubits(v
[5],6);
2931 subsecx
= usec2subsecx(v
[6]);
2934 /* when argc == 8, v[6] is timezone, but ignored */
2939 vtm
->sec
= obj2subsecx(v
[5], &subsecx
);
2942 vtm
->subsecx
= subsecx
;
2945 RB_GC_GUARD(subsecx
);
2952 * ensure about negative years in proleptic Gregorian calendar.
2954 unsigned long uy
= (unsigned long)(LIKELY(y
>= 0) ? y
: -y
);
2956 if (LIKELY(uy
% 4 != 0)) return 0;
2958 unsigned long century
= uy
/ 100;
2959 if (LIKELY(uy
!= century
* 100)) return 1;
2960 return century
% 4 == 0;
2964 timegm_noleapsecond(struct tm
*tm
)
2966 long tm_year
= tm
->tm_year
;
2967 int tm_yday
= calc_tm_yday(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
2970 * `Seconds Since the Epoch' in SUSv3:
2971 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
2972 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
2973 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
2975 return tm
->tm_sec
+ tm
->tm_min
*60 + tm
->tm_hour
*3600 +
2979 DIV(tm_year
-1,100) +
2980 DIV(tm_year
+299,400))*86400;
2984 #define DEBUG_FIND_TIME_NUMGUESS
2985 #define DEBUG_GUESSRANGE
2988 static const bool debug_guessrange
=
2989 #ifdef DEBUG_GUESSRANGE
2995 #define DEBUG_REPORT_GUESSRANGE \
2996 (debug_guessrange ? debug_report_guessrange(guess_lo, guess_hi) : (void)0)
2999 debug_report_guessrange(time_t guess_lo
, time_t guess_hi
)
3001 unsigned_time_t guess_diff
= (unsigned_time_t
)(guess_hi
-guess_lo
);
3002 fprintf(stderr
, "find time guess range: %"PRI_TIMET_PREFIX
"d - "
3003 "%"PRI_TIMET_PREFIX
"d : %"PRI_TIMET_PREFIX
"u\n",
3004 guess_lo
, guess_hi
, guess_diff
);
3007 static const bool debug_find_time_numguess
=
3008 #ifdef DEBUG_FIND_TIME_NUMGUESS
3014 #define DEBUG_FIND_TIME_NUMGUESS_INC \
3015 (void)(debug_find_time_numguess && find_time_numguess++),
3016 static unsigned long long find_time_numguess
;
3019 find_time_numguess_getter(ID name
, VALUE
*data
)
3021 unsigned long long *numguess
= (void *)data
;
3022 return ULL2NUM(*numguess
);
3026 find_time_t(struct tm
*tptr
, int utc_p
, time_t *tp
)
3028 time_t guess
, guess0
, guess_lo
, guess_hi
;
3029 struct tm
*tm
, tm0
, tm_lo
, tm_hi
;
3036 #define GUESS(p) (DEBUG_FIND_TIME_NUMGUESS_INC (utc_p ? gmtime_with_leapsecond((p), &result) : LOCALTIME((p), result)))
3038 guess_lo
= TIMET_MIN
;
3039 guess_hi
= TIMET_MAX
;
3041 find_dst
= 0 < tptr
->tm_isdst
;
3043 /* /etc/localtime might be changed. reload it. */
3047 if (tm0
.tm_mon
< 0) {
3054 else if (11 < tm0
.tm_mon
) {
3061 else if (tm0
.tm_mday
< 1) {
3067 else if ((d
= days_in_month_in(1900 + tm0
.tm_year
)[tm0
.tm_mon
]) < tm0
.tm_mday
) {
3073 else if (tm0
.tm_hour
< 0) {
3078 else if (23 < tm0
.tm_hour
) {
3083 else if (tm0
.tm_min
< 0) {
3087 else if (59 < tm0
.tm_min
) {
3091 else if (tm0
.tm_sec
< 0) {
3094 else if (60 < tm0
.tm_sec
) {
3098 DEBUG_REPORT_GUESSRANGE
;
3099 guess0
= guess
= timegm_noleapsecond(&tm0
);
3102 d
= tmcmp(tptr
, tm
);
3103 if (d
== 0) { goto found
; }
3106 guess
-= 24 * 60 * 60;
3110 guess
+= 24 * 60 * 60;
3112 DEBUG_REPORT_GUESSRANGE
;
3113 if (guess_lo
< guess
&& guess
< guess_hi
&& (tm
= GUESS(&guess
)) != NULL
) {
3114 d
= tmcmp(tptr
, tm
);
3115 if (d
== 0) { goto found
; }
3120 DEBUG_REPORT_GUESSRANGE
;
3124 tm
= GUESS(&guess_lo
);
3125 if (!tm
) goto error
;
3126 d
= tmcmp(tptr
, tm
);
3127 if (d
< 0) goto out_of_range
;
3128 if (d
== 0) { guess
= guess_lo
; goto found
; }
3131 tm
= GUESS(&guess_hi
);
3132 if (!tm
) goto error
;
3133 d
= tmcmp(tptr
, tm
);
3134 if (d
> 0) goto out_of_range
;
3135 if (d
== 0) { guess
= guess_hi
; goto found
; }
3138 DEBUG_REPORT_GUESSRANGE
;
3142 while (guess_lo
+ 1 < guess_hi
) {
3145 guess
= guess_lo
/ 2 + guess_hi
/ 2;
3146 if (guess
<= guess_lo
)
3147 guess
= guess_lo
+ 1;
3148 else if (guess
>= guess_hi
)
3149 guess
= guess_hi
- 1;
3154 time_t guess0_hi
= timegm_noleapsecond(&tm_hi
);
3155 guess
= guess_hi
- (guess0_hi
- guess0
);
3156 if (guess
== guess_hi
) /* hh:mm:60 tends to cause this condition. */
3160 else if (status
== 2) {
3161 time_t guess0_lo
= timegm_noleapsecond(&tm_lo
);
3162 guess
= guess_lo
+ (guess0
- guess0_lo
);
3163 if (guess
== guess_lo
)
3167 if (guess
<= guess_lo
|| guess_hi
<= guess
) {
3168 /* Previous guess is invalid. try binary search. */
3169 if (debug_guessrange
) {
3170 if (guess
<= guess_lo
) {
3171 fprintf(stderr
, "too small guess: %"PRI_TIMET_PREFIX
"d"\
3172 " <= %"PRI_TIMET_PREFIX
"d\n", guess
, guess_lo
);
3174 if (guess_hi
<= guess
) {
3175 fprintf(stderr
, "too big guess: %"PRI_TIMET_PREFIX
"d"\
3176 " <= %"PRI_TIMET_PREFIX
"d\n", guess_hi
, guess
);
3185 if (!tm
) goto error
;
3187 d
= tmcmp(tptr
, tm
);
3192 DEBUG_REPORT_GUESSRANGE
;
3197 DEBUG_REPORT_GUESSRANGE
;
3204 /* Given argument has no corresponding time_t. Let's extrapolate. */
3206 * `Seconds Since the Epoch' in SUSv3:
3207 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
3208 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
3209 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
3212 tptr_tm_yday
= calc_tm_yday(tptr
->tm_year
, tptr
->tm_mon
, tptr
->tm_mday
);
3215 ((tptr
->tm_year
- tm_lo
.tm_year
) * 365 +
3216 DIV((tptr
->tm_year
-69), 4) -
3217 DIV((tptr
->tm_year
-1), 100) +
3218 DIV((tptr
->tm_year
+299), 400) -
3219 DIV((tm_lo
.tm_year
-69), 4) +
3220 DIV((tm_lo
.tm_year
-1), 100) -
3221 DIV((tm_lo
.tm_year
+299), 400) +
3223 tm_lo
.tm_yday
) * 86400 +
3224 (tptr
->tm_hour
- tm_lo
.tm_hour
) * 3600 +
3225 (tptr
->tm_min
- tm_lo
.tm_min
) * 60 +
3226 (tptr
->tm_sec
- (tm_lo
.tm_sec
== 60 ? 59 : tm_lo
.tm_sec
));
3232 /* If localtime is nonmonotonic, another result may exist. */
3235 guess2
= guess
- 2 * 60 * 60;
3236 tm
= LOCALTIME(&guess2
, result
);
3238 if (tptr
->tm_hour
!= (tm
->tm_hour
+ 2) % 24 ||
3239 tptr
->tm_min
!= tm
->tm_min
||
3240 tptr
->tm_sec
!= tm
->tm_sec
) {
3241 guess2
-= (tm
->tm_hour
- tptr
->tm_hour
) * 60 * 60 +
3242 (tm
->tm_min
- tptr
->tm_min
) * 60 +
3243 (tm
->tm_sec
- tptr
->tm_sec
);
3244 if (tptr
->tm_mday
!= tm
->tm_mday
)
3245 guess2
+= 24 * 60 * 60;
3246 if (guess
!= guess2
) {
3247 tm
= LOCALTIME(&guess2
, result
);
3248 if (tm
&& tmcmp(tptr
, tm
) == 0) {
3260 guess2
= guess
+ 2 * 60 * 60;
3261 tm
= LOCALTIME(&guess2
, result
);
3263 if ((tptr
->tm_hour
+ 2) % 24 != tm
->tm_hour
||
3264 tptr
->tm_min
!= tm
->tm_min
||
3265 tptr
->tm_sec
!= tm
->tm_sec
) {
3266 guess2
-= (tm
->tm_hour
- tptr
->tm_hour
) * 60 * 60 +
3267 (tm
->tm_min
- tptr
->tm_min
) * 60 +
3268 (tm
->tm_sec
- tptr
->tm_sec
);
3269 if (tptr
->tm_mday
!= tm
->tm_mday
)
3270 guess2
-= 24 * 60 * 60;
3271 if (guess
!= guess2
) {
3272 tm
= LOCALTIME(&guess2
, result
);
3273 if (tm
&& tmcmp(tptr
, tm
) == 0) {
3289 return "time out of range";
3292 return "gmtime/localtime error";
3296 vtmcmp(struct vtm
*a
, struct vtm
*b
)
3298 if (ne(a
->year
, b
->year
))
3299 return lt(a
->year
, b
->year
) ? -1 : 1;
3300 else if (a
->mon
!= b
->mon
)
3301 return a
->mon
< b
->mon
? -1 : 1;
3302 else if (a
->mday
!= b
->mday
)
3303 return a
->mday
< b
->mday
? -1 : 1;
3304 else if (a
->hour
!= b
->hour
)
3305 return a
->hour
< b
->hour
? -1 : 1;
3306 else if (a
->min
!= b
->min
)
3307 return a
->min
< b
->min
? -1 : 1;
3308 else if (a
->sec
!= b
->sec
)
3309 return a
->sec
< b
->sec
? -1 : 1;
3310 else if (ne(a
->subsecx
, b
->subsecx
))
3311 return lt(a
->subsecx
, b
->subsecx
) ? -1 : 1;
3317 tmcmp(struct tm
*a
, struct tm
*b
)
3319 if (a
->tm_year
!= b
->tm_year
)
3320 return a
->tm_year
< b
->tm_year
? -1 : 1;
3321 else if (a
->tm_mon
!= b
->tm_mon
)
3322 return a
->tm_mon
< b
->tm_mon
? -1 : 1;
3323 else if (a
->tm_mday
!= b
->tm_mday
)
3324 return a
->tm_mday
< b
->tm_mday
? -1 : 1;
3325 else if (a
->tm_hour
!= b
->tm_hour
)
3326 return a
->tm_hour
< b
->tm_hour
? -1 : 1;
3327 else if (a
->tm_min
!= b
->tm_min
)
3328 return a
->tm_min
< b
->tm_min
? -1 : 1;
3329 else if (a
->tm_sec
!= b
->tm_sec
)
3330 return a
->tm_sec
< b
->tm_sec
? -1 : 1;
3337 * Time.utc(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3338 * Time.utc(sec_i, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3340 * Returns a new \Time object based the on given arguments;
3341 * its timezone is UTC.
3343 * In the first form (up to seven arguments), argument +year+ is required.
3345 * Time.utc(2000) # => 2000-01-01 00:00:00 UTC
3346 * Time.utc(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 UTC
3348 * In the second form, all ten arguments are required,
3349 * though the last four are ignored.
3350 * This form is useful for creating a time from a 10-element array
3351 * such as is returned by #to_a.
3353 * array = Time.now.to_a
3354 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3356 * Time.utc(*array) # => 2000-04-24 13:26:57 UTC
3359 * :include: doc/time/year.rdoc
3360 * :include: doc/time/mon-min.rdoc
3361 * :include: doc/time/sec_i.rdoc
3362 * :include: doc/time/usec.rdoc
3366 * Related: Time.local.
3370 time_s_mkutc(int argc
, VALUE
*argv
, VALUE klass
)
3374 time_arg(argc
, argv
, &vtm
);
3375 return time_gmtime(time_new_timew(klass
, timegmw(&vtm
)));
3380 * Time.local(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3381 * Time.local(sec, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3383 * Returns a new \Time object based the on given arguments;
3384 * its timezone is the local timezone.
3386 * In the first form (up to seven arguments), argument +year+ is required.
3388 * Time.local(2000) # => 2000-01-01 00:00:00 -0600
3389 * Time.local(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 -0600
3391 * In the second form, all ten arguments are required,
3392 * though the last four are ignored.
3393 * This form is useful for creating a time from a 10-element array
3394 * such as those returned by #to_a.
3396 * array = Time.now.to_a
3397 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3399 * Time.local(*array) # => 2000-04-24 13:26:57 -0500
3402 * :include: doc/time/year.rdoc
3403 * :include: doc/time/mon-min.rdoc
3404 * :include: doc/time/sec_i.rdoc
3405 * :include: doc/time/usec.rdoc
3407 * Alias: Time.mktime.
3409 * Related: Time.utc.
3413 time_s_mktime(int argc
, VALUE
*argv
, VALUE klass
)
3417 time_arg(argc
, argv
, &vtm
);
3418 return time_localtime(time_new_timew(klass
, timelocalw(&vtm
)));
3424 * time.tv_sec -> int
3426 * Returns the value of _time_ as an integer number of seconds
3429 * If _time_ contains subsecond, they are truncated.
3431 * t = Time.now #=> 2020-07-21 01:41:29.746012609 +0900
3432 * t.to_i #=> 1595263289
3436 time_to_i(VALUE time
)
3438 struct time_object
*tobj
;
3440 GetTimeval(time
, tobj
);
3441 return w2v(wdiv(tobj
->timew
, WINT2FIXWV(TIME_SCALE
)));
3446 * time.to_f -> float
3448 * Returns the value of _time_ as a floating point number of
3449 * seconds since the Epoch.
3450 * The return value approximate the exact value in the Time object
3451 * because floating point numbers cannot represent all rational numbers
3454 * t = Time.now #=> 2020-07-20 22:00:29.38740268 +0900
3455 * t.to_f #=> 1595250029.3874028
3456 * t.to_i #=> 1595250029
3458 * Note that IEEE 754 double is not accurate enough to represent
3459 * the exact number of nanoseconds since the Epoch.
3460 * (IEEE 754 double has 53bit mantissa.
3461 * So it can represent exact number of nanoseconds only in
3462 * <tt>2 ** 53 / 1_000_000_000 / 60 / 60 / 24 = 104.2</tt> days.)
3463 * When Ruby uses a nanosecond-resolution clock function,
3464 * such as +clock_gettime+ of POSIX, to obtain the current time,
3465 * Time#to_f can lose information of a Time object created with +Time.now+.
3469 time_to_f(VALUE time
)
3471 struct time_object
*tobj
;
3473 GetTimeval(time
, tobj
);
3474 return rb_Float(rb_time_unmagnify_to_float(tobj
->timew
));
3479 * time.to_r -> a_rational
3481 * Returns the value of _time_ as a rational number of seconds
3484 * t = Time.now #=> 2020-07-20 22:03:45.212167333 +0900
3485 * t.to_r #=> (1595250225212167333/1000000000)
3487 * This method is intended to be used to get an accurate value
3488 * representing the seconds (including subsecond) since the Epoch.
3492 time_to_r(VALUE time
)
3494 struct time_object
*tobj
;
3497 GetTimeval(time
, tobj
);
3498 v
= rb_time_unmagnify_to_rational(tobj
->timew
);
3499 if (!RB_TYPE_P(v
, T_RATIONAL
)) {
3500 v
= rb_Rational1(v
);
3508 * time.tv_usec -> int
3510 * Returns the number of microseconds for the subsecond part of _time_.
3511 * The result is a non-negative integer less than 10**6.
3513 * t = Time.now #=> 2020-07-20 22:05:58.459785953 +0900
3516 * If _time_ has fraction of microsecond (such as nanoseconds),
3519 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3522 * Time#subsec can be used to obtain the subsecond part exactly.
3526 time_usec(VALUE time
)
3528 struct time_object
*tobj
;
3531 GetTimeval(time
, tobj
);
3533 w
= wmod(tobj
->timew
, WINT2WV(TIME_SCALE
));
3534 wmuldivmod(w
, WINT2FIXWV(1000000), WINT2FIXWV(TIME_SCALE
), &q
, &r
);
3535 return rb_to_int(w2v(q
));
3541 * time.tv_nsec -> int
3543 * Returns the number of nanoseconds for the subsecond part of _time_.
3544 * The result is a non-negative integer less than 10**9.
3546 * t = Time.now #=> 2020-07-20 22:07:10.963933942 +0900
3547 * t.nsec #=> 963933942
3549 * If _time_ has fraction of nanosecond (such as picoseconds),
3552 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3553 * t.nsec #=> 666777888
3555 * Time#subsec can be used to obtain the subsecond part exactly.
3559 time_nsec(VALUE time
)
3561 struct time_object
*tobj
;
3563 GetTimeval(time
, tobj
);
3564 return rb_to_int(w2v(wmulquoll(wmod(tobj
->timew
, WINT2WV(TIME_SCALE
)), 1000000000, TIME_SCALE
)));
3569 * time.subsec -> number
3571 * Returns the subsecond for _time_.
3573 * The return value can be a rational number.
3575 * t = Time.now #=> 2020-07-20 15:40:26.867462289 +0900
3576 * t.subsec #=> (867462289/1000000000)
3578 * t = Time.now #=> 2020-07-20 15:40:50.313828595 +0900
3579 * t.subsec #=> (62765719/200000000)
3581 * t = Time.new(2000,1,1,2,3,4) #=> 2000-01-01 02:03:04 +0900
3584 * Time.new(2000,1,1,0,0,1/3r,"UTC").subsec #=> (1/3)
3589 time_subsec(VALUE time
)
3591 struct time_object
*tobj
;
3593 GetTimeval(time
, tobj
);
3594 return quov(w2v(wmod(tobj
->timew
, WINT2FIXWV(TIME_SCALE
))), INT2FIX(TIME_SCALE
));
3599 * time <=> other_time -> -1, 0, +1, or nil
3601 * Compares +time+ with +other_time+.
3603 * -1, 0, +1 or nil depending on whether +time+ is less than, equal to, or
3604 * greater than +other_time+.
3606 * +nil+ is returned if the two values are incomparable.
3608 * t = Time.now #=> 2007-11-19 08:12:12 -0600
3609 * t2 = t + 2592000 #=> 2007-12-19 08:12:12 -0600
3613 * t = Time.now #=> 2007-11-19 08:13:38 -0600
3614 * t2 = t + 0.1 #=> 2007-11-19 08:13:38 -0600
3615 * t.nsec #=> 98222999
3616 * t2.nsec #=> 198222999
3623 time_cmp(VALUE time1
, VALUE time2
)
3625 struct time_object
*tobj1
, *tobj2
;
3628 GetTimeval(time1
, tobj1
);
3629 if (IsTimeval(time2
)) {
3630 GetTimeval(time2
, tobj2
);
3631 n
= wcmp(tobj1
->timew
, tobj2
->timew
);
3634 return rb_invcmp(time1
, time2
);
3636 if (n
== 0) return INT2FIX(0);
3637 if (n
> 0) return INT2FIX(1);
3643 * time.eql?(other_time)
3645 * Returns +true+ if _time_ and +other_time+ are
3646 * both Time objects with the same seconds (including subsecond) from the Epoch.
3650 time_eql(VALUE time1
, VALUE time2
)
3652 struct time_object
*tobj1
, *tobj2
;
3654 GetTimeval(time1
, tobj1
);
3655 if (IsTimeval(time2
)) {
3656 GetTimeval(time2
, tobj2
);
3657 return rb_equal(w2v(tobj1
->timew
), w2v(tobj2
->timew
));
3664 * time.utc? -> true or false
3665 * time.gmt? -> true or false
3667 * Returns +true+ if _time_ represents a time in UTC (GMT).
3669 * t = Time.now #=> 2007-11-19 08:15:23 -0600
3671 * t = Time.gm(2000,"jan",1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3674 * t = Time.now #=> 2007-11-19 08:16:03 -0600
3676 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3681 time_utc_p(VALUE time
)
3683 struct time_object
*tobj
;
3685 GetTimeval(time
, tobj
);
3686 return RBOOL(TZMODE_UTC_P(tobj
));
3691 * time.hash -> integer
3693 * Returns a hash code for this Time object.
3695 * See also Object#hash.
3699 time_hash(VALUE time
)
3701 struct time_object
*tobj
;
3703 GetTimeval(time
, tobj
);
3704 return rb_hash(w2v(tobj
->timew
));
3709 time_init_copy(VALUE copy
, VALUE time
)
3711 struct time_object
*tobj
, *tcopy
;
3713 if (!OBJ_INIT_COPY(copy
, time
)) return copy
;
3714 GetTimeval(time
, tobj
);
3715 GetNewTimeval(copy
, tcopy
);
3716 MEMCPY(tcopy
, tobj
, struct time_object
, 1);
3722 time_dup(VALUE time
)
3724 VALUE dup
= time_s_alloc(rb_obj_class(time
));
3725 time_init_copy(dup
, time
);
3730 time_localtime(VALUE time
)
3732 struct time_object
*tobj
;
3736 GetTimeval(time
, tobj
);
3737 if (TZMODE_LOCALTIME_P(tobj
)) {
3745 zone
= tobj
->vtm
.zone
;
3746 if (maybe_tzobj_p(zone
) && zone_localtime(zone
, time
)) {
3750 if (!localtimew(tobj
->timew
, &vtm
))
3751 rb_raise(rb_eArgError
, "localtime error");
3755 TZMODE_SET_LOCALTIME(tobj
);
3760 time_zonelocal(VALUE time
, VALUE off
)
3763 if (zone_localtime(zone
, time
)) return time
;
3765 if (NIL_P(off
= utc_offset_arg(off
))) {
3767 if (NIL_P(zone
= find_timezone(time
, off
))) invalid_utc_offset(off
);
3768 if (!zone_localtime(zone
, time
)) invalid_utc_offset(off
);
3771 else if (off
== UTC_ZONE
) {
3772 return time_gmtime(time
);
3774 validate_utc_offset(off
);
3776 time_set_utc_offset(time
, off
);
3777 return time_fixoff(time
);
3782 * time.localtime -> time
3783 * time.localtime(utc_offset) -> time
3785 * Converts _time_ to local time (using the local time zone in
3786 * effect at the creation time of _time_) modifying the receiver.
3788 * If +utc_offset+ is given, it is used instead of the local time.
3790 * t = Time.utc(2000, "jan", 1, 20, 15, 1) #=> 2000-01-01 20:15:01 UTC
3793 * t.localtime #=> 2000-01-01 14:15:01 -0600
3796 * t.localtime("+09:00") #=> 2000-01-02 05:15:01 +0900
3799 * If +utc_offset+ is not given and _time_ is local time, just returns
3804 time_localtime_m(int argc
, VALUE
*argv
, VALUE time
)
3808 if (rb_check_arity(argc
, 0, 1) && !NIL_P(off
= argv
[0])) {
3809 return time_zonelocal(time
, off
);
3812 return time_localtime(time
);
3817 * time.gmtime -> time
3820 * Converts _time_ to UTC (GMT), modifying the receiver.
3822 * t = Time.now #=> 2007-11-19 08:18:31 -0600
3824 * t.gmtime #=> 2007-11-19 14:18:31 UTC
3827 * t = Time.now #=> 2007-11-19 08:18:51 -0600
3829 * t.utc #=> 2007-11-19 14:18:51 UTC
3834 time_gmtime(VALUE time
)
3836 struct time_object
*tobj
;
3839 GetTimeval(time
, tobj
);
3840 if (TZMODE_UTC_P(tobj
)) {
3849 GMTIMEW(tobj
->timew
, &vtm
);
3853 TZMODE_SET_UTC(tobj
);
3858 time_fixoff(VALUE time
)
3860 struct time_object
*tobj
;
3864 GetTimeval(time
, tobj
);
3865 if (TZMODE_FIXOFF_P(tobj
)) {
3873 if (TZMODE_FIXOFF_P(tobj
))
3874 off
= tobj
->vtm
.utc_offset
;
3878 GMTIMEW(tobj
->timew
, &vtm
);
3880 zone
= tobj
->vtm
.zone
;
3882 tobj
->vtm
.zone
= zone
;
3883 vtm_add_offset(&tobj
->vtm
, off
, +1);
3886 TZMODE_SET_FIXOFF(tobj
, off
);
3892 * time.getlocal -> new_time
3893 * time.getlocal(utc_offset) -> new_time
3894 * time.getlocal(timezone) -> new_time
3896 * Returns a new Time object representing _time_ in
3897 * local time (using the local time zone in effect for this process).
3899 * If +utc_offset+ is given, it is used instead of the local time.
3900 * +utc_offset+ can be given as a human-readable string (eg. <code>"+09:00"</code>)
3901 * or as a number of seconds (eg. <code>32400</code>).
3903 * t = Time.utc(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3906 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
3910 * j = t.getlocal("+09:00") #=> 2000-01-02 05:15:01 +0900
3914 * k = t.getlocal(9*60*60) #=> 2000-01-02 05:15:01 +0900
3920 time_getlocaltime(int argc
, VALUE
*argv
, VALUE time
)
3924 if (rb_check_arity(argc
, 0, 1) && !NIL_P(off
= argv
[0])) {
3926 if (maybe_tzobj_p(zone
)) {
3927 VALUE t
= time_dup(time
);
3928 if (zone_localtime(off
, t
)) return t
;
3931 if (NIL_P(off
= utc_offset_arg(off
))) {
3933 if (NIL_P(zone
= find_timezone(time
, off
))) invalid_utc_offset(off
);
3934 time
= time_dup(time
);
3935 if (!zone_localtime(zone
, time
)) invalid_utc_offset(off
);
3938 else if (off
== UTC_ZONE
) {
3939 return time_gmtime(time_dup(time
));
3941 validate_utc_offset(off
);
3943 time
= time_dup(time
);
3944 time_set_utc_offset(time
, off
);
3945 return time_fixoff(time
);
3948 return time_localtime(time_dup(time
));
3953 * time.getgm -> new_time
3954 * time.getutc -> new_time
3956 * Returns a new Time object representing _time_ in UTC.
3958 * t = Time.local(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 -0600
3960 * y = t.getgm #=> 2000-01-02 02:15:01 UTC
3966 time_getgmtime(VALUE time
)
3968 return time_gmtime(time_dup(time
));
3972 time_get_tm(VALUE time
, struct time_object
*tobj
)
3974 if (TZMODE_UTC_P(tobj
)) return time_gmtime(time
);
3975 if (TZMODE_FIXOFF_P(tobj
)) return time_fixoff(time
);
3976 return time_localtime(time
);
3979 static VALUE
strftime_cstr(const char *fmt
, size_t len
, VALUE time
, rb_encoding
*enc
);
3980 #define strftimev(fmt, time, enc) strftime_cstr((fmt), rb_strlen_lit(fmt), (time), (enc))
3984 * time.asctime -> string
3985 * time.ctime -> string
3987 * Returns a canonical string representation of _time_.
3989 * Time.now.asctime #=> "Wed Apr 9 08:56:03 2003"
3990 * Time.now.ctime #=> "Wed Apr 9 08:56:03 2003"
3994 time_asctime(VALUE time
)
3996 return strftimev("%a %b %e %T %Y", time
, rb_usascii_encoding());
4001 * time.to_s -> string
4003 * Returns a string representing _time_. Equivalent to calling
4004 * #strftime with the appropriate format string.
4007 * t.to_s #=> "2012-11-10 18:16:12 +0100"
4008 * t.strftime "%Y-%m-%d %H:%M:%S %z" #=> "2012-11-10 18:16:12 +0100"
4010 * t.utc.to_s #=> "2012-11-10 17:16:12 UTC"
4011 * t.strftime "%Y-%m-%d %H:%M:%S UTC" #=> "2012-11-10 17:16:12 UTC"
4015 time_to_s(VALUE time
)
4017 struct time_object
*tobj
;
4019 GetTimeval(time
, tobj
);
4020 if (TZMODE_UTC_P(tobj
))
4021 return strftimev("%Y-%m-%d %H:%M:%S UTC", time
, rb_usascii_encoding());
4023 return strftimev("%Y-%m-%d %H:%M:%S %z", time
, rb_usascii_encoding());
4028 * time.inspect -> string
4030 * Returns a detailed string representing _time_. Unlike to_s,
4031 * preserves subsecond in the representation for easier debugging.
4034 * t.inspect #=> "2012-11-10 18:16:12.261257655 +0100"
4035 * t.strftime "%Y-%m-%d %H:%M:%S.%N %z" #=> "2012-11-10 18:16:12.261257655 +0100"
4037 * t.utc.inspect #=> "2012-11-10 17:16:12.261257655 UTC"
4038 * t.strftime "%Y-%m-%d %H:%M:%S.%N UTC" #=> "2012-11-10 17:16:12.261257655 UTC"
4042 time_inspect(VALUE time
)
4044 struct time_object
*tobj
;
4047 GetTimeval(time
, tobj
);
4048 str
= strftimev("%Y-%m-%d %H:%M:%S", time
, rb_usascii_encoding());
4049 subsec
= w2v(wmod(tobj
->timew
, WINT2FIXWV(TIME_SCALE
)));
4050 if (FIXNUM_P(subsec
) && FIX2LONG(subsec
) == 0) {
4052 else if (FIXNUM_P(subsec
) && FIX2LONG(subsec
) < TIME_SCALE
) {
4054 rb_str_catf(str
, ".%09ld", FIX2LONG(subsec
));
4055 for (len
=RSTRING_LEN(str
); RSTRING_PTR(str
)[len
-1] == '0' && len
> 0; len
--)
4057 rb_str_resize(str
, len
);
4060 rb_str_cat_cstr(str
, " ");
4061 subsec
= quov(subsec
, INT2FIX(TIME_SCALE
));
4062 rb_str_concat(str
, rb_obj_as_string(subsec
));
4064 if (TZMODE_UTC_P(tobj
)) {
4065 rb_str_cat_cstr(str
, " UTC");
4068 /* ?TODO: subsecond offset */
4069 long off
= NUM2LONG(rb_funcall(tobj
->vtm
.utc_offset
, rb_intern("round"), 0));
4070 char sign
= (off
< 0) ? (off
= -off
, '-') : '+';
4072 int min
= (off
/= 60) % 60;
4074 rb_str_catf(str
, " %c%.2d%.2d", sign
, (int)off
, min
);
4075 if (sec
) rb_str_catf(str
, "%.2d", sec
);
4081 time_add0(VALUE klass
, const struct time_object
*tobj
, VALUE torig
, VALUE offset
, int sign
)
4084 struct time_object
*result_tobj
;
4086 offset
= num_exact(offset
);
4088 result
= time_new_timew(klass
, wsub(tobj
->timew
, rb_time_magnify(v2w(offset
))));
4090 result
= time_new_timew(klass
, wadd(tobj
->timew
, rb_time_magnify(v2w(offset
))));
4091 GetTimeval(result
, result_tobj
);
4092 TZMODE_COPY(result_tobj
, tobj
);
4098 time_add(const struct time_object
*tobj
, VALUE torig
, VALUE offset
, int sign
)
4100 return time_add0(rb_cTime
, tobj
, torig
, offset
, sign
);
4105 * time + numeric -> time
4107 * Adds some number of seconds (possibly including subsecond) to
4108 * _time_ and returns that value as a new Time object.
4110 * t = Time.now #=> 2020-07-20 22:14:43.170490982 +0900
4111 * t + (60 * 60 * 24) #=> 2020-07-21 22:14:43.170490982 +0900
4115 time_plus(VALUE time1
, VALUE time2
)
4117 struct time_object
*tobj
;
4118 GetTimeval(time1
, tobj
);
4120 if (IsTimeval(time2
)) {
4121 rb_raise(rb_eTypeError
, "time + time?");
4123 return time_add(tobj
, time1
, time2
, 1);
4128 * time - other_time -> float
4129 * time - numeric -> time
4131 * Returns a difference in seconds as a Float
4132 * between _time_ and +other_time+, or subtracts the given number
4133 * of seconds in +numeric+ from _time_.
4135 * t = Time.now #=> 2020-07-20 22:15:49.302766336 +0900
4136 * t2 = t + 2592000 #=> 2020-08-19 22:15:49.302766336 +0900
4137 * t2 - t #=> 2592000.0
4138 * t2 - 2592000 #=> 2020-07-20 22:15:49.302766336 +0900
4142 time_minus(VALUE time1
, VALUE time2
)
4144 struct time_object
*tobj
;
4146 GetTimeval(time1
, tobj
);
4147 if (IsTimeval(time2
)) {
4148 struct time_object
*tobj2
;
4150 GetTimeval(time2
, tobj2
);
4151 return rb_Float(rb_time_unmagnify_to_float(wsub(tobj
->timew
, tobj2
->timew
)));
4153 return time_add(tobj
, time1
, time2
, -1);
4157 ndigits_denominator(VALUE ndigits
)
4159 long nd
= NUM2LONG(ndigits
);
4162 rb_raise(rb_eArgError
, "negative ndigits given");
4167 return rb_rational_new(INT2FIX(1),
4168 rb_int_positive_pow(10, (unsigned long)nd
));
4173 * time.round([ndigits]) -> new_time
4175 * Rounds subsecond to a given precision in decimal digits (0 digits by default).
4176 * It returns a new Time object.
4177 * +ndigits+ should be zero or a positive integer.
4179 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4180 * t #=> 2010-03-30 05:43:25.123456789 UTC
4181 * t.round #=> 2010-03-30 05:43:25 UTC
4182 * t.round(0) #=> 2010-03-30 05:43:25 UTC
4183 * t.round(1) #=> 2010-03-30 05:43:25.1 UTC
4184 * t.round(2) #=> 2010-03-30 05:43:25.12 UTC
4185 * t.round(3) #=> 2010-03-30 05:43:25.123 UTC
4186 * t.round(4) #=> 2010-03-30 05:43:25.1235 UTC
4188 * t = Time.utc(1999,12,31, 23,59,59)
4189 * (t + 0.4).round #=> 1999-12-31 23:59:59 UTC
4190 * (t + 0.49).round #=> 1999-12-31 23:59:59 UTC
4191 * (t + 0.5).round #=> 2000-01-01 00:00:00 UTC
4192 * (t + 1.4).round #=> 2000-01-01 00:00:00 UTC
4193 * (t + 1.49).round #=> 2000-01-01 00:00:00 UTC
4194 * (t + 1.5).round #=> 2000-01-01 00:00:01 UTC
4196 * t = Time.utc(1999,12,31, 23,59,59) #=> 1999-12-31 23:59:59 UTC
4197 * (t + 0.123456789).round(4).iso8601(6) #=> 1999-12-31 23:59:59.1235 UTC
4201 time_round(int argc
, VALUE
*argv
, VALUE time
)
4203 VALUE ndigits
, v
, den
;
4204 struct time_object
*tobj
;
4206 if (!rb_check_arity(argc
, 0, 1) || NIL_P(ndigits
= argv
[0]))
4209 den
= ndigits_denominator(ndigits
);
4211 GetTimeval(time
, tobj
);
4212 v
= w2v(rb_time_unmagnify(tobj
->timew
));
4215 if (lt(v
, quov(den
, INT2FIX(2))))
4216 return time_add(tobj
, time
, v
, -1);
4218 return time_add(tobj
, time
, subv(den
, v
), 1);
4223 * time.floor([ndigits]) -> new_time
4225 * Floors subsecond to a given precision in decimal digits (0 digits by default).
4226 * It returns a new Time object.
4227 * +ndigits+ should be zero or a positive integer.
4229 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4230 * t #=> 2010-03-30 05:43:25.123456789 UTC
4231 * t.floor #=> 2010-03-30 05:43:25 UTC
4232 * t.floor(0) #=> 2010-03-30 05:43:25 UTC
4233 * t.floor(1) #=> 2010-03-30 05:43:25.1 UTC
4234 * t.floor(2) #=> 2010-03-30 05:43:25.12 UTC
4235 * t.floor(3) #=> 2010-03-30 05:43:25.123 UTC
4236 * t.floor(4) #=> 2010-03-30 05:43:25.1234 UTC
4238 * t = Time.utc(1999,12,31, 23,59,59)
4239 * (t + 0.4).floor #=> 1999-12-31 23:59:59 UTC
4240 * (t + 0.9).floor #=> 1999-12-31 23:59:59 UTC
4241 * (t + 1.4).floor #=> 2000-01-01 00:00:00 UTC
4242 * (t + 1.9).floor #=> 2000-01-01 00:00:00 UTC
4244 * t = Time.utc(1999,12,31, 23,59,59)
4245 * (t + 0.123456789).floor(4) #=> 1999-12-31 23:59:59.1234 UTC
4249 time_floor(int argc
, VALUE
*argv
, VALUE time
)
4251 VALUE ndigits
, v
, den
;
4252 struct time_object
*tobj
;
4254 if (!rb_check_arity(argc
, 0, 1) || NIL_P(ndigits
= argv
[0]))
4257 den
= ndigits_denominator(ndigits
);
4259 GetTimeval(time
, tobj
);
4260 v
= w2v(rb_time_unmagnify(tobj
->timew
));
4263 return time_add(tobj
, time
, v
, -1);
4268 * time.ceil([ndigits]) -> new_time
4270 * Ceils subsecond to a given precision in decimal digits (0 digits by default).
4271 * It returns a new Time object.
4272 * +ndigits+ should be zero or a positive integer.
4274 * t = Time.utc(2010,3,30, 5,43,25.0123456789r)
4275 * t #=> 2010-03-30 05:43:25 123456789/10000000000 UTC
4276 * t.ceil #=> 2010-03-30 05:43:26 UTC
4277 * t.ceil(0) #=> 2010-03-30 05:43:26 UTC
4278 * t.ceil(1) #=> 2010-03-30 05:43:25.1 UTC
4279 * t.ceil(2) #=> 2010-03-30 05:43:25.02 UTC
4280 * t.ceil(3) #=> 2010-03-30 05:43:25.013 UTC
4281 * t.ceil(4) #=> 2010-03-30 05:43:25.0124 UTC
4283 * t = Time.utc(1999,12,31, 23,59,59)
4284 * (t + 0.4).ceil #=> 2000-01-01 00:00:00 UTC
4285 * (t + 0.9).ceil #=> 2000-01-01 00:00:00 UTC
4286 * (t + 1.4).ceil #=> 2000-01-01 00:00:01 UTC
4287 * (t + 1.9).ceil #=> 2000-01-01 00:00:01 UTC
4289 * t = Time.utc(1999,12,31, 23,59,59)
4290 * (t + 0.123456789).ceil(4) #=> 1999-12-31 23:59:59.1235 UTC
4294 time_ceil(int argc
, VALUE
*argv
, VALUE time
)
4296 VALUE ndigits
, v
, den
;
4297 struct time_object
*tobj
;
4299 if (!rb_check_arity(argc
, 0, 1) || NIL_P(ndigits
= argv
[0]))
4302 den
= ndigits_denominator(ndigits
);
4304 GetTimeval(time
, tobj
);
4305 v
= w2v(rb_time_unmagnify(tobj
->timew
));
4308 if (!rb_equal(v
, INT2FIX(0))) {
4311 return time_add(tobj
, time
, v
, 1);
4316 * time.sec -> integer
4318 * Returns the second of the minute (0..60) for _time_.
4320 * *Note:* Seconds range from zero to 60 to allow the system to inject
4321 * leap seconds. See https://en.wikipedia.org/wiki/Leap_second for further
4324 * t = Time.now #=> 2007-11-19 08:25:02 -0600
4329 time_sec(VALUE time
)
4331 struct time_object
*tobj
;
4333 GetTimeval(time
, tobj
);
4334 MAKE_TM(time
, tobj
);
4335 return INT2FIX(tobj
->vtm
.sec
);
4340 * time.min -> integer
4342 * Returns the minute of the hour (0..59) for _time_.
4344 * t = Time.now #=> 2007-11-19 08:25:51 -0600
4349 time_min(VALUE time
)
4351 struct time_object
*tobj
;
4353 GetTimeval(time
, tobj
);
4354 MAKE_TM(time
, tobj
);
4355 return INT2FIX(tobj
->vtm
.min
);
4360 * time.hour -> integer
4362 * Returns the hour of the day (0..23) for _time_.
4364 * t = Time.now #=> 2007-11-19 08:26:20 -0600
4369 time_hour(VALUE time
)
4371 struct time_object
*tobj
;
4373 GetTimeval(time
, tobj
);
4374 MAKE_TM(time
, tobj
);
4375 return INT2FIX(tobj
->vtm
.hour
);
4380 * time.day -> integer
4381 * time.mday -> integer
4383 * Returns the day of the month (1..31) for _time_.
4385 * t = Time.now #=> 2007-11-19 08:27:03 -0600
4391 time_mday(VALUE time
)
4393 struct time_object
*tobj
;
4395 GetTimeval(time
, tobj
);
4396 MAKE_TM(time
, tobj
);
4397 return INT2FIX(tobj
->vtm
.mday
);
4402 * time.mon -> integer
4403 * time.month -> integer
4405 * Returns the month of the year (1..12) for _time_.
4407 * t = Time.now #=> 2007-11-19 08:27:30 -0600
4413 time_mon(VALUE time
)
4415 struct time_object
*tobj
;
4417 GetTimeval(time
, tobj
);
4418 MAKE_TM(time
, tobj
);
4419 return INT2FIX(tobj
->vtm
.mon
);
4424 * time.year -> integer
4426 * Returns the year for _time_ (including the century).
4428 * t = Time.now #=> 2007-11-19 08:27:51 -0600
4433 time_year(VALUE time
)
4435 struct time_object
*tobj
;
4437 GetTimeval(time
, tobj
);
4438 MAKE_TM(time
, tobj
);
4439 return tobj
->vtm
.year
;
4444 * time.wday -> integer
4446 * Returns an integer representing the day of the week, 0..6, with
4449 * t = Time.now #=> 2007-11-20 02:35:35 -0600
4451 * t.sunday? #=> false
4452 * t.monday? #=> false
4453 * t.tuesday? #=> true
4454 * t.wednesday? #=> false
4455 * t.thursday? #=> false
4456 * t.friday? #=> false
4457 * t.saturday? #=> false
4461 time_wday(VALUE time
)
4463 struct time_object
*tobj
;
4465 GetTimeval(time
, tobj
);
4466 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.wday
!= VTM_WDAY_INITVAL
);
4467 return INT2FIX((int)tobj
->vtm
.wday
);
4470 #define wday_p(n) {\
4471 return RBOOL(time_wday(time) == INT2FIX(n)); \
4476 * time.sunday? -> true or false
4478 * Returns +true+ if _time_ represents Sunday.
4480 * t = Time.local(1990, 4, 1) #=> 1990-04-01 00:00:00 -0600
4481 * t.sunday? #=> true
4485 time_sunday(VALUE time
)
4492 * time.monday? -> true or false
4494 * Returns +true+ if _time_ represents Monday.
4496 * t = Time.local(2003, 8, 4) #=> 2003-08-04 00:00:00 -0500
4497 * t.monday? #=> true
4501 time_monday(VALUE time
)
4508 * time.tuesday? -> true or false
4510 * Returns +true+ if _time_ represents Tuesday.
4512 * t = Time.local(1991, 2, 19) #=> 1991-02-19 00:00:00 -0600
4513 * t.tuesday? #=> true
4517 time_tuesday(VALUE time
)
4524 * time.wednesday? -> true or false
4526 * Returns +true+ if _time_ represents Wednesday.
4528 * t = Time.local(1993, 2, 24) #=> 1993-02-24 00:00:00 -0600
4529 * t.wednesday? #=> true
4533 time_wednesday(VALUE time
)
4540 * time.thursday? -> true or false
4542 * Returns +true+ if _time_ represents Thursday.
4544 * t = Time.local(1995, 12, 21) #=> 1995-12-21 00:00:00 -0600
4545 * t.thursday? #=> true
4549 time_thursday(VALUE time
)
4556 * time.friday? -> true or false
4558 * Returns +true+ if _time_ represents Friday.
4560 * t = Time.local(1987, 12, 18) #=> 1987-12-18 00:00:00 -0600
4561 * t.friday? #=> true
4565 time_friday(VALUE time
)
4572 * time.saturday? -> true or false
4574 * Returns +true+ if _time_ represents Saturday.
4576 * t = Time.local(2006, 6, 10) #=> 2006-06-10 00:00:00 -0500
4577 * t.saturday? #=> true
4581 time_saturday(VALUE time
)
4588 * time.yday -> integer
4590 * Returns an integer representing the day of the year, 1..366.
4592 * t = Time.now #=> 2007-11-19 08:32:31 -0600
4597 time_yday(VALUE time
)
4599 struct time_object
*tobj
;
4601 GetTimeval(time
, tobj
);
4602 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.yday
!= 0);
4603 return INT2FIX(tobj
->vtm
.yday
);
4608 * time.isdst -> true or false
4609 * time.dst? -> true or false
4611 * Returns +true+ if _time_ occurs during Daylight
4612 * Saving Time in its time zone.
4615 * Time.local(2000, 1, 1).zone #=> "CST"
4616 * Time.local(2000, 1, 1).isdst #=> false
4617 * Time.local(2000, 1, 1).dst? #=> false
4618 * Time.local(2000, 7, 1).zone #=> "CDT"
4619 * Time.local(2000, 7, 1).isdst #=> true
4620 * Time.local(2000, 7, 1).dst? #=> true
4623 * Time.local(2000, 1, 1).zone #=> "JST"
4624 * Time.local(2000, 1, 1).isdst #=> false
4625 * Time.local(2000, 1, 1).dst? #=> false
4626 * Time.local(2000, 7, 1).zone #=> "JST"
4627 * Time.local(2000, 7, 1).isdst #=> false
4628 * Time.local(2000, 7, 1).dst? #=> false
4632 time_isdst(VALUE time
)
4634 struct time_object
*tobj
;
4636 GetTimeval(time
, tobj
);
4637 MAKE_TM(time
, tobj
);
4638 if (tobj
->vtm
.isdst
== VTM_ISDST_INITVAL
) {
4639 rb_raise(rb_eRuntimeError
, "isdst is not set yet");
4641 return RBOOL(tobj
->vtm
.isdst
);
4646 * time.zone -> string or timezone
4648 * Returns the name of the time zone used for _time_. As of Ruby
4649 * 1.8, returns ``UTC'' rather than ``GMT'' for UTC times.
4651 * t = Time.gm(2000, "jan", 1, 20, 15, 1)
4653 * t = Time.local(2000, "jan", 1, 20, 15, 1)
4658 time_zone(VALUE time
)
4660 struct time_object
*tobj
;
4663 GetTimeval(time
, tobj
);
4664 MAKE_TM(time
, tobj
);
4666 if (TZMODE_UTC_P(tobj
)) {
4667 return rb_usascii_str_new_cstr("UTC");
4669 zone
= tobj
->vtm
.zone
;
4673 if (RB_TYPE_P(zone
, T_STRING
))
4674 zone
= rb_str_dup(zone
);
4680 * time.gmt_offset -> integer
4681 * time.gmtoff -> integer
4682 * time.utc_offset -> integer
4684 * Returns the offset in seconds between the timezone of _time_
4687 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
4688 * t.gmt_offset #=> 0
4689 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
4690 * l.gmt_offset #=> -21600
4694 rb_time_utc_offset(VALUE time
)
4696 struct time_object
*tobj
;
4698 GetTimeval(time
, tobj
);
4700 if (TZMODE_UTC_P(tobj
)) {
4704 MAKE_TM(time
, tobj
);
4705 return tobj
->vtm
.utc_offset
;
4711 * time.to_a -> array
4713 * Returns a ten-element _array_ of values for _time_:
4715 * [sec, min, hour, day, month, year, wday, yday, isdst, zone]
4717 * See the individual methods for an explanation of the
4718 * valid ranges of each value. The ten elements can be passed directly
4719 * to Time.utc or Time.local to create a
4722 * t = Time.now #=> 2007-11-19 08:36:01 -0600
4723 * now = t.to_a #=> [1, 36, 8, 19, 11, 2007, 1, 323, false, "CST"]
4727 time_to_a(VALUE time
)
4729 struct time_object
*tobj
;
4731 GetTimeval(time
, tobj
);
4732 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.yday
!= 0);
4733 return rb_ary_new3(10,
4734 INT2FIX(tobj
->vtm
.sec
),
4735 INT2FIX(tobj
->vtm
.min
),
4736 INT2FIX(tobj
->vtm
.hour
),
4737 INT2FIX(tobj
->vtm
.mday
),
4738 INT2FIX(tobj
->vtm
.mon
),
4740 INT2FIX(tobj
->vtm
.wday
),
4741 INT2FIX(tobj
->vtm
.yday
),
4742 RBOOL(tobj
->vtm
.isdst
),
4747 rb_strftime_alloc(const char *format
, size_t format_len
, rb_encoding
*enc
,
4748 VALUE time
, struct vtm
*vtm
, wideval_t timew
, int gmt
)
4753 if (!timew2timespec_exact(timew
, &ts
))
4754 timev
= w2v(rb_time_unmagnify(timew
));
4757 return rb_strftime_timespec(format
, format_len
, enc
, time
, vtm
, &ts
, gmt
);
4760 return rb_strftime(format
, format_len
, enc
, time
, vtm
, timev
, gmt
);
4765 strftime_cstr(const char *fmt
, size_t len
, VALUE time
, rb_encoding
*enc
)
4767 struct time_object
*tobj
;
4770 GetTimeval(time
, tobj
);
4771 MAKE_TM(time
, tobj
);
4772 str
= rb_strftime_alloc(fmt
, len
, enc
, time
, &tobj
->vtm
, tobj
->timew
, TZMODE_UTC_P(tobj
));
4773 if (!str
) rb_raise(rb_eArgError
, "invalid format: %s", fmt
);
4779 * time.strftime( string ) -> string
4781 * Formats _time_ according to the directives in the given format string.
4783 * The directives begin with a percent (%) character.
4784 * Any text not listed as a directive will be passed through to the
4787 * The directive consists of a percent (%) character,
4788 * zero or more flags, optional minimum field width,
4789 * optional modifier and a conversion specifier
4792 * %<flags><width><modifier><conversion>
4795 * - don't pad a numerical output
4796 * _ use spaces for padding
4797 * 0 use zeros for padding
4798 * ^ upcase the result string
4800 * : use colons for %z
4802 * The minimum field width specifies the minimum width.
4804 * The modifiers are "E" and "O".
4807 * Format directives:
4809 * Date (Year, Month, Day):
4810 * %Y - Year with century if provided, will pad result at least 4 digits.
4811 * -0001, 0000, 1995, 2009, 14292, etc.
4812 * %C - year / 100 (rounded down such as 20 in 2009)
4813 * %y - year % 100 (00..99)
4815 * %m - Month of the year, zero-padded (01..12)
4816 * %_m blank-padded ( 1..12)
4817 * %-m no-padded (1..12)
4818 * %B - The full month name (``January'')
4819 * %^B uppercased (``JANUARY'')
4820 * %b - The abbreviated month name (``Jan'')
4821 * %^b uppercased (``JAN'')
4822 * %h - Equivalent to %b
4824 * %d - Day of the month, zero-padded (01..31)
4825 * %-d no-padded (1..31)
4826 * %e - Day of the month, blank-padded ( 1..31)
4828 * %j - Day of the year (001..366)
4830 * Time (Hour, Minute, Second, Subsecond):
4831 * %H - Hour of the day, 24-hour clock, zero-padded (00..23)
4832 * %k - Hour of the day, 24-hour clock, blank-padded ( 0..23)
4833 * %I - Hour of the day, 12-hour clock, zero-padded (01..12)
4834 * %l - Hour of the day, 12-hour clock, blank-padded ( 1..12)
4835 * %P - Meridian indicator, lowercase (``am'' or ``pm'')
4836 * %p - Meridian indicator, uppercase (``AM'' or ``PM'')
4838 * %M - Minute of the hour (00..59)
4840 * %S - Second of the minute (00..60)
4842 * %L - Millisecond of the second (000..999)
4843 * The digits under millisecond are truncated to not produce 1000.
4844 * %N - Fractional seconds digits, default is 9 digits (nanosecond)
4845 * %3N millisecond (3 digits)
4846 * %6N microsecond (6 digits)
4847 * %9N nanosecond (9 digits)
4848 * %12N picosecond (12 digits)
4849 * %15N femtosecond (15 digits)
4850 * %18N attosecond (18 digits)
4851 * %21N zeptosecond (21 digits)
4852 * %24N yoctosecond (24 digits)
4853 * The digits under the specified length are truncated to avoid
4857 * %z - Time zone as hour and minute offset from UTC (e.g. +0900)
4858 * %:z - hour and minute offset from UTC with a colon (e.g. +09:00)
4859 * %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
4860 * %Z - Abbreviated time zone name or similar information. (OS dependent)
4863 * %A - The full weekday name (``Sunday'')
4864 * %^A uppercased (``SUNDAY'')
4865 * %a - The abbreviated name (``Sun'')
4866 * %^a uppercased (``SUN'')
4867 * %u - Day of the week (Monday is 1, 1..7)
4868 * %w - Day of the week (Sunday is 0, 0..6)
4870 * ISO 8601 week-based year and week number:
4871 * The first week of YYYY starts with a Monday and includes YYYY-01-04.
4872 * The days in the year before the first week are in the last week of
4873 * the previous year.
4874 * %G - The week-based year
4875 * %g - The last 2 digits of the week-based year (00..99)
4876 * %V - Week number of the week-based year (01..53)
4879 * The first week of YYYY that starts with a Sunday or Monday (according to %U
4880 * or %W). The days in the year before the first week are in week 0.
4881 * %U - Week number of the year. The week starts with Sunday. (00..53)
4882 * %W - Week number of the year. The week starts with Monday. (00..53)
4884 * Seconds since the Epoch:
4885 * %s - Number of seconds since 1970-01-01 00:00:00 UTC.
4888 * %n - Newline character (\n)
4889 * %t - Tab character (\t)
4890 * %% - Literal ``%'' character
4893 * %c - date and time (%a %b %e %T %Y)
4894 * %D - Date (%m/%d/%y)
4895 * %F - The ISO 8601 date format (%Y-%m-%d)
4896 * %v - VMS date (%e-%^b-%4Y)
4899 * %r - 12-hour time (%I:%M:%S %p)
4900 * %R - 24-hour time (%H:%M)
4901 * %T - 24-hour time (%H:%M:%S)
4903 * This method is similar to strftime() function defined in ISO C and POSIX.
4905 * While all directives are locale independent since Ruby 1.9, %Z is platform
4907 * So, the result may differ even if the same format string is used in other
4908 * systems such as C.
4910 * %z is recommended over %Z.
4911 * %Z doesn't identify the timezone.
4912 * For example, "CST" is used at America/Chicago (-06:00),
4913 * America/Havana (-05:00), Asia/Harbin (+08:00), Australia/Darwin (+09:30)
4914 * and Australia/Adelaide (+10:30).
4915 * Also, %Z is highly dependent on the operating system.
4916 * For example, it may generate a non ASCII string on Japanese Windows,
4917 * i.e. the result can be different to "JST".
4918 * So the numeric time zone offset, %z, is recommended.
4922 * t = Time.new(2007,11,19,8,37,48,"-06:00") #=> 2007-11-19 08:37:48 -0600
4923 * t.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007"
4924 * t.strftime("at %I:%M %p") #=> "at 08:37 AM"
4926 * Various ISO 8601 formats:
4927 * %Y%m%d => 20071119 Calendar date (basic)
4928 * %F => 2007-11-19 Calendar date (extended)
4929 * %Y-%m => 2007-11 Calendar date, reduced accuracy, specific month
4930 * %Y => 2007 Calendar date, reduced accuracy, specific year
4931 * %C => 20 Calendar date, reduced accuracy, specific century
4932 * %Y%j => 2007323 Ordinal date (basic)
4933 * %Y-%j => 2007-323 Ordinal date (extended)
4934 * %GW%V%u => 2007W471 Week date (basic)
4935 * %G-W%V-%u => 2007-W47-1 Week date (extended)
4936 * %GW%V => 2007W47 Week date, reduced accuracy, specific week (basic)
4937 * %G-W%V => 2007-W47 Week date, reduced accuracy, specific week (extended)
4938 * %H%M%S => 083748 Local time (basic)
4939 * %T => 08:37:48 Local time (extended)
4940 * %H%M => 0837 Local time, reduced accuracy, specific minute (basic)
4941 * %H:%M => 08:37 Local time, reduced accuracy, specific minute (extended)
4942 * %H => 08 Local time, reduced accuracy, specific hour
4943 * %H%M%S,%L => 083748,000 Local time with decimal fraction, comma as decimal sign (basic)
4944 * %T,%L => 08:37:48,000 Local time with decimal fraction, comma as decimal sign (extended)
4945 * %H%M%S.%L => 083748.000 Local time with decimal fraction, full stop as decimal sign (basic)
4946 * %T.%L => 08:37:48.000 Local time with decimal fraction, full stop as decimal sign (extended)
4947 * %H%M%S%z => 083748-0600 Local time and the difference from UTC (basic)
4948 * %T%:z => 08:37:48-06:00 Local time and the difference from UTC (extended)
4949 * %Y%m%dT%H%M%S%z => 20071119T083748-0600 Date and time of day for calendar date (basic)
4950 * %FT%T%:z => 2007-11-19T08:37:48-06:00 Date and time of day for calendar date (extended)
4951 * %Y%jT%H%M%S%z => 2007323T083748-0600 Date and time of day for ordinal date (basic)
4952 * %Y-%jT%T%:z => 2007-323T08:37:48-06:00 Date and time of day for ordinal date (extended)
4953 * %GW%V%uT%H%M%S%z => 2007W471T083748-0600 Date and time of day for week date (basic)
4954 * %G-W%V-%uT%T%:z => 2007-W47-1T08:37:48-06:00 Date and time of day for week date (extended)
4955 * %Y%m%dT%H%M => 20071119T0837 Calendar date and local time (basic)
4956 * %FT%R => 2007-11-19T08:37 Calendar date and local time (extended)
4957 * %Y%jT%H%MZ => 2007323T0837Z Ordinal date and UTC of day (basic)
4958 * %Y-%jT%RZ => 2007-323T08:37Z Ordinal date and UTC of day (extended)
4959 * %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic)
4960 * %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended)
4965 time_strftime(VALUE time
, VALUE format
)
4967 struct time_object
*tobj
;
4973 GetTimeval(time
, tobj
);
4974 MAKE_TM_ENSURE(time
, tobj
, tobj
->vtm
.yday
!= 0);
4975 StringValue(format
);
4976 if (!rb_enc_str_asciicompat_p(format
)) {
4977 rb_raise(rb_eArgError
, "format should have ASCII compatible encoding");
4979 tmp
= rb_str_tmp_frozen_acquire(format
);
4980 fmt
= RSTRING_PTR(tmp
);
4981 len
= RSTRING_LEN(tmp
);
4982 enc
= rb_enc_get(format
);
4984 rb_warning("strftime called with empty format string");
4985 return rb_enc_str_new(0, 0, enc
);
4988 VALUE str
= rb_strftime_alloc(fmt
, len
, enc
, time
, &tobj
->vtm
, tobj
->timew
,
4989 TZMODE_UTC_P(tobj
));
4990 rb_str_tmp_frozen_release(format
, tmp
);
4991 if (!str
) rb_raise(rb_eArgError
, "invalid format: %"PRIsVALUE
, format
);
4996 int ruby_marshal_write_long(long x
, char *buf
);
4998 enum {base_dump_size
= 8};
5002 time_mdump(VALUE time
)
5004 struct time_object
*tobj
;
5006 char buf
[base_dump_size
+ sizeof(long) + 1];
5013 VALUE subsecx
, nano
, subnano
, v
, zone
;
5015 VALUE year_extend
= Qnil
;
5016 const int max_year
= 1900+0xffff;
5018 GetTimeval(time
, tobj
);
5020 gmtimew(tobj
->timew
, &vtm
);
5022 if (FIXNUM_P(vtm
.year
)) {
5023 year
= FIX2LONG(vtm
.year
);
5024 if (year
> max_year
) {
5025 year_extend
= INT2FIX(year
- max_year
);
5028 else if (year
< 1900) {
5029 year_extend
= LONG2NUM(1900 - year
);
5034 if (rb_int_positive_p(vtm
.year
)) {
5035 year_extend
= rb_int_minus(vtm
.year
, INT2FIX(max_year
));
5039 year_extend
= rb_int_minus(INT2FIX(1900), vtm
.year
);
5044 subsecx
= vtm
.subsecx
;
5046 nano
= mulquov(subsecx
, INT2FIX(1000000000), INT2FIX(TIME_SCALE
));
5047 divmodv(nano
, INT2FIX(1), &v
, &subnano
);
5052 nano
= addv(LONG2FIX(nsec
), subnano
);
5054 p
= 0x1UL
<< 31 | /* 1 */
5055 TZMODE_UTC_P(tobj
) << 30 | /* 1 */
5056 (year
-1900) << 14 | /* 16 */
5057 (vtm
.mon
-1) << 10 | /* 4 */
5058 vtm
.mday
<< 5 | /* 5 */
5060 s
= (unsigned long)vtm
.min
<< 26 | /* 6 */
5061 vtm
.sec
<< 20 | /* 6 */
5064 for (i
=0; i
<4; i
++) {
5065 buf
[i
] = (unsigned char)p
;
5068 for (i
=4; i
<8; i
++) {
5069 buf
[i
] = (unsigned char)s
;
5073 if (!NIL_P(year_extend
)) {
5075 * Append extended year distance from 1900..(1900+0xffff). In
5076 * each cases, there is no sign as the value is positive. The
5077 * format is length (marshaled long) + little endian packed
5078 * binary (like as Integer).
5080 size_t ysize
= rb_absint_size(year_extend
, NULL
);
5081 char *p
, *const buf_year_extend
= buf
+ base_dump_size
;
5082 if (ysize
> LONG_MAX
||
5083 (i
= ruby_marshal_write_long((long)ysize
, buf_year_extend
)) < 0) {
5084 rb_raise(rb_eArgError
, "year too %s to marshal: %"PRIsVALUE
" UTC",
5085 (year
== 1900 ? "small" : "big"), vtm
.year
);
5087 i
+= base_dump_size
;
5088 str
= rb_str_new(NULL
, i
+ ysize
);
5089 p
= RSTRING_PTR(str
);
5092 rb_integer_pack(year_extend
, p
, ysize
, 1, 0, INTEGER_PACK_LITTLE_ENDIAN
);
5095 str
= rb_str_new(buf
, base_dump_size
);
5097 rb_copy_generic_ivar(str
, time
);
5098 if (!rb_equal(nano
, INT2FIX(0))) {
5099 if (RB_TYPE_P(nano
, T_RATIONAL
)) {
5100 rb_ivar_set(str
, id_nano_num
, RRATIONAL(nano
)->num
);
5101 rb_ivar_set(str
, id_nano_den
, RRATIONAL(nano
)->den
);
5104 rb_ivar_set(str
, id_nano_num
, nano
);
5105 rb_ivar_set(str
, id_nano_den
, INT2FIX(1));
5108 if (nsec
) { /* submicro is only for Ruby 1.9.1 compatibility */
5110 * submicro is formatted in fixed-point packed BCD (without sign).
5111 * It represent digits under microsecond.
5112 * For nanosecond resolution, 3 digits (2 bytes) are used.
5113 * However it can be longer.
5114 * Extra digits are ignored for loading.
5117 int len
= (int)sizeof(buf
);
5118 buf
[1] = (char)((nsec
% 10) << 4);
5120 buf
[0] = (char)(nsec
% 10);
5122 buf
[0] |= (char)((nsec
% 10) << 4);
5125 rb_ivar_set(str
, id_submicro
, rb_str_new(buf
, len
));
5127 if (!TZMODE_UTC_P(tobj
)) {
5128 VALUE off
= rb_time_utc_offset(time
), div
, mod
;
5129 divmodv(off
, INT2FIX(1), &div
, &mod
);
5130 if (rb_equal(mod
, INT2FIX(0)))
5131 off
= rb_Integer(div
);
5132 rb_ivar_set(str
, id_offset
, off
);
5134 zone
= tobj
->vtm
.zone
;
5135 if (maybe_tzobj_p(zone
)) {
5136 zone
= rb_funcallv(zone
, id_name
, 0, 0);
5138 rb_ivar_set(str
, id_zone
, zone
);
5144 time_dump(int argc
, VALUE
*argv
, VALUE time
)
5148 rb_check_arity(argc
, 0, 1);
5149 str
= time_mdump(time
);
5155 mload_findzone(VALUE arg
)
5157 VALUE
*argp
= (VALUE
*)arg
;
5158 VALUE time
= argp
[0], zone
= argp
[1];
5159 return find_timezone(time
, zone
);
5163 mload_zone(VALUE time
, VALUE zone
)
5168 z
= rb_rescue(mload_findzone
, (VALUE
)args
, 0, Qnil
);
5169 if (NIL_P(z
)) return rb_fstring(zone
);
5170 if (RB_TYPE_P(z
, T_STRING
)) return rb_fstring(z
);
5174 long ruby_marshal_read_long(const char **buf
, long len
);
5178 time_mload(VALUE time
, VALUE str
)
5180 struct time_object
*tobj
;
5188 VALUE submicro
, nano_num
, nano_den
, offset
, zone
, year
;
5193 #define get_attr(attr, iffound) \
5194 attr = rb_attr_delete(str, id_##attr); \
5195 if (!NIL_P(attr)) { \
5199 get_attr(nano_num
, {});
5200 get_attr(nano_den
, {});
5201 get_attr(submicro
, {});
5202 get_attr(offset
, (offset
= rb_rescue(validate_utc_offset
, offset
, 0, Qnil
)));
5203 get_attr(zone
, (zone
= rb_rescue(validate_zone_name
, zone
, 0, Qnil
)));
5208 rb_copy_generic_ivar(time
, str
);
5211 buf
= (unsigned char *)RSTRING_PTR(str
);
5212 if (RSTRING_LEN(str
) < base_dump_size
) {
5213 goto invalid_format
;
5217 for (i
=0; i
<4; i
++) {
5218 p
|= (unsigned long)buf
[i
]<<(8*i
);
5220 for (i
=4; i
<8; i
++) {
5221 s
|= (unsigned long)buf
[i
]<<(8*(i
-4));
5224 if ((p
& (1UL<<31)) == 0) {
5230 timew
= wadd(rb_time_magnify(TIMET2WV(sec
)), wmulquoll(WINT2FIXWV(usec
), TIME_SCALE
, 1000000));
5234 gmt
= (int)((p
>> 30) & 0x1);
5237 year
= INT2FIX(((int)(p
>> 14) & 0xffff) + 1900);
5239 if (RSTRING_LEN(str
) > base_dump_size
) {
5240 long len
= RSTRING_LEN(str
) - base_dump_size
;
5243 const char *ybuf
= (const char *)(buf
+= base_dump_size
);
5244 ysize
= ruby_marshal_read_long(&ybuf
, len
);
5245 len
-= ybuf
- (const char *)buf
;
5246 if (ysize
< 0 || ysize
> len
) goto invalid_format
;
5247 year_extend
= rb_integer_unpack(ybuf
, ysize
, 1, 0, INTEGER_PACK_LITTLE_ENDIAN
);
5248 if (year
== INT2FIX(1900)) {
5249 year
= rb_int_minus(year
, year_extend
);
5252 year
= rb_int_plus(year
, year_extend
);
5255 unsigned int mon
= ((int)(p
>> 10) & 0xf); /* 0...12 */
5258 year
= addv(year
, LONG2FIX(1));
5262 vtm
.mday
= (int)(p
>> 5) & 0x1f;
5263 vtm
.hour
= (int) p
& 0x1f;
5264 vtm
.min
= (int)(s
>> 26) & 0x3f;
5265 vtm
.sec
= (int)(s
>> 20) & 0x3f;
5266 vtm
.utc_offset
= INT2FIX(0);
5267 vtm
.yday
= vtm
.wday
= 0;
5269 vtm
.zone
= str_empty
;
5271 usec
= (long)(s
& 0xfffff);
5275 vtm
.subsecx
= mulquov(LONG2FIX(nsec
), INT2FIX(TIME_SCALE
), LONG2FIX(1000000000));
5276 if (nano_num
!= Qnil
) {
5277 VALUE nano
= quov(num_exact(nano_num
), num_exact(nano_den
));
5278 vtm
.subsecx
= addv(vtm
.subsecx
, mulquov(nano
, INT2FIX(TIME_SCALE
), LONG2FIX(1000000000)));
5280 else if (submicro
!= Qnil
) { /* for Ruby 1.9.1 compatibility */
5284 ptr
= (unsigned char*)StringValuePtr(submicro
);
5285 len
= RSTRING_LEN(submicro
);
5288 if (10 <= (digit
= ptr
[0] >> 4)) goto end_submicro
;
5289 nsec
+= digit
* 100;
5290 if (10 <= (digit
= ptr
[0] & 0xf)) goto end_submicro
;
5294 if (10 <= (digit
= ptr
[1] >> 4)) goto end_submicro
;
5297 vtm
.subsecx
= addv(vtm
.subsecx
, mulquov(LONG2FIX(nsec
), INT2FIX(TIME_SCALE
), LONG2FIX(1000000000)));
5300 timew
= timegmw(&vtm
);
5303 GetNewTimeval(time
, tobj
);
5304 tobj
->tzmode
= TIME_TZMODE_LOCALTIME
;
5306 tobj
->timew
= timew
;
5308 TZMODE_SET_UTC(tobj
);
5310 else if (!NIL_P(offset
)) {
5311 time_set_utc_offset(time
, offset
);
5315 zone
= mload_zone(time
, zone
);
5316 tobj
->vtm
.zone
= zone
;
5317 zone_localtime(zone
, time
);
5323 rb_raise(rb_eTypeError
, "marshaled time format differ");
5324 UNREACHABLE_RETURN(Qundef
);
5329 time_load(VALUE klass
, VALUE str
)
5331 VALUE time
= time_s_alloc(klass
);
5333 time_mload(time
, str
);
5338 /* Document-class: Time::tm
5340 * A container class for timezone conversion.
5346 * Time::tm.from_time(t) -> tm
5348 * Creates new Time::tm object from a Time object.
5352 tm_from_time(VALUE klass
, VALUE time
)
5354 struct time_object
*tobj
;
5358 struct time_object
*ttm
;
5360 GetTimeval(time
, tobj
);
5361 tm
= time_s_alloc(klass
);
5364 GMTIMEW(ttm
->timew
= tobj
->timew
, v
);
5365 ttm
->timew
= wsub(ttm
->timew
, v
->subsecx
);
5366 v
->subsecx
= INT2FIX(0);
5370 TZMODE_SET_UTC(ttm
);
5376 GetTimeval(time
, tobj
);
5377 if (tobj
->tm_got
&& TZMODE_UTC_P(tobj
))
5380 GMTIMEW(tobj
->timew
, v
= &vtm
);
5381 args
[i
++] = v
->year
;
5382 args
[i
++] = INT2FIX(v
->mon
);
5383 args
[i
++] = INT2FIX(v
->mday
);
5384 args
[i
++] = INT2FIX(v
->hour
);
5385 args
[i
++] = INT2FIX(v
->min
);
5386 args
[i
++] = INT2FIX(v
->sec
);
5388 case 0: args
[i
++] = Qfalse
; break;
5389 case 1: args
[i
++] = Qtrue
; break;
5390 default: args
[i
++] = Qnil
; break;
5392 args
[i
++] = w2v(rb_time_unmagnify(tobj
->timew
));
5393 return rb_class_new_instance(i
, args
, klass
);
5400 * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm
5402 * Creates new Time::tm object.
5406 tm_initialize(int argc
, VALUE
*argv
, VALUE tm
)
5411 if (rb_check_arity(argc
, 1, 7) > 6) argc
= 6;
5412 time_arg(argc
, argv
, &vtm
);
5416 struct time_object
*tobj
= DATA_PTR(tm
);
5417 tobj
->tzmode
= TIME_TZMODE_UTC
;
5422 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.sec
));
5423 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.min
));
5424 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.hour
));
5425 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.mday
));
5426 RSTRUCT_SET(tm
, i
++, INT2FIX(vtm
.mon
));
5427 RSTRUCT_SET(tm
, i
++, vtm
.year
);
5428 RSTRUCT_SET(tm
, i
++, w2v(rb_time_unmagnify(t
)));
5436 * tm.to_time -> time
5438 * Returns a new Time object.
5442 tm_to_time(VALUE tm
)
5445 struct time_object
*torig
= get_timeval(tm
);
5446 VALUE dup
= time_s_alloc(rb_cTime
);
5447 struct time_object
*tobj
= DATA_PTR(dup
);
5452 const VALUE
*p
= RSTRUCT_CONST_PTR(tm
);
5455 for (i
= 0; i
< numberof(t
); ++i
) {
5456 t
[i
] = p
[numberof(t
) - 1 - i
];
5458 return time_s_mkutc(numberof(t
), t
, rb_cTime
);
5469 #define tm_subsec tm_zero
5470 #define tm_utc_offset tm_zero
5481 const VALUE
*p
= RSTRUCT_CONST_PTR(tm
);
5483 return rb_sprintf("%.4"PRIsVALUE
"-%.2"PRIsVALUE
"-%.2"PRIsVALUE
" "
5484 "%.2"PRIsVALUE
":%.2"PRIsVALUE
":%.2"PRIsVALUE
" "
5486 p
[5], p
[4], p
[3], p
[2], p
[1], p
[0]);
5490 tm_plus(VALUE tm
, VALUE offset
)
5492 return time_add0(rb_obj_class(tm
), get_timeval(tm
), tm
, offset
, +1);
5496 tm_minus(VALUE tm
, VALUE offset
)
5498 return time_add0(rb_obj_class(tm
), get_timeval(tm
), tm
, offset
, -1);
5503 Init_tm(VALUE outer
, const char *name
)
5508 tm
= rb_define_class_under(outer
, name
, rb_cObject
);
5509 rb_define_alloc_func(tm
, time_s_alloc
);
5510 rb_define_method(tm
, "sec", time_sec
, 0);
5511 rb_define_method(tm
, "min", time_min
, 0);
5512 rb_define_method(tm
, "hour", time_hour
, 0);
5513 rb_define_method(tm
, "mday", time_mday
, 0);
5514 rb_define_method(tm
, "day", time_mday
, 0);
5515 rb_define_method(tm
, "mon", time_mon
, 0);
5516 rb_define_method(tm
, "month", time_mon
, 0);
5517 rb_define_method(tm
, "year", time_year
, 0);
5518 rb_define_method(tm
, "isdst", time_isdst
, 0);
5519 rb_define_method(tm
, "dst?", time_isdst
, 0);
5520 rb_define_method(tm
, "zone", time_zone
, 0);
5521 rb_define_method(tm
, "gmtoff", rb_time_utc_offset
, 0);
5522 rb_define_method(tm
, "gmt_offset", rb_time_utc_offset
, 0);
5523 rb_define_method(tm
, "utc_offset", rb_time_utc_offset
, 0);
5524 rb_define_method(tm
, "utc?", time_utc_p
, 0);
5525 rb_define_method(tm
, "gmt?", time_utc_p
, 0);
5526 rb_define_method(tm
, "to_s", time_to_s
, 0);
5527 rb_define_method(tm
, "inspect", time_inspect
, 0);
5528 rb_define_method(tm
, "to_a", time_to_a
, 0);
5529 rb_define_method(tm
, "tv_sec", time_to_i
, 0);
5530 rb_define_method(tm
, "tv_usec", time_usec
, 0);
5531 rb_define_method(tm
, "usec", time_usec
, 0);
5532 rb_define_method(tm
, "tv_nsec", time_nsec
, 0);
5533 rb_define_method(tm
, "nsec", time_nsec
, 0);
5534 rb_define_method(tm
, "subsec", time_subsec
, 0);
5535 rb_define_method(tm
, "to_i", time_to_i
, 0);
5536 rb_define_method(tm
, "to_f", time_to_f
, 0);
5537 rb_define_method(tm
, "to_r", time_to_r
, 0);
5538 rb_define_method(tm
, "+", tm_plus
, 1);
5539 rb_define_method(tm
, "-", tm_minus
, 1);
5541 tm
= rb_struct_define_under(outer
, "tm",
5542 "sec", "min", "hour",
5543 "mday", "mon", "year",
5545 rb_define_method(tm
, "subsec", tm_subsec
, 0);
5546 rb_define_method(tm
, "utc_offset", tm_utc_offset
, 0);
5547 rb_define_method(tm
, "to_s", tm_to_s
, 0);
5548 rb_define_method(tm
, "inspect", tm_to_s
, 0);
5549 rb_define_method(tm
, "isdst", tm_isdst
, 0);
5550 rb_define_method(tm
, "dst?", tm_isdst
, 0);
5552 rb_define_method(tm
, "initialize", tm_initialize
, -1);
5553 rb_define_method(tm
, "utc", tm_to_time
, 0);
5554 rb_alias(tm
, rb_intern_const("to_time"), rb_intern_const("utc"));
5555 rb_define_singleton_method(tm
, "from_time", tm_from_time
, 1);
5562 rb_time_zone_abbreviation(VALUE zone
, VALUE time
)
5564 VALUE tm
, abbr
, strftime_args
[2];
5566 abbr
= rb_check_string_type(zone
);
5567 if (!NIL_P(abbr
)) return abbr
;
5569 tm
= tm_from_time(rb_cTimeTM
, time
);
5570 abbr
= rb_check_funcall(zone
, rb_intern("abbr"), 1, &tm
);
5571 if (abbr
!= Qundef
) {
5574 #ifdef SUPPORT_TZINFO_ZONE_ABBREVIATION
5575 abbr
= rb_check_funcall(zone
, rb_intern("period_for_utc"), 1, &tm
);
5576 if (abbr
!= Qundef
) {
5577 abbr
= rb_funcallv(abbr
, rb_intern("abbreviation"), 0, 0);
5581 strftime_args
[0] = rb_fstring_lit("%Z");
5582 strftime_args
[1] = tm
;
5583 abbr
= rb_check_funcall(zone
, rb_intern("strftime"), 2, strftime_args
);
5584 if (abbr
!= Qundef
) {
5587 abbr
= rb_check_funcall_default(zone
, idName
, 0, 0, Qnil
);
5589 return rb_obj_as_string(abbr
);
5592 /* Internal Details:
5594 * Since Ruby 1.9.2, Time implementation uses a signed 63 bit integer or
5595 * Integer(T_BIGNUM), Rational.
5596 * The integer is a number of nanoseconds since the _Epoch_ which can
5597 * represent 1823-11-12 to 2116-02-20.
5598 * When Integer(T_BIGNUM) or Rational is used (before 1823, after 2116, under
5599 * nanosecond), Time works slower than when integer is used.
5606 id_submicro
= rb_intern_const("submicro");
5607 id_nano_num
= rb_intern_const("nano_num");
5608 id_nano_den
= rb_intern_const("nano_den");
5609 id_offset
= rb_intern_const("offset");
5610 id_zone
= rb_intern_const("zone");
5611 id_nanosecond
= rb_intern_const("nanosecond");
5612 id_microsecond
= rb_intern_const("microsecond");
5613 id_millisecond
= rb_intern_const("millisecond");
5614 id_nsec
= rb_intern_const("nsec");
5615 id_usec
= rb_intern_const("usec");
5616 id_local_to_utc
= rb_intern_const("local_to_utc");
5617 id_utc_to_local
= rb_intern_const("utc_to_local");
5618 id_year
= rb_intern_const("year");
5619 id_mon
= rb_intern_const("mon");
5620 id_mday
= rb_intern_const("mday");
5621 id_hour
= rb_intern_const("hour");
5622 id_min
= rb_intern_const("min");
5623 id_sec
= rb_intern_const("sec");
5624 id_isdst
= rb_intern_const("isdst");
5625 id_find_timezone
= rb_intern_const("find_timezone");
5627 str_utc
= rb_fstring_lit("UTC");
5628 rb_gc_register_mark_object(str_utc
);
5629 str_empty
= rb_fstring_lit("");
5630 rb_gc_register_mark_object(str_empty
);
5632 rb_cTime
= rb_define_class("Time", rb_cObject
);
5633 rb_include_module(rb_cTime
, rb_mComparable
);
5635 rb_define_alloc_func(rb_cTime
, time_s_alloc
);
5636 rb_define_singleton_method(rb_cTime
, "utc", time_s_mkutc
, -1);
5637 rb_define_singleton_method(rb_cTime
, "local", time_s_mktime
, -1);
5638 rb_define_alias(rb_singleton_class(rb_cTime
), "gm", "utc");
5639 rb_define_alias(rb_singleton_class(rb_cTime
), "mktime", "local");
5641 rb_define_method(rb_cTime
, "to_i", time_to_i
, 0);
5642 rb_define_method(rb_cTime
, "to_f", time_to_f
, 0);
5643 rb_define_method(rb_cTime
, "to_r", time_to_r
, 0);
5644 rb_define_method(rb_cTime
, "<=>", time_cmp
, 1);
5645 rb_define_method(rb_cTime
, "eql?", time_eql
, 1);
5646 rb_define_method(rb_cTime
, "hash", time_hash
, 0);
5647 rb_define_method(rb_cTime
, "initialize_copy", time_init_copy
, 1);
5649 rb_define_method(rb_cTime
, "localtime", time_localtime_m
, -1);
5650 rb_define_method(rb_cTime
, "gmtime", time_gmtime
, 0);
5651 rb_define_method(rb_cTime
, "utc", time_gmtime
, 0);
5652 rb_define_method(rb_cTime
, "getlocal", time_getlocaltime
, -1);
5653 rb_define_method(rb_cTime
, "getgm", time_getgmtime
, 0);
5654 rb_define_method(rb_cTime
, "getutc", time_getgmtime
, 0);
5656 rb_define_method(rb_cTime
, "ctime", time_asctime
, 0);
5657 rb_define_method(rb_cTime
, "asctime", time_asctime
, 0);
5658 rb_define_method(rb_cTime
, "to_s", time_to_s
, 0);
5659 rb_define_method(rb_cTime
, "inspect", time_inspect
, 0);
5660 rb_define_method(rb_cTime
, "to_a", time_to_a
, 0);
5662 rb_define_method(rb_cTime
, "+", time_plus
, 1);
5663 rb_define_method(rb_cTime
, "-", time_minus
, 1);
5665 rb_define_method(rb_cTime
, "round", time_round
, -1);
5666 rb_define_method(rb_cTime
, "floor", time_floor
, -1);
5667 rb_define_method(rb_cTime
, "ceil", time_ceil
, -1);
5669 rb_define_method(rb_cTime
, "sec", time_sec
, 0);
5670 rb_define_method(rb_cTime
, "min", time_min
, 0);
5671 rb_define_method(rb_cTime
, "hour", time_hour
, 0);
5672 rb_define_method(rb_cTime
, "mday", time_mday
, 0);
5673 rb_define_method(rb_cTime
, "day", time_mday
, 0);
5674 rb_define_method(rb_cTime
, "mon", time_mon
, 0);
5675 rb_define_method(rb_cTime
, "month", time_mon
, 0);
5676 rb_define_method(rb_cTime
, "year", time_year
, 0);
5677 rb_define_method(rb_cTime
, "wday", time_wday
, 0);
5678 rb_define_method(rb_cTime
, "yday", time_yday
, 0);
5679 rb_define_method(rb_cTime
, "isdst", time_isdst
, 0);
5680 rb_define_method(rb_cTime
, "dst?", time_isdst
, 0);
5681 rb_define_method(rb_cTime
, "zone", time_zone
, 0);
5682 rb_define_method(rb_cTime
, "gmtoff", rb_time_utc_offset
, 0);
5683 rb_define_method(rb_cTime
, "gmt_offset", rb_time_utc_offset
, 0);
5684 rb_define_method(rb_cTime
, "utc_offset", rb_time_utc_offset
, 0);
5686 rb_define_method(rb_cTime
, "utc?", time_utc_p
, 0);
5687 rb_define_method(rb_cTime
, "gmt?", time_utc_p
, 0);
5689 rb_define_method(rb_cTime
, "sunday?", time_sunday
, 0);
5690 rb_define_method(rb_cTime
, "monday?", time_monday
, 0);
5691 rb_define_method(rb_cTime
, "tuesday?", time_tuesday
, 0);
5692 rb_define_method(rb_cTime
, "wednesday?", time_wednesday
, 0);
5693 rb_define_method(rb_cTime
, "thursday?", time_thursday
, 0);
5694 rb_define_method(rb_cTime
, "friday?", time_friday
, 0);
5695 rb_define_method(rb_cTime
, "saturday?", time_saturday
, 0);
5697 rb_define_method(rb_cTime
, "tv_sec", time_to_i
, 0);
5698 rb_define_method(rb_cTime
, "tv_usec", time_usec
, 0);
5699 rb_define_method(rb_cTime
, "usec", time_usec
, 0);
5700 rb_define_method(rb_cTime
, "tv_nsec", time_nsec
, 0);
5701 rb_define_method(rb_cTime
, "nsec", time_nsec
, 0);
5702 rb_define_method(rb_cTime
, "subsec", time_subsec
, 0);
5704 rb_define_method(rb_cTime
, "strftime", time_strftime
, 1);
5706 /* methods for marshaling */
5707 rb_define_private_method(rb_cTime
, "_dump", time_dump
, -1);
5708 rb_define_private_method(rb_singleton_class(rb_cTime
), "_load", time_load
, 1);
5710 /* Time will support marshal_dump and marshal_load in the future (1.9 maybe) */
5711 rb_define_private_method(rb_cTime
, "marshal_dump", time_mdump
, 0);
5712 rb_define_private_method(rb_cTime
, "marshal_load", time_mload
, 1);
5715 if (debug_find_time_numguess
) {
5716 rb_define_hooked_variable("$find_time_numguess", (VALUE
*)&find_time_numguess
,
5717 find_time_numguess_getter
, NULL
);
5720 rb_cTimeTM
= Init_tm(rb_cTime
, "tm");
5723 #include "timev.rbinc"