* 2022-01-18 [ci skip]
[ruby-80x24.org.git] / time.c
blob3a004f876354b1cdf0015de01c5f5b81eb948896
1 /**********************************************************************
3 time.c -
5 $Author$
6 created at: Tue Dec 28 14:31:59 JST 1993
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
10 **********************************************************************/
12 #define _DEFAULT_SOURCE
13 #define _BSD_SOURCE
14 #include "ruby/internal/config.h"
16 #include <errno.h>
17 #include <float.h>
18 #include <math.h>
19 #include <time.h>
20 #include <sys/types.h>
22 #ifdef HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
26 #ifdef HAVE_STRINGS_H
27 # include <strings.h>
28 #endif
30 #if defined(HAVE_SYS_TIME_H)
31 # include <sys/time.h>
32 #endif
34 #include "id.h"
35 #include "internal.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"
44 #include "timev.h"
46 #include "builtin.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;
54 #define id_quo idQuo
55 #define id_div idDiv
56 #define id_divmod idDivmod
57 #define id_name idName
58 #define UTC_ZONE Qundef
60 #ifndef TM_IS_TIME
61 #define TM_IS_TIME 1
62 #endif
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)
71 static int
72 eq(VALUE x, VALUE y)
74 if (FIXNUM_P(x) && FIXNUM_P(y)) {
75 return x == y;
77 return RTEST(rb_funcall(x, idEq, 1, y));
80 static int
81 cmp(VALUE x, VALUE y)
83 if (FIXNUM_P(x) && FIXNUM_P(y)) {
84 if ((long)x < (long)y)
85 return -1;
86 if ((long)x > (long)y)
87 return 1;
88 return 0;
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)
100 static VALUE
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);
110 static VALUE
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);
120 static VALUE
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);
131 static VALUE
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);
142 static VALUE
143 modv(VALUE x, VALUE y)
145 if (FIXNUM_P(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)))
155 static VALUE
156 quor(VALUE x, VALUE y)
158 if (FIXNUM_P(x) && FIXNUM_P(y)) {
159 long a, b, c;
160 a = FIX2LONG(x);
161 b = FIX2LONG(y);
162 if (b == 0) rb_num_zerodiv();
163 if (a == FIXNUM_MIN && b == -1) return LONG2NUM(-a);
164 c = a / b;
165 if (c * b == a) {
166 return LONG2FIX(c);
169 return rb_numeric_quo(x, y);
172 static VALUE
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;
180 return ret;
183 #define mulquov(x,y,z) (((y) == (z)) ? (x) : quov(mulv((x),(y)),(z)))
185 static void
186 divmodv(VALUE n, VALUE d, VALUE *q, VALUE *r)
188 VALUE tmp, ary;
189 if (FIXNUM_P(d)) {
190 if (FIX2LONG(d) == 0) rb_num_zerodiv();
191 if (FIXNUM_P(n)) {
192 rb_fix_divmod_fix(n, d, q, r);
193 return;
196 tmp = rb_funcall(n, id_divmod, 1, d);
197 ary = rb_check_array_type(tmp);
198 if (NIL_P(ary)) {
199 rb_raise(rb_eTypeError, "unexpected divmod result: into %"PRIsVALUE,
200 rb_obj_class(tmp));
202 *q = rb_ary_entry(ary, 0);
203 *r = rb_ary_entry(ary, 1);
206 #if SIZEOF_LONG == 8
207 # define INT64toNUM(x) LONG2NUM(x)
208 #elif defined(HAVE_LONG_LONG) && SIZEOF_LONG_LONG == 8
209 # define INT64toNUM(x) LL2NUM(x)
210 #endif
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))
229 #else
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))
244 #endif
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 */
254 typedef struct {
255 WIDEVALUE value;
256 } wideval_t;
257 static inline wideval_t WIDEVAL_WRAP(WIDEVALUE v) { wideval_t w = { v }; return w; }
258 # define WIDEVAL_GET(w) ((w).value)
259 #else
260 typedef WIDEVALUE wideval_t;
261 # define WIDEVAL_WRAP(v) (v)
262 # define WIDEVAL_GET(w) (w)
263 #endif
265 #if WIDEVALUE_IS_WIDER
266 static inline wideval_t
267 wint2wv(wideint_t wi)
269 if (FIXWVABLE(wi))
270 return WINT2FIXWV(wi);
271 else
272 return WIDEVAL_WRAP(INT64toNUM(wi));
274 # define WINT2WV(wi) wint2wv(wi)
275 #else
276 # define WINT2WV(wi) WIDEVAL_WRAP(LONG2NUM(wi))
277 #endif
279 static inline VALUE
280 w2v(wideval_t w)
282 #if WIDEVALUE_IS_WIDER
283 if (FIXWV_P(w))
284 return INT64toNUM(FIXWV2WINT(w));
285 return (VALUE)WIDEVAL_GET(w);
286 #else
287 return WIDEVAL_GET(w);
288 #endif
291 #if WIDEVALUE_IS_WIDER
292 static wideval_t
293 v2w_bignum(VALUE v)
295 int sign;
296 uwideint_t u;
297 sign = rb_integer_pack(v, &u, 1, sizeof(u), 0,
298 INTEGER_PACK_NATIVE_BYTE_ORDER);
299 if (sign == 0)
300 return WINT2FIXWV(0);
301 else if (sign == -1) {
302 if (u <= -FIXWV_MIN)
303 return WINT2FIXWV(-(wideint_t)u);
305 else if (sign == +1) {
306 if (u <= FIXWV_MAX)
307 return WINT2FIXWV((wideint_t)u);
309 return WIDEVAL_WRAP(v);
311 #endif
313 static inline wideval_t
314 v2w(VALUE v)
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
322 if (FIXNUM_P(v)) {
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);
329 #endif
330 return WIDEVAL_WRAP(v);
333 static int
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)));
341 #else
342 return eq(WIDEVAL_GET(wx), WIDEVAL_GET(wy));
343 #endif
346 static int
347 wcmp(wideval_t wx, wideval_t wy)
349 VALUE x, y;
350 #if WIDEVALUE_IS_WIDER
351 if (FIXWV_P(wx) && FIXWV_P(wy)) {
352 wideint_t a, b;
353 a = FIXWV2WINT(wx);
354 b = FIXWV2WINT(wy);
355 if (a < b)
356 return -1;
357 if (a > b)
358 return 1;
359 return 0;
361 #endif
362 x = w2v(wx);
363 y = w2v(wy);
364 return cmp(x, y);
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)
373 static wideval_t
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);
379 return WINT2WV(r);
381 #endif
382 return v2w(addv(w2v(wx), w2v(wy)));
385 static wideval_t
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);
391 return WINT2WV(r);
393 #endif
394 return v2w(subv(w2v(wx), w2v(wy)));
397 static wideval_t
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));
405 #endif
406 return v2w(mulv(w2v(wx), w2v(wy)));
409 static wideval_t
410 wquo(wideval_t wx, wideval_t wy)
412 #if WIDEVALUE_IS_WIDER
413 if (FIXWV_P(wx) && FIXWV_P(wy)) {
414 wideint_t a, b, c;
415 a = FIXWV2WINT(wx);
416 b = FIXWV2WINT(wy);
417 if (b == 0) rb_num_zerodiv();
418 c = a / b;
419 if (c * b == a) {
420 return WINT2WV(c);
423 #endif
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
431 static int
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;
436 d = FIXWV2WINT(wd);
437 if (d == 0) rb_num_zerodiv();
438 if (d == 1) {
439 *wq = wn;
440 *wr = WINT2FIXWV(0);
441 return 1;
443 if (d == -1) {
444 wideint_t xneg = -FIXWV2WINT(wn);
445 *wq = WINT2WV(xneg);
446 *wr = WINT2FIXWV(0);
447 return 1;
449 n = FIXWV2WINT(wn);
450 if (n == 0) {
451 *wq = WINT2FIXWV(0);
452 *wr = WINT2FIXWV(0);
453 return 1;
455 q = n / d;
456 r = n % d;
457 if (d > 0 ? r < 0 : r > 0) {
458 q -= 1;
459 r += d;
461 *wq = WINT2FIXWV(q);
462 *wr = WINT2FIXWV(r);
463 return 1;
465 return 0;
467 #endif
469 static void
470 wdivmod(wideval_t wn, wideval_t wd, wideval_t *wq, wideval_t *wr)
472 VALUE vq, vr;
473 #if WIDEVALUE_IS_WIDER
474 if (wdivmod0(wn, wd, wq, wr)) return;
475 #endif
476 divmodv(w2v(wn), w2v(wd), &vq, &vr);
477 *wq = v2w(vq);
478 *wr = v2w(vr);
481 static void
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)) {
485 *wq = wx;
486 *wr = WINT2FIXWV(0);
487 return;
489 wdivmod(wmul(wx,wy), wz, wq, wr);
492 static wideval_t
493 wdiv(wideval_t wx, wideval_t wy)
495 #if WIDEVALUE_IS_WIDER
496 wideval_t q, dmy;
497 if (wdivmod0(wx, wy, &q, &dmy)) return q;
498 #endif
499 return v2w(divv(w2v(wx), w2v(wy)));
502 static wideval_t
503 wmod(wideval_t wx, wideval_t wy)
505 #if WIDEVALUE_IS_WIDER
506 wideval_t r, dmy;
507 if (wdivmod0(wx, wy, &dmy, &r)) return r;
508 #endif
509 return v2w(modv(w2v(wx), w2v(wy)));
512 static VALUE
513 num_exact(VALUE v)
515 VALUE tmp;
517 switch (TYPE(v)) {
518 case T_FIXNUM:
519 case T_BIGNUM:
520 return v;
522 case T_RATIONAL:
523 return rb_rational_canonicalize(v);
525 default:
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)) {
530 /* FALLTHROUGH */
532 else if (RB_INTEGER_TYPE_P(tmp)) {
533 return 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))) {
540 return tmp;
543 case T_NIL:
544 case T_STRING:
545 rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into an exact number",
546 rb_obj_class(v));
550 /* time_t */
552 static wideval_t
553 rb_time_magnify(wideval_t w)
555 return wmul(w, WINT2FIXWV(TIME_SCALE));
558 static VALUE
559 rb_time_unmagnify_to_rational(wideval_t w)
561 return quor(w2v(w), INT2FIX(TIME_SCALE));
564 static wideval_t
565 rb_time_unmagnify(wideval_t w)
567 return v2w(rb_time_unmagnify_to_rational(w));
570 static VALUE
571 rb_time_unmagnify_to_float(wideval_t w)
573 VALUE v;
574 #if WIDEVALUE_IS_WIDER
575 if (FIXWV_P(w)) {
576 wideint_t a, b, c;
577 a = FIXWV2WINT(w);
578 b = TIME_SCALE;
579 c = a / b;
580 if (c * b == a) {
581 return DBL2NUM((double)c);
583 v = DBL2NUM((double)FIXWV2WINT(w));
584 return quov(v, DBL2NUM(TIME_SCALE));
586 #endif
587 v = w2v(w);
588 if (RB_TYPE_P(v, T_RATIONAL))
589 return rb_Float(quov(v, INT2FIX(TIME_SCALE)));
590 else
591 return quov(v, DBL2NUM(TIME_SCALE));
594 static void
595 split_second(wideval_t timew, wideval_t *timew_p, VALUE *subsecx_p)
597 wideval_t q, r;
598 wdivmod(timew, WINT2FIXWV(TIME_SCALE), &q, &r);
599 *timew_p = q;
600 *subsecx_p = w2v(r);
603 static wideval_t
604 timet2wv(time_t t)
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);
613 else {
614 wideint_t wi = (wideint_t)t;
615 if (FIXWV_MIN <= wi && wi <= FIXWV_MAX) {
616 return WINT2FIXWV(wi);
619 #endif
620 return v2w(TIMET2NUM(t));
622 #define TIMET2WV(t) timet2wv(t)
624 static time_t
625 wv2timet(wideval_t w)
627 #if WIDEVALUE_IS_WIDER
628 if (FIXWV_P(w)) {
629 wideint_t wi = FIXWV2WINT(w);
630 if (TIMET_MIN == 0) {
631 if (wi < 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'");
636 else {
637 if (wi < TIMET_MIN || TIMET_MAX < wi)
638 rb_raise(rb_eRangeError, "too big to convert into `time_t'");
640 return (time_t)wi;
642 #endif
643 return NUM2TIMET(w2v(w));
645 #define WV2TIMET(t) wv2timet(t)
647 VALUE rb_cTime;
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;
678 void
679 ruby_reset_timezone(void)
681 ruby_tz_uptodate_p = false;
682 ruby_reset_leap_second_info();
685 static void
686 update_tz(void)
688 if (ruby_tz_uptodate_p) return;
689 ruby_tz_uptodate_p = true;
690 tzset();
693 static struct tm *
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;
698 #endif
699 update_tz();
700 #ifdef HAVE_GMTIME_R
701 result = localtime_r(t, result);
702 #else
704 struct tm *tmp = localtime(t);
705 if (tmp) *result = *tmp;
707 #endif
708 #if defined(HAVE_MKTIME) && defined(LOCALTIME_OVERFLOW_PROBLEM)
709 if (result) {
710 long gmtoff1 = 0;
711 long gmtoff2 = 0;
712 struct tm tmp = *result;
713 time_t t2;
714 t2 = mktime(&tmp);
715 # if defined(HAVE_STRUCT_TM_TM_GMTOFF)
716 gmtoff1 = result->tm_gmtoff;
717 gmtoff2 = tmp.tm_gmtoff;
718 # endif
719 if (*t + gmtoff1 != t2 + gmtoff2)
720 result = NULL;
722 #endif
723 return result;
725 #define LOCALTIME(tm, result) rb_localtime_r((tm), &(result))
727 #ifndef HAVE_STRUCT_TM_TM_GMTOFF
728 static struct tm *
729 rb_gmtime_r(const time_t *t, struct tm *result)
731 #ifdef HAVE_GMTIME_R
732 result = gmtime_r(t, result);
733 #else
734 struct tm *tmp = gmtime(t);
735 if (tmp) *result = *tmp;
736 #endif
737 #if defined(HAVE_TIMEGM) && defined(LOCALTIME_OVERFLOW_PROBLEM)
738 if (result && *t != timegm(result)) {
739 return NULL;
741 #endif
742 return result;
744 # define GMTIME(tm, result) rb_gmtime_r((tm), &(result))
745 #endif
747 static const int16_t common_year_yday_offset[] = {
749 -1 + 31,
750 -1 + 31 + 28,
751 -1 + 31 + 28 + 31,
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[] = {
764 -1 + 31,
765 -1 + 31 + 29,
766 -1 + 31 + 29 + 31,
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))
789 #define M28(m) \
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)
793 #define M29(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)
797 #define M30(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)
801 #define M31(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)
815 #undef M28
816 #undef M29
817 #undef M30
818 #undef M31
820 #define D28 \
821 1,2,3,4,5,6,7,8,9, \
822 10,11,12,13,14,15,16,17,18,19, \
823 20,21,22,23,24,25,26,27,28
824 #define D29 \
825 1,2,3,4,5,6,7,8,9, \
826 10,11,12,13,14,15,16,17,18,19, \
827 20,21,22,23,24,25,26,27,28,29
828 #define D30 \
829 1,2,3,4,5,6,7,8,9, \
830 10,11,12,13,14,15,16,17,18,19, \
831 20,21,22,23,24,25,26,27,28,29,30
832 #define D31 \
833 1,2,3,4,5,6,7,8,9, \
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
845 #undef D28
846 #undef D29
847 #undef D30
848 #undef D31
850 static int
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];
858 else
859 tm_yday += common_year_yday_offset[tm_mon];
861 return tm_yday;
864 static wideval_t
865 timegmw_noleapsecond(struct vtm *vtm)
867 VALUE year1900;
868 VALUE q400, r400;
869 int year_mod400;
870 int yday;
871 long days_in400;
872 VALUE vdays, ret;
873 wideval_t wret;
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
889 + vtm->min*60
890 + vtm->hour*3600);
891 days_in400 = yday
892 - 70*365
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));
902 return wret;
905 static VALUE
906 zone_str(const char *zone)
908 const char *p;
909 int ascii_only = 1;
910 VALUE str;
911 size_t len;
913 if (zone == NULL) {
914 return rb_fstring_lit("(NO-TIMEZONE-ABBREVIATION)");
917 for (p = zone; *p; p++)
918 if (!ISASCII(*p)) {
919 ascii_only = 0;
920 break;
922 len = p - zone + strlen(p);
923 if (ascii_only) {
924 str = rb_usascii_str_new(zone, len);
926 else {
927 str = rb_enc_str_new(zone, len, rb_locale_encoding());
929 return rb_fstring(str);
932 static void
933 gmtimew_noleapsecond(wideval_t timew, struct vtm *vtm)
935 VALUE v;
936 int n, x, y;
937 int wday;
938 VALUE timev;
939 wideval_t timew2, w, w2;
940 VALUE subsecx;
942 vtm->isdst = 0;
944 split_second(timew, &timew2, &subsecx);
945 vtm->subsecx = subsecx;
947 wdivmod(timew2, WINT2FIXWV(86400), &w2, &w);
948 timev = w2v(w2);
949 v = w2v(w);
951 wday = NUM2INT(modv(timev, INT2FIX(7)));
952 vtm->wday = (wday + 4) % 7;
954 n = NUM2INT(v);
955 vtm->sec = n % 60; n = n / 60;
956 vtm->min = n % 60; n = n / 60;
957 vtm->hour = n;
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. */
966 n = NUM2INT(v);
967 y = 1970;
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 */
975 if (n < 31*365+8) {
976 /* 2000-02-29 to 2000-12-31 */
977 y += 30;
978 n -= 30*365+7;
979 goto found;
981 else {
982 /* 2001-01-01 or after */
983 n -= 1;
987 x = n / (365*100 + 24);
988 n = n % (365*100 + 24);
989 y += x * 100;
990 if (30*365+7+31+29-1 <= n) {
991 if (n < 31*365+7) {
992 y += 30;
993 n -= 30*365+7;
994 goto found;
996 else
997 n += 1;
1000 x = n / (365*4 + 1);
1001 n = n % (365*4 + 1);
1002 y += x * 4;
1003 if (365*2+31+29-1 <= n) {
1004 if (n < 365*2+366) {
1005 y += 2;
1006 n -= 365*2;
1007 goto found;
1009 else
1010 n -= 1;
1013 x = n / 365;
1014 n = n % 365;
1015 y += x;
1017 found:
1018 vtm->yday = n+1;
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];
1025 else {
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;
1034 static struct tm *
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. */
1039 struct tm *t;
1040 int sign;
1041 int gmtoff_sec, gmtoff_min, gmtoff_hour, gmtoff_day;
1042 long gmtoff;
1043 t = LOCALTIME(timep, *result);
1044 if (t == NULL)
1045 return NULL;
1047 /* subtract gmtoff */
1048 if (t->tm_gmtoff < 0) {
1049 sign = 1;
1050 gmtoff = -t->tm_gmtoff;
1052 else {
1053 sign = -1;
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 */
1062 gmtoff_sec *= sign;
1063 gmtoff_min *= sign;
1064 gmtoff_hour *= sign;
1066 gmtoff_day = 0;
1068 if (gmtoff_sec) {
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;
1074 gmtoff_min -= 1;
1076 if (60 <= result->tm_sec) {
1077 result->tm_sec -= 60;
1078 gmtoff_min += 1;
1081 if (gmtoff_min) {
1082 result->tm_min += gmtoff_min;
1083 if (result->tm_min < 0) {
1084 result->tm_min += 60;
1085 gmtoff_hour -= 1;
1087 if (60 <= result->tm_min) {
1088 result->tm_min -= 60;
1089 gmtoff_hour += 1;
1092 if (gmtoff_hour) {
1093 result->tm_hour += gmtoff_hour;
1094 if (result->tm_hour < 0) {
1095 result->tm_hour += 24;
1096 gmtoff_day = -1;
1098 if (24 <= result->tm_hour) {
1099 result->tm_hour -= 24;
1100 gmtoff_day = 1;
1104 if (gmtoff_day) {
1105 if (gmtoff_day < 0) {
1106 if (result->tm_yday == 0) {
1107 result->tm_mday = 31;
1108 result->tm_mon = 11; /* December */
1109 result->tm_year--;
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);
1114 result->tm_mon--;
1115 result->tm_mday = days_in_month[result->tm_mon];
1116 result->tm_yday--;
1118 else {
1119 result->tm_mday--;
1120 result->tm_yday--;
1122 result->tm_wday = (result->tm_wday + 6) % 7;
1124 else {
1125 int leap = leap_year_p(result->tm_year + 1900);
1126 if (result->tm_yday == (leap ? 365 : 364)) {
1127 result->tm_year++;
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]) {
1133 result->tm_mon++;
1134 result->tm_mday = 1;
1135 result->tm_yday++;
1137 else {
1138 result->tm_mday++;
1139 result->tm_yday++;
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";
1148 #endif
1149 return result;
1150 #else
1151 return GMTIME(timep, *result);
1152 #endif
1155 static long this_year = 0;
1156 static time_t known_leap_seconds_limit;
1157 static int number_of_leap_seconds_known;
1159 static void
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) {
1168 time_t now;
1169 struct tm *tm, result;
1170 struct vtm vtm;
1171 wideval_t timew;
1172 now = time(NULL);
1173 #ifdef HAVE_GMTIME_R
1174 gmtime_r(&now, &result);
1175 #else
1176 gmtime(&now);
1177 #endif
1178 tm = gmtime_with_leapsecond(&now, &result);
1179 if (!tm) return;
1180 this_year = tm->tm_year;
1182 if (TIMET_MAX - now < (time_t)(366*86400))
1183 known_leap_seconds_limit = TIMET_MAX;
1184 else
1185 known_leap_seconds_limit = now + (time_t)(366*86400);
1187 if (!gmtime_with_leapsecond(&known_leap_seconds_limit, &result))
1188 return;
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() */
1206 void
1207 ruby_reset_leap_second_info(void)
1209 this_year = 0;
1212 static wideval_t
1213 timegmw(struct vtm *vtm)
1215 wideval_t timew;
1216 struct tm tm;
1217 time_t t;
1218 const char *errmsg;
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.
1234 return timew;
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;
1246 tm.tm_isdst = 0;
1248 errmsg = find_time_t(&tm, 1, &t);
1249 if (errmsg)
1250 rb_raise(rb_eArgError, "%s", errmsg);
1251 return wadd(rb_time_magnify(TIMET2WV(t)), v2w(vtm->subsecx));
1254 static struct vtm *
1255 gmtimew(wideval_t timew, struct vtm *result)
1257 time_t t;
1258 struct tm tm;
1259 VALUE subsecx;
1260 wideval_t timew2;
1262 if (wlt(timew, WINT2FIXWV(0))) {
1263 gmtimew_noleapsecond(timew, result);
1264 return 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);
1274 return 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);
1279 return result;
1282 split_second(timew, &timew2, &subsecx);
1284 t = WV2TIMET(timew2);
1285 if (!gmtime_with_leapsecond(&t, &tm))
1286 return NULL;
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;
1299 #if 0
1300 result->zone = rb_fstring_lit("UTC");
1301 #endif
1303 return result;
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
1320 * #!/usr/bin/ruby
1322 * require 'date'
1324 * h = {}
1325 * 2036.downto(2010) {|y|
1326 * 1.upto(12) {|m|
1327 * next if m == 2 && y % 4 == 0
1328 * d = Date.new(y,m,1)
1329 * h[m] ||= {}
1330 * h[m][d.wday] ||= y
1334 * 1.upto(12) {|m|
1335 * print "{"
1336 * 0.upto(6) {|w|
1337 * y = h[m][w]
1338 * print " #{y},"
1340 * puts "},"
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.
1363 * #!/usr/bin/ruby
1365 * require 'date'
1367 * h = {}
1368 * 2037.downto(2010) {|y|
1369 * 1.upto(12) {|m|
1370 * next unless m == 2 && y % 4 == 0
1371 * d = Date.new(y,m,1)
1372 * h[m] ||= {}
1373 * h[m][d.wday] ||= y
1377 * 2.upto(2) {|m|
1378 * 0.upto(6) {|w|
1379 * y = h[m][w]
1380 * print " #{y},"
1382 * puts
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 */
1390 static int
1391 calc_wday(int year_mod400, int month, int day)
1393 int a, y, m;
1394 int wday;
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;
1400 wday = wday % 7;
1401 return wday;
1404 static VALUE
1405 guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, VALUE *zone_ret)
1407 struct tm tm;
1408 long gmtoff;
1409 VALUE zone;
1410 time_t t;
1411 struct vtm vtm2;
1412 VALUE timev;
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);
1419 int isdst = 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)
1426 # else
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)
1430 # endif
1431 if (localtime_with_gmtoff_zone((t = THE_TIME_OLD_ENOUGH, &t), &tm, &gmtoff, &zone)) {
1432 off = LONG2FIX(gmtoff);
1433 isdst = tm.tm_isdst;
1435 else
1436 # endif
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;
1443 if (isdst_ret)
1444 *isdst_ret = isdst;
1445 if (zone_ret)
1446 *zone_ret = zone;
1447 return off;
1450 /* It is difficult to guess the future. */
1452 vtm2 = *vtm_utc;
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]);
1459 else
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);
1464 zone = str_utc;
1465 if (localtime_with_gmtoff_zone(&t, &tm, &gmtoff, &zone)) {
1466 if (isdst_ret)
1467 *isdst_ret = tm.tm_isdst;
1468 if (zone_ret)
1469 *zone_ret = zone;
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;
1479 if (now == 0) {
1480 VALUE zone;
1481 now = time(NULL);
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);
1486 now_zone = zone;
1488 if (isdst_ret)
1489 *isdst_ret = now_isdst;
1490 if (zone_ret)
1491 *zone_ret = now_zone;
1492 return LONG2FIX(now_gmtoff);
1496 static VALUE
1497 small_vtm_sub(struct vtm *vtm1, struct vtm *vtm2)
1499 int off;
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);
1514 static wideval_t
1515 timelocalw(struct vtm *vtm)
1517 time_t t;
1518 struct tm tm;
1519 VALUE v;
1520 wideval_t timew1, timew2;
1521 struct vtm vtm1, vtm2;
1522 int n;
1524 if (FIXNUM_P(vtm->year)) {
1525 long l = FIX2LONG(vtm->year) - 1900;
1526 if (l < INT_MIN || INT_MAX < l)
1527 goto no_localtime;
1528 tm.tm_year = (int)l;
1530 else {
1531 v = subv(vtm->year, INT2FIX(1900));
1532 if (lt(v, INT2NUM(INT_MIN)) || lt(INT2NUM(INT_MAX), v))
1533 goto no_localtime;
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))
1545 goto no_localtime;
1546 return wadd(rb_time_magnify(TIMET2WV(t)), v2w(vtm->subsecx));
1548 no_localtime:
1549 timew1 = timegmw(vtm);
1551 if (!localtimew(timew1, &vtm1))
1552 rb_raise(rb_eArgError, "localtimew error");
1554 n = vtmcmp(vtm, &vtm1);
1555 if (n == 0) {
1556 timew1 = wsub(timew1, rb_time_magnify(WINT2FIXWV(12*3600)));
1557 if (!localtimew(timew1, &vtm1))
1558 rb_raise(rb_eArgError, "localtimew error");
1559 n = 1;
1562 if (n < 0) {
1563 timew2 = timew1;
1564 vtm2 = vtm1;
1565 timew1 = wsub(timew1, rb_time_magnify(WINT2FIXWV(24*3600)));
1566 if (!localtimew(timew1, &vtm1))
1567 rb_raise(rb_eArgError, "localtimew error");
1569 else {
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))
1578 return timew1;
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)
1583 return timew2;
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)
1588 return timew1;
1590 if (vtm->isdst)
1591 return lt(vtm1.utc_offset, vtm2.utc_offset) ? timew2 : timew1;
1592 else
1593 return lt(vtm1.utc_offset, vtm2.utc_offset) ? timew1 : timew2;
1596 static struct tm *
1597 localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, VALUE *zone)
1599 struct tm tm;
1601 if (LOCALTIME(t, tm)) {
1602 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1603 *gmtoff = tm.tm_gmtoff;
1604 #else
1605 struct tm *u, *l;
1606 long off;
1607 struct tm tmbuf;
1608 l = &tm;
1609 u = GMTIME(t, tmbuf);
1610 if (!u)
1611 return NULL;
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;
1618 else
1619 off = 0;
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;
1623 *gmtoff = off;
1624 #endif
1626 if (zone) {
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
1633 # endif
1634 /* this needs tzset or localtime, instead of localtime_r */
1635 *zone = zone_str(tzname[daylight && tm.tm_isdst]);
1636 #else
1638 char buf[64];
1639 strftime(buf, sizeof(buf), "%Z", &tm);
1640 *zone = zone_str(buf);
1642 #endif
1645 *result = tm;
1646 return result;
1648 return NULL;
1651 static int
1652 timew_out_of_timet_range(wideval_t timew)
1654 VALUE timexv;
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)
1660 return 1;
1661 return 0;
1663 #endif
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) {
1668 return 0;
1670 else {
1671 if (t < 0)
1672 return 1;
1673 return 0;
1676 #endif
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))
1680 return 1;
1681 return 0;
1684 static struct vtm *
1685 localtimew(wideval_t timew, struct vtm *result)
1687 VALUE subsecx, offset;
1688 VALUE zone;
1689 int isdst;
1691 if (!timew_out_of_timet_range(timew)) {
1692 time_t t;
1693 struct tm tm;
1694 long gmtoff;
1695 wideval_t timew2;
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;
1714 return result;
1718 if (!gmtimew(timew, result))
1719 return NULL;
1721 offset = guess_local_offset(result, &isdst, &zone);
1723 if (!gmtimew(wadd(timew, rb_time_magnify(v2w(offset))), result))
1724 return NULL;
1726 result->utc_offset = offset;
1727 result->isdst = isdst;
1728 result->zone = zone;
1730 return result;
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. */
1740 struct vtm vtm;
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) \
1769 do { \
1770 if ((tobj)->tm_got == 0) { \
1771 time_get_tm((time), (tobj)); \
1773 } while (0)
1774 #define MAKE_TM_ENSURE(time, tobj, cond) \
1775 do { \
1776 MAKE_TM(time, tobj); \
1777 if (!(cond)) { \
1778 VALUE zone = (tobj)->vtm.zone; \
1779 if (!NIL_P(zone)) zone_localtime(zone, (time)); \
1781 } while (0)
1783 static void
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);
1795 static size_t
1796 time_memsize(const void *tobj)
1798 return sizeof(struct time_object);
1801 static const rb_data_type_t time_data_type = {
1802 "time",
1803 {time_mark, RUBY_TYPED_DEFAULT_FREE, time_memsize,},
1804 0, 0,
1805 (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE),
1808 static VALUE
1809 time_s_alloc(VALUE klass)
1811 VALUE obj;
1812 struct time_object *tobj;
1814 obj = TypedData_Make_Struct(klass, struct time_object, &time_data_type, tobj);
1815 tobj->tzmode = TIME_TZMODE_UNINITIALIZED;
1816 tobj->tm_got=0;
1817 tobj->timew = WINT2FIXWV(0);
1818 tobj->vtm.zone = Qnil;
1820 return obj;
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));
1831 return tobj;
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));
1842 return tobj;
1845 static void
1846 time_modify(VALUE time)
1848 rb_check_frozen(time);
1851 static wideval_t
1852 timenano2timew(time_t sec, long nsec)
1854 wideval_t timew;
1856 timew = rb_time_magnify(TIMET2WV(sec));
1857 if (nsec)
1858 timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000));
1859 return timew;
1862 static struct timespec
1863 timew2timespec(wideval_t timew)
1865 VALUE subsecx;
1866 struct timespec ts;
1867 wideval_t timew2;
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)));
1874 return ts;
1877 static struct timespec *
1878 timew2timespec_exact(wideval_t timew, struct timespec *ts)
1880 VALUE subsecx;
1881 wideval_t timew2;
1882 VALUE nsecv;
1884 if (timew_out_of_timet_range(timew))
1885 return NULL;
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))
1890 return NULL;
1891 ts->tv_nsec = NUM2LONG(nsecv);
1892 return ts;
1895 void
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");
1902 #else
1904 struct timeval tv;
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;
1911 #endif
1914 static VALUE
1915 time_init_now(rb_execution_context_t *ec, VALUE time, VALUE zone)
1917 struct time_object *tobj;
1918 struct timespec ts;
1920 time_modify(time);
1921 GetNewTimeval(time, tobj);
1922 tobj->tzmode = TIME_TZMODE_LOCALTIME;
1923 tobj->tm_got=0;
1924 tobj->timew = WINT2FIXWV(0);
1925 rb_timespec_now(&ts);
1926 tobj->timew = timenano2timew(ts.tv_sec, ts.tv_nsec);
1928 if (!NIL_P(zone)) {
1929 time_zonelocal(time, zone);
1931 return time;
1934 static VALUE
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);
1941 static VALUE
1942 time_set_utc_offset(VALUE time, VALUE off)
1944 struct time_object *tobj;
1945 off = num_exact(off);
1947 time_modify(time);
1948 GetTimeval(time, tobj);
1950 tobj->tm_got = 0;
1951 tobj->vtm.zone = Qnil;
1952 TZMODE_SET_FIXOFF(tobj, off);
1954 return time;
1957 static void
1958 vtm_add_offset(struct vtm *vtm, VALUE off, int sign)
1960 VALUE subsec, v;
1961 int sec, min, hour;
1962 int day;
1964 if (lt(off, INT2FIX(0))) {
1965 sign = -sign;
1966 off = neg(off);
1968 divmodv(off, INT2FIX(1), &off, &subsec);
1969 divmodv(off, INT2FIX(60), &off, &v);
1970 sec = NUM2INT(v);
1971 divmodv(off, INT2FIX(60), &off, &v);
1972 min = NUM2INT(v);
1973 divmodv(off, INT2FIX(24), &off, &v);
1974 hour = NUM2INT(v);
1976 if (sign < 0) {
1977 subsec = neg(subsec);
1978 sec = -sec;
1979 min = -min;
1980 hour = -hour;
1983 day = 0;
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));
1989 sec -= 1;
1991 if (le(INT2FIX(TIME_SCALE), vtm->subsecx)) {
1992 vtm->subsecx = subv(vtm->subsecx, INT2FIX(TIME_SCALE));
1993 sec += 1;
1996 if (sec) {
1997 /* If sec + subsec == 0, don't change vtm->sec.
1998 * It may be 60 which is a leap second. */
1999 sec += vtm->sec;
2000 if (sec < 0) {
2001 sec += 60;
2002 min -= 1;
2004 if (60 <= sec) {
2005 sec -= 60;
2006 min += 1;
2008 vtm->sec = sec;
2010 if (min) {
2011 min += vtm->min;
2012 if (min < 0) {
2013 min += 60;
2014 hour -= 1;
2016 if (60 <= min) {
2017 min -= 60;
2018 hour += 1;
2020 vtm->min = min;
2022 if (hour) {
2023 hour += vtm->hour;
2024 if (hour < 0) {
2025 hour += 24;
2026 day = -1;
2028 if (24 <= hour) {
2029 hour -= 24;
2030 day = 1;
2032 vtm->hour = hour;
2035 if (day) {
2036 if (day < 0) {
2037 if (vtm->mon == 1 && vtm->mday == 1) {
2038 vtm->mday = 31;
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);
2045 vtm->mon--;
2046 vtm->mday = days_in_month[vtm->mon-1];
2047 vtm->yday--;
2049 else {
2050 vtm->mday--;
2051 vtm->yday--;
2053 vtm->wday = (vtm->wday + 6) % 7;
2055 else {
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 */
2060 vtm->mday = 1;
2061 vtm->yday = 1;
2063 else if (vtm->mday == days_in_month_of(leap)[vtm->mon-1]) {
2064 vtm->mon++;
2065 vtm->mday = 1;
2066 vtm->yday++;
2068 else {
2069 vtm->mday++;
2070 vtm->yday++;
2072 vtm->wday = (vtm->wday + 1) % 7;
2077 static int
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;
2083 return TRUE;
2086 NORETURN(static void invalid_utc_offset(VALUE));
2087 static void
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,
2092 zone);
2095 static VALUE
2096 utc_offset_arg(VALUE arg)
2098 VALUE tmp;
2099 if (!NIL_P(tmp = rb_check_string_type(arg))) {
2100 int n = 0;
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)) {
2106 case 1:
2107 if (s[0] == 'Z') {
2108 return UTC_ZONE;
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];
2120 else {
2121 goto invalid_utc_offset;
2123 n *= 3600;
2124 return INT2FIX(n);
2125 case 3:
2126 if (STRNCASECMP("UTC", s, 3) == 0) {
2127 return UTC_ZONE;
2129 break; /* +HH */
2130 case 5: /* +HHMM */
2131 min = s+3;
2132 break;
2133 case 6: /* +HH:MM */
2134 min = s+4;
2135 break;
2136 case 7: /* +HHMMSS */
2137 sec = s+5;
2138 min = s+3;
2139 break;
2140 case 9: /* +HH:MM:SS */
2141 sec = s+7;
2142 min = s+4;
2143 break;
2144 default:
2145 goto invalid_utc_offset;
2147 if (sec) {
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);
2152 if (min) {
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;
2161 if (s[0] == '-') {
2162 if (n == 0) return UTC_ZONE;
2163 n = -n;
2165 return INT2FIX(n);
2167 else {
2168 return num_exact(arg);
2170 invalid_utc_offset:
2171 return Qnil;
2174 static void
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);
2180 VALUE off = w2v(w);
2181 validate_utc_offset(off);
2182 tobj->vtm.utc_offset = off;
2183 tobj->vtm.zone = zone;
2184 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2187 static wideval_t
2188 extract_time(VALUE time)
2190 wideval_t t;
2191 const ID id_to_i = idTo_i;
2193 #define EXTRACT_TIME() do { \
2194 t = v2w(rb_Integer(AREF(to_i))); \
2195 } while (0)
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))
2205 EXTRACT_TIME();
2206 #undef AREF
2208 else {
2209 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2210 EXTRACT_TIME();
2211 #undef AREF
2213 #undef EXTRACT_TIME
2215 return t;
2218 static wideval_t
2219 extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx)
2221 wideval_t t;
2222 const ID id_to_i = idTo_i;
2224 #define EXTRACT_VTM() do { \
2225 VALUE subsecx; \
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))); \
2235 } while (0)
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);
2241 *vtm = tobj->vtm;
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))
2248 EXTRACT_VTM();
2249 #undef AREF
2251 else if (rb_integer_type_p(time)) {
2252 t = v2w(time);
2253 GMTIMEW(rb_time_magnify(t), vtm);
2255 else {
2256 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2257 EXTRACT_VTM();
2258 #undef AREF
2260 #undef EXTRACT_VTM
2261 vtm->subsecx = subsecx;
2262 validate_vtm(vtm);
2263 return t;
2266 static void
2267 zone_set_dst(VALUE zone, struct time_object *tobj, VALUE tm)
2269 ID id_dst_p;
2270 VALUE dst;
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));
2276 static int
2277 zone_timelocal(VALUE zone, VALUE time)
2279 VALUE utc, tm;
2280 struct time_object *tobj = DATA_PTR(time);
2281 wideval_t t, s;
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));
2294 tobj->timew = s;
2295 zone_set_dst(zone, tobj, tm);
2296 return 1;
2299 static int
2300 zone_localtime(VALUE zone, VALUE time)
2302 VALUE local, tm, subsecx;
2303 struct time_object *tobj = DATA_PTR(time);
2304 wideval_t t, s;
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);
2313 tobj->tm_got = 1;
2314 zone_set_offset(zone, tobj, s, t);
2315 zone_set_dst(zone, tobj, tm);
2316 return 1;
2319 static VALUE
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);
2327 static VALUE
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)
2330 struct vtm vtm;
2331 VALUE utc = Qnil;
2332 struct time_object *tobj;
2334 vtm.wday = VTM_WDAY_INITVAL;
2335 vtm.yday = 0;
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);
2348 if (NIL_P(sec)) {
2349 vtm.sec = 0;
2350 vtm.subsecx = INT2FIX(0);
2352 else {
2353 VALUE subsecx;
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;
2361 if (!NIL_P(arg)) {
2362 zone = Qnil;
2363 if (arg == ID2SYM(rb_intern("dst")))
2364 vtm.isdst = 1;
2365 else if (arg == ID2SYM(rb_intern("std")))
2366 vtm.isdst = 0;
2367 else if (maybe_tzobj_p(arg))
2368 zone = 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);
2375 validate_vtm(&vtm);
2377 time_modify(time);
2378 GetNewTimeval(time, tobj);
2380 if (!NIL_P(zone)) {
2381 tobj->timew = timegmw(&vtm);
2382 tobj->vtm = vtm;
2383 tobj->tm_got = 1;
2384 TZMODE_SET_LOCALTIME(tobj);
2385 if (zone_timelocal(zone, time)) {
2386 return 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);
2396 tobj->vtm = vtm;
2397 tobj->tm_got = 1;
2398 TZMODE_SET_UTC(tobj);
2399 return time;
2402 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2403 tobj->tm_got=0;
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);
2413 else {
2414 tobj->timew = timelocalw(&vtm);
2415 return time_localtime(time);
2419 static void
2420 subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec)
2422 time_t sec = *secp;
2423 long subsec = *subsecp;
2424 long sec2;
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;
2432 sec += sec2;
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;
2440 sec += sec2;
2442 #ifndef NEGATIVE_TIME_T
2443 if (sec < 0)
2444 rb_raise(rb_eArgError, "time must be positive");
2445 #endif
2446 *secp = sec;
2447 *subsecp = subsec;
2450 #define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
2451 #define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000)
2453 static wideval_t
2454 nsec2timew(time_t sec, long nsec)
2456 time_nsec_normalize(&sec, &nsec);
2457 return timenano2timew(sec, nsec);
2460 static VALUE
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;
2470 return time;
2473 VALUE
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 */
2481 VALUE
2482 rb_time_nano_new(time_t sec, long nsec)
2484 return time_new_timew(rb_cTime, nsec2timew(sec, nsec));
2487 VALUE
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);
2503 else {
2504 rb_raise(rb_eArgError, "utc_offset out of range");
2507 return time;
2510 VALUE
2511 rb_time_num_new(VALUE timev, VALUE off)
2513 VALUE time = time_new_timew(rb_cTime, rb_time_magnify(v2w(timev)));
2515 if (!NIL_P(off)) {
2516 VALUE zone = off;
2518 if (maybe_tzobj_p(zone)) {
2519 time_gmtime(time);
2520 if (zone_timelocal(zone, time)) return time;
2522 if (NIL_P(off = utc_offset_arg(off))) {
2523 off = zone;
2524 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
2525 time_gmtime(time);
2526 if (!zone_timelocal(zone, time)) invalid_utc_offset(off);
2527 return time;
2529 else if (off == UTC_ZONE) {
2530 return time_gmtime(time);
2533 validate_utc_offset(off);
2534 time_set_utc_offset(time, off);
2535 return time;
2538 return time;
2541 static struct timespec
2542 time_timespec(VALUE num, int interval)
2544 struct timespec t;
2545 const char *const tstr = interval ? "time interval" : "time";
2546 VALUE i, f, ary;
2548 #ifndef NEGATIVE_TIME_T
2549 # define arg_range_check(v) \
2550 (((v) < 0) ? \
2551 rb_raise(rb_eArgError, "%s must not be negative", tstr) : \
2552 (void)0)
2553 #else
2554 # define arg_range_check(v) \
2555 ((interval && (v) < 0) ? \
2556 rb_raise(rb_eArgError, "time interval must not be negative") : \
2557 (void)0)
2558 #endif
2560 if (FIXNUM_P(num)) {
2561 t.tv_sec = NUM2TIMET(num);
2562 arg_range_check(t.tv_sec);
2563 t.tv_nsec = 0;
2565 else if (RB_FLOAT_TYPE_P(num)) {
2566 double x = RFLOAT_VALUE(num);
2567 arg_range_check(x);
2569 double f, d;
2571 d = modf(x, &f);
2572 if (d >= 0) {
2573 t.tv_nsec = (int)(d*1e9+0.5);
2574 if (t.tv_nsec >= 1000000000) {
2575 t.tv_nsec -= 1000000000;
2576 f += 1;
2579 else if ((t.tv_nsec = (int)(-d*1e9+0.5)) > 0) {
2580 t.tv_nsec = 1000000000 - t.tv_nsec;
2581 f -= 1;
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);
2592 t.tv_nsec = 0;
2594 else {
2595 i = INT2FIX(1);
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);
2605 else {
2606 rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into %s",
2607 rb_obj_class(num), tstr);
2610 return t;
2611 #undef arg_range_check
2614 static struct timeval
2615 time_timeval(VALUE num, int interval)
2617 struct timespec ts;
2618 struct timeval tv;
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);
2624 return tv;
2627 struct timeval
2628 rb_time_interval(VALUE num)
2630 return time_timeval(num, TRUE);
2633 struct timeval
2634 rb_time_timeval(VALUE time)
2636 struct time_object *tobj;
2637 struct timeval t;
2638 struct timespec ts;
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);
2645 return t;
2647 return time_timeval(time, FALSE);
2650 struct timespec
2651 rb_time_timespec(VALUE time)
2653 struct time_object *tobj;
2654 struct timespec t;
2656 if (IsTimeval(time)) {
2657 GetTimeval(time, tobj);
2658 t = timew2timespec(tobj->timew);
2659 return t;
2661 return time_timespec(time, FALSE);
2664 struct timespec
2665 rb_time_timespec_interval(VALUE num)
2667 return time_timespec(num, TRUE);
2670 static int
2671 get_scale(VALUE unit)
2673 if (unit == ID2SYM(id_nanosecond) || unit == ID2SYM(id_nsec)) {
2674 return 1000000000;
2676 else if (unit == ID2SYM(id_microsecond) || unit == ID2SYM(id_usec)) {
2677 return 1000000;
2679 else if (unit == ID2SYM(id_millisecond)) {
2680 return 1000;
2682 else {
2683 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
2687 static VALUE
2688 time_s_at(rb_execution_context_t *ec, VALUE klass, VALUE time, VALUE subsec, VALUE unit, VALUE zone)
2690 VALUE t;
2691 wideval_t timew;
2693 if (subsec) {
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);
2707 else {
2708 timew = rb_time_magnify(v2w(num_exact(time)));
2709 t = time_new_timew(klass, timew);
2711 if (!NIL_P(zone)) {
2712 time_zonelocal(t, zone);
2715 return t;
2718 static VALUE
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",
2729 static int
2730 obj2int(VALUE obj)
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 */
2740 static uint32_t
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;
2751 static VALUE
2752 obj2vint(VALUE obj)
2754 if (RB_TYPE_P(obj, T_STRING)) {
2755 obj = rb_str_to_inum(obj, 10, TRUE);
2757 else {
2758 obj = rb_to_int(obj);
2761 return obj;
2764 static uint32_t
2765 obj2subsecx(VALUE obj, VALUE *subsecx)
2767 VALUE subsec;
2769 if (RB_TYPE_P(obj, T_STRING)) {
2770 obj = rb_str_to_inum(obj, 10, TRUE);
2771 *subsecx = INT2FIX(0);
2773 else {
2774 divmodv(num_exact(obj), INT2FIX(1), &obj, &subsec);
2775 *subsecx = w2v(rb_time_magnify(v2w(subsec)));
2777 return obj2ubits(obj, 6); /* vtm->sec */
2780 static VALUE
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));
2790 static uint32_t
2791 month_arg(VALUE arg)
2793 int i, mon;
2795 if (FIXNUM_P(arg)) {
2796 return obj2ubits(arg, 4);
2799 mon = 0;
2800 VALUE s = rb_check_string_type(arg);
2801 if (!NIL_P(s) && RSTRING_LEN(s) > 0) {
2802 arg = s;
2803 for (i=0; i<12; i++) {
2804 if (RSTRING_LEN(s) == 3 &&
2805 STRNCASECMP(months[i], RSTRING_PTR(s), 3) == 0) {
2806 mon = i+1;
2807 break;
2811 if (mon == 0) {
2812 mon = obj2ubits(arg, 4);
2814 return mon;
2817 static VALUE
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");
2822 return utc_offset;
2825 static VALUE
2826 validate_zone_name(VALUE zone_name)
2828 StringValueCStr(zone_name);
2829 return zone_name;
2832 static void
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
2849 static void
2850 time_arg(int argc, const VALUE *argv, struct vtm *vtm)
2852 VALUE v[8];
2853 VALUE subsecx = INT2FIX(0);
2855 vtm->year = INT2FIX(0);
2856 vtm->mon = 0;
2857 vtm->mday = 0;
2858 vtm->hour = 0;
2859 vtm->min = 0;
2860 vtm->sec = 0;
2861 vtm->subsecx = INT2FIX(0);
2862 vtm->utc_offset = Qnil;
2863 vtm->wday = 0;
2864 vtm->yday = 0;
2865 vtm->isdst = 0;
2866 vtm->zone = str_empty;
2868 if (argc == 10) {
2869 v[0] = argv[5];
2870 v[1] = argv[4];
2871 v[2] = argv[3];
2872 v[3] = argv[2];
2873 v[4] = argv[1];
2874 v[5] = argv[0];
2875 v[6] = Qnil;
2876 vtm->isdst = RTEST(argv[8]) ? 1 : 0;
2878 else {
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]);
2888 if (NIL_P(v[1])) {
2889 vtm->mon = 1;
2891 else {
2892 vtm->mon = month_arg(v[1]);
2895 if (NIL_P(v[2])) {
2896 vtm->mday = 1;
2898 else {
2899 vtm->mday = obj2ubits(v[2], 5);
2902 /* normalize month-mday */
2903 switch (vtm->mon) {
2904 case 2:
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) {
2909 vtm->mday -= mday2;
2910 vtm->mon++;
2913 break;
2914 case 4:
2915 case 6:
2916 case 9:
2917 case 11:
2918 if (vtm->mday == 31) {
2919 vtm->mon++;
2920 vtm->mday = 1;
2922 break;
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]);
2933 else {
2934 /* when argc == 8, v[6] is timezone, but ignored */
2935 if (NIL_P(v[5])) {
2936 vtm->sec = 0;
2938 else {
2939 vtm->sec = obj2subsecx(v[5], &subsecx);
2942 vtm->subsecx = subsecx;
2944 validate_vtm(vtm);
2945 RB_GC_GUARD(subsecx);
2948 static int
2949 leap_year_p(long y)
2951 /* TODO:
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;
2963 static time_t
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 +
2976 (time_t)(tm_yday +
2977 (tm_year-70)*365 +
2978 DIV(tm_year-69,4) -
2979 DIV(tm_year-1,100) +
2980 DIV(tm_year+299,400))*86400;
2983 #if 0
2984 #define DEBUG_FIND_TIME_NUMGUESS
2985 #define DEBUG_GUESSRANGE
2986 #endif
2988 static const bool debug_guessrange =
2989 #ifdef DEBUG_GUESSRANGE
2990 true;
2991 #else
2992 false;
2993 #endif
2995 #define DEBUG_REPORT_GUESSRANGE \
2996 (debug_guessrange ? debug_report_guessrange(guess_lo, guess_hi) : (void)0)
2998 static inline void
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
3009 true;
3010 #else
3011 false;
3012 #endif
3014 #define DEBUG_FIND_TIME_NUMGUESS_INC \
3015 (void)(debug_find_time_numguess && find_time_numguess++),
3016 static unsigned long long find_time_numguess;
3018 static VALUE
3019 find_time_numguess_getter(ID name, VALUE *data)
3021 unsigned long long *numguess = (void *)data;
3022 return ULL2NUM(*numguess);
3025 static const char *
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;
3030 int d;
3031 int find_dst;
3032 struct tm result;
3033 int status;
3034 int tptr_tm_yday;
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. */
3044 update_tz();
3046 tm0 = *tptr;
3047 if (tm0.tm_mon < 0) {
3048 tm0.tm_mon = 0;
3049 tm0.tm_mday = 1;
3050 tm0.tm_hour = 0;
3051 tm0.tm_min = 0;
3052 tm0.tm_sec = 0;
3054 else if (11 < tm0.tm_mon) {
3055 tm0.tm_mon = 11;
3056 tm0.tm_mday = 31;
3057 tm0.tm_hour = 23;
3058 tm0.tm_min = 59;
3059 tm0.tm_sec = 60;
3061 else if (tm0.tm_mday < 1) {
3062 tm0.tm_mday = 1;
3063 tm0.tm_hour = 0;
3064 tm0.tm_min = 0;
3065 tm0.tm_sec = 0;
3067 else if ((d = days_in_month_in(1900 + tm0.tm_year)[tm0.tm_mon]) < tm0.tm_mday) {
3068 tm0.tm_mday = d;
3069 tm0.tm_hour = 23;
3070 tm0.tm_min = 59;
3071 tm0.tm_sec = 60;
3073 else if (tm0.tm_hour < 0) {
3074 tm0.tm_hour = 0;
3075 tm0.tm_min = 0;
3076 tm0.tm_sec = 0;
3078 else if (23 < tm0.tm_hour) {
3079 tm0.tm_hour = 23;
3080 tm0.tm_min = 59;
3081 tm0.tm_sec = 60;
3083 else if (tm0.tm_min < 0) {
3084 tm0.tm_min = 0;
3085 tm0.tm_sec = 0;
3087 else if (59 < tm0.tm_min) {
3088 tm0.tm_min = 59;
3089 tm0.tm_sec = 60;
3091 else if (tm0.tm_sec < 0) {
3092 tm0.tm_sec = 0;
3094 else if (60 < tm0.tm_sec) {
3095 tm0.tm_sec = 60;
3098 DEBUG_REPORT_GUESSRANGE;
3099 guess0 = guess = timegm_noleapsecond(&tm0);
3100 tm = GUESS(&guess);
3101 if (tm) {
3102 d = tmcmp(tptr, tm);
3103 if (d == 0) { goto found; }
3104 if (d < 0) {
3105 guess_hi = guess;
3106 guess -= 24 * 60 * 60;
3108 else {
3109 guess_lo = guess;
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; }
3116 if (d < 0)
3117 guess_hi = guess;
3118 else
3119 guess_lo = guess;
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; }
3129 tm_lo = *tm;
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; }
3136 tm_hi = *tm;
3138 DEBUG_REPORT_GUESSRANGE;
3140 status = 1;
3142 while (guess_lo + 1 < guess_hi) {
3143 binsearch:
3144 if (status == 0) {
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;
3150 status = 1;
3152 else {
3153 if (status == 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. */
3157 guess--;
3158 status = 2;
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)
3164 guess++;
3165 status = 0;
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);
3179 status = 0;
3180 goto binsearch;
3184 tm = GUESS(&guess);
3185 if (!tm) goto error;
3187 d = tmcmp(tptr, tm);
3189 if (d < 0) {
3190 guess_hi = guess;
3191 tm_hi = *tm;
3192 DEBUG_REPORT_GUESSRANGE;
3194 else if (d > 0) {
3195 guess_lo = guess;
3196 tm_lo = *tm;
3197 DEBUG_REPORT_GUESSRANGE;
3199 else {
3200 goto found;
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);
3214 *tp = guess_lo +
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) +
3222 tptr_tm_yday -
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));
3228 return NULL;
3230 found:
3231 if (!utc_p) {
3232 /* If localtime is nonmonotonic, another result may exist. */
3233 time_t guess2;
3234 if (find_dst) {
3235 guess2 = guess - 2 * 60 * 60;
3236 tm = LOCALTIME(&guess2, result);
3237 if (tm) {
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) {
3249 if (guess < guess2)
3250 *tp = guess;
3251 else
3252 *tp = guess2;
3253 return NULL;
3259 else {
3260 guess2 = guess + 2 * 60 * 60;
3261 tm = LOCALTIME(&guess2, result);
3262 if (tm) {
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) {
3274 if (guess < guess2)
3275 *tp = guess2;
3276 else
3277 *tp = guess;
3278 return NULL;
3285 *tp = guess;
3286 return NULL;
3288 out_of_range:
3289 return "time out of range";
3291 error:
3292 return "gmtime/localtime error";
3295 static int
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;
3312 else
3313 return 0;
3316 static int
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;
3331 else
3332 return 0;
3336 * call-seq:
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"]
3355 * array[5] = 2000
3356 * Time.utc(*array) # => 2000-04-24 13:26:57 UTC
3358 * Parameters:
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
3364 * Alias: Time.gm.
3366 * Related: Time.local.
3369 static VALUE
3370 time_s_mkutc(int argc, VALUE *argv, VALUE klass)
3372 struct vtm vtm;
3374 time_arg(argc, argv, &vtm);
3375 return time_gmtime(time_new_timew(klass, timegmw(&vtm)));
3379 * call-seq:
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"]
3398 * array[5] = 2000
3399 * Time.local(*array) # => 2000-04-24 13:26:57 -0500
3401 * Parameters:
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.
3412 static VALUE
3413 time_s_mktime(int argc, VALUE *argv, VALUE klass)
3415 struct vtm vtm;
3417 time_arg(argc, argv, &vtm);
3418 return time_localtime(time_new_timew(klass, timelocalw(&vtm)));
3422 * call-seq:
3423 * time.to_i -> int
3424 * time.tv_sec -> int
3426 * Returns the value of _time_ as an integer number of seconds
3427 * since the Epoch.
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
3435 static VALUE
3436 time_to_i(VALUE time)
3438 struct time_object *tobj;
3440 GetTimeval(time, tobj);
3441 return w2v(wdiv(tobj->timew, WINT2FIXWV(TIME_SCALE)));
3445 * call-seq:
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
3452 * exactly.
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+.
3468 static VALUE
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));
3478 * call-seq:
3479 * time.to_r -> a_rational
3481 * Returns the value of _time_ as a rational number of seconds
3482 * since the Epoch.
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.
3491 static VALUE
3492 time_to_r(VALUE time)
3494 struct time_object *tobj;
3495 VALUE v;
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);
3502 return v;
3506 * call-seq:
3507 * time.usec -> int
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
3514 * t.usec #=> 459785
3516 * If _time_ has fraction of microsecond (such as nanoseconds),
3517 * it is truncated.
3519 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3520 * t.usec #=> 666777
3522 * Time#subsec can be used to obtain the subsecond part exactly.
3525 static VALUE
3526 time_usec(VALUE time)
3528 struct time_object *tobj;
3529 wideval_t w, q, r;
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));
3539 * call-seq:
3540 * time.nsec -> int
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),
3550 * it is truncated.
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.
3558 static VALUE
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)));
3568 * call-seq:
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
3582 * t.subsec #=> 0
3584 * Time.new(2000,1,1,0,0,1/3r,"UTC").subsec #=> (1/3)
3588 static VALUE
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));
3598 * call-seq:
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
3610 * t <=> t2 #=> -1
3611 * t2 <=> t #=> 1
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
3617 * t <=> t2 #=> -1
3618 * t2 <=> t #=> 1
3619 * t <=> t #=> 0
3622 static VALUE
3623 time_cmp(VALUE time1, VALUE time2)
3625 struct time_object *tobj1, *tobj2;
3626 int n;
3628 GetTimeval(time1, tobj1);
3629 if (IsTimeval(time2)) {
3630 GetTimeval(time2, tobj2);
3631 n = wcmp(tobj1->timew, tobj2->timew);
3633 else {
3634 return rb_invcmp(time1, time2);
3636 if (n == 0) return INT2FIX(0);
3637 if (n > 0) return INT2FIX(1);
3638 return INT2FIX(-1);
3642 * call-seq:
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.
3649 static VALUE
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));
3659 return Qfalse;
3663 * call-seq:
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
3670 * t.utc? #=> false
3671 * t = Time.gm(2000,"jan",1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3672 * t.utc? #=> true
3674 * t = Time.now #=> 2007-11-19 08:16:03 -0600
3675 * t.gmt? #=> false
3676 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3677 * t.gmt? #=> true
3680 static VALUE
3681 time_utc_p(VALUE time)
3683 struct time_object *tobj;
3685 GetTimeval(time, tobj);
3686 return RBOOL(TZMODE_UTC_P(tobj));
3690 * call-seq:
3691 * time.hash -> integer
3693 * Returns a hash code for this Time object.
3695 * See also Object#hash.
3698 static VALUE
3699 time_hash(VALUE time)
3701 struct time_object *tobj;
3703 GetTimeval(time, tobj);
3704 return rb_hash(w2v(tobj->timew));
3707 /* :nodoc: */
3708 static VALUE
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);
3718 return copy;
3721 static VALUE
3722 time_dup(VALUE time)
3724 VALUE dup = time_s_alloc(rb_obj_class(time));
3725 time_init_copy(dup, time);
3726 return dup;
3729 static VALUE
3730 time_localtime(VALUE time)
3732 struct time_object *tobj;
3733 struct vtm vtm;
3734 VALUE zone;
3736 GetTimeval(time, tobj);
3737 if (TZMODE_LOCALTIME_P(tobj)) {
3738 if (tobj->tm_got)
3739 return time;
3741 else {
3742 time_modify(time);
3745 zone = tobj->vtm.zone;
3746 if (maybe_tzobj_p(zone) && zone_localtime(zone, time)) {
3747 return time;
3750 if (!localtimew(tobj->timew, &vtm))
3751 rb_raise(rb_eArgError, "localtime error");
3752 tobj->vtm = vtm;
3754 tobj->tm_got = 1;
3755 TZMODE_SET_LOCALTIME(tobj);
3756 return time;
3759 static VALUE
3760 time_zonelocal(VALUE time, VALUE off)
3762 VALUE zone = off;
3763 if (zone_localtime(zone, time)) return time;
3765 if (NIL_P(off = utc_offset_arg(off))) {
3766 off = zone;
3767 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
3768 if (!zone_localtime(zone, time)) invalid_utc_offset(off);
3769 return time;
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);
3781 * call-seq:
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
3791 * t.utc? #=> true
3793 * t.localtime #=> 2000-01-01 14:15:01 -0600
3794 * t.utc? #=> false
3796 * t.localtime("+09:00") #=> 2000-01-02 05:15:01 +0900
3797 * t.utc? #=> false
3799 * If +utc_offset+ is not given and _time_ is local time, just returns
3800 * the receiver.
3803 static VALUE
3804 time_localtime_m(int argc, VALUE *argv, VALUE time)
3806 VALUE off;
3808 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
3809 return time_zonelocal(time, off);
3812 return time_localtime(time);
3816 * call-seq:
3817 * time.gmtime -> time
3818 * time.utc -> time
3820 * Converts _time_ to UTC (GMT), modifying the receiver.
3822 * t = Time.now #=> 2007-11-19 08:18:31 -0600
3823 * t.gmt? #=> false
3824 * t.gmtime #=> 2007-11-19 14:18:31 UTC
3825 * t.gmt? #=> true
3827 * t = Time.now #=> 2007-11-19 08:18:51 -0600
3828 * t.utc? #=> false
3829 * t.utc #=> 2007-11-19 14:18:51 UTC
3830 * t.utc? #=> true
3833 static VALUE
3834 time_gmtime(VALUE time)
3836 struct time_object *tobj;
3837 struct vtm vtm;
3839 GetTimeval(time, tobj);
3840 if (TZMODE_UTC_P(tobj)) {
3841 if (tobj->tm_got)
3842 return time;
3844 else {
3845 time_modify(time);
3848 vtm.zone = str_utc;
3849 GMTIMEW(tobj->timew, &vtm);
3850 tobj->vtm = vtm;
3852 tobj->tm_got = 1;
3853 TZMODE_SET_UTC(tobj);
3854 return time;
3857 static VALUE
3858 time_fixoff(VALUE time)
3860 struct time_object *tobj;
3861 struct vtm vtm;
3862 VALUE off, zone;
3864 GetTimeval(time, tobj);
3865 if (TZMODE_FIXOFF_P(tobj)) {
3866 if (tobj->tm_got)
3867 return time;
3869 else {
3870 time_modify(time);
3873 if (TZMODE_FIXOFF_P(tobj))
3874 off = tobj->vtm.utc_offset;
3875 else
3876 off = INT2FIX(0);
3878 GMTIMEW(tobj->timew, &vtm);
3880 zone = tobj->vtm.zone;
3881 tobj->vtm = vtm;
3882 tobj->vtm.zone = zone;
3883 vtm_add_offset(&tobj->vtm, off, +1);
3885 tobj->tm_got = 1;
3886 TZMODE_SET_FIXOFF(tobj, off);
3887 return time;
3891 * call-seq:
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
3904 * t.utc? #=> true
3906 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
3907 * l.utc? #=> false
3908 * t == l #=> true
3910 * j = t.getlocal("+09:00") #=> 2000-01-02 05:15:01 +0900
3911 * j.utc? #=> false
3912 * t == j #=> true
3914 * k = t.getlocal(9*60*60) #=> 2000-01-02 05:15:01 +0900
3915 * k.utc? #=> false
3916 * t == k #=> true
3919 static VALUE
3920 time_getlocaltime(int argc, VALUE *argv, VALUE time)
3922 VALUE off;
3924 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
3925 VALUE zone = off;
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))) {
3932 off = zone;
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);
3936 return time;
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));
3952 * call-seq:
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
3959 * t.gmt? #=> false
3960 * y = t.getgm #=> 2000-01-02 02:15:01 UTC
3961 * y.gmt? #=> true
3962 * t == y #=> true
3965 static VALUE
3966 time_getgmtime(VALUE time)
3968 return time_gmtime(time_dup(time));
3971 static VALUE
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))
3983 * call-seq:
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"
3993 static VALUE
3994 time_asctime(VALUE time)
3996 return strftimev("%a %b %e %T %Y", time, rb_usascii_encoding());
4000 * call-seq:
4001 * time.to_s -> string
4003 * Returns a string representing _time_. Equivalent to calling
4004 * #strftime with the appropriate format string.
4006 * t = Time.now
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"
4014 static VALUE
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());
4022 else
4023 return strftimev("%Y-%m-%d %H:%M:%S %z", time, rb_usascii_encoding());
4027 * call-seq:
4028 * time.inspect -> string
4030 * Returns a detailed string representing _time_. Unlike to_s,
4031 * preserves subsecond in the representation for easier debugging.
4033 * t = Time.now
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"
4041 static VALUE
4042 time_inspect(VALUE time)
4044 struct time_object *tobj;
4045 VALUE str, subsec;
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) {
4053 long len;
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);
4059 else {
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");
4067 else {
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, '-') : '+';
4071 int sec = off % 60;
4072 int min = (off /= 60) % 60;
4073 off /= 60;
4074 rb_str_catf(str, " %c%.2d%.2d", sign, (int)off, min);
4075 if (sec) rb_str_catf(str, "%.2d", sec);
4077 return str;
4080 static VALUE
4081 time_add0(VALUE klass, const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4083 VALUE result;
4084 struct time_object *result_tobj;
4086 offset = num_exact(offset);
4087 if (sign < 0)
4088 result = time_new_timew(klass, wsub(tobj->timew, rb_time_magnify(v2w(offset))));
4089 else
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);
4094 return result;
4097 static VALUE
4098 time_add(const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4100 return time_add0(rb_cTime, tobj, torig, offset, sign);
4104 * call-seq:
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
4114 static VALUE
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);
4127 * call-seq:
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
4141 static VALUE
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);
4156 static VALUE
4157 ndigits_denominator(VALUE ndigits)
4159 long nd = NUM2LONG(ndigits);
4161 if (nd < 0) {
4162 rb_raise(rb_eArgError, "negative ndigits given");
4164 if (nd == 0) {
4165 return INT2FIX(1);
4167 return rb_rational_new(INT2FIX(1),
4168 rb_int_positive_pow(10, (unsigned long)nd));
4172 * call-seq:
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
4200 static VALUE
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]))
4207 den = INT2FIX(1);
4208 else
4209 den = ndigits_denominator(ndigits);
4211 GetTimeval(time, tobj);
4212 v = w2v(rb_time_unmagnify(tobj->timew));
4214 v = modv(v, den);
4215 if (lt(v, quov(den, INT2FIX(2))))
4216 return time_add(tobj, time, v, -1);
4217 else
4218 return time_add(tobj, time, subv(den, v), 1);
4222 * call-seq:
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
4248 static VALUE
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]))
4255 den = INT2FIX(1);
4256 else
4257 den = ndigits_denominator(ndigits);
4259 GetTimeval(time, tobj);
4260 v = w2v(rb_time_unmagnify(tobj->timew));
4262 v = modv(v, den);
4263 return time_add(tobj, time, v, -1);
4267 * call-seq:
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
4293 static VALUE
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]))
4300 den = INT2FIX(1);
4301 else
4302 den = ndigits_denominator(ndigits);
4304 GetTimeval(time, tobj);
4305 v = w2v(rb_time_unmagnify(tobj->timew));
4307 v = modv(v, den);
4308 if (!rb_equal(v, INT2FIX(0))) {
4309 v = subv(den, v);
4311 return time_add(tobj, time, v, 1);
4315 * call-seq:
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
4322 * details.
4324 * t = Time.now #=> 2007-11-19 08:25:02 -0600
4325 * t.sec #=> 2
4328 static VALUE
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);
4339 * call-seq:
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
4345 * t.min #=> 25
4348 static VALUE
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);
4359 * call-seq:
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
4365 * t.hour #=> 8
4368 static VALUE
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);
4379 * call-seq:
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
4386 * t.day #=> 19
4387 * t.mday #=> 19
4390 static VALUE
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);
4401 * call-seq:
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
4408 * t.mon #=> 11
4409 * t.month #=> 11
4412 static VALUE
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);
4423 * call-seq:
4424 * time.year -> integer
4426 * Returns the year for _time_ (including the century).
4428 * t = Time.now #=> 2007-11-19 08:27:51 -0600
4429 * t.year #=> 2007
4432 static VALUE
4433 time_year(VALUE time)
4435 struct time_object *tobj;
4437 GetTimeval(time, tobj);
4438 MAKE_TM(time, tobj);
4439 return tobj->vtm.year;
4443 * call-seq:
4444 * time.wday -> integer
4446 * Returns an integer representing the day of the week, 0..6, with
4447 * Sunday == 0.
4449 * t = Time.now #=> 2007-11-20 02:35:35 -0600
4450 * t.wday #=> 2
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
4460 static VALUE
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)); \
4475 * call-seq:
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
4484 static VALUE
4485 time_sunday(VALUE time)
4487 wday_p(0);
4491 * call-seq:
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
4500 static VALUE
4501 time_monday(VALUE time)
4503 wday_p(1);
4507 * call-seq:
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
4516 static VALUE
4517 time_tuesday(VALUE time)
4519 wday_p(2);
4523 * call-seq:
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
4532 static VALUE
4533 time_wednesday(VALUE time)
4535 wday_p(3);
4539 * call-seq:
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
4548 static VALUE
4549 time_thursday(VALUE time)
4551 wday_p(4);
4555 * call-seq:
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
4564 static VALUE
4565 time_friday(VALUE time)
4567 wday_p(5);
4571 * call-seq:
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
4580 static VALUE
4581 time_saturday(VALUE time)
4583 wday_p(6);
4587 * call-seq:
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
4593 * t.yday #=> 323
4596 static VALUE
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);
4607 * call-seq:
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.
4614 * # CST6CDT:
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
4622 * # Asia/Tokyo:
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
4631 static VALUE
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);
4645 * call-seq:
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)
4652 * t.zone #=> "UTC"
4653 * t = Time.local(2000, "jan", 1, 20, 15, 1)
4654 * t.zone #=> "CST"
4657 static VALUE
4658 time_zone(VALUE time)
4660 struct time_object *tobj;
4661 VALUE zone;
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;
4670 if (NIL_P(zone))
4671 return Qnil;
4673 if (RB_TYPE_P(zone, T_STRING))
4674 zone = rb_str_dup(zone);
4675 return zone;
4679 * call-seq:
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_
4685 * and UTC.
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
4693 VALUE
4694 rb_time_utc_offset(VALUE time)
4696 struct time_object *tobj;
4698 GetTimeval(time, tobj);
4700 if (TZMODE_UTC_P(tobj)) {
4701 return INT2FIX(0);
4703 else {
4704 MAKE_TM(time, tobj);
4705 return tobj->vtm.utc_offset;
4710 * call-seq:
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
4720 * new Time object.
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"]
4726 static VALUE
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),
4739 tobj->vtm.year,
4740 INT2FIX(tobj->vtm.wday),
4741 INT2FIX(tobj->vtm.yday),
4742 RBOOL(tobj->vtm.isdst),
4743 time_zone(time));
4746 static VALUE
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)
4750 VALUE timev = Qnil;
4751 struct timespec ts;
4753 if (!timew2timespec_exact(timew, &ts))
4754 timev = w2v(rb_time_unmagnify(timew));
4756 if (NIL_P(timev)) {
4757 return rb_strftime_timespec(format, format_len, enc, time, vtm, &ts, gmt);
4759 else {
4760 return rb_strftime(format, format_len, enc, time, vtm, timev, gmt);
4764 static VALUE
4765 strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc)
4767 struct time_object *tobj;
4768 VALUE str;
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);
4774 return str;
4778 * call-seq:
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
4785 * output string.
4787 * The directive consists of a percent (%) character,
4788 * zero or more flags, optional minimum field width,
4789 * optional modifier and a conversion specifier
4790 * as follows:
4792 * %<flags><width><modifier><conversion>
4794 * Flags:
4795 * - don't pad a numerical output
4796 * _ use spaces for padding
4797 * 0 use zeros for padding
4798 * ^ upcase the result string
4799 * # change case
4800 * : use colons for %z
4802 * The minimum field width specifies the minimum width.
4804 * The modifiers are "E" and "O".
4805 * They are ignored.
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
4854 * carry up.
4856 * Time zone:
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)
4862 * Weekday:
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)
4878 * Week number:
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.
4887 * Literal string:
4888 * %n - Newline character (\n)
4889 * %t - Tab character (\t)
4890 * %% - Literal ``%'' character
4892 * Combination:
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)
4897 * %x - Same as %D
4898 * %X - Same as %T
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
4906 * dependent.
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.
4920 * Examples:
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)
4964 static VALUE
4965 time_strftime(VALUE time, VALUE format)
4967 struct time_object *tobj;
4968 const char *fmt;
4969 long len;
4970 rb_encoding *enc;
4971 VALUE tmp;
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);
4983 if (len == 0) {
4984 rb_warning("strftime called with empty format string");
4985 return rb_enc_str_new(0, 0, enc);
4987 else {
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);
4992 return str;
4996 int ruby_marshal_write_long(long x, char *buf);
4998 enum {base_dump_size = 8};
5000 /* :nodoc: */
5001 static VALUE
5002 time_mdump(VALUE time)
5004 struct time_object *tobj;
5005 unsigned long p, s;
5006 char buf[base_dump_size + sizeof(long) + 1];
5007 int i;
5008 VALUE str;
5010 struct vtm vtm;
5011 long year;
5012 long usec, nsec;
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);
5026 year = max_year;
5028 else if (year < 1900) {
5029 year_extend = LONG2NUM(1900 - year);
5030 year = 1900;
5033 else {
5034 if (rb_int_positive_p(vtm.year)) {
5035 year_extend = rb_int_minus(vtm.year, INT2FIX(max_year));
5036 year = max_year;
5038 else {
5039 year_extend = rb_int_minus(INT2FIX(1900), vtm.year);
5040 year = 1900;
5044 subsecx = vtm.subsecx;
5046 nano = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
5047 divmodv(nano, INT2FIX(1), &v, &subnano);
5048 nsec = FIX2LONG(v);
5049 usec = nsec / 1000;
5050 nsec = nsec % 1000;
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 */
5059 vtm.hour; /* 5 */
5060 s = (unsigned long)vtm.min << 26 | /* 6 */
5061 vtm.sec << 20 | /* 6 */
5062 usec; /* 20 */
5064 for (i=0; i<4; i++) {
5065 buf[i] = (unsigned char)p;
5066 p = RSHIFT(p, 8);
5068 for (i=4; i<8; i++) {
5069 buf[i] = (unsigned char)s;
5070 s = RSHIFT(s, 8);
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);
5090 memcpy(p, buf, i);
5091 p += i;
5092 rb_integer_pack(year_extend, p, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
5094 else {
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);
5103 else {
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.
5116 char buf[2];
5117 int len = (int)sizeof(buf);
5118 buf[1] = (char)((nsec % 10) << 4);
5119 nsec /= 10;
5120 buf[0] = (char)(nsec % 10);
5121 nsec /= 10;
5122 buf[0] |= (char)((nsec % 10) << 4);
5123 if (buf[1] == 0)
5124 len = 1;
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);
5139 return str;
5142 /* :nodoc: */
5143 static VALUE
5144 time_dump(int argc, VALUE *argv, VALUE time)
5146 VALUE str;
5148 rb_check_arity(argc, 0, 1);
5149 str = time_mdump(time);
5151 return str;
5154 static VALUE
5155 mload_findzone(VALUE arg)
5157 VALUE *argp = (VALUE *)arg;
5158 VALUE time = argp[0], zone = argp[1];
5159 return find_timezone(time, zone);
5162 static VALUE
5163 mload_zone(VALUE time, VALUE zone)
5165 VALUE z, args[2];
5166 args[0] = time;
5167 args[1] = 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);
5171 return z;
5174 long ruby_marshal_read_long(const char **buf, long len);
5176 /* :nodoc: */
5177 static VALUE
5178 time_mload(VALUE time, VALUE str)
5180 struct time_object *tobj;
5181 unsigned long p, s;
5182 time_t sec;
5183 long usec;
5184 unsigned char *buf;
5185 struct vtm vtm;
5186 int i, gmt;
5187 long nsec;
5188 VALUE submicro, nano_num, nano_den, offset, zone, year;
5189 wideval_t timew;
5191 time_modify(time);
5193 #define get_attr(attr, iffound) \
5194 attr = rb_attr_delete(str, id_##attr); \
5195 if (!NIL_P(attr)) { \
5196 iffound; \
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)));
5204 get_attr(year, {});
5206 #undef get_attr
5208 rb_copy_generic_ivar(time, str);
5210 StringValue(str);
5211 buf = (unsigned char *)RSTRING_PTR(str);
5212 if (RSTRING_LEN(str) < base_dump_size) {
5213 goto invalid_format;
5216 p = s = 0;
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) {
5225 gmt = 0;
5226 offset = Qnil;
5227 sec = p;
5228 usec = s;
5229 nsec = usec * 1000;
5230 timew = wadd(rb_time_magnify(TIMET2WV(sec)), wmulquoll(WINT2FIXWV(usec), TIME_SCALE, 1000000));
5232 else {
5233 p &= ~(1UL<<31);
5234 gmt = (int)((p >> 30) & 0x1);
5236 if (NIL_P(year)) {
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;
5241 long ysize = 0;
5242 VALUE year_extend;
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);
5251 else {
5252 year = rb_int_plus(year, year_extend);
5255 unsigned int mon = ((int)(p >> 10) & 0xf); /* 0...12 */
5256 if (mon >= 12) {
5257 mon -= 12;
5258 year = addv(year, LONG2FIX(1));
5260 vtm.year = year;
5261 vtm.mon = mon + 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;
5268 vtm.isdst = 0;
5269 vtm.zone = str_empty;
5271 usec = (long)(s & 0xfffff);
5272 nsec = usec * 1000;
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 */
5281 unsigned char *ptr;
5282 long len;
5283 int digit;
5284 ptr = (unsigned char*)StringValuePtr(submicro);
5285 len = RSTRING_LEN(submicro);
5286 nsec = 0;
5287 if (0 < len) {
5288 if (10 <= (digit = ptr[0] >> 4)) goto end_submicro;
5289 nsec += digit * 100;
5290 if (10 <= (digit = ptr[0] & 0xf)) goto end_submicro;
5291 nsec += digit * 10;
5293 if (1 < len) {
5294 if (10 <= (digit = ptr[1] >> 4)) goto end_submicro;
5295 nsec += digit;
5297 vtm.subsecx = addv(vtm.subsecx, mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
5298 end_submicro: ;
5300 timew = timegmw(&vtm);
5303 GetNewTimeval(time, tobj);
5304 tobj->tzmode = TIME_TZMODE_LOCALTIME;
5305 tobj->tm_got = 0;
5306 tobj->timew = timew;
5307 if (gmt) {
5308 TZMODE_SET_UTC(tobj);
5310 else if (!NIL_P(offset)) {
5311 time_set_utc_offset(time, offset);
5312 time_fixoff(time);
5314 if (!NIL_P(zone)) {
5315 zone = mload_zone(time, zone);
5316 tobj->vtm.zone = zone;
5317 zone_localtime(zone, time);
5320 return time;
5322 invalid_format:
5323 rb_raise(rb_eTypeError, "marshaled time format differ");
5324 UNREACHABLE_RETURN(Qundef);
5327 /* :nodoc: */
5328 static VALUE
5329 time_load(VALUE klass, VALUE str)
5331 VALUE time = time_s_alloc(klass);
5333 time_mload(time, str);
5334 return time;
5337 /* :nodoc:*/
5338 /* Document-class: Time::tm
5340 * A container class for timezone conversion.
5344 * call-seq:
5346 * Time::tm.from_time(t) -> tm
5348 * Creates new Time::tm object from a Time object.
5351 static VALUE
5352 tm_from_time(VALUE klass, VALUE time)
5354 struct time_object *tobj;
5355 struct vtm vtm, *v;
5356 #if TM_IS_TIME
5357 VALUE tm;
5358 struct time_object *ttm;
5360 GetTimeval(time, tobj);
5361 tm = time_s_alloc(klass);
5362 ttm = DATA_PTR(tm);
5363 v = &vtm;
5364 GMTIMEW(ttm->timew = tobj->timew, v);
5365 ttm->timew = wsub(ttm->timew, v->subsecx);
5366 v->subsecx = INT2FIX(0);
5367 v->zone = Qnil;
5368 ttm->vtm = *v;
5369 ttm->tm_got = 1;
5370 TZMODE_SET_UTC(ttm);
5371 return tm;
5372 #else
5373 VALUE args[8];
5374 int i = 0;
5376 GetTimeval(time, tobj);
5377 if (tobj->tm_got && TZMODE_UTC_P(tobj))
5378 v = &tobj->vtm;
5379 else
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);
5387 switch (v->isdst) {
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);
5394 #endif
5398 * call-seq:
5400 * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm
5402 * Creates new Time::tm object.
5405 static VALUE
5406 tm_initialize(int argc, VALUE *argv, VALUE tm)
5408 struct vtm vtm;
5409 wideval_t t;
5411 if (rb_check_arity(argc, 1, 7) > 6) argc = 6;
5412 time_arg(argc, argv, &vtm);
5413 t = timegmw(&vtm);
5415 #if TM_IS_TIME
5416 struct time_object *tobj = DATA_PTR(tm);
5417 tobj->tzmode = TIME_TZMODE_UTC;
5418 tobj->timew = t;
5419 tobj->vtm = vtm;
5420 #else
5421 int i = 0;
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)));
5429 #endif
5431 return tm;
5434 /* call-seq:
5436 * tm.to_time -> time
5438 * Returns a new Time object.
5441 static VALUE
5442 tm_to_time(VALUE tm)
5444 #if TM_IS_TIME
5445 struct time_object *torig = get_timeval(tm);
5446 VALUE dup = time_s_alloc(rb_cTime);
5447 struct time_object *tobj = DATA_PTR(dup);
5448 *tobj = *torig;
5449 return dup;
5450 #else
5451 VALUE t[6];
5452 const VALUE *p = RSTRUCT_CONST_PTR(tm);
5453 int i;
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);
5459 #endif
5462 #if !TM_IS_TIME
5463 static VALUE
5464 tm_zero(VALUE tm)
5466 return INT2FIX(0);
5469 #define tm_subsec tm_zero
5470 #define tm_utc_offset tm_zero
5472 static VALUE
5473 tm_isdst(VALUE tm)
5475 return Qfalse;
5478 static VALUE
5479 tm_to_s(VALUE tm)
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" "
5485 "UTC",
5486 p[5], p[4], p[3], p[2], p[1], p[0]);
5488 #else
5489 static VALUE
5490 tm_plus(VALUE tm, VALUE offset)
5492 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, +1);
5495 static VALUE
5496 tm_minus(VALUE tm, VALUE offset)
5498 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, -1);
5500 #endif
5502 static VALUE
5503 Init_tm(VALUE outer, const char *name)
5505 /* :stopdoc:*/
5506 VALUE tm;
5507 #if TM_IS_TIME
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);
5540 #else
5541 tm = rb_struct_define_under(outer, "tm",
5542 "sec", "min", "hour",
5543 "mday", "mon", "year",
5544 "to_i", NULL);
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);
5551 #endif
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);
5556 /* :startdoc:*/
5558 return tm;
5561 VALUE
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) {
5572 goto found;
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);
5578 goto found;
5580 #endif
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) {
5585 goto found;
5587 abbr = rb_check_funcall_default(zone, idName, 0, 0, Qnil);
5588 found:
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.
5603 void
5604 Init_Time(void)
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);
5709 #if 0
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);
5713 #endif
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"