[ruby/etc] bump up to 1.3.1
[ruby-80x24.org.git] / time.c
blob77f42fc89abbe4806b15f86302c8d38d6469ca99
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_set_utc_offset(VALUE time, VALUE off)
1937 struct time_object *tobj;
1938 off = num_exact(off);
1940 time_modify(time);
1941 GetTimeval(time, tobj);
1943 tobj->tm_got = 0;
1944 tobj->vtm.zone = Qnil;
1945 TZMODE_SET_FIXOFF(tobj, off);
1947 return time;
1950 static void
1951 vtm_add_offset(struct vtm *vtm, VALUE off, int sign)
1953 VALUE subsec, v;
1954 int sec, min, hour;
1955 int day;
1957 if (lt(off, INT2FIX(0))) {
1958 sign = -sign;
1959 off = neg(off);
1961 divmodv(off, INT2FIX(1), &off, &subsec);
1962 divmodv(off, INT2FIX(60), &off, &v);
1963 sec = NUM2INT(v);
1964 divmodv(off, INT2FIX(60), &off, &v);
1965 min = NUM2INT(v);
1966 divmodv(off, INT2FIX(24), &off, &v);
1967 hour = NUM2INT(v);
1969 if (sign < 0) {
1970 subsec = neg(subsec);
1971 sec = -sec;
1972 min = -min;
1973 hour = -hour;
1976 day = 0;
1978 if (!rb_equal(subsec, INT2FIX(0))) {
1979 vtm->subsecx = addv(vtm->subsecx, w2v(rb_time_magnify(v2w(subsec))));
1980 if (lt(vtm->subsecx, INT2FIX(0))) {
1981 vtm->subsecx = addv(vtm->subsecx, INT2FIX(TIME_SCALE));
1982 sec -= 1;
1984 if (le(INT2FIX(TIME_SCALE), vtm->subsecx)) {
1985 vtm->subsecx = subv(vtm->subsecx, INT2FIX(TIME_SCALE));
1986 sec += 1;
1989 if (sec) {
1990 /* If sec + subsec == 0, don't change vtm->sec.
1991 * It may be 60 which is a leap second. */
1992 sec += vtm->sec;
1993 if (sec < 0) {
1994 sec += 60;
1995 min -= 1;
1997 if (60 <= sec) {
1998 sec -= 60;
1999 min += 1;
2001 vtm->sec = sec;
2003 if (min) {
2004 min += vtm->min;
2005 if (min < 0) {
2006 min += 60;
2007 hour -= 1;
2009 if (60 <= min) {
2010 min -= 60;
2011 hour += 1;
2013 vtm->min = min;
2015 if (hour) {
2016 hour += vtm->hour;
2017 if (hour < 0) {
2018 hour += 24;
2019 day = -1;
2021 if (24 <= hour) {
2022 hour -= 24;
2023 day = 1;
2025 vtm->hour = hour;
2028 if (day) {
2029 if (day < 0) {
2030 if (vtm->mon == 1 && vtm->mday == 1) {
2031 vtm->mday = 31;
2032 vtm->mon = 12; /* December */
2033 vtm->year = subv(vtm->year, INT2FIX(1));
2034 vtm->yday = leap_year_v_p(vtm->year) ? 366 : 365;
2036 else if (vtm->mday == 1) {
2037 const int8_t *days_in_month = days_in_month_in_v(vtm->year);
2038 vtm->mon--;
2039 vtm->mday = days_in_month[vtm->mon-1];
2040 vtm->yday--;
2042 else {
2043 vtm->mday--;
2044 vtm->yday--;
2046 vtm->wday = (vtm->wday + 6) % 7;
2048 else {
2049 int leap = leap_year_v_p(vtm->year);
2050 if (vtm->mon == 12 && vtm->mday == 31) {
2051 vtm->year = addv(vtm->year, INT2FIX(1));
2052 vtm->mon = 1; /* January */
2053 vtm->mday = 1;
2054 vtm->yday = 1;
2056 else if (vtm->mday == days_in_month_of(leap)[vtm->mon-1]) {
2057 vtm->mon++;
2058 vtm->mday = 1;
2059 vtm->yday++;
2061 else {
2062 vtm->mday++;
2063 vtm->yday++;
2065 vtm->wday = (vtm->wday + 1) % 7;
2070 static int
2071 maybe_tzobj_p(VALUE obj)
2073 if (NIL_P(obj)) return FALSE;
2074 if (RB_INTEGER_TYPE_P(obj)) return FALSE;
2075 if (RB_TYPE_P(obj, T_STRING)) return FALSE;
2076 return TRUE;
2079 NORETURN(static void invalid_utc_offset(VALUE));
2080 static void
2081 invalid_utc_offset(VALUE zone)
2083 rb_raise(rb_eArgError, "\"+HH:MM\", \"-HH:MM\", \"UTC\" or "
2084 "\"A\"..\"I\",\"K\"..\"Z\" expected for utc_offset: %"PRIsVALUE,
2085 zone);
2088 static VALUE
2089 utc_offset_arg(VALUE arg)
2091 VALUE tmp;
2092 if (!NIL_P(tmp = rb_check_string_type(arg))) {
2093 int n = 0;
2094 const char *s = RSTRING_PTR(tmp), *min = NULL, *sec = NULL;
2095 if (!rb_enc_str_asciicompat_p(tmp)) {
2096 goto invalid_utc_offset;
2098 switch (RSTRING_LEN(tmp)) {
2099 case 1:
2100 if (s[0] == 'Z') {
2101 return UTC_ZONE;
2103 /* Military Time Zone Names */
2104 if (s[0] >= 'A' && s[0] <= 'I') {
2105 n = (int)s[0] - 'A' + 1;
2107 else if (s[0] >= 'K' && s[0] <= 'M') {
2108 n = (int)s[0] - 'A';
2110 else if (s[0] >= 'N' && s[0] <= 'Y') {
2111 n = 'M' - (int)s[0];
2113 else {
2114 goto invalid_utc_offset;
2116 n *= 3600;
2117 return INT2FIX(n);
2118 case 3:
2119 if (STRNCASECMP("UTC", s, 3) == 0) {
2120 return UTC_ZONE;
2122 break; /* +HH */
2123 case 5: /* +HHMM */
2124 min = s+3;
2125 break;
2126 case 6: /* +HH:MM */
2127 min = s+4;
2128 break;
2129 case 7: /* +HHMMSS */
2130 sec = s+5;
2131 min = s+3;
2132 break;
2133 case 9: /* +HH:MM:SS */
2134 sec = s+7;
2135 min = s+4;
2136 break;
2137 default:
2138 goto invalid_utc_offset;
2140 if (sec) {
2141 if (sec == s+7 && *(sec-1) != ':') goto invalid_utc_offset;
2142 if (!ISDIGIT(sec[0]) || !ISDIGIT(sec[1])) goto invalid_utc_offset;
2143 n += (sec[0] * 10 + sec[1] - '0' * 11);
2145 if (min) {
2146 if (min == s+4 && *(min-1) != ':') goto invalid_utc_offset;
2147 if (!ISDIGIT(min[0]) || !ISDIGIT(min[1])) goto invalid_utc_offset;
2148 if (min[0] > '5') goto invalid_utc_offset;
2149 n += (min[0] * 10 + min[1] - '0' * 11) * 60;
2151 if (s[0] != '+' && s[0] != '-') goto invalid_utc_offset;
2152 if (!ISDIGIT(s[1]) || !ISDIGIT(s[2])) goto invalid_utc_offset;
2153 n += (s[1] * 10 + s[2] - '0' * 11) * 3600;
2154 if (s[0] == '-') {
2155 if (n == 0) return UTC_ZONE;
2156 n = -n;
2158 return INT2FIX(n);
2160 else {
2161 return num_exact(arg);
2163 invalid_utc_offset:
2164 return Qnil;
2167 static void
2168 zone_set_offset(VALUE zone, struct time_object *tobj,
2169 wideval_t tlocal, wideval_t tutc)
2171 /* tlocal and tutc must be unmagnified and in seconds */
2172 wideval_t w = wsub(tlocal, tutc);
2173 VALUE off = w2v(w);
2174 validate_utc_offset(off);
2175 tobj->vtm.utc_offset = off;
2176 tobj->vtm.zone = zone;
2177 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2180 static wideval_t
2181 extract_time(VALUE time)
2183 wideval_t t;
2184 const ID id_to_i = idTo_i;
2186 #define EXTRACT_TIME() do { \
2187 t = v2w(rb_Integer(AREF(to_i))); \
2188 } while (0)
2190 if (rb_typeddata_is_kind_of(time, &time_data_type)) {
2191 struct time_object *tobj = DATA_PTR(time);
2193 time_gmtime(time); /* ensure tm got */
2194 t = rb_time_unmagnify(tobj->timew);
2196 else if (RB_TYPE_P(time, T_STRUCT)) {
2197 #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2198 EXTRACT_TIME();
2199 #undef AREF
2201 else {
2202 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2203 EXTRACT_TIME();
2204 #undef AREF
2206 #undef EXTRACT_TIME
2208 return t;
2211 static wideval_t
2212 extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx)
2214 wideval_t t;
2215 const ID id_to_i = idTo_i;
2217 #define EXTRACT_VTM() do { \
2218 VALUE subsecx; \
2219 vtm->year = obj2vint(AREF(year)); \
2220 vtm->mon = month_arg(AREF(mon)); \
2221 vtm->mday = obj2ubits(AREF(mday), 5); \
2222 vtm->hour = obj2ubits(AREF(hour), 5); \
2223 vtm->min = obj2ubits(AREF(min), 6); \
2224 vtm->sec = obj2subsecx(AREF(sec), &subsecx); \
2225 vtm->isdst = RTEST(AREF(isdst)); \
2226 vtm->utc_offset = Qnil; \
2227 t = v2w(rb_Integer(AREF(to_i))); \
2228 } while (0)
2230 if (rb_typeddata_is_kind_of(time, &time_data_type)) {
2231 struct time_object *tobj = DATA_PTR(time);
2233 time_get_tm(time, tobj);
2234 *vtm = tobj->vtm;
2235 t = rb_time_unmagnify(tobj->timew);
2236 if (TZMODE_FIXOFF_P(tobj) && vtm->utc_offset != INT2FIX(0))
2237 t = wadd(t, v2w(vtm->utc_offset));
2239 else if (RB_TYPE_P(time, T_STRUCT)) {
2240 #define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2241 EXTRACT_VTM();
2242 #undef AREF
2244 else if (rb_integer_type_p(time)) {
2245 t = v2w(time);
2246 GMTIMEW(rb_time_magnify(t), vtm);
2248 else {
2249 #define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2250 EXTRACT_VTM();
2251 #undef AREF
2253 #undef EXTRACT_VTM
2254 vtm->subsecx = subsecx;
2255 validate_vtm(vtm);
2256 return t;
2259 static void
2260 zone_set_dst(VALUE zone, struct time_object *tobj, VALUE tm)
2262 ID id_dst_p;
2263 VALUE dst;
2264 CONST_ID(id_dst_p, "dst?");
2265 dst = rb_check_funcall(zone, id_dst_p, 1, &tm);
2266 tobj->vtm.isdst = (dst != Qundef && RTEST(dst));
2269 static int
2270 zone_timelocal(VALUE zone, VALUE time)
2272 VALUE utc, tm;
2273 struct time_object *tobj = DATA_PTR(time);
2274 wideval_t t, s;
2276 t = rb_time_unmagnify(tobj->timew);
2277 tm = tm_from_time(rb_cTimeTM, time);
2278 utc = rb_check_funcall(zone, id_local_to_utc, 1, &tm);
2279 if (utc == Qundef) return 0;
2281 s = extract_time(utc);
2282 zone_set_offset(zone, tobj, t, s);
2283 s = rb_time_magnify(s);
2284 if (tobj->vtm.subsecx != INT2FIX(0)) {
2285 s = wadd(s, v2w(tobj->vtm.subsecx));
2287 tobj->timew = s;
2288 zone_set_dst(zone, tobj, tm);
2289 return 1;
2292 static int
2293 zone_localtime(VALUE zone, VALUE time)
2295 VALUE local, tm, subsecx;
2296 struct time_object *tobj = DATA_PTR(time);
2297 wideval_t t, s;
2299 split_second(tobj->timew, &t, &subsecx);
2300 tm = tm_from_time(rb_cTimeTM, time);
2302 local = rb_check_funcall(zone, id_utc_to_local, 1, &tm);
2303 if (local == Qundef) return 0;
2305 s = extract_vtm(local, &tobj->vtm, subsecx);
2306 tobj->tm_got = 1;
2307 zone_set_offset(zone, tobj, s, t);
2308 zone_set_dst(zone, tobj, tm);
2309 return 1;
2312 static VALUE
2313 find_timezone(VALUE time, VALUE zone)
2315 VALUE klass = CLASS_OF(time);
2317 return rb_check_funcall_default(klass, id_find_timezone, 1, &zone, Qnil);
2320 static VALUE
2321 time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday, VALUE hour, VALUE min, VALUE sec, VALUE zone)
2323 struct vtm vtm;
2324 VALUE utc = Qnil;
2325 struct time_object *tobj;
2327 vtm.wday = VTM_WDAY_INITVAL;
2328 vtm.yday = 0;
2329 vtm.zone = str_empty;
2331 vtm.year = obj2vint(year);
2333 vtm.mon = NIL_P(mon) ? 1 : month_arg(mon);
2335 vtm.mday = NIL_P(mday) ? 1 : obj2ubits(mday, 5);
2337 vtm.hour = NIL_P(hour) ? 0 : obj2ubits(hour, 5);
2339 vtm.min = NIL_P(min) ? 0 : obj2ubits(min, 6);
2341 if (NIL_P(sec)) {
2342 vtm.sec = 0;
2343 vtm.subsecx = INT2FIX(0);
2345 else {
2346 VALUE subsecx;
2347 vtm.sec = obj2subsecx(sec, &subsecx);
2348 vtm.subsecx = subsecx;
2351 vtm.isdst = VTM_ISDST_INITVAL;
2352 vtm.utc_offset = Qnil;
2353 const VALUE arg = zone;
2354 if (!NIL_P(arg)) {
2355 zone = Qnil;
2356 if (arg == ID2SYM(rb_intern("dst")))
2357 vtm.isdst = 1;
2358 else if (arg == ID2SYM(rb_intern("std")))
2359 vtm.isdst = 0;
2360 else if (maybe_tzobj_p(arg))
2361 zone = arg;
2362 else if (!NIL_P(utc = utc_offset_arg(arg)))
2363 vtm.utc_offset = utc == UTC_ZONE ? INT2FIX(0) : utc;
2364 else if (NIL_P(zone = find_timezone(time, arg)))
2365 invalid_utc_offset(arg);
2368 validate_vtm(&vtm);
2370 time_modify(time);
2371 GetNewTimeval(time, tobj);
2373 if (!NIL_P(zone)) {
2374 tobj->timew = timegmw(&vtm);
2375 tobj->vtm = vtm;
2376 tobj->tm_got = 1;
2377 TZMODE_SET_LOCALTIME(tobj);
2378 if (zone_timelocal(zone, time)) {
2379 return time;
2381 else if (NIL_P(vtm.utc_offset = utc_offset_arg(zone))) {
2382 if (NIL_P(zone = find_timezone(time, zone)) || !zone_timelocal(zone, time))
2383 invalid_utc_offset(arg);
2387 if (utc == UTC_ZONE) {
2388 tobj->timew = timegmw(&vtm);
2389 tobj->vtm = vtm;
2390 tobj->tm_got = 1;
2391 TZMODE_SET_UTC(tobj);
2392 return time;
2395 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2396 tobj->tm_got=0;
2397 tobj->timew = WINT2FIXWV(0);
2399 if (!NIL_P(vtm.utc_offset)) {
2400 VALUE off = vtm.utc_offset;
2401 vtm_add_offset(&vtm, off, -1);
2402 vtm.utc_offset = Qnil;
2403 tobj->timew = timegmw(&vtm);
2404 return time_set_utc_offset(time, off);
2406 else {
2407 tobj->timew = timelocalw(&vtm);
2408 return time_localtime(time);
2412 static void
2413 subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec)
2415 time_t sec = *secp;
2416 long subsec = *subsecp;
2417 long sec2;
2419 if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */
2420 sec2 = subsec / maxsubsec;
2421 if (TIMET_MAX - sec2 < sec) {
2422 rb_raise(rb_eRangeError, "out of Time range");
2424 subsec -= sec2 * maxsubsec;
2425 sec += sec2;
2427 else if (UNLIKELY(subsec < 0)) { /* subsec negative overflow */
2428 sec2 = NDIV(subsec, maxsubsec); /* negative div */
2429 if (sec < TIMET_MIN - sec2) {
2430 rb_raise(rb_eRangeError, "out of Time range");
2432 subsec -= sec2 * maxsubsec;
2433 sec += sec2;
2435 #ifndef NEGATIVE_TIME_T
2436 if (sec < 0)
2437 rb_raise(rb_eArgError, "time must be positive");
2438 #endif
2439 *secp = sec;
2440 *subsecp = subsec;
2443 #define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
2444 #define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000)
2446 static wideval_t
2447 nsec2timew(time_t sec, long nsec)
2449 time_nsec_normalize(&sec, &nsec);
2450 return timenano2timew(sec, nsec);
2453 static VALUE
2454 time_new_timew(VALUE klass, wideval_t timew)
2456 VALUE time = time_s_alloc(klass);
2457 struct time_object *tobj;
2459 tobj = DATA_PTR(time); /* skip type check */
2460 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2461 tobj->timew = timew;
2463 return time;
2466 VALUE
2467 rb_time_new(time_t sec, long usec)
2469 time_usec_normalize(&sec, &usec);
2470 return time_new_timew(rb_cTime, timenano2timew(sec, usec * 1000));
2473 /* returns localtime time object */
2474 VALUE
2475 rb_time_nano_new(time_t sec, long nsec)
2477 return time_new_timew(rb_cTime, nsec2timew(sec, nsec));
2480 VALUE
2481 rb_time_timespec_new(const struct timespec *ts, int offset)
2483 struct time_object *tobj;
2484 VALUE time = time_new_timew(rb_cTime, nsec2timew(ts->tv_sec, ts->tv_nsec));
2486 if (-86400 < offset && offset < 86400) { /* fixoff */
2487 GetTimeval(time, tobj);
2488 TZMODE_SET_FIXOFF(tobj, INT2FIX(offset));
2490 else if (offset == INT_MAX) { /* localtime */
2492 else if (offset == INT_MAX-1) { /* UTC */
2493 GetTimeval(time, tobj);
2494 TZMODE_SET_UTC(tobj);
2496 else {
2497 rb_raise(rb_eArgError, "utc_offset out of range");
2500 return time;
2503 VALUE
2504 rb_time_num_new(VALUE timev, VALUE off)
2506 VALUE time = time_new_timew(rb_cTime, rb_time_magnify(v2w(timev)));
2508 if (!NIL_P(off)) {
2509 VALUE zone = off;
2511 if (maybe_tzobj_p(zone)) {
2512 time_gmtime(time);
2513 if (zone_timelocal(zone, time)) return time;
2515 if (NIL_P(off = utc_offset_arg(off))) {
2516 off = zone;
2517 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
2518 time_gmtime(time);
2519 if (!zone_timelocal(zone, time)) invalid_utc_offset(off);
2520 return time;
2522 else if (off == UTC_ZONE) {
2523 return time_gmtime(time);
2526 validate_utc_offset(off);
2527 time_set_utc_offset(time, off);
2528 return time;
2531 return time;
2534 static struct timespec
2535 time_timespec(VALUE num, int interval)
2537 struct timespec t;
2538 const char *const tstr = interval ? "time interval" : "time";
2539 VALUE i, f, ary;
2541 #ifndef NEGATIVE_TIME_T
2542 # define arg_range_check(v) \
2543 (((v) < 0) ? \
2544 rb_raise(rb_eArgError, "%s must not be negative", tstr) : \
2545 (void)0)
2546 #else
2547 # define arg_range_check(v) \
2548 ((interval && (v) < 0) ? \
2549 rb_raise(rb_eArgError, "time interval must not be negative") : \
2550 (void)0)
2551 #endif
2553 if (FIXNUM_P(num)) {
2554 t.tv_sec = NUM2TIMET(num);
2555 arg_range_check(t.tv_sec);
2556 t.tv_nsec = 0;
2558 else if (RB_FLOAT_TYPE_P(num)) {
2559 double x = RFLOAT_VALUE(num);
2560 arg_range_check(x);
2562 double f, d;
2564 d = modf(x, &f);
2565 if (d >= 0) {
2566 t.tv_nsec = (int)(d*1e9+0.5);
2567 if (t.tv_nsec >= 1000000000) {
2568 t.tv_nsec -= 1000000000;
2569 f += 1;
2572 else if ((t.tv_nsec = (int)(-d*1e9+0.5)) > 0) {
2573 t.tv_nsec = 1000000000 - t.tv_nsec;
2574 f -= 1;
2576 t.tv_sec = (time_t)f;
2577 if (f != t.tv_sec) {
2578 rb_raise(rb_eRangeError, "%f out of Time range", x);
2582 else if (RB_BIGNUM_TYPE_P(num)) {
2583 t.tv_sec = NUM2TIMET(num);
2584 arg_range_check(t.tv_sec);
2585 t.tv_nsec = 0;
2587 else {
2588 i = INT2FIX(1);
2589 ary = rb_check_funcall(num, id_divmod, 1, &i);
2590 if (ary != Qundef && !NIL_P(ary = rb_check_array_type(ary))) {
2591 i = rb_ary_entry(ary, 0);
2592 f = rb_ary_entry(ary, 1);
2593 t.tv_sec = NUM2TIMET(i);
2594 arg_range_check(t.tv_sec);
2595 f = rb_funcall(f, '*', 1, INT2FIX(1000000000));
2596 t.tv_nsec = NUM2LONG(f);
2598 else {
2599 rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into %s",
2600 rb_obj_class(num), tstr);
2603 return t;
2604 #undef arg_range_check
2607 static struct timeval
2608 time_timeval(VALUE num, int interval)
2610 struct timespec ts;
2611 struct timeval tv;
2613 ts = time_timespec(num, interval);
2614 tv.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
2615 tv.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
2617 return tv;
2620 struct timeval
2621 rb_time_interval(VALUE num)
2623 return time_timeval(num, TRUE);
2626 struct timeval
2627 rb_time_timeval(VALUE time)
2629 struct time_object *tobj;
2630 struct timeval t;
2631 struct timespec ts;
2633 if (IsTimeval(time)) {
2634 GetTimeval(time, tobj);
2635 ts = timew2timespec(tobj->timew);
2636 t.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
2637 t.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
2638 return t;
2640 return time_timeval(time, FALSE);
2643 struct timespec
2644 rb_time_timespec(VALUE time)
2646 struct time_object *tobj;
2647 struct timespec t;
2649 if (IsTimeval(time)) {
2650 GetTimeval(time, tobj);
2651 t = timew2timespec(tobj->timew);
2652 return t;
2654 return time_timespec(time, FALSE);
2657 struct timespec
2658 rb_time_timespec_interval(VALUE num)
2660 return time_timespec(num, TRUE);
2663 static int
2664 get_scale(VALUE unit)
2666 if (unit == ID2SYM(id_nanosecond) || unit == ID2SYM(id_nsec)) {
2667 return 1000000000;
2669 else if (unit == ID2SYM(id_microsecond) || unit == ID2SYM(id_usec)) {
2670 return 1000000;
2672 else if (unit == ID2SYM(id_millisecond)) {
2673 return 1000;
2675 else {
2676 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
2680 static VALUE
2681 time_s_at(rb_execution_context_t *ec, VALUE klass, VALUE time, VALUE subsec, VALUE unit, VALUE zone)
2683 VALUE t;
2684 wideval_t timew;
2686 if (subsec) {
2687 int scale = get_scale(unit);
2688 time = num_exact(time);
2689 t = num_exact(subsec);
2690 timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, scale));
2691 t = time_new_timew(klass, timew);
2693 else if (IsTimeval(time)) {
2694 struct time_object *tobj, *tobj2;
2695 GetTimeval(time, tobj);
2696 t = time_new_timew(klass, tobj->timew);
2697 GetTimeval(t, tobj2);
2698 TZMODE_COPY(tobj2, tobj);
2700 else {
2701 timew = rb_time_magnify(v2w(num_exact(time)));
2702 t = time_new_timew(klass, timew);
2704 if (!NIL_P(zone)) {
2705 time_zonelocal(t, zone);
2708 return t;
2711 static VALUE
2712 time_s_at1(rb_execution_context_t *ec, VALUE klass, VALUE time)
2714 return time_s_at(ec, klass, time, Qfalse, ID2SYM(id_microsecond), Qnil);
2717 static const char months[][4] = {
2718 "jan", "feb", "mar", "apr", "may", "jun",
2719 "jul", "aug", "sep", "oct", "nov", "dec",
2722 static int
2723 obj2int(VALUE obj)
2725 if (RB_TYPE_P(obj, T_STRING)) {
2726 obj = rb_str_to_inum(obj, 10, TRUE);
2729 return NUM2INT(obj);
2732 /* bits should be 0 <= x <= 31 */
2733 static uint32_t
2734 obj2ubits(VALUE obj, unsigned int bits)
2736 const unsigned int usable_mask = (1U << bits) - 1;
2737 unsigned int rv = (unsigned int)obj2int(obj);
2739 if ((rv & usable_mask) != rv)
2740 rb_raise(rb_eArgError, "argument out of range");
2741 return (uint32_t)rv;
2744 static VALUE
2745 obj2vint(VALUE obj)
2747 if (RB_TYPE_P(obj, T_STRING)) {
2748 obj = rb_str_to_inum(obj, 10, TRUE);
2750 else {
2751 obj = rb_to_int(obj);
2754 return obj;
2757 static uint32_t
2758 obj2subsecx(VALUE obj, VALUE *subsecx)
2760 VALUE subsec;
2762 if (RB_TYPE_P(obj, T_STRING)) {
2763 obj = rb_str_to_inum(obj, 10, TRUE);
2764 *subsecx = INT2FIX(0);
2766 else {
2767 divmodv(num_exact(obj), INT2FIX(1), &obj, &subsec);
2768 *subsecx = w2v(rb_time_magnify(v2w(subsec)));
2770 return obj2ubits(obj, 6); /* vtm->sec */
2773 static VALUE
2774 usec2subsecx(VALUE obj)
2776 if (RB_TYPE_P(obj, T_STRING)) {
2777 obj = rb_str_to_inum(obj, 10, TRUE);
2780 return mulquov(num_exact(obj), INT2FIX(TIME_SCALE), INT2FIX(1000000));
2783 static uint32_t
2784 month_arg(VALUE arg)
2786 int i, mon;
2788 if (FIXNUM_P(arg)) {
2789 return obj2ubits(arg, 4);
2792 mon = 0;
2793 VALUE s = rb_check_string_type(arg);
2794 if (!NIL_P(s) && RSTRING_LEN(s) > 0) {
2795 arg = s;
2796 for (i=0; i<12; i++) {
2797 if (RSTRING_LEN(s) == 3 &&
2798 STRNCASECMP(months[i], RSTRING_PTR(s), 3) == 0) {
2799 mon = i+1;
2800 break;
2804 if (mon == 0) {
2805 mon = obj2ubits(arg, 4);
2807 return mon;
2810 static VALUE
2811 validate_utc_offset(VALUE utc_offset)
2813 if (le(utc_offset, INT2FIX(-86400)) || ge(utc_offset, INT2FIX(86400)))
2814 rb_raise(rb_eArgError, "utc_offset out of range");
2815 return utc_offset;
2818 static VALUE
2819 validate_zone_name(VALUE zone_name)
2821 StringValueCStr(zone_name);
2822 return zone_name;
2825 static void
2826 validate_vtm(struct vtm *vtm)
2828 #define validate_vtm_range(mem, b, e) \
2829 ((vtm->mem < b || vtm->mem > e) ? \
2830 rb_raise(rb_eArgError, #mem" out of range") : (void)0)
2831 validate_vtm_range(mon, 1, 12);
2832 validate_vtm_range(mday, 1, 31);
2833 validate_vtm_range(hour, 0, 24);
2834 validate_vtm_range(min, 0, (vtm->hour == 24 ? 0 : 59));
2835 validate_vtm_range(sec, 0, (vtm->hour == 24 ? 0 : 60));
2836 if (lt(vtm->subsecx, INT2FIX(0)) || ge(vtm->subsecx, INT2FIX(TIME_SCALE)))
2837 rb_raise(rb_eArgError, "subsecx out of range");
2838 if (!NIL_P(vtm->utc_offset)) validate_utc_offset(vtm->utc_offset);
2839 #undef validate_vtm_range
2842 static void
2843 time_arg(int argc, const VALUE *argv, struct vtm *vtm)
2845 VALUE v[8];
2846 VALUE subsecx = INT2FIX(0);
2848 vtm->year = INT2FIX(0);
2849 vtm->mon = 0;
2850 vtm->mday = 0;
2851 vtm->hour = 0;
2852 vtm->min = 0;
2853 vtm->sec = 0;
2854 vtm->subsecx = INT2FIX(0);
2855 vtm->utc_offset = Qnil;
2856 vtm->wday = 0;
2857 vtm->yday = 0;
2858 vtm->isdst = 0;
2859 vtm->zone = str_empty;
2861 if (argc == 10) {
2862 v[0] = argv[5];
2863 v[1] = argv[4];
2864 v[2] = argv[3];
2865 v[3] = argv[2];
2866 v[4] = argv[1];
2867 v[5] = argv[0];
2868 v[6] = Qnil;
2869 vtm->isdst = RTEST(argv[8]) ? 1 : 0;
2871 else {
2872 rb_scan_args(argc, argv, "17", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6],&v[7]);
2873 /* v[6] may be usec or zone (parsedate) */
2874 /* v[7] is wday (parsedate; ignored) */
2875 vtm->wday = VTM_WDAY_INITVAL;
2876 vtm->isdst = VTM_ISDST_INITVAL;
2879 vtm->year = obj2vint(v[0]);
2881 if (NIL_P(v[1])) {
2882 vtm->mon = 1;
2884 else {
2885 vtm->mon = month_arg(v[1]);
2888 if (NIL_P(v[2])) {
2889 vtm->mday = 1;
2891 else {
2892 vtm->mday = obj2ubits(v[2], 5);
2895 /* normalize month-mday */
2896 switch (vtm->mon) {
2897 case 2:
2899 /* this drops higher bits but it's not a problem to calc leap year */
2900 unsigned int mday2 = leap_year_v_p(vtm->year) ? 29 : 28;
2901 if (vtm->mday > mday2) {
2902 vtm->mday -= mday2;
2903 vtm->mon++;
2906 break;
2907 case 4:
2908 case 6:
2909 case 9:
2910 case 11:
2911 if (vtm->mday == 31) {
2912 vtm->mon++;
2913 vtm->mday = 1;
2915 break;
2918 vtm->hour = NIL_P(v[3])?0:obj2ubits(v[3], 5);
2920 vtm->min = NIL_P(v[4])?0:obj2ubits(v[4], 6);
2922 if (!NIL_P(v[6]) && argc == 7) {
2923 vtm->sec = NIL_P(v[5])?0:obj2ubits(v[5],6);
2924 subsecx = usec2subsecx(v[6]);
2926 else {
2927 /* when argc == 8, v[6] is timezone, but ignored */
2928 if (NIL_P(v[5])) {
2929 vtm->sec = 0;
2931 else {
2932 vtm->sec = obj2subsecx(v[5], &subsecx);
2935 vtm->subsecx = subsecx;
2937 validate_vtm(vtm);
2938 RB_GC_GUARD(subsecx);
2941 static int
2942 leap_year_p(long y)
2944 /* TODO:
2945 * ensure about negative years in proleptic Gregorian calendar.
2947 unsigned long uy = (unsigned long)(LIKELY(y >= 0) ? y : -y);
2949 if (LIKELY(uy % 4 != 0)) return 0;
2951 unsigned long century = uy / 100;
2952 if (LIKELY(uy != century * 100)) return 1;
2953 return century % 4 == 0;
2956 static time_t
2957 timegm_noleapsecond(struct tm *tm)
2959 long tm_year = tm->tm_year;
2960 int tm_yday = calc_tm_yday(tm->tm_year, tm->tm_mon, tm->tm_mday);
2963 * `Seconds Since the Epoch' in SUSv3:
2964 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
2965 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
2966 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
2968 return tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 +
2969 (time_t)(tm_yday +
2970 (tm_year-70)*365 +
2971 DIV(tm_year-69,4) -
2972 DIV(tm_year-1,100) +
2973 DIV(tm_year+299,400))*86400;
2976 #if 0
2977 #define DEBUG_FIND_TIME_NUMGUESS
2978 #define DEBUG_GUESSRANGE
2979 #endif
2981 static const bool debug_guessrange =
2982 #ifdef DEBUG_GUESSRANGE
2983 true;
2984 #else
2985 false;
2986 #endif
2988 #define DEBUG_REPORT_GUESSRANGE \
2989 (debug_guessrange ? debug_report_guessrange(guess_lo, guess_hi) : (void)0)
2991 static inline void
2992 debug_report_guessrange(time_t guess_lo, time_t guess_hi)
2994 unsigned_time_t guess_diff = (unsigned_time_t)(guess_hi-guess_lo);
2995 fprintf(stderr, "find time guess range: %"PRI_TIMET_PREFIX"d - "
2996 "%"PRI_TIMET_PREFIX"d : %"PRI_TIMET_PREFIX"u\n",
2997 guess_lo, guess_hi, guess_diff);
3000 static const bool debug_find_time_numguess =
3001 #ifdef DEBUG_FIND_TIME_NUMGUESS
3002 true;
3003 #else
3004 false;
3005 #endif
3007 #define DEBUG_FIND_TIME_NUMGUESS_INC \
3008 (void)(debug_find_time_numguess && find_time_numguess++),
3009 static unsigned long long find_time_numguess;
3011 static VALUE
3012 find_time_numguess_getter(ID name, VALUE *data)
3014 unsigned long long *numguess = (void *)data;
3015 return ULL2NUM(*numguess);
3018 static const char *
3019 find_time_t(struct tm *tptr, int utc_p, time_t *tp)
3021 time_t guess, guess0, guess_lo, guess_hi;
3022 struct tm *tm, tm0, tm_lo, tm_hi;
3023 int d;
3024 int find_dst;
3025 struct tm result;
3026 int status;
3027 int tptr_tm_yday;
3029 #define GUESS(p) (DEBUG_FIND_TIME_NUMGUESS_INC (utc_p ? gmtime_with_leapsecond((p), &result) : LOCALTIME((p), result)))
3031 guess_lo = TIMET_MIN;
3032 guess_hi = TIMET_MAX;
3034 find_dst = 0 < tptr->tm_isdst;
3036 /* /etc/localtime might be changed. reload it. */
3037 update_tz();
3039 tm0 = *tptr;
3040 if (tm0.tm_mon < 0) {
3041 tm0.tm_mon = 0;
3042 tm0.tm_mday = 1;
3043 tm0.tm_hour = 0;
3044 tm0.tm_min = 0;
3045 tm0.tm_sec = 0;
3047 else if (11 < tm0.tm_mon) {
3048 tm0.tm_mon = 11;
3049 tm0.tm_mday = 31;
3050 tm0.tm_hour = 23;
3051 tm0.tm_min = 59;
3052 tm0.tm_sec = 60;
3054 else if (tm0.tm_mday < 1) {
3055 tm0.tm_mday = 1;
3056 tm0.tm_hour = 0;
3057 tm0.tm_min = 0;
3058 tm0.tm_sec = 0;
3060 else if ((d = days_in_month_in(1900 + tm0.tm_year)[tm0.tm_mon]) < tm0.tm_mday) {
3061 tm0.tm_mday = d;
3062 tm0.tm_hour = 23;
3063 tm0.tm_min = 59;
3064 tm0.tm_sec = 60;
3066 else if (tm0.tm_hour < 0) {
3067 tm0.tm_hour = 0;
3068 tm0.tm_min = 0;
3069 tm0.tm_sec = 0;
3071 else if (23 < tm0.tm_hour) {
3072 tm0.tm_hour = 23;
3073 tm0.tm_min = 59;
3074 tm0.tm_sec = 60;
3076 else if (tm0.tm_min < 0) {
3077 tm0.tm_min = 0;
3078 tm0.tm_sec = 0;
3080 else if (59 < tm0.tm_min) {
3081 tm0.tm_min = 59;
3082 tm0.tm_sec = 60;
3084 else if (tm0.tm_sec < 0) {
3085 tm0.tm_sec = 0;
3087 else if (60 < tm0.tm_sec) {
3088 tm0.tm_sec = 60;
3091 DEBUG_REPORT_GUESSRANGE;
3092 guess0 = guess = timegm_noleapsecond(&tm0);
3093 tm = GUESS(&guess);
3094 if (tm) {
3095 d = tmcmp(tptr, tm);
3096 if (d == 0) { goto found; }
3097 if (d < 0) {
3098 guess_hi = guess;
3099 guess -= 24 * 60 * 60;
3101 else {
3102 guess_lo = guess;
3103 guess += 24 * 60 * 60;
3105 DEBUG_REPORT_GUESSRANGE;
3106 if (guess_lo < guess && guess < guess_hi && (tm = GUESS(&guess)) != NULL) {
3107 d = tmcmp(tptr, tm);
3108 if (d == 0) { goto found; }
3109 if (d < 0)
3110 guess_hi = guess;
3111 else
3112 guess_lo = guess;
3113 DEBUG_REPORT_GUESSRANGE;
3117 tm = GUESS(&guess_lo);
3118 if (!tm) goto error;
3119 d = tmcmp(tptr, tm);
3120 if (d < 0) goto out_of_range;
3121 if (d == 0) { guess = guess_lo; goto found; }
3122 tm_lo = *tm;
3124 tm = GUESS(&guess_hi);
3125 if (!tm) goto error;
3126 d = tmcmp(tptr, tm);
3127 if (d > 0) goto out_of_range;
3128 if (d == 0) { guess = guess_hi; goto found; }
3129 tm_hi = *tm;
3131 DEBUG_REPORT_GUESSRANGE;
3133 status = 1;
3135 while (guess_lo + 1 < guess_hi) {
3136 binsearch:
3137 if (status == 0) {
3138 guess = guess_lo / 2 + guess_hi / 2;
3139 if (guess <= guess_lo)
3140 guess = guess_lo + 1;
3141 else if (guess >= guess_hi)
3142 guess = guess_hi - 1;
3143 status = 1;
3145 else {
3146 if (status == 1) {
3147 time_t guess0_hi = timegm_noleapsecond(&tm_hi);
3148 guess = guess_hi - (guess0_hi - guess0);
3149 if (guess == guess_hi) /* hh:mm:60 tends to cause this condition. */
3150 guess--;
3151 status = 2;
3153 else if (status == 2) {
3154 time_t guess0_lo = timegm_noleapsecond(&tm_lo);
3155 guess = guess_lo + (guess0 - guess0_lo);
3156 if (guess == guess_lo)
3157 guess++;
3158 status = 0;
3160 if (guess <= guess_lo || guess_hi <= guess) {
3161 /* Previous guess is invalid. try binary search. */
3162 if (debug_guessrange) {
3163 if (guess <= guess_lo) {
3164 fprintf(stderr, "too small guess: %"PRI_TIMET_PREFIX"d"\
3165 " <= %"PRI_TIMET_PREFIX"d\n", guess, guess_lo);
3167 if (guess_hi <= guess) {
3168 fprintf(stderr, "too big guess: %"PRI_TIMET_PREFIX"d"\
3169 " <= %"PRI_TIMET_PREFIX"d\n", guess_hi, guess);
3172 status = 0;
3173 goto binsearch;
3177 tm = GUESS(&guess);
3178 if (!tm) goto error;
3180 d = tmcmp(tptr, tm);
3182 if (d < 0) {
3183 guess_hi = guess;
3184 tm_hi = *tm;
3185 DEBUG_REPORT_GUESSRANGE;
3187 else if (d > 0) {
3188 guess_lo = guess;
3189 tm_lo = *tm;
3190 DEBUG_REPORT_GUESSRANGE;
3192 else {
3193 goto found;
3197 /* Given argument has no corresponding time_t. Let's extrapolate. */
3199 * `Seconds Since the Epoch' in SUSv3:
3200 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
3201 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
3202 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
3205 tptr_tm_yday = calc_tm_yday(tptr->tm_year, tptr->tm_mon, tptr->tm_mday);
3207 *tp = guess_lo +
3208 ((tptr->tm_year - tm_lo.tm_year) * 365 +
3209 DIV((tptr->tm_year-69), 4) -
3210 DIV((tptr->tm_year-1), 100) +
3211 DIV((tptr->tm_year+299), 400) -
3212 DIV((tm_lo.tm_year-69), 4) +
3213 DIV((tm_lo.tm_year-1), 100) -
3214 DIV((tm_lo.tm_year+299), 400) +
3215 tptr_tm_yday -
3216 tm_lo.tm_yday) * 86400 +
3217 (tptr->tm_hour - tm_lo.tm_hour) * 3600 +
3218 (tptr->tm_min - tm_lo.tm_min) * 60 +
3219 (tptr->tm_sec - (tm_lo.tm_sec == 60 ? 59 : tm_lo.tm_sec));
3221 return NULL;
3223 found:
3224 if (!utc_p) {
3225 /* If localtime is nonmonotonic, another result may exist. */
3226 time_t guess2;
3227 if (find_dst) {
3228 guess2 = guess - 2 * 60 * 60;
3229 tm = LOCALTIME(&guess2, result);
3230 if (tm) {
3231 if (tptr->tm_hour != (tm->tm_hour + 2) % 24 ||
3232 tptr->tm_min != tm->tm_min ||
3233 tptr->tm_sec != tm->tm_sec) {
3234 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
3235 (tm->tm_min - tptr->tm_min) * 60 +
3236 (tm->tm_sec - tptr->tm_sec);
3237 if (tptr->tm_mday != tm->tm_mday)
3238 guess2 += 24 * 60 * 60;
3239 if (guess != guess2) {
3240 tm = LOCALTIME(&guess2, result);
3241 if (tm && tmcmp(tptr, tm) == 0) {
3242 if (guess < guess2)
3243 *tp = guess;
3244 else
3245 *tp = guess2;
3246 return NULL;
3252 else {
3253 guess2 = guess + 2 * 60 * 60;
3254 tm = LOCALTIME(&guess2, result);
3255 if (tm) {
3256 if ((tptr->tm_hour + 2) % 24 != tm->tm_hour ||
3257 tptr->tm_min != tm->tm_min ||
3258 tptr->tm_sec != tm->tm_sec) {
3259 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
3260 (tm->tm_min - tptr->tm_min) * 60 +
3261 (tm->tm_sec - tptr->tm_sec);
3262 if (tptr->tm_mday != tm->tm_mday)
3263 guess2 -= 24 * 60 * 60;
3264 if (guess != guess2) {
3265 tm = LOCALTIME(&guess2, result);
3266 if (tm && tmcmp(tptr, tm) == 0) {
3267 if (guess < guess2)
3268 *tp = guess2;
3269 else
3270 *tp = guess;
3271 return NULL;
3278 *tp = guess;
3279 return NULL;
3281 out_of_range:
3282 return "time out of range";
3284 error:
3285 return "gmtime/localtime error";
3288 static int
3289 vtmcmp(struct vtm *a, struct vtm *b)
3291 if (ne(a->year, b->year))
3292 return lt(a->year, b->year) ? -1 : 1;
3293 else if (a->mon != b->mon)
3294 return a->mon < b->mon ? -1 : 1;
3295 else if (a->mday != b->mday)
3296 return a->mday < b->mday ? -1 : 1;
3297 else if (a->hour != b->hour)
3298 return a->hour < b->hour ? -1 : 1;
3299 else if (a->min != b->min)
3300 return a->min < b->min ? -1 : 1;
3301 else if (a->sec != b->sec)
3302 return a->sec < b->sec ? -1 : 1;
3303 else if (ne(a->subsecx, b->subsecx))
3304 return lt(a->subsecx, b->subsecx) ? -1 : 1;
3305 else
3306 return 0;
3309 static int
3310 tmcmp(struct tm *a, struct tm *b)
3312 if (a->tm_year != b->tm_year)
3313 return a->tm_year < b->tm_year ? -1 : 1;
3314 else if (a->tm_mon != b->tm_mon)
3315 return a->tm_mon < b->tm_mon ? -1 : 1;
3316 else if (a->tm_mday != b->tm_mday)
3317 return a->tm_mday < b->tm_mday ? -1 : 1;
3318 else if (a->tm_hour != b->tm_hour)
3319 return a->tm_hour < b->tm_hour ? -1 : 1;
3320 else if (a->tm_min != b->tm_min)
3321 return a->tm_min < b->tm_min ? -1 : 1;
3322 else if (a->tm_sec != b->tm_sec)
3323 return a->tm_sec < b->tm_sec ? -1 : 1;
3324 else
3325 return 0;
3329 * call-seq:
3330 * Time.utc(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3331 * Time.utc(sec_i, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3333 * Returns a new \Time object based the on given arguments;
3334 * its timezone is UTC.
3336 * In the first form (up to seven arguments), argument +year+ is required.
3338 * Time.utc(2000) # => 2000-01-01 00:00:00 UTC
3339 * Time.utc(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 UTC
3341 * In the second form, all ten arguments are required,
3342 * though the last four are ignored.
3343 * This form is useful for creating a time from a 10-element array
3344 * such as is returned by #to_a.
3346 * array = Time.now.to_a
3347 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3348 * array[5] = 2000
3349 * Time.utc(*array) # => 2000-04-24 13:26:57 UTC
3351 * Parameters:
3352 * :include: doc/time/year.rdoc
3353 * :include: doc/time/mon-min.rdoc
3354 * :include: doc/time/sec_i.rdoc
3355 * :include: doc/time/usec.rdoc
3357 * Alias: Time.gm.
3359 * Related: Time.local.
3362 static VALUE
3363 time_s_mkutc(int argc, VALUE *argv, VALUE klass)
3365 struct vtm vtm;
3367 time_arg(argc, argv, &vtm);
3368 return time_gmtime(time_new_timew(klass, timegmw(&vtm)));
3372 * call-seq:
3373 * Time.local(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3374 * Time.local(sec, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3376 * Returns a new \Time object based the on given arguments;
3377 * its timezone is the local timezone.
3379 * In the first form (up to seven arguments), argument +year+ is required.
3381 * Time.local(2000) # => 2000-01-01 00:00:00 -0600
3382 * Time.local(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 -0600
3384 * In the second form, all ten arguments are required,
3385 * though the last four are ignored.
3386 * This form is useful for creating a time from a 10-element array
3387 * such as those returned by #to_a.
3389 * array = Time.now.to_a
3390 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3391 * array[5] = 2000
3392 * Time.local(*array) # => 2000-04-24 13:26:57 -0500
3394 * Parameters:
3395 * :include: doc/time/year.rdoc
3396 * :include: doc/time/mon-min.rdoc
3397 * :include: doc/time/sec_i.rdoc
3398 * :include: doc/time/usec.rdoc
3400 * Alias: Time.mktime.
3402 * Related: Time.utc.
3405 static VALUE
3406 time_s_mktime(int argc, VALUE *argv, VALUE klass)
3408 struct vtm vtm;
3410 time_arg(argc, argv, &vtm);
3411 return time_localtime(time_new_timew(klass, timelocalw(&vtm)));
3415 * call-seq:
3416 * time.to_i -> int
3417 * time.tv_sec -> int
3419 * Returns the value of _time_ as an integer number of seconds
3420 * since the Epoch.
3422 * If _time_ contains subsecond, they are truncated.
3424 * t = Time.now #=> 2020-07-21 01:41:29.746012609 +0900
3425 * t.to_i #=> 1595263289
3428 static VALUE
3429 time_to_i(VALUE time)
3431 struct time_object *tobj;
3433 GetTimeval(time, tobj);
3434 return w2v(wdiv(tobj->timew, WINT2FIXWV(TIME_SCALE)));
3438 * call-seq:
3439 * time.to_f -> float
3441 * Returns the value of _time_ as a floating point number of
3442 * seconds since the Epoch.
3443 * The return value approximate the exact value in the Time object
3444 * because floating point numbers cannot represent all rational numbers
3445 * exactly.
3447 * t = Time.now #=> 2020-07-20 22:00:29.38740268 +0900
3448 * t.to_f #=> 1595250029.3874028
3449 * t.to_i #=> 1595250029
3451 * Note that IEEE 754 double is not accurate enough to represent
3452 * the exact number of nanoseconds since the Epoch.
3453 * (IEEE 754 double has 53bit mantissa.
3454 * So it can represent exact number of nanoseconds only in
3455 * <tt>2 ** 53 / 1_000_000_000 / 60 / 60 / 24 = 104.2</tt> days.)
3456 * When Ruby uses a nanosecond-resolution clock function,
3457 * such as +clock_gettime+ of POSIX, to obtain the current time,
3458 * Time#to_f can lose information of a Time object created with +Time.now+.
3461 static VALUE
3462 time_to_f(VALUE time)
3464 struct time_object *tobj;
3466 GetTimeval(time, tobj);
3467 return rb_Float(rb_time_unmagnify_to_float(tobj->timew));
3471 * call-seq:
3472 * time.to_r -> a_rational
3474 * Returns the value of _time_ as a rational number of seconds
3475 * since the Epoch.
3477 * t = Time.now #=> 2020-07-20 22:03:45.212167333 +0900
3478 * t.to_r #=> (1595250225212167333/1000000000)
3480 * This method is intended to be used to get an accurate value
3481 * representing the seconds (including subsecond) since the Epoch.
3484 static VALUE
3485 time_to_r(VALUE time)
3487 struct time_object *tobj;
3488 VALUE v;
3490 GetTimeval(time, tobj);
3491 v = rb_time_unmagnify_to_rational(tobj->timew);
3492 if (!RB_TYPE_P(v, T_RATIONAL)) {
3493 v = rb_Rational1(v);
3495 return v;
3499 * call-seq:
3500 * time.usec -> int
3501 * time.tv_usec -> int
3503 * Returns the number of microseconds for the subsecond part of _time_.
3504 * The result is a non-negative integer less than 10**6.
3506 * t = Time.now #=> 2020-07-20 22:05:58.459785953 +0900
3507 * t.usec #=> 459785
3509 * If _time_ has fraction of microsecond (such as nanoseconds),
3510 * it is truncated.
3512 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3513 * t.usec #=> 666777
3515 * Time#subsec can be used to obtain the subsecond part exactly.
3518 static VALUE
3519 time_usec(VALUE time)
3521 struct time_object *tobj;
3522 wideval_t w, q, r;
3524 GetTimeval(time, tobj);
3526 w = wmod(tobj->timew, WINT2WV(TIME_SCALE));
3527 wmuldivmod(w, WINT2FIXWV(1000000), WINT2FIXWV(TIME_SCALE), &q, &r);
3528 return rb_to_int(w2v(q));
3532 * call-seq:
3533 * time.nsec -> int
3534 * time.tv_nsec -> int
3536 * Returns the number of nanoseconds for the subsecond part of _time_.
3537 * The result is a non-negative integer less than 10**9.
3539 * t = Time.now #=> 2020-07-20 22:07:10.963933942 +0900
3540 * t.nsec #=> 963933942
3542 * If _time_ has fraction of nanosecond (such as picoseconds),
3543 * it is truncated.
3545 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3546 * t.nsec #=> 666777888
3548 * Time#subsec can be used to obtain the subsecond part exactly.
3551 static VALUE
3552 time_nsec(VALUE time)
3554 struct time_object *tobj;
3556 GetTimeval(time, tobj);
3557 return rb_to_int(w2v(wmulquoll(wmod(tobj->timew, WINT2WV(TIME_SCALE)), 1000000000, TIME_SCALE)));
3561 * call-seq:
3562 * time.subsec -> number
3564 * Returns the subsecond for _time_.
3566 * The return value can be a rational number.
3568 * t = Time.now #=> 2020-07-20 15:40:26.867462289 +0900
3569 * t.subsec #=> (867462289/1000000000)
3571 * t = Time.now #=> 2020-07-20 15:40:50.313828595 +0900
3572 * t.subsec #=> (62765719/200000000)
3574 * t = Time.new(2000,1,1,2,3,4) #=> 2000-01-01 02:03:04 +0900
3575 * t.subsec #=> 0
3577 * Time.new(2000,1,1,0,0,1/3r,"UTC").subsec #=> (1/3)
3581 static VALUE
3582 time_subsec(VALUE time)
3584 struct time_object *tobj;
3586 GetTimeval(time, tobj);
3587 return quov(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE));
3591 * call-seq:
3592 * time <=> other_time -> -1, 0, +1, or nil
3594 * Compares +time+ with +other_time+.
3596 * -1, 0, +1 or nil depending on whether +time+ is less than, equal to, or
3597 * greater than +other_time+.
3599 * +nil+ is returned if the two values are incomparable.
3601 * t = Time.now #=> 2007-11-19 08:12:12 -0600
3602 * t2 = t + 2592000 #=> 2007-12-19 08:12:12 -0600
3603 * t <=> t2 #=> -1
3604 * t2 <=> t #=> 1
3606 * t = Time.now #=> 2007-11-19 08:13:38 -0600
3607 * t2 = t + 0.1 #=> 2007-11-19 08:13:38 -0600
3608 * t.nsec #=> 98222999
3609 * t2.nsec #=> 198222999
3610 * t <=> t2 #=> -1
3611 * t2 <=> t #=> 1
3612 * t <=> t #=> 0
3615 static VALUE
3616 time_cmp(VALUE time1, VALUE time2)
3618 struct time_object *tobj1, *tobj2;
3619 int n;
3621 GetTimeval(time1, tobj1);
3622 if (IsTimeval(time2)) {
3623 GetTimeval(time2, tobj2);
3624 n = wcmp(tobj1->timew, tobj2->timew);
3626 else {
3627 return rb_invcmp(time1, time2);
3629 if (n == 0) return INT2FIX(0);
3630 if (n > 0) return INT2FIX(1);
3631 return INT2FIX(-1);
3635 * call-seq:
3636 * time.eql?(other_time)
3638 * Returns +true+ if _time_ and +other_time+ are
3639 * both Time objects with the same seconds (including subsecond) from the Epoch.
3642 static VALUE
3643 time_eql(VALUE time1, VALUE time2)
3645 struct time_object *tobj1, *tobj2;
3647 GetTimeval(time1, tobj1);
3648 if (IsTimeval(time2)) {
3649 GetTimeval(time2, tobj2);
3650 return rb_equal(w2v(tobj1->timew), w2v(tobj2->timew));
3652 return Qfalse;
3656 * call-seq:
3657 * time.utc? -> true or false
3658 * time.gmt? -> true or false
3660 * Returns +true+ if _time_ represents a time in UTC (GMT).
3662 * t = Time.now #=> 2007-11-19 08:15:23 -0600
3663 * t.utc? #=> false
3664 * t = Time.gm(2000,"jan",1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3665 * t.utc? #=> true
3667 * t = Time.now #=> 2007-11-19 08:16:03 -0600
3668 * t.gmt? #=> false
3669 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3670 * t.gmt? #=> true
3673 static VALUE
3674 time_utc_p(VALUE time)
3676 struct time_object *tobj;
3678 GetTimeval(time, tobj);
3679 return RBOOL(TZMODE_UTC_P(tobj));
3683 * call-seq:
3684 * time.hash -> integer
3686 * Returns a hash code for this Time object.
3688 * See also Object#hash.
3691 static VALUE
3692 time_hash(VALUE time)
3694 struct time_object *tobj;
3696 GetTimeval(time, tobj);
3697 return rb_hash(w2v(tobj->timew));
3700 /* :nodoc: */
3701 static VALUE
3702 time_init_copy(VALUE copy, VALUE time)
3704 struct time_object *tobj, *tcopy;
3706 if (!OBJ_INIT_COPY(copy, time)) return copy;
3707 GetTimeval(time, tobj);
3708 GetNewTimeval(copy, tcopy);
3709 MEMCPY(tcopy, tobj, struct time_object, 1);
3711 return copy;
3714 static VALUE
3715 time_dup(VALUE time)
3717 VALUE dup = time_s_alloc(rb_obj_class(time));
3718 time_init_copy(dup, time);
3719 return dup;
3722 static VALUE
3723 time_localtime(VALUE time)
3725 struct time_object *tobj;
3726 struct vtm vtm;
3727 VALUE zone;
3729 GetTimeval(time, tobj);
3730 if (TZMODE_LOCALTIME_P(tobj)) {
3731 if (tobj->tm_got)
3732 return time;
3734 else {
3735 time_modify(time);
3738 zone = tobj->vtm.zone;
3739 if (maybe_tzobj_p(zone) && zone_localtime(zone, time)) {
3740 return time;
3743 if (!localtimew(tobj->timew, &vtm))
3744 rb_raise(rb_eArgError, "localtime error");
3745 tobj->vtm = vtm;
3747 tobj->tm_got = 1;
3748 TZMODE_SET_LOCALTIME(tobj);
3749 return time;
3752 static VALUE
3753 time_zonelocal(VALUE time, VALUE off)
3755 VALUE zone = off;
3756 if (zone_localtime(zone, time)) return time;
3758 if (NIL_P(off = utc_offset_arg(off))) {
3759 off = zone;
3760 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
3761 if (!zone_localtime(zone, time)) invalid_utc_offset(off);
3762 return time;
3764 else if (off == UTC_ZONE) {
3765 return time_gmtime(time);
3767 validate_utc_offset(off);
3769 time_set_utc_offset(time, off);
3770 return time_fixoff(time);
3774 * call-seq:
3775 * time.localtime -> time
3776 * time.localtime(utc_offset) -> time
3778 * Converts _time_ to local time (using the local time zone in
3779 * effect at the creation time of _time_) modifying the receiver.
3781 * If +utc_offset+ is given, it is used instead of the local time.
3783 * t = Time.utc(2000, "jan", 1, 20, 15, 1) #=> 2000-01-01 20:15:01 UTC
3784 * t.utc? #=> true
3786 * t.localtime #=> 2000-01-01 14:15:01 -0600
3787 * t.utc? #=> false
3789 * t.localtime("+09:00") #=> 2000-01-02 05:15:01 +0900
3790 * t.utc? #=> false
3792 * If +utc_offset+ is not given and _time_ is local time, just returns
3793 * the receiver.
3796 static VALUE
3797 time_localtime_m(int argc, VALUE *argv, VALUE time)
3799 VALUE off;
3801 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
3802 return time_zonelocal(time, off);
3805 return time_localtime(time);
3809 * call-seq:
3810 * time.gmtime -> time
3811 * time.utc -> time
3813 * Converts _time_ to UTC (GMT), modifying the receiver.
3815 * t = Time.now #=> 2007-11-19 08:18:31 -0600
3816 * t.gmt? #=> false
3817 * t.gmtime #=> 2007-11-19 14:18:31 UTC
3818 * t.gmt? #=> true
3820 * t = Time.now #=> 2007-11-19 08:18:51 -0600
3821 * t.utc? #=> false
3822 * t.utc #=> 2007-11-19 14:18:51 UTC
3823 * t.utc? #=> true
3826 static VALUE
3827 time_gmtime(VALUE time)
3829 struct time_object *tobj;
3830 struct vtm vtm;
3832 GetTimeval(time, tobj);
3833 if (TZMODE_UTC_P(tobj)) {
3834 if (tobj->tm_got)
3835 return time;
3837 else {
3838 time_modify(time);
3841 vtm.zone = str_utc;
3842 GMTIMEW(tobj->timew, &vtm);
3843 tobj->vtm = vtm;
3845 tobj->tm_got = 1;
3846 TZMODE_SET_UTC(tobj);
3847 return time;
3850 static VALUE
3851 time_fixoff(VALUE time)
3853 struct time_object *tobj;
3854 struct vtm vtm;
3855 VALUE off, zone;
3857 GetTimeval(time, tobj);
3858 if (TZMODE_FIXOFF_P(tobj)) {
3859 if (tobj->tm_got)
3860 return time;
3862 else {
3863 time_modify(time);
3866 if (TZMODE_FIXOFF_P(tobj))
3867 off = tobj->vtm.utc_offset;
3868 else
3869 off = INT2FIX(0);
3871 GMTIMEW(tobj->timew, &vtm);
3873 zone = tobj->vtm.zone;
3874 tobj->vtm = vtm;
3875 tobj->vtm.zone = zone;
3876 vtm_add_offset(&tobj->vtm, off, +1);
3878 tobj->tm_got = 1;
3879 TZMODE_SET_FIXOFF(tobj, off);
3880 return time;
3884 * call-seq:
3885 * time.getlocal -> new_time
3886 * time.getlocal(utc_offset) -> new_time
3887 * time.getlocal(timezone) -> new_time
3889 * Returns a new Time object representing _time_ in
3890 * local time (using the local time zone in effect for this process).
3892 * If +utc_offset+ is given, it is used instead of the local time.
3893 * +utc_offset+ can be given as a human-readable string (eg. <code>"+09:00"</code>)
3894 * or as a number of seconds (eg. <code>32400</code>).
3896 * t = Time.utc(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3897 * t.utc? #=> true
3899 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
3900 * l.utc? #=> false
3901 * t == l #=> true
3903 * j = t.getlocal("+09:00") #=> 2000-01-02 05:15:01 +0900
3904 * j.utc? #=> false
3905 * t == j #=> true
3907 * k = t.getlocal(9*60*60) #=> 2000-01-02 05:15:01 +0900
3908 * k.utc? #=> false
3909 * t == k #=> true
3912 static VALUE
3913 time_getlocaltime(int argc, VALUE *argv, VALUE time)
3915 VALUE off;
3917 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
3918 VALUE zone = off;
3919 if (maybe_tzobj_p(zone)) {
3920 VALUE t = time_dup(time);
3921 if (zone_localtime(off, t)) return t;
3924 if (NIL_P(off = utc_offset_arg(off))) {
3925 off = zone;
3926 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
3927 time = time_dup(time);
3928 if (!zone_localtime(zone, time)) invalid_utc_offset(off);
3929 return time;
3931 else if (off == UTC_ZONE) {
3932 return time_gmtime(time_dup(time));
3934 validate_utc_offset(off);
3936 time = time_dup(time);
3937 time_set_utc_offset(time, off);
3938 return time_fixoff(time);
3941 return time_localtime(time_dup(time));
3945 * call-seq:
3946 * time.getgm -> new_time
3947 * time.getutc -> new_time
3949 * Returns a new Time object representing _time_ in UTC.
3951 * t = Time.local(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 -0600
3952 * t.gmt? #=> false
3953 * y = t.getgm #=> 2000-01-02 02:15:01 UTC
3954 * y.gmt? #=> true
3955 * t == y #=> true
3958 static VALUE
3959 time_getgmtime(VALUE time)
3961 return time_gmtime(time_dup(time));
3964 static VALUE
3965 time_get_tm(VALUE time, struct time_object *tobj)
3967 if (TZMODE_UTC_P(tobj)) return time_gmtime(time);
3968 if (TZMODE_FIXOFF_P(tobj)) return time_fixoff(time);
3969 return time_localtime(time);
3972 static VALUE strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc);
3973 #define strftimev(fmt, time, enc) strftime_cstr((fmt), rb_strlen_lit(fmt), (time), (enc))
3976 * call-seq:
3977 * time.asctime -> string
3978 * time.ctime -> string
3980 * Returns a canonical string representation of _time_.
3982 * Time.now.asctime #=> "Wed Apr 9 08:56:03 2003"
3983 * Time.now.ctime #=> "Wed Apr 9 08:56:03 2003"
3986 static VALUE
3987 time_asctime(VALUE time)
3989 return strftimev("%a %b %e %T %Y", time, rb_usascii_encoding());
3993 * call-seq:
3994 * time.to_s -> string
3996 * Returns a string representing _time_. Equivalent to calling
3997 * #strftime with the appropriate format string.
3999 * t = Time.now
4000 * t.to_s #=> "2012-11-10 18:16:12 +0100"
4001 * t.strftime "%Y-%m-%d %H:%M:%S %z" #=> "2012-11-10 18:16:12 +0100"
4003 * t.utc.to_s #=> "2012-11-10 17:16:12 UTC"
4004 * t.strftime "%Y-%m-%d %H:%M:%S UTC" #=> "2012-11-10 17:16:12 UTC"
4007 static VALUE
4008 time_to_s(VALUE time)
4010 struct time_object *tobj;
4012 GetTimeval(time, tobj);
4013 if (TZMODE_UTC_P(tobj))
4014 return strftimev("%Y-%m-%d %H:%M:%S UTC", time, rb_usascii_encoding());
4015 else
4016 return strftimev("%Y-%m-%d %H:%M:%S %z", time, rb_usascii_encoding());
4020 * call-seq:
4021 * time.inspect -> string
4023 * Returns a detailed string representing _time_. Unlike to_s,
4024 * preserves subsecond in the representation for easier debugging.
4026 * t = Time.now
4027 * t.inspect #=> "2012-11-10 18:16:12.261257655 +0100"
4028 * t.strftime "%Y-%m-%d %H:%M:%S.%N %z" #=> "2012-11-10 18:16:12.261257655 +0100"
4030 * t.utc.inspect #=> "2012-11-10 17:16:12.261257655 UTC"
4031 * t.strftime "%Y-%m-%d %H:%M:%S.%N UTC" #=> "2012-11-10 17:16:12.261257655 UTC"
4034 static VALUE
4035 time_inspect(VALUE time)
4037 struct time_object *tobj;
4038 VALUE str, subsec;
4040 GetTimeval(time, tobj);
4041 str = strftimev("%Y-%m-%d %H:%M:%S", time, rb_usascii_encoding());
4042 subsec = w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE)));
4043 if (FIXNUM_P(subsec) && FIX2LONG(subsec) == 0) {
4045 else if (FIXNUM_P(subsec) && FIX2LONG(subsec) < TIME_SCALE) {
4046 long len;
4047 rb_str_catf(str, ".%09ld", FIX2LONG(subsec));
4048 for (len=RSTRING_LEN(str); RSTRING_PTR(str)[len-1] == '0' && len > 0; len--)
4050 rb_str_resize(str, len);
4052 else {
4053 rb_str_cat_cstr(str, " ");
4054 subsec = quov(subsec, INT2FIX(TIME_SCALE));
4055 rb_str_concat(str, rb_obj_as_string(subsec));
4057 if (TZMODE_UTC_P(tobj)) {
4058 rb_str_cat_cstr(str, " UTC");
4060 else {
4061 /* ?TODO: subsecond offset */
4062 long off = NUM2LONG(rb_funcall(tobj->vtm.utc_offset, rb_intern("round"), 0));
4063 char sign = (off < 0) ? (off = -off, '-') : '+';
4064 int sec = off % 60;
4065 int min = (off /= 60) % 60;
4066 off /= 60;
4067 rb_str_catf(str, " %c%.2d%.2d", sign, (int)off, min);
4068 if (sec) rb_str_catf(str, "%.2d", sec);
4070 return str;
4073 static VALUE
4074 time_add0(VALUE klass, const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4076 VALUE result;
4077 struct time_object *result_tobj;
4079 offset = num_exact(offset);
4080 if (sign < 0)
4081 result = time_new_timew(klass, wsub(tobj->timew, rb_time_magnify(v2w(offset))));
4082 else
4083 result = time_new_timew(klass, wadd(tobj->timew, rb_time_magnify(v2w(offset))));
4084 GetTimeval(result, result_tobj);
4085 TZMODE_COPY(result_tobj, tobj);
4087 return result;
4090 static VALUE
4091 time_add(const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4093 return time_add0(rb_cTime, tobj, torig, offset, sign);
4097 * call-seq:
4098 * time + numeric -> time
4100 * Adds some number of seconds (possibly including subsecond) to
4101 * _time_ and returns that value as a new Time object.
4103 * t = Time.now #=> 2020-07-20 22:14:43.170490982 +0900
4104 * t + (60 * 60 * 24) #=> 2020-07-21 22:14:43.170490982 +0900
4107 static VALUE
4108 time_plus(VALUE time1, VALUE time2)
4110 struct time_object *tobj;
4111 GetTimeval(time1, tobj);
4113 if (IsTimeval(time2)) {
4114 rb_raise(rb_eTypeError, "time + time?");
4116 return time_add(tobj, time1, time2, 1);
4120 * call-seq:
4121 * time - other_time -> float
4122 * time - numeric -> time
4124 * Returns a difference in seconds as a Float
4125 * between _time_ and +other_time+, or subtracts the given number
4126 * of seconds in +numeric+ from _time_.
4128 * t = Time.now #=> 2020-07-20 22:15:49.302766336 +0900
4129 * t2 = t + 2592000 #=> 2020-08-19 22:15:49.302766336 +0900
4130 * t2 - t #=> 2592000.0
4131 * t2 - 2592000 #=> 2020-07-20 22:15:49.302766336 +0900
4134 static VALUE
4135 time_minus(VALUE time1, VALUE time2)
4137 struct time_object *tobj;
4139 GetTimeval(time1, tobj);
4140 if (IsTimeval(time2)) {
4141 struct time_object *tobj2;
4143 GetTimeval(time2, tobj2);
4144 return rb_Float(rb_time_unmagnify_to_float(wsub(tobj->timew, tobj2->timew)));
4146 return time_add(tobj, time1, time2, -1);
4149 static VALUE
4150 ndigits_denominator(VALUE ndigits)
4152 long nd = NUM2LONG(ndigits);
4154 if (nd < 0) {
4155 rb_raise(rb_eArgError, "negative ndigits given");
4157 if (nd == 0) {
4158 return INT2FIX(1);
4160 return rb_rational_new(INT2FIX(1),
4161 rb_int_positive_pow(10, (unsigned long)nd));
4165 * call-seq:
4166 * time.round([ndigits]) -> new_time
4168 * Rounds subsecond to a given precision in decimal digits (0 digits by default).
4169 * It returns a new Time object.
4170 * +ndigits+ should be zero or a positive integer.
4172 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4173 * t #=> 2010-03-30 05:43:25.123456789 UTC
4174 * t.round #=> 2010-03-30 05:43:25 UTC
4175 * t.round(0) #=> 2010-03-30 05:43:25 UTC
4176 * t.round(1) #=> 2010-03-30 05:43:25.1 UTC
4177 * t.round(2) #=> 2010-03-30 05:43:25.12 UTC
4178 * t.round(3) #=> 2010-03-30 05:43:25.123 UTC
4179 * t.round(4) #=> 2010-03-30 05:43:25.1235 UTC
4181 * t = Time.utc(1999,12,31, 23,59,59)
4182 * (t + 0.4).round #=> 1999-12-31 23:59:59 UTC
4183 * (t + 0.49).round #=> 1999-12-31 23:59:59 UTC
4184 * (t + 0.5).round #=> 2000-01-01 00:00:00 UTC
4185 * (t + 1.4).round #=> 2000-01-01 00:00:00 UTC
4186 * (t + 1.49).round #=> 2000-01-01 00:00:00 UTC
4187 * (t + 1.5).round #=> 2000-01-01 00:00:01 UTC
4189 * t = Time.utc(1999,12,31, 23,59,59) #=> 1999-12-31 23:59:59 UTC
4190 * (t + 0.123456789).round(4).iso8601(6) #=> 1999-12-31 23:59:59.1235 UTC
4193 static VALUE
4194 time_round(int argc, VALUE *argv, VALUE time)
4196 VALUE ndigits, v, den;
4197 struct time_object *tobj;
4199 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4200 den = INT2FIX(1);
4201 else
4202 den = ndigits_denominator(ndigits);
4204 GetTimeval(time, tobj);
4205 v = w2v(rb_time_unmagnify(tobj->timew));
4207 v = modv(v, den);
4208 if (lt(v, quov(den, INT2FIX(2))))
4209 return time_add(tobj, time, v, -1);
4210 else
4211 return time_add(tobj, time, subv(den, v), 1);
4215 * call-seq:
4216 * time.floor([ndigits]) -> new_time
4218 * Floors subsecond to a given precision in decimal digits (0 digits by default).
4219 * It returns a new Time object.
4220 * +ndigits+ should be zero or a positive integer.
4222 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4223 * t #=> 2010-03-30 05:43:25.123456789 UTC
4224 * t.floor #=> 2010-03-30 05:43:25 UTC
4225 * t.floor(0) #=> 2010-03-30 05:43:25 UTC
4226 * t.floor(1) #=> 2010-03-30 05:43:25.1 UTC
4227 * t.floor(2) #=> 2010-03-30 05:43:25.12 UTC
4228 * t.floor(3) #=> 2010-03-30 05:43:25.123 UTC
4229 * t.floor(4) #=> 2010-03-30 05:43:25.1234 UTC
4231 * t = Time.utc(1999,12,31, 23,59,59)
4232 * (t + 0.4).floor #=> 1999-12-31 23:59:59 UTC
4233 * (t + 0.9).floor #=> 1999-12-31 23:59:59 UTC
4234 * (t + 1.4).floor #=> 2000-01-01 00:00:00 UTC
4235 * (t + 1.9).floor #=> 2000-01-01 00:00:00 UTC
4237 * t = Time.utc(1999,12,31, 23,59,59)
4238 * (t + 0.123456789).floor(4) #=> 1999-12-31 23:59:59.1234 UTC
4241 static VALUE
4242 time_floor(int argc, VALUE *argv, VALUE time)
4244 VALUE ndigits, v, den;
4245 struct time_object *tobj;
4247 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4248 den = INT2FIX(1);
4249 else
4250 den = ndigits_denominator(ndigits);
4252 GetTimeval(time, tobj);
4253 v = w2v(rb_time_unmagnify(tobj->timew));
4255 v = modv(v, den);
4256 return time_add(tobj, time, v, -1);
4260 * call-seq:
4261 * time.ceil([ndigits]) -> new_time
4263 * Ceils subsecond to a given precision in decimal digits (0 digits by default).
4264 * It returns a new Time object.
4265 * +ndigits+ should be zero or a positive integer.
4267 * t = Time.utc(2010,3,30, 5,43,25.0123456789r)
4268 * t #=> 2010-03-30 05:43:25 123456789/10000000000 UTC
4269 * t.ceil #=> 2010-03-30 05:43:26 UTC
4270 * t.ceil(0) #=> 2010-03-30 05:43:26 UTC
4271 * t.ceil(1) #=> 2010-03-30 05:43:25.1 UTC
4272 * t.ceil(2) #=> 2010-03-30 05:43:25.02 UTC
4273 * t.ceil(3) #=> 2010-03-30 05:43:25.013 UTC
4274 * t.ceil(4) #=> 2010-03-30 05:43:25.0124 UTC
4276 * t = Time.utc(1999,12,31, 23,59,59)
4277 * (t + 0.4).ceil #=> 2000-01-01 00:00:00 UTC
4278 * (t + 0.9).ceil #=> 2000-01-01 00:00:00 UTC
4279 * (t + 1.4).ceil #=> 2000-01-01 00:00:01 UTC
4280 * (t + 1.9).ceil #=> 2000-01-01 00:00:01 UTC
4282 * t = Time.utc(1999,12,31, 23,59,59)
4283 * (t + 0.123456789).ceil(4) #=> 1999-12-31 23:59:59.1235 UTC
4286 static VALUE
4287 time_ceil(int argc, VALUE *argv, VALUE time)
4289 VALUE ndigits, v, den;
4290 struct time_object *tobj;
4292 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4293 den = INT2FIX(1);
4294 else
4295 den = ndigits_denominator(ndigits);
4297 GetTimeval(time, tobj);
4298 v = w2v(rb_time_unmagnify(tobj->timew));
4300 v = modv(v, den);
4301 if (!rb_equal(v, INT2FIX(0))) {
4302 v = subv(den, v);
4304 return time_add(tobj, time, v, 1);
4308 * call-seq:
4309 * time.sec -> integer
4311 * Returns the second of the minute (0..60) for _time_.
4313 * *Note:* Seconds range from zero to 60 to allow the system to inject
4314 * leap seconds. See https://en.wikipedia.org/wiki/Leap_second for further
4315 * details.
4317 * t = Time.now #=> 2007-11-19 08:25:02 -0600
4318 * t.sec #=> 2
4321 static VALUE
4322 time_sec(VALUE time)
4324 struct time_object *tobj;
4326 GetTimeval(time, tobj);
4327 MAKE_TM(time, tobj);
4328 return INT2FIX(tobj->vtm.sec);
4332 * call-seq:
4333 * time.min -> integer
4335 * Returns the minute of the hour (0..59) for _time_.
4337 * t = Time.now #=> 2007-11-19 08:25:51 -0600
4338 * t.min #=> 25
4341 static VALUE
4342 time_min(VALUE time)
4344 struct time_object *tobj;
4346 GetTimeval(time, tobj);
4347 MAKE_TM(time, tobj);
4348 return INT2FIX(tobj->vtm.min);
4352 * call-seq:
4353 * time.hour -> integer
4355 * Returns the hour of the day (0..23) for _time_.
4357 * t = Time.now #=> 2007-11-19 08:26:20 -0600
4358 * t.hour #=> 8
4361 static VALUE
4362 time_hour(VALUE time)
4364 struct time_object *tobj;
4366 GetTimeval(time, tobj);
4367 MAKE_TM(time, tobj);
4368 return INT2FIX(tobj->vtm.hour);
4372 * call-seq:
4373 * time.day -> integer
4374 * time.mday -> integer
4376 * Returns the day of the month (1..31) for _time_.
4378 * t = Time.now #=> 2007-11-19 08:27:03 -0600
4379 * t.day #=> 19
4380 * t.mday #=> 19
4383 static VALUE
4384 time_mday(VALUE time)
4386 struct time_object *tobj;
4388 GetTimeval(time, tobj);
4389 MAKE_TM(time, tobj);
4390 return INT2FIX(tobj->vtm.mday);
4394 * call-seq:
4395 * time.mon -> integer
4396 * time.month -> integer
4398 * Returns the month of the year (1..12) for _time_.
4400 * t = Time.now #=> 2007-11-19 08:27:30 -0600
4401 * t.mon #=> 11
4402 * t.month #=> 11
4405 static VALUE
4406 time_mon(VALUE time)
4408 struct time_object *tobj;
4410 GetTimeval(time, tobj);
4411 MAKE_TM(time, tobj);
4412 return INT2FIX(tobj->vtm.mon);
4416 * call-seq:
4417 * time.year -> integer
4419 * Returns the year for _time_ (including the century).
4421 * t = Time.now #=> 2007-11-19 08:27:51 -0600
4422 * t.year #=> 2007
4425 static VALUE
4426 time_year(VALUE time)
4428 struct time_object *tobj;
4430 GetTimeval(time, tobj);
4431 MAKE_TM(time, tobj);
4432 return tobj->vtm.year;
4436 * call-seq:
4437 * time.wday -> integer
4439 * Returns an integer representing the day of the week, 0..6, with
4440 * Sunday == 0.
4442 * t = Time.now #=> 2007-11-20 02:35:35 -0600
4443 * t.wday #=> 2
4444 * t.sunday? #=> false
4445 * t.monday? #=> false
4446 * t.tuesday? #=> true
4447 * t.wednesday? #=> false
4448 * t.thursday? #=> false
4449 * t.friday? #=> false
4450 * t.saturday? #=> false
4453 static VALUE
4454 time_wday(VALUE time)
4456 struct time_object *tobj;
4458 GetTimeval(time, tobj);
4459 MAKE_TM_ENSURE(time, tobj, tobj->vtm.wday != VTM_WDAY_INITVAL);
4460 return INT2FIX((int)tobj->vtm.wday);
4463 #define wday_p(n) {\
4464 return RBOOL(time_wday(time) == INT2FIX(n)); \
4468 * call-seq:
4469 * time.sunday? -> true or false
4471 * Returns +true+ if _time_ represents Sunday.
4473 * t = Time.local(1990, 4, 1) #=> 1990-04-01 00:00:00 -0600
4474 * t.sunday? #=> true
4477 static VALUE
4478 time_sunday(VALUE time)
4480 wday_p(0);
4484 * call-seq:
4485 * time.monday? -> true or false
4487 * Returns +true+ if _time_ represents Monday.
4489 * t = Time.local(2003, 8, 4) #=> 2003-08-04 00:00:00 -0500
4490 * t.monday? #=> true
4493 static VALUE
4494 time_monday(VALUE time)
4496 wday_p(1);
4500 * call-seq:
4501 * time.tuesday? -> true or false
4503 * Returns +true+ if _time_ represents Tuesday.
4505 * t = Time.local(1991, 2, 19) #=> 1991-02-19 00:00:00 -0600
4506 * t.tuesday? #=> true
4509 static VALUE
4510 time_tuesday(VALUE time)
4512 wday_p(2);
4516 * call-seq:
4517 * time.wednesday? -> true or false
4519 * Returns +true+ if _time_ represents Wednesday.
4521 * t = Time.local(1993, 2, 24) #=> 1993-02-24 00:00:00 -0600
4522 * t.wednesday? #=> true
4525 static VALUE
4526 time_wednesday(VALUE time)
4528 wday_p(3);
4532 * call-seq:
4533 * time.thursday? -> true or false
4535 * Returns +true+ if _time_ represents Thursday.
4537 * t = Time.local(1995, 12, 21) #=> 1995-12-21 00:00:00 -0600
4538 * t.thursday? #=> true
4541 static VALUE
4542 time_thursday(VALUE time)
4544 wday_p(4);
4548 * call-seq:
4549 * time.friday? -> true or false
4551 * Returns +true+ if _time_ represents Friday.
4553 * t = Time.local(1987, 12, 18) #=> 1987-12-18 00:00:00 -0600
4554 * t.friday? #=> true
4557 static VALUE
4558 time_friday(VALUE time)
4560 wday_p(5);
4564 * call-seq:
4565 * time.saturday? -> true or false
4567 * Returns +true+ if _time_ represents Saturday.
4569 * t = Time.local(2006, 6, 10) #=> 2006-06-10 00:00:00 -0500
4570 * t.saturday? #=> true
4573 static VALUE
4574 time_saturday(VALUE time)
4576 wday_p(6);
4580 * call-seq:
4581 * time.yday -> integer
4583 * Returns an integer representing the day of the year, 1..366.
4585 * t = Time.now #=> 2007-11-19 08:32:31 -0600
4586 * t.yday #=> 323
4589 static VALUE
4590 time_yday(VALUE time)
4592 struct time_object *tobj;
4594 GetTimeval(time, tobj);
4595 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
4596 return INT2FIX(tobj->vtm.yday);
4600 * call-seq:
4601 * time.isdst -> true or false
4602 * time.dst? -> true or false
4604 * Returns +true+ if _time_ occurs during Daylight
4605 * Saving Time in its time zone.
4607 * # CST6CDT:
4608 * Time.local(2000, 1, 1).zone #=> "CST"
4609 * Time.local(2000, 1, 1).isdst #=> false
4610 * Time.local(2000, 1, 1).dst? #=> false
4611 * Time.local(2000, 7, 1).zone #=> "CDT"
4612 * Time.local(2000, 7, 1).isdst #=> true
4613 * Time.local(2000, 7, 1).dst? #=> true
4615 * # Asia/Tokyo:
4616 * Time.local(2000, 1, 1).zone #=> "JST"
4617 * Time.local(2000, 1, 1).isdst #=> false
4618 * Time.local(2000, 1, 1).dst? #=> false
4619 * Time.local(2000, 7, 1).zone #=> "JST"
4620 * Time.local(2000, 7, 1).isdst #=> false
4621 * Time.local(2000, 7, 1).dst? #=> false
4624 static VALUE
4625 time_isdst(VALUE time)
4627 struct time_object *tobj;
4629 GetTimeval(time, tobj);
4630 MAKE_TM(time, tobj);
4631 if (tobj->vtm.isdst == VTM_ISDST_INITVAL) {
4632 rb_raise(rb_eRuntimeError, "isdst is not set yet");
4634 return RBOOL(tobj->vtm.isdst);
4638 * call-seq:
4639 * time.zone -> string or timezone
4641 * Returns the name of the time zone used for _time_. As of Ruby
4642 * 1.8, returns ``UTC'' rather than ``GMT'' for UTC times.
4644 * t = Time.gm(2000, "jan", 1, 20, 15, 1)
4645 * t.zone #=> "UTC"
4646 * t = Time.local(2000, "jan", 1, 20, 15, 1)
4647 * t.zone #=> "CST"
4650 static VALUE
4651 time_zone(VALUE time)
4653 struct time_object *tobj;
4654 VALUE zone;
4656 GetTimeval(time, tobj);
4657 MAKE_TM(time, tobj);
4659 if (TZMODE_UTC_P(tobj)) {
4660 return rb_usascii_str_new_cstr("UTC");
4662 zone = tobj->vtm.zone;
4663 if (NIL_P(zone))
4664 return Qnil;
4666 if (RB_TYPE_P(zone, T_STRING))
4667 zone = rb_str_dup(zone);
4668 return zone;
4672 * call-seq:
4673 * time.gmt_offset -> integer
4674 * time.gmtoff -> integer
4675 * time.utc_offset -> integer
4677 * Returns the offset in seconds between the timezone of _time_
4678 * and UTC.
4680 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
4681 * t.gmt_offset #=> 0
4682 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
4683 * l.gmt_offset #=> -21600
4686 VALUE
4687 rb_time_utc_offset(VALUE time)
4689 struct time_object *tobj;
4691 GetTimeval(time, tobj);
4693 if (TZMODE_UTC_P(tobj)) {
4694 return INT2FIX(0);
4696 else {
4697 MAKE_TM(time, tobj);
4698 return tobj->vtm.utc_offset;
4703 * call-seq:
4704 * time.to_a -> array
4706 * Returns a ten-element _array_ of values for _time_:
4708 * [sec, min, hour, day, month, year, wday, yday, isdst, zone]
4710 * See the individual methods for an explanation of the
4711 * valid ranges of each value. The ten elements can be passed directly
4712 * to Time.utc or Time.local to create a
4713 * new Time object.
4715 * t = Time.now #=> 2007-11-19 08:36:01 -0600
4716 * now = t.to_a #=> [1, 36, 8, 19, 11, 2007, 1, 323, false, "CST"]
4719 static VALUE
4720 time_to_a(VALUE time)
4722 struct time_object *tobj;
4724 GetTimeval(time, tobj);
4725 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
4726 return rb_ary_new3(10,
4727 INT2FIX(tobj->vtm.sec),
4728 INT2FIX(tobj->vtm.min),
4729 INT2FIX(tobj->vtm.hour),
4730 INT2FIX(tobj->vtm.mday),
4731 INT2FIX(tobj->vtm.mon),
4732 tobj->vtm.year,
4733 INT2FIX(tobj->vtm.wday),
4734 INT2FIX(tobj->vtm.yday),
4735 RBOOL(tobj->vtm.isdst),
4736 time_zone(time));
4739 static VALUE
4740 rb_strftime_alloc(const char *format, size_t format_len, rb_encoding *enc,
4741 VALUE time, struct vtm *vtm, wideval_t timew, int gmt)
4743 VALUE timev = Qnil;
4744 struct timespec ts;
4746 if (!timew2timespec_exact(timew, &ts))
4747 timev = w2v(rb_time_unmagnify(timew));
4749 if (NIL_P(timev)) {
4750 return rb_strftime_timespec(format, format_len, enc, time, vtm, &ts, gmt);
4752 else {
4753 return rb_strftime(format, format_len, enc, time, vtm, timev, gmt);
4757 static VALUE
4758 strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc)
4760 struct time_object *tobj;
4761 VALUE str;
4763 GetTimeval(time, tobj);
4764 MAKE_TM(time, tobj);
4765 str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew, TZMODE_UTC_P(tobj));
4766 if (!str) rb_raise(rb_eArgError, "invalid format: %s", fmt);
4767 return str;
4771 * call-seq:
4772 * time.strftime( string ) -> string
4774 * Formats _time_ according to the directives in the given format string.
4776 * The directives begin with a percent (%) character.
4777 * Any text not listed as a directive will be passed through to the
4778 * output string.
4780 * The directive consists of a percent (%) character,
4781 * zero or more flags, optional minimum field width,
4782 * optional modifier and a conversion specifier
4783 * as follows:
4785 * %<flags><width><modifier><conversion>
4787 * Flags:
4788 * - don't pad a numerical output
4789 * _ use spaces for padding
4790 * 0 use zeros for padding
4791 * ^ upcase the result string
4792 * # change case
4793 * : use colons for %z
4795 * The minimum field width specifies the minimum width.
4797 * The modifiers are "E" and "O".
4798 * They are ignored.
4800 * Format directives:
4802 * Date (Year, Month, Day):
4803 * %Y - Year with century if provided, will pad result at least 4 digits.
4804 * -0001, 0000, 1995, 2009, 14292, etc.
4805 * %C - year / 100 (rounded down such as 20 in 2009)
4806 * %y - year % 100 (00..99)
4808 * %m - Month of the year, zero-padded (01..12)
4809 * %_m blank-padded ( 1..12)
4810 * %-m no-padded (1..12)
4811 * %B - The full month name (``January'')
4812 * %^B uppercased (``JANUARY'')
4813 * %b - The abbreviated month name (``Jan'')
4814 * %^b uppercased (``JAN'')
4815 * %h - Equivalent to %b
4817 * %d - Day of the month, zero-padded (01..31)
4818 * %-d no-padded (1..31)
4819 * %e - Day of the month, blank-padded ( 1..31)
4821 * %j - Day of the year (001..366)
4823 * Time (Hour, Minute, Second, Subsecond):
4824 * %H - Hour of the day, 24-hour clock, zero-padded (00..23)
4825 * %k - Hour of the day, 24-hour clock, blank-padded ( 0..23)
4826 * %I - Hour of the day, 12-hour clock, zero-padded (01..12)
4827 * %l - Hour of the day, 12-hour clock, blank-padded ( 1..12)
4828 * %P - Meridian indicator, lowercase (``am'' or ``pm'')
4829 * %p - Meridian indicator, uppercase (``AM'' or ``PM'')
4831 * %M - Minute of the hour (00..59)
4833 * %S - Second of the minute (00..60)
4835 * %L - Millisecond of the second (000..999)
4836 * The digits under millisecond are truncated to not produce 1000.
4837 * %N - Fractional seconds digits, default is 9 digits (nanosecond)
4838 * %3N millisecond (3 digits)
4839 * %6N microsecond (6 digits)
4840 * %9N nanosecond (9 digits)
4841 * %12N picosecond (12 digits)
4842 * %15N femtosecond (15 digits)
4843 * %18N attosecond (18 digits)
4844 * %21N zeptosecond (21 digits)
4845 * %24N yoctosecond (24 digits)
4846 * The digits under the specified length are truncated to avoid
4847 * carry up.
4849 * Time zone:
4850 * %z - Time zone as hour and minute offset from UTC (e.g. +0900)
4851 * %:z - hour and minute offset from UTC with a colon (e.g. +09:00)
4852 * %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
4853 * %Z - Abbreviated time zone name or similar information. (OS dependent)
4855 * Weekday:
4856 * %A - The full weekday name (``Sunday'')
4857 * %^A uppercased (``SUNDAY'')
4858 * %a - The abbreviated name (``Sun'')
4859 * %^a uppercased (``SUN'')
4860 * %u - Day of the week (Monday is 1, 1..7)
4861 * %w - Day of the week (Sunday is 0, 0..6)
4863 * ISO 8601 week-based year and week number:
4864 * The first week of YYYY starts with a Monday and includes YYYY-01-04.
4865 * The days in the year before the first week are in the last week of
4866 * the previous year.
4867 * %G - The week-based year
4868 * %g - The last 2 digits of the week-based year (00..99)
4869 * %V - Week number of the week-based year (01..53)
4871 * Week number:
4872 * The first week of YYYY that starts with a Sunday or Monday (according to %U
4873 * or %W). The days in the year before the first week are in week 0.
4874 * %U - Week number of the year. The week starts with Sunday. (00..53)
4875 * %W - Week number of the year. The week starts with Monday. (00..53)
4877 * Seconds since the Epoch:
4878 * %s - Number of seconds since 1970-01-01 00:00:00 UTC.
4880 * Literal string:
4881 * %n - Newline character (\n)
4882 * %t - Tab character (\t)
4883 * %% - Literal ``%'' character
4885 * Combination:
4886 * %c - date and time (%a %b %e %T %Y)
4887 * %D - Date (%m/%d/%y)
4888 * %F - The ISO 8601 date format (%Y-%m-%d)
4889 * %v - VMS date (%e-%^b-%4Y)
4890 * %x - Same as %D
4891 * %X - Same as %T
4892 * %r - 12-hour time (%I:%M:%S %p)
4893 * %R - 24-hour time (%H:%M)
4894 * %T - 24-hour time (%H:%M:%S)
4896 * This method is similar to strftime() function defined in ISO C and POSIX.
4898 * While all directives are locale independent since Ruby 1.9, %Z is platform
4899 * dependent.
4900 * So, the result may differ even if the same format string is used in other
4901 * systems such as C.
4903 * %z is recommended over %Z.
4904 * %Z doesn't identify the timezone.
4905 * For example, "CST" is used at America/Chicago (-06:00),
4906 * America/Havana (-05:00), Asia/Harbin (+08:00), Australia/Darwin (+09:30)
4907 * and Australia/Adelaide (+10:30).
4908 * Also, %Z is highly dependent on the operating system.
4909 * For example, it may generate a non ASCII string on Japanese Windows,
4910 * i.e. the result can be different to "JST".
4911 * So the numeric time zone offset, %z, is recommended.
4913 * Examples:
4915 * t = Time.new(2007,11,19,8,37,48,"-06:00") #=> 2007-11-19 08:37:48 -0600
4916 * t.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007"
4917 * t.strftime("at %I:%M %p") #=> "at 08:37 AM"
4919 * Various ISO 8601 formats:
4920 * %Y%m%d => 20071119 Calendar date (basic)
4921 * %F => 2007-11-19 Calendar date (extended)
4922 * %Y-%m => 2007-11 Calendar date, reduced accuracy, specific month
4923 * %Y => 2007 Calendar date, reduced accuracy, specific year
4924 * %C => 20 Calendar date, reduced accuracy, specific century
4925 * %Y%j => 2007323 Ordinal date (basic)
4926 * %Y-%j => 2007-323 Ordinal date (extended)
4927 * %GW%V%u => 2007W471 Week date (basic)
4928 * %G-W%V-%u => 2007-W47-1 Week date (extended)
4929 * %GW%V => 2007W47 Week date, reduced accuracy, specific week (basic)
4930 * %G-W%V => 2007-W47 Week date, reduced accuracy, specific week (extended)
4931 * %H%M%S => 083748 Local time (basic)
4932 * %T => 08:37:48 Local time (extended)
4933 * %H%M => 0837 Local time, reduced accuracy, specific minute (basic)
4934 * %H:%M => 08:37 Local time, reduced accuracy, specific minute (extended)
4935 * %H => 08 Local time, reduced accuracy, specific hour
4936 * %H%M%S,%L => 083748,000 Local time with decimal fraction, comma as decimal sign (basic)
4937 * %T,%L => 08:37:48,000 Local time with decimal fraction, comma as decimal sign (extended)
4938 * %H%M%S.%L => 083748.000 Local time with decimal fraction, full stop as decimal sign (basic)
4939 * %T.%L => 08:37:48.000 Local time with decimal fraction, full stop as decimal sign (extended)
4940 * %H%M%S%z => 083748-0600 Local time and the difference from UTC (basic)
4941 * %T%:z => 08:37:48-06:00 Local time and the difference from UTC (extended)
4942 * %Y%m%dT%H%M%S%z => 20071119T083748-0600 Date and time of day for calendar date (basic)
4943 * %FT%T%:z => 2007-11-19T08:37:48-06:00 Date and time of day for calendar date (extended)
4944 * %Y%jT%H%M%S%z => 2007323T083748-0600 Date and time of day for ordinal date (basic)
4945 * %Y-%jT%T%:z => 2007-323T08:37:48-06:00 Date and time of day for ordinal date (extended)
4946 * %GW%V%uT%H%M%S%z => 2007W471T083748-0600 Date and time of day for week date (basic)
4947 * %G-W%V-%uT%T%:z => 2007-W47-1T08:37:48-06:00 Date and time of day for week date (extended)
4948 * %Y%m%dT%H%M => 20071119T0837 Calendar date and local time (basic)
4949 * %FT%R => 2007-11-19T08:37 Calendar date and local time (extended)
4950 * %Y%jT%H%MZ => 2007323T0837Z Ordinal date and UTC of day (basic)
4951 * %Y-%jT%RZ => 2007-323T08:37Z Ordinal date and UTC of day (extended)
4952 * %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic)
4953 * %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended)
4957 static VALUE
4958 time_strftime(VALUE time, VALUE format)
4960 struct time_object *tobj;
4961 const char *fmt;
4962 long len;
4963 rb_encoding *enc;
4964 VALUE tmp;
4966 GetTimeval(time, tobj);
4967 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
4968 StringValue(format);
4969 if (!rb_enc_str_asciicompat_p(format)) {
4970 rb_raise(rb_eArgError, "format should have ASCII compatible encoding");
4972 tmp = rb_str_tmp_frozen_acquire(format);
4973 fmt = RSTRING_PTR(tmp);
4974 len = RSTRING_LEN(tmp);
4975 enc = rb_enc_get(format);
4976 if (len == 0) {
4977 rb_warning("strftime called with empty format string");
4978 return rb_enc_str_new(0, 0, enc);
4980 else {
4981 VALUE str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew,
4982 TZMODE_UTC_P(tobj));
4983 rb_str_tmp_frozen_release(format, tmp);
4984 if (!str) rb_raise(rb_eArgError, "invalid format: %"PRIsVALUE, format);
4985 return str;
4989 int ruby_marshal_write_long(long x, char *buf);
4991 enum {base_dump_size = 8};
4993 /* :nodoc: */
4994 static VALUE
4995 time_mdump(VALUE time)
4997 struct time_object *tobj;
4998 unsigned long p, s;
4999 char buf[base_dump_size + sizeof(long) + 1];
5000 int i;
5001 VALUE str;
5003 struct vtm vtm;
5004 long year;
5005 long usec, nsec;
5006 VALUE subsecx, nano, subnano, v, zone;
5008 VALUE year_extend = Qnil;
5009 const int max_year = 1900+0xffff;
5011 GetTimeval(time, tobj);
5013 gmtimew(tobj->timew, &vtm);
5015 if (FIXNUM_P(vtm.year)) {
5016 year = FIX2LONG(vtm.year);
5017 if (year > max_year) {
5018 year_extend = INT2FIX(year - max_year);
5019 year = max_year;
5021 else if (year < 1900) {
5022 year_extend = LONG2NUM(1900 - year);
5023 year = 1900;
5026 else {
5027 if (rb_int_positive_p(vtm.year)) {
5028 year_extend = rb_int_minus(vtm.year, INT2FIX(max_year));
5029 year = max_year;
5031 else {
5032 year_extend = rb_int_minus(INT2FIX(1900), vtm.year);
5033 year = 1900;
5037 subsecx = vtm.subsecx;
5039 nano = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
5040 divmodv(nano, INT2FIX(1), &v, &subnano);
5041 nsec = FIX2LONG(v);
5042 usec = nsec / 1000;
5043 nsec = nsec % 1000;
5045 nano = addv(LONG2FIX(nsec), subnano);
5047 p = 0x1UL << 31 | /* 1 */
5048 TZMODE_UTC_P(tobj) << 30 | /* 1 */
5049 (year-1900) << 14 | /* 16 */
5050 (vtm.mon-1) << 10 | /* 4 */
5051 vtm.mday << 5 | /* 5 */
5052 vtm.hour; /* 5 */
5053 s = (unsigned long)vtm.min << 26 | /* 6 */
5054 vtm.sec << 20 | /* 6 */
5055 usec; /* 20 */
5057 for (i=0; i<4; i++) {
5058 buf[i] = (unsigned char)p;
5059 p = RSHIFT(p, 8);
5061 for (i=4; i<8; i++) {
5062 buf[i] = (unsigned char)s;
5063 s = RSHIFT(s, 8);
5066 if (!NIL_P(year_extend)) {
5068 * Append extended year distance from 1900..(1900+0xffff). In
5069 * each cases, there is no sign as the value is positive. The
5070 * format is length (marshaled long) + little endian packed
5071 * binary (like as Integer).
5073 size_t ysize = rb_absint_size(year_extend, NULL);
5074 char *p, *const buf_year_extend = buf + base_dump_size;
5075 if (ysize > LONG_MAX ||
5076 (i = ruby_marshal_write_long((long)ysize, buf_year_extend)) < 0) {
5077 rb_raise(rb_eArgError, "year too %s to marshal: %"PRIsVALUE" UTC",
5078 (year == 1900 ? "small" : "big"), vtm.year);
5080 i += base_dump_size;
5081 str = rb_str_new(NULL, i + ysize);
5082 p = RSTRING_PTR(str);
5083 memcpy(p, buf, i);
5084 p += i;
5085 rb_integer_pack(year_extend, p, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
5087 else {
5088 str = rb_str_new(buf, base_dump_size);
5090 rb_copy_generic_ivar(str, time);
5091 if (!rb_equal(nano, INT2FIX(0))) {
5092 if (RB_TYPE_P(nano, T_RATIONAL)) {
5093 rb_ivar_set(str, id_nano_num, RRATIONAL(nano)->num);
5094 rb_ivar_set(str, id_nano_den, RRATIONAL(nano)->den);
5096 else {
5097 rb_ivar_set(str, id_nano_num, nano);
5098 rb_ivar_set(str, id_nano_den, INT2FIX(1));
5101 if (nsec) { /* submicro is only for Ruby 1.9.1 compatibility */
5103 * submicro is formatted in fixed-point packed BCD (without sign).
5104 * It represent digits under microsecond.
5105 * For nanosecond resolution, 3 digits (2 bytes) are used.
5106 * However it can be longer.
5107 * Extra digits are ignored for loading.
5109 char buf[2];
5110 int len = (int)sizeof(buf);
5111 buf[1] = (char)((nsec % 10) << 4);
5112 nsec /= 10;
5113 buf[0] = (char)(nsec % 10);
5114 nsec /= 10;
5115 buf[0] |= (char)((nsec % 10) << 4);
5116 if (buf[1] == 0)
5117 len = 1;
5118 rb_ivar_set(str, id_submicro, rb_str_new(buf, len));
5120 if (!TZMODE_UTC_P(tobj)) {
5121 VALUE off = rb_time_utc_offset(time), div, mod;
5122 divmodv(off, INT2FIX(1), &div, &mod);
5123 if (rb_equal(mod, INT2FIX(0)))
5124 off = rb_Integer(div);
5125 rb_ivar_set(str, id_offset, off);
5127 zone = tobj->vtm.zone;
5128 if (maybe_tzobj_p(zone)) {
5129 zone = rb_funcallv(zone, id_name, 0, 0);
5131 rb_ivar_set(str, id_zone, zone);
5132 return str;
5135 /* :nodoc: */
5136 static VALUE
5137 time_dump(int argc, VALUE *argv, VALUE time)
5139 VALUE str;
5141 rb_check_arity(argc, 0, 1);
5142 str = time_mdump(time);
5144 return str;
5147 static VALUE
5148 mload_findzone(VALUE arg)
5150 VALUE *argp = (VALUE *)arg;
5151 VALUE time = argp[0], zone = argp[1];
5152 return find_timezone(time, zone);
5155 static VALUE
5156 mload_zone(VALUE time, VALUE zone)
5158 VALUE z, args[2];
5159 args[0] = time;
5160 args[1] = zone;
5161 z = rb_rescue(mload_findzone, (VALUE)args, 0, Qnil);
5162 if (NIL_P(z)) return rb_fstring(zone);
5163 if (RB_TYPE_P(z, T_STRING)) return rb_fstring(z);
5164 return z;
5167 long ruby_marshal_read_long(const char **buf, long len);
5169 /* :nodoc: */
5170 static VALUE
5171 time_mload(VALUE time, VALUE str)
5173 struct time_object *tobj;
5174 unsigned long p, s;
5175 time_t sec;
5176 long usec;
5177 unsigned char *buf;
5178 struct vtm vtm;
5179 int i, gmt;
5180 long nsec;
5181 VALUE submicro, nano_num, nano_den, offset, zone, year;
5182 wideval_t timew;
5184 time_modify(time);
5186 #define get_attr(attr, iffound) \
5187 attr = rb_attr_delete(str, id_##attr); \
5188 if (!NIL_P(attr)) { \
5189 iffound; \
5192 get_attr(nano_num, {});
5193 get_attr(nano_den, {});
5194 get_attr(submicro, {});
5195 get_attr(offset, (offset = rb_rescue(validate_utc_offset, offset, 0, Qnil)));
5196 get_attr(zone, (zone = rb_rescue(validate_zone_name, zone, 0, Qnil)));
5197 get_attr(year, {});
5199 #undef get_attr
5201 rb_copy_generic_ivar(time, str);
5203 StringValue(str);
5204 buf = (unsigned char *)RSTRING_PTR(str);
5205 if (RSTRING_LEN(str) < base_dump_size) {
5206 goto invalid_format;
5209 p = s = 0;
5210 for (i=0; i<4; i++) {
5211 p |= (unsigned long)buf[i]<<(8*i);
5213 for (i=4; i<8; i++) {
5214 s |= (unsigned long)buf[i]<<(8*(i-4));
5217 if ((p & (1UL<<31)) == 0) {
5218 gmt = 0;
5219 offset = Qnil;
5220 sec = p;
5221 usec = s;
5222 nsec = usec * 1000;
5223 timew = wadd(rb_time_magnify(TIMET2WV(sec)), wmulquoll(WINT2FIXWV(usec), TIME_SCALE, 1000000));
5225 else {
5226 p &= ~(1UL<<31);
5227 gmt = (int)((p >> 30) & 0x1);
5229 if (NIL_P(year)) {
5230 year = INT2FIX(((int)(p >> 14) & 0xffff) + 1900);
5232 if (RSTRING_LEN(str) > base_dump_size) {
5233 long len = RSTRING_LEN(str) - base_dump_size;
5234 long ysize = 0;
5235 VALUE year_extend;
5236 const char *ybuf = (const char *)(buf += base_dump_size);
5237 ysize = ruby_marshal_read_long(&ybuf, len);
5238 len -= ybuf - (const char *)buf;
5239 if (ysize < 0 || ysize > len) goto invalid_format;
5240 year_extend = rb_integer_unpack(ybuf, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
5241 if (year == INT2FIX(1900)) {
5242 year = rb_int_minus(year, year_extend);
5244 else {
5245 year = rb_int_plus(year, year_extend);
5248 unsigned int mon = ((int)(p >> 10) & 0xf); /* 0...12 */
5249 if (mon >= 12) {
5250 mon -= 12;
5251 year = addv(year, LONG2FIX(1));
5253 vtm.year = year;
5254 vtm.mon = mon + 1;
5255 vtm.mday = (int)(p >> 5) & 0x1f;
5256 vtm.hour = (int) p & 0x1f;
5257 vtm.min = (int)(s >> 26) & 0x3f;
5258 vtm.sec = (int)(s >> 20) & 0x3f;
5259 vtm.utc_offset = INT2FIX(0);
5260 vtm.yday = vtm.wday = 0;
5261 vtm.isdst = 0;
5262 vtm.zone = str_empty;
5264 usec = (long)(s & 0xfffff);
5265 nsec = usec * 1000;
5268 vtm.subsecx = mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000));
5269 if (nano_num != Qnil) {
5270 VALUE nano = quov(num_exact(nano_num), num_exact(nano_den));
5271 vtm.subsecx = addv(vtm.subsecx, mulquov(nano, INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
5273 else if (submicro != Qnil) { /* for Ruby 1.9.1 compatibility */
5274 unsigned char *ptr;
5275 long len;
5276 int digit;
5277 ptr = (unsigned char*)StringValuePtr(submicro);
5278 len = RSTRING_LEN(submicro);
5279 nsec = 0;
5280 if (0 < len) {
5281 if (10 <= (digit = ptr[0] >> 4)) goto end_submicro;
5282 nsec += digit * 100;
5283 if (10 <= (digit = ptr[0] & 0xf)) goto end_submicro;
5284 nsec += digit * 10;
5286 if (1 < len) {
5287 if (10 <= (digit = ptr[1] >> 4)) goto end_submicro;
5288 nsec += digit;
5290 vtm.subsecx = addv(vtm.subsecx, mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
5291 end_submicro: ;
5293 timew = timegmw(&vtm);
5296 GetNewTimeval(time, tobj);
5297 tobj->tzmode = TIME_TZMODE_LOCALTIME;
5298 tobj->tm_got = 0;
5299 tobj->timew = timew;
5300 if (gmt) {
5301 TZMODE_SET_UTC(tobj);
5303 else if (!NIL_P(offset)) {
5304 time_set_utc_offset(time, offset);
5305 time_fixoff(time);
5307 if (!NIL_P(zone)) {
5308 zone = mload_zone(time, zone);
5309 tobj->vtm.zone = zone;
5310 zone_localtime(zone, time);
5313 return time;
5315 invalid_format:
5316 rb_raise(rb_eTypeError, "marshaled time format differ");
5317 UNREACHABLE_RETURN(Qundef);
5320 /* :nodoc: */
5321 static VALUE
5322 time_load(VALUE klass, VALUE str)
5324 VALUE time = time_s_alloc(klass);
5326 time_mload(time, str);
5327 return time;
5330 /* :nodoc:*/
5331 /* Document-class: Time::tm
5333 * A container class for timezone conversion.
5337 * call-seq:
5339 * Time::tm.from_time(t) -> tm
5341 * Creates new Time::tm object from a Time object.
5344 static VALUE
5345 tm_from_time(VALUE klass, VALUE time)
5347 struct time_object *tobj;
5348 struct vtm vtm, *v;
5349 #if TM_IS_TIME
5350 VALUE tm;
5351 struct time_object *ttm;
5353 GetTimeval(time, tobj);
5354 tm = time_s_alloc(klass);
5355 ttm = DATA_PTR(tm);
5356 v = &vtm;
5357 GMTIMEW(ttm->timew = tobj->timew, v);
5358 ttm->timew = wsub(ttm->timew, v->subsecx);
5359 v->subsecx = INT2FIX(0);
5360 v->zone = Qnil;
5361 ttm->vtm = *v;
5362 ttm->tm_got = 1;
5363 TZMODE_SET_UTC(ttm);
5364 return tm;
5365 #else
5366 VALUE args[8];
5367 int i = 0;
5369 GetTimeval(time, tobj);
5370 if (tobj->tm_got && TZMODE_UTC_P(tobj))
5371 v = &tobj->vtm;
5372 else
5373 GMTIMEW(tobj->timew, v = &vtm);
5374 args[i++] = v->year;
5375 args[i++] = INT2FIX(v->mon);
5376 args[i++] = INT2FIX(v->mday);
5377 args[i++] = INT2FIX(v->hour);
5378 args[i++] = INT2FIX(v->min);
5379 args[i++] = INT2FIX(v->sec);
5380 switch (v->isdst) {
5381 case 0: args[i++] = Qfalse; break;
5382 case 1: args[i++] = Qtrue; break;
5383 default: args[i++] = Qnil; break;
5385 args[i++] = w2v(rb_time_unmagnify(tobj->timew));
5386 return rb_class_new_instance(i, args, klass);
5387 #endif
5391 * call-seq:
5393 * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm
5395 * Creates new Time::tm object.
5398 static VALUE
5399 tm_initialize(int argc, VALUE *argv, VALUE tm)
5401 struct vtm vtm;
5402 wideval_t t;
5404 if (rb_check_arity(argc, 1, 7) > 6) argc = 6;
5405 time_arg(argc, argv, &vtm);
5406 t = timegmw(&vtm);
5408 #if TM_IS_TIME
5409 struct time_object *tobj = DATA_PTR(tm);
5410 tobj->tzmode = TIME_TZMODE_UTC;
5411 tobj->timew = t;
5412 tobj->vtm = vtm;
5413 #else
5414 int i = 0;
5415 RSTRUCT_SET(tm, i++, INT2FIX(vtm.sec));
5416 RSTRUCT_SET(tm, i++, INT2FIX(vtm.min));
5417 RSTRUCT_SET(tm, i++, INT2FIX(vtm.hour));
5418 RSTRUCT_SET(tm, i++, INT2FIX(vtm.mday));
5419 RSTRUCT_SET(tm, i++, INT2FIX(vtm.mon));
5420 RSTRUCT_SET(tm, i++, vtm.year);
5421 RSTRUCT_SET(tm, i++, w2v(rb_time_unmagnify(t)));
5422 #endif
5424 return tm;
5427 /* call-seq:
5429 * tm.to_time -> time
5431 * Returns a new Time object.
5434 static VALUE
5435 tm_to_time(VALUE tm)
5437 #if TM_IS_TIME
5438 struct time_object *torig = get_timeval(tm);
5439 VALUE dup = time_s_alloc(rb_cTime);
5440 struct time_object *tobj = DATA_PTR(dup);
5441 *tobj = *torig;
5442 return dup;
5443 #else
5444 VALUE t[6];
5445 const VALUE *p = RSTRUCT_CONST_PTR(tm);
5446 int i;
5448 for (i = 0; i < numberof(t); ++i) {
5449 t[i] = p[numberof(t) - 1 - i];
5451 return time_s_mkutc(numberof(t), t, rb_cTime);
5452 #endif
5455 #if !TM_IS_TIME
5456 static VALUE
5457 tm_zero(VALUE tm)
5459 return INT2FIX(0);
5462 #define tm_subsec tm_zero
5463 #define tm_utc_offset tm_zero
5465 static VALUE
5466 tm_isdst(VALUE tm)
5468 return Qfalse;
5471 static VALUE
5472 tm_to_s(VALUE tm)
5474 const VALUE *p = RSTRUCT_CONST_PTR(tm);
5476 return rb_sprintf("%.4"PRIsVALUE"-%.2"PRIsVALUE"-%.2"PRIsVALUE" "
5477 "%.2"PRIsVALUE":%.2"PRIsVALUE":%.2"PRIsVALUE" "
5478 "UTC",
5479 p[5], p[4], p[3], p[2], p[1], p[0]);
5481 #else
5482 static VALUE
5483 tm_plus(VALUE tm, VALUE offset)
5485 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, +1);
5488 static VALUE
5489 tm_minus(VALUE tm, VALUE offset)
5491 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, -1);
5493 #endif
5495 static VALUE
5496 Init_tm(VALUE outer, const char *name)
5498 /* :stopdoc:*/
5499 VALUE tm;
5500 #if TM_IS_TIME
5501 tm = rb_define_class_under(outer, name, rb_cObject);
5502 rb_define_alloc_func(tm, time_s_alloc);
5503 rb_define_method(tm, "sec", time_sec, 0);
5504 rb_define_method(tm, "min", time_min, 0);
5505 rb_define_method(tm, "hour", time_hour, 0);
5506 rb_define_method(tm, "mday", time_mday, 0);
5507 rb_define_method(tm, "day", time_mday, 0);
5508 rb_define_method(tm, "mon", time_mon, 0);
5509 rb_define_method(tm, "month", time_mon, 0);
5510 rb_define_method(tm, "year", time_year, 0);
5511 rb_define_method(tm, "isdst", time_isdst, 0);
5512 rb_define_method(tm, "dst?", time_isdst, 0);
5513 rb_define_method(tm, "zone", time_zone, 0);
5514 rb_define_method(tm, "gmtoff", rb_time_utc_offset, 0);
5515 rb_define_method(tm, "gmt_offset", rb_time_utc_offset, 0);
5516 rb_define_method(tm, "utc_offset", rb_time_utc_offset, 0);
5517 rb_define_method(tm, "utc?", time_utc_p, 0);
5518 rb_define_method(tm, "gmt?", time_utc_p, 0);
5519 rb_define_method(tm, "to_s", time_to_s, 0);
5520 rb_define_method(tm, "inspect", time_inspect, 0);
5521 rb_define_method(tm, "to_a", time_to_a, 0);
5522 rb_define_method(tm, "tv_sec", time_to_i, 0);
5523 rb_define_method(tm, "tv_usec", time_usec, 0);
5524 rb_define_method(tm, "usec", time_usec, 0);
5525 rb_define_method(tm, "tv_nsec", time_nsec, 0);
5526 rb_define_method(tm, "nsec", time_nsec, 0);
5527 rb_define_method(tm, "subsec", time_subsec, 0);
5528 rb_define_method(tm, "to_i", time_to_i, 0);
5529 rb_define_method(tm, "to_f", time_to_f, 0);
5530 rb_define_method(tm, "to_r", time_to_r, 0);
5531 rb_define_method(tm, "+", tm_plus, 1);
5532 rb_define_method(tm, "-", tm_minus, 1);
5533 #else
5534 tm = rb_struct_define_under(outer, "tm",
5535 "sec", "min", "hour",
5536 "mday", "mon", "year",
5537 "to_i", NULL);
5538 rb_define_method(tm, "subsec", tm_subsec, 0);
5539 rb_define_method(tm, "utc_offset", tm_utc_offset, 0);
5540 rb_define_method(tm, "to_s", tm_to_s, 0);
5541 rb_define_method(tm, "inspect", tm_to_s, 0);
5542 rb_define_method(tm, "isdst", tm_isdst, 0);
5543 rb_define_method(tm, "dst?", tm_isdst, 0);
5544 #endif
5545 rb_define_method(tm, "initialize", tm_initialize, -1);
5546 rb_define_method(tm, "utc", tm_to_time, 0);
5547 rb_alias(tm, rb_intern_const("to_time"), rb_intern_const("utc"));
5548 rb_define_singleton_method(tm, "from_time", tm_from_time, 1);
5549 /* :startdoc:*/
5551 return tm;
5554 VALUE
5555 rb_time_zone_abbreviation(VALUE zone, VALUE time)
5557 VALUE tm, abbr, strftime_args[2];
5559 abbr = rb_check_string_type(zone);
5560 if (!NIL_P(abbr)) return abbr;
5562 tm = tm_from_time(rb_cTimeTM, time);
5563 abbr = rb_check_funcall(zone, rb_intern("abbr"), 1, &tm);
5564 if (abbr != Qundef) {
5565 goto found;
5567 #ifdef SUPPORT_TZINFO_ZONE_ABBREVIATION
5568 abbr = rb_check_funcall(zone, rb_intern("period_for_utc"), 1, &tm);
5569 if (abbr != Qundef) {
5570 abbr = rb_funcallv(abbr, rb_intern("abbreviation"), 0, 0);
5571 goto found;
5573 #endif
5574 strftime_args[0] = rb_fstring_lit("%Z");
5575 strftime_args[1] = tm;
5576 abbr = rb_check_funcall(zone, rb_intern("strftime"), 2, strftime_args);
5577 if (abbr != Qundef) {
5578 goto found;
5580 abbr = rb_check_funcall_default(zone, idName, 0, 0, Qnil);
5581 found:
5582 return rb_obj_as_string(abbr);
5585 /* Internal Details:
5587 * Since Ruby 1.9.2, Time implementation uses a signed 63 bit integer or
5588 * Integer(T_BIGNUM), Rational.
5589 * The integer is a number of nanoseconds since the _Epoch_ which can
5590 * represent 1823-11-12 to 2116-02-20.
5591 * When Integer(T_BIGNUM) or Rational is used (before 1823, after 2116, under
5592 * nanosecond), Time works slower than when integer is used.
5596 void
5597 Init_Time(void)
5599 id_submicro = rb_intern_const("submicro");
5600 id_nano_num = rb_intern_const("nano_num");
5601 id_nano_den = rb_intern_const("nano_den");
5602 id_offset = rb_intern_const("offset");
5603 id_zone = rb_intern_const("zone");
5604 id_nanosecond = rb_intern_const("nanosecond");
5605 id_microsecond = rb_intern_const("microsecond");
5606 id_millisecond = rb_intern_const("millisecond");
5607 id_nsec = rb_intern_const("nsec");
5608 id_usec = rb_intern_const("usec");
5609 id_local_to_utc = rb_intern_const("local_to_utc");
5610 id_utc_to_local = rb_intern_const("utc_to_local");
5611 id_year = rb_intern_const("year");
5612 id_mon = rb_intern_const("mon");
5613 id_mday = rb_intern_const("mday");
5614 id_hour = rb_intern_const("hour");
5615 id_min = rb_intern_const("min");
5616 id_sec = rb_intern_const("sec");
5617 id_isdst = rb_intern_const("isdst");
5618 id_find_timezone = rb_intern_const("find_timezone");
5620 str_utc = rb_fstring_lit("UTC");
5621 rb_gc_register_mark_object(str_utc);
5622 str_empty = rb_fstring_lit("");
5623 rb_gc_register_mark_object(str_empty);
5625 rb_cTime = rb_define_class("Time", rb_cObject);
5626 rb_include_module(rb_cTime, rb_mComparable);
5628 rb_define_alloc_func(rb_cTime, time_s_alloc);
5629 rb_define_singleton_method(rb_cTime, "utc", time_s_mkutc, -1);
5630 rb_define_singleton_method(rb_cTime, "local", time_s_mktime, -1);
5631 rb_define_alias(rb_singleton_class(rb_cTime), "gm", "utc");
5632 rb_define_alias(rb_singleton_class(rb_cTime), "mktime", "local");
5634 rb_define_method(rb_cTime, "to_i", time_to_i, 0);
5635 rb_define_method(rb_cTime, "to_f", time_to_f, 0);
5636 rb_define_method(rb_cTime, "to_r", time_to_r, 0);
5637 rb_define_method(rb_cTime, "<=>", time_cmp, 1);
5638 rb_define_method(rb_cTime, "eql?", time_eql, 1);
5639 rb_define_method(rb_cTime, "hash", time_hash, 0);
5640 rb_define_method(rb_cTime, "initialize_copy", time_init_copy, 1);
5642 rb_define_method(rb_cTime, "localtime", time_localtime_m, -1);
5643 rb_define_method(rb_cTime, "gmtime", time_gmtime, 0);
5644 rb_define_method(rb_cTime, "utc", time_gmtime, 0);
5645 rb_define_method(rb_cTime, "getlocal", time_getlocaltime, -1);
5646 rb_define_method(rb_cTime, "getgm", time_getgmtime, 0);
5647 rb_define_method(rb_cTime, "getutc", time_getgmtime, 0);
5649 rb_define_method(rb_cTime, "ctime", time_asctime, 0);
5650 rb_define_method(rb_cTime, "asctime", time_asctime, 0);
5651 rb_define_method(rb_cTime, "to_s", time_to_s, 0);
5652 rb_define_method(rb_cTime, "inspect", time_inspect, 0);
5653 rb_define_method(rb_cTime, "to_a", time_to_a, 0);
5655 rb_define_method(rb_cTime, "+", time_plus, 1);
5656 rb_define_method(rb_cTime, "-", time_minus, 1);
5658 rb_define_method(rb_cTime, "round", time_round, -1);
5659 rb_define_method(rb_cTime, "floor", time_floor, -1);
5660 rb_define_method(rb_cTime, "ceil", time_ceil, -1);
5662 rb_define_method(rb_cTime, "sec", time_sec, 0);
5663 rb_define_method(rb_cTime, "min", time_min, 0);
5664 rb_define_method(rb_cTime, "hour", time_hour, 0);
5665 rb_define_method(rb_cTime, "mday", time_mday, 0);
5666 rb_define_method(rb_cTime, "day", time_mday, 0);
5667 rb_define_method(rb_cTime, "mon", time_mon, 0);
5668 rb_define_method(rb_cTime, "month", time_mon, 0);
5669 rb_define_method(rb_cTime, "year", time_year, 0);
5670 rb_define_method(rb_cTime, "wday", time_wday, 0);
5671 rb_define_method(rb_cTime, "yday", time_yday, 0);
5672 rb_define_method(rb_cTime, "isdst", time_isdst, 0);
5673 rb_define_method(rb_cTime, "dst?", time_isdst, 0);
5674 rb_define_method(rb_cTime, "zone", time_zone, 0);
5675 rb_define_method(rb_cTime, "gmtoff", rb_time_utc_offset, 0);
5676 rb_define_method(rb_cTime, "gmt_offset", rb_time_utc_offset, 0);
5677 rb_define_method(rb_cTime, "utc_offset", rb_time_utc_offset, 0);
5679 rb_define_method(rb_cTime, "utc?", time_utc_p, 0);
5680 rb_define_method(rb_cTime, "gmt?", time_utc_p, 0);
5682 rb_define_method(rb_cTime, "sunday?", time_sunday, 0);
5683 rb_define_method(rb_cTime, "monday?", time_monday, 0);
5684 rb_define_method(rb_cTime, "tuesday?", time_tuesday, 0);
5685 rb_define_method(rb_cTime, "wednesday?", time_wednesday, 0);
5686 rb_define_method(rb_cTime, "thursday?", time_thursday, 0);
5687 rb_define_method(rb_cTime, "friday?", time_friday, 0);
5688 rb_define_method(rb_cTime, "saturday?", time_saturday, 0);
5690 rb_define_method(rb_cTime, "tv_sec", time_to_i, 0);
5691 rb_define_method(rb_cTime, "tv_usec", time_usec, 0);
5692 rb_define_method(rb_cTime, "usec", time_usec, 0);
5693 rb_define_method(rb_cTime, "tv_nsec", time_nsec, 0);
5694 rb_define_method(rb_cTime, "nsec", time_nsec, 0);
5695 rb_define_method(rb_cTime, "subsec", time_subsec, 0);
5697 rb_define_method(rb_cTime, "strftime", time_strftime, 1);
5699 /* methods for marshaling */
5700 rb_define_private_method(rb_cTime, "_dump", time_dump, -1);
5701 rb_define_private_method(rb_singleton_class(rb_cTime), "_load", time_load, 1);
5702 #if 0
5703 /* Time will support marshal_dump and marshal_load in the future (1.9 maybe) */
5704 rb_define_private_method(rb_cTime, "marshal_dump", time_mdump, 0);
5705 rb_define_private_method(rb_cTime, "marshal_load", time_mload, 1);
5706 #endif
5708 if (debug_find_time_numguess) {
5709 rb_define_hooked_variable("$find_time_numguess", (VALUE *)&find_time_numguess,
5710 find_time_numguess_getter, NULL);
5713 rb_cTimeTM = Init_tm(rb_cTime, "tm");
5716 #include "timev.rbinc"