Ruby 3.5.0dev (2025-06-27 revision fe9a3be2966b0e54b93e9349bf47d4e5b879a6e1)
time.c (fe9a3be2966b0e54b93e9349bf47d4e5b879a6e1)
1/**********************************************************************
2
3 time.c -
4
5 $Author$
6 created at: Tue Dec 28 14:31:59 JST 1993
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9
10**********************************************************************/
11
12#define _DEFAULT_SOURCE
13#define _BSD_SOURCE
14#include "ruby/internal/config.h"
15
16#include <errno.h>
17#include <float.h>
18#include <math.h>
19#include <time.h>
20#include <sys/types.h>
21
22#ifdef HAVE_UNISTD_H
23# include <unistd.h>
24#endif
25
26#ifdef HAVE_STRINGS_H
27# include <strings.h>
28#endif
29
30#if defined(HAVE_SYS_TIME_H)
31# include <sys/time.h>
32#endif
33
34#include "id.h"
35#include "internal.h"
36#include "internal/array.h"
37#include "internal/hash.h"
38#include "internal/compar.h"
39#include "internal/numeric.h"
40#include "internal/rational.h"
41#include "internal/string.h"
42#include "internal/time.h"
43#include "internal/variable.h"
44#include "ruby/encoding.h"
45#include "ruby/util.h"
46#include "timev.h"
47
48#if defined(_WIN32)
49# include "timezoneapi.h" /* DYNAMIC_TIME_ZONE_INFORMATION */
50#endif
51
52#include "builtin.h"
53
54static ID id_submicro, id_nano_num, id_nano_den, id_offset, id_zone;
55static ID id_nanosecond, id_microsecond, id_millisecond, id_nsec, id_usec;
56static ID id_local_to_utc, id_utc_to_local, id_find_timezone;
57static ID id_year, id_mon, id_mday, id_hour, id_min, id_sec, id_isdst;
58static VALUE str_utc, str_empty;
59
60// used by deconstruct_keys
61static VALUE sym_year, sym_month, sym_day, sym_yday, sym_wday;
62static VALUE sym_hour, sym_min, sym_sec, sym_subsec, sym_dst, sym_zone;
63
64#define id_quo idQuo
65#define id_div idDiv
66#define id_divmod idDivmod
67#define id_name idName
68#define UTC_ZONE Qundef
69
70#define NDIV(x,y) (-(-((x)+1)/(y))-1)
71#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
72#define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
73#define MOD(n,d) ((n)<0 ? NMOD((n),(d)) : (n)%(d))
74#define VTM_WDAY_INITVAL (7)
75#define VTM_ISDST_INITVAL (3)
76
77static int
78eq(VALUE x, VALUE y)
79{
80 if (FIXNUM_P(x) && FIXNUM_P(y)) {
81 return x == y;
82 }
83 return RTEST(rb_funcall(x, idEq, 1, y));
84}
85
86static int
87cmp(VALUE x, VALUE y)
88{
89 if (FIXNUM_P(x) && FIXNUM_P(y)) {
90 if ((long)x < (long)y)
91 return -1;
92 if ((long)x > (long)y)
93 return 1;
94 return 0;
95 }
96 if (RB_BIGNUM_TYPE_P(x)) return FIX2INT(rb_big_cmp(x, y));
97 return rb_cmpint(rb_funcall(x, idCmp, 1, y), x, y);
98}
99
100#define ne(x,y) (!eq((x),(y)))
101#define lt(x,y) (cmp((x),(y)) < 0)
102#define gt(x,y) (cmp((x),(y)) > 0)
103#define le(x,y) (cmp((x),(y)) <= 0)
104#define ge(x,y) (cmp((x),(y)) >= 0)
105
106static VALUE
107addv(VALUE x, VALUE y)
108{
109 if (FIXNUM_P(x) && FIXNUM_P(y)) {
110 return LONG2NUM(FIX2LONG(x) + FIX2LONG(y));
111 }
112 if (RB_BIGNUM_TYPE_P(x)) return rb_big_plus(x, y);
113 return rb_funcall(x, '+', 1, y);
114}
115
116static VALUE
117subv(VALUE x, VALUE y)
118{
119 if (FIXNUM_P(x) && FIXNUM_P(y)) {
120 return LONG2NUM(FIX2LONG(x) - FIX2LONG(y));
121 }
122 if (RB_BIGNUM_TYPE_P(x)) return rb_big_minus(x, y);
123 return rb_funcall(x, '-', 1, y);
124}
125
126static VALUE
127mulv(VALUE x, VALUE y)
128{
129 if (FIXNUM_P(x) && FIXNUM_P(y)) {
130 return rb_fix_mul_fix(x, y);
131 }
132 if (RB_BIGNUM_TYPE_P(x))
133 return rb_big_mul(x, y);
134 return rb_funcall(x, '*', 1, y);
135}
136
137static VALUE
138divv(VALUE x, VALUE y)
139{
140 if (FIXNUM_P(x) && FIXNUM_P(y)) {
141 return rb_fix_div_fix(x, y);
142 }
143 if (RB_BIGNUM_TYPE_P(x))
144 return rb_big_div(x, y);
145 return rb_funcall(x, id_div, 1, y);
146}
147
148static VALUE
149modv(VALUE x, VALUE y)
150{
151 if (FIXNUM_P(y)) {
152 if (FIX2LONG(y) == 0) rb_num_zerodiv();
153 if (FIXNUM_P(x)) return rb_fix_mod_fix(x, y);
154 }
155 if (RB_BIGNUM_TYPE_P(x)) return rb_big_modulo(x, y);
156 return rb_funcall(x, '%', 1, y);
157}
158
159#define neg(x) (subv(INT2FIX(0), (x)))
160
161static VALUE
162quor(VALUE x, VALUE y)
163{
164 if (FIXNUM_P(x) && FIXNUM_P(y)) {
165 long a, b, c;
166 a = FIX2LONG(x);
167 b = FIX2LONG(y);
168 if (b == 0) rb_num_zerodiv();
169 if (a == FIXNUM_MIN && b == -1) return LONG2NUM(-a);
170 c = a / b;
171 if (c * b == a) {
172 return LONG2FIX(c);
173 }
174 }
175 return rb_numeric_quo(x, y);
176}
177
178static VALUE
179quov(VALUE x, VALUE y)
180{
181 VALUE ret = quor(x, y);
182 if (RB_TYPE_P(ret, T_RATIONAL) &&
183 RRATIONAL(ret)->den == INT2FIX(1)) {
184 ret = RRATIONAL(ret)->num;
185 }
186 return ret;
187}
188
189#define mulquov(x,y,z) (((y) == (z)) ? (x) : quov(mulv((x),(y)),(z)))
190
191static void
192divmodv(VALUE n, VALUE d, VALUE *q, VALUE *r)
193{
194 VALUE tmp, ary;
195 if (FIXNUM_P(d)) {
196 if (FIX2LONG(d) == 0) rb_num_zerodiv();
197 if (FIXNUM_P(n)) {
198 rb_fix_divmod_fix(n, d, q, r);
199 return;
200 }
201 }
202 tmp = rb_funcall(n, id_divmod, 1, d);
203 ary = rb_check_array_type(tmp);
204 if (NIL_P(ary)) {
205 rb_raise(rb_eTypeError, "unexpected divmod result: into %"PRIsVALUE,
206 rb_obj_class(tmp));
207 }
208 *q = rb_ary_entry(ary, 0);
209 *r = rb_ary_entry(ary, 1);
210}
211
212#if SIZEOF_LONG == 8
213# define INT64toNUM(x) LONG2NUM(x)
214#elif defined(HAVE_LONG_LONG) && SIZEOF_LONG_LONG == 8
215# define INT64toNUM(x) LL2NUM(x)
216#endif
217
218#if defined(HAVE_UINT64_T) && SIZEOF_LONG*2 <= SIZEOF_UINT64_T
219 typedef uint64_t uwideint_t;
220 typedef int64_t wideint_t;
221 typedef uint64_t WIDEVALUE;
222 typedef int64_t SIGNED_WIDEVALUE;
223# define WIDEVALUE_IS_WIDER 1
224# define UWIDEINT_MAX UINT64_MAX
225# define WIDEINT_MAX INT64_MAX
226# define WIDEINT_MIN INT64_MIN
227# define FIXWINT_P(tv) ((tv) & 1)
228# define FIXWVtoINT64(tv) RSHIFT((SIGNED_WIDEVALUE)(tv), 1)
229# define INT64toFIXWV(wi) ((WIDEVALUE)((SIGNED_WIDEVALUE)(wi) << 1 | FIXNUM_FLAG))
230# define FIXWV_MAX (((int64_t)1 << 62) - 1)
231# define FIXWV_MIN (-((int64_t)1 << 62))
232# define FIXWVABLE(wi) (POSFIXWVABLE(wi) && NEGFIXWVABLE(wi))
233# define WINT2FIXWV(i) WIDEVAL_WRAP(INT64toFIXWV(i))
234# define FIXWV2WINT(w) FIXWVtoINT64(WIDEVAL_GET(w))
235#else
236 typedef unsigned long uwideint_t;
237 typedef long wideint_t;
238 typedef VALUE WIDEVALUE;
239 typedef SIGNED_VALUE SIGNED_WIDEVALUE;
240# define WIDEVALUE_IS_WIDER 0
241# define UWIDEINT_MAX ULONG_MAX
242# define WIDEINT_MAX LONG_MAX
243# define WIDEINT_MIN LONG_MIN
244# define FIXWINT_P(v) FIXNUM_P(v)
245# define FIXWV_MAX FIXNUM_MAX
246# define FIXWV_MIN FIXNUM_MIN
247# define FIXWVABLE(i) FIXABLE(i)
248# define WINT2FIXWV(i) WIDEVAL_WRAP(LONG2FIX(i))
249# define FIXWV2WINT(w) FIX2LONG(WIDEVAL_GET(w))
250#endif
251
252#define SIZEOF_WIDEINT SIZEOF_INT64_T
253#define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1)
254#define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN)
255#define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w))
256#define MUL_OVERFLOW_FIXWV_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXWV_MIN, FIXWV_MAX)
257
258/* #define STRUCT_WIDEVAL */
259#ifdef STRUCT_WIDEVAL
260 /* for type checking */
261 typedef struct {
262 WIDEVALUE value;
263 } wideval_t;
264 static inline wideval_t WIDEVAL_WRAP(WIDEVALUE v) { wideval_t w = { v }; return w; }
265# define WIDEVAL_GET(w) ((w).value)
266#else
267 typedef WIDEVALUE wideval_t;
268# define WIDEVAL_WRAP(v) (v)
269# define WIDEVAL_GET(w) (w)
270#endif
271
272#if WIDEVALUE_IS_WIDER
273 static inline wideval_t
274 wint2wv(wideint_t wi)
275 {
276 if (FIXWVABLE(wi))
277 return WINT2FIXWV(wi);
278 else
279 return WIDEVAL_WRAP(INT64toNUM(wi));
280 }
281# define WINT2WV(wi) wint2wv(wi)
282#else
283# define WINT2WV(wi) WIDEVAL_WRAP(LONG2NUM(wi))
284#endif
285
286static inline VALUE
287w2v(wideval_t w)
288{
289#if WIDEVALUE_IS_WIDER
290 if (FIXWV_P(w))
291 return INT64toNUM(FIXWV2WINT(w));
292 return (VALUE)WIDEVAL_GET(w);
293#else
294 return WIDEVAL_GET(w);
295#endif
296}
297
298#if WIDEVALUE_IS_WIDER
299static wideval_t
300v2w_bignum(VALUE v)
301{
302 int sign;
303 uwideint_t u;
304 sign = rb_integer_pack(v, &u, 1, sizeof(u), 0,
306 if (sign == 0)
307 return WINT2FIXWV(0);
308 else if (sign == -1) {
309 if (u <= -FIXWV_MIN)
310 return WINT2FIXWV(-(wideint_t)u);
311 }
312 else if (sign == +1) {
313 if (u <= FIXWV_MAX)
314 return WINT2FIXWV((wideint_t)u);
315 }
316 return WIDEVAL_WRAP(v);
317}
318#endif
319
320static inline wideval_t
321v2w(VALUE v)
322{
323 if (RB_TYPE_P(v, T_RATIONAL)) {
324 if (RRATIONAL(v)->den != LONG2FIX(1))
325 return WIDEVAL_WRAP(v);
326 v = RRATIONAL(v)->num;
327 }
328#if WIDEVALUE_IS_WIDER
329 if (FIXNUM_P(v)) {
330 return WIDEVAL_WRAP((WIDEVALUE)(SIGNED_WIDEVALUE)(long)v);
331 }
332 else if (RB_BIGNUM_TYPE_P(v) &&
333 rb_absint_size(v, NULL) <= sizeof(WIDEVALUE)) {
334 return v2w_bignum(v);
335 }
336#endif
337 return WIDEVAL_WRAP(v);
338}
339
340#define NUM2WV(v) v2w(rb_Integer(v))
341
342static int
343weq(wideval_t wx, wideval_t wy)
344{
345#if WIDEVALUE_IS_WIDER
346 if (FIXWV_P(wx) && FIXWV_P(wy)) {
347 return WIDEVAL_GET(wx) == WIDEVAL_GET(wy);
348 }
349 return RTEST(rb_funcall(w2v(wx), idEq, 1, w2v(wy)));
350#else
351 return eq(WIDEVAL_GET(wx), WIDEVAL_GET(wy));
352#endif
353}
354
355static int
356wcmp(wideval_t wx, wideval_t wy)
357{
358 VALUE x, y;
359#if WIDEVALUE_IS_WIDER
360 if (FIXWV_P(wx) && FIXWV_P(wy)) {
361 wideint_t a, b;
362 a = FIXWV2WINT(wx);
363 b = FIXWV2WINT(wy);
364 if (a < b)
365 return -1;
366 if (a > b)
367 return 1;
368 return 0;
369 }
370#endif
371 x = w2v(wx);
372 y = w2v(wy);
373 return cmp(x, y);
374}
375
376#define wne(x,y) (!weq((x),(y)))
377#define wlt(x,y) (wcmp((x),(y)) < 0)
378#define wgt(x,y) (wcmp((x),(y)) > 0)
379#define wle(x,y) (wcmp((x),(y)) <= 0)
380#define wge(x,y) (wcmp((x),(y)) >= 0)
381
382static wideval_t
383wadd(wideval_t wx, wideval_t wy)
384{
385#if WIDEVALUE_IS_WIDER
386 if (FIXWV_P(wx) && FIXWV_P(wy)) {
387 wideint_t r = FIXWV2WINT(wx) + FIXWV2WINT(wy);
388 return WINT2WV(r);
389 }
390#endif
391 return v2w(addv(w2v(wx), w2v(wy)));
392}
393
394static wideval_t
395wsub(wideval_t wx, wideval_t wy)
396{
397#if WIDEVALUE_IS_WIDER
398 if (FIXWV_P(wx) && FIXWV_P(wy)) {
399 wideint_t r = FIXWV2WINT(wx) - FIXWV2WINT(wy);
400 return WINT2WV(r);
401 }
402#endif
403 return v2w(subv(w2v(wx), w2v(wy)));
404}
405
406static wideval_t
407wmul(wideval_t wx, wideval_t wy)
408{
409#if WIDEVALUE_IS_WIDER
410 if (FIXWV_P(wx) && FIXWV_P(wy)) {
411 if (!MUL_OVERFLOW_FIXWV_P(FIXWV2WINT(wx), FIXWV2WINT(wy)))
412 return WINT2WV(FIXWV2WINT(wx) * FIXWV2WINT(wy));
413 }
414#endif
415 return v2w(mulv(w2v(wx), w2v(wy)));
416}
417
418static wideval_t
419wquo(wideval_t wx, wideval_t wy)
420{
421#if WIDEVALUE_IS_WIDER
422 if (FIXWV_P(wx) && FIXWV_P(wy)) {
423 wideint_t a, b, c;
424 a = FIXWV2WINT(wx);
425 b = FIXWV2WINT(wy);
426 if (b == 0) rb_num_zerodiv();
427 c = a / b;
428 if (c * b == a) {
429 return WINT2WV(c);
430 }
431 }
432#endif
433 return v2w(quov(w2v(wx), w2v(wy)));
434}
435
436#define wmulquo(x,y,z) ((WIDEVAL_GET(y) == WIDEVAL_GET(z)) ? (x) : wquo(wmul((x),(y)),(z)))
437#define wmulquoll(x,y,z) (((y) == (z)) ? (x) : wquo(wmul((x),WINT2WV(y)),WINT2WV(z)))
438
439#if WIDEVALUE_IS_WIDER
440static int
441wdivmod0(wideval_t wn, wideval_t wd, wideval_t *wq, wideval_t *wr)
442{
443 if (FIXWV_P(wn) && FIXWV_P(wd)) {
444 wideint_t n, d, q, r;
445 d = FIXWV2WINT(wd);
446 if (d == 0) rb_num_zerodiv();
447 if (d == 1) {
448 *wq = wn;
449 *wr = WINT2FIXWV(0);
450 return 1;
451 }
452 if (d == -1) {
453 wideint_t xneg = -FIXWV2WINT(wn);
454 *wq = WINT2WV(xneg);
455 *wr = WINT2FIXWV(0);
456 return 1;
457 }
458 n = FIXWV2WINT(wn);
459 if (n == 0) {
460 *wq = WINT2FIXWV(0);
461 *wr = WINT2FIXWV(0);
462 return 1;
463 }
464 q = n / d;
465 r = n % d;
466 if (d > 0 ? r < 0 : r > 0) {
467 q -= 1;
468 r += d;
469 }
470 *wq = WINT2FIXWV(q);
471 *wr = WINT2FIXWV(r);
472 return 1;
473 }
474 return 0;
475}
476#endif
477
478static void
479wdivmod(wideval_t wn, wideval_t wd, wideval_t *wq, wideval_t *wr)
480{
481 VALUE vq, vr;
482#if WIDEVALUE_IS_WIDER
483 if (wdivmod0(wn, wd, wq, wr)) return;
484#endif
485 divmodv(w2v(wn), w2v(wd), &vq, &vr);
486 *wq = v2w(vq);
487 *wr = v2w(vr);
488}
489
490static void
491wmuldivmod(wideval_t wx, wideval_t wy, wideval_t wz, wideval_t *wq, wideval_t *wr)
492{
493 if (WIDEVAL_GET(wy) == WIDEVAL_GET(wz)) {
494 *wq = wx;
495 *wr = WINT2FIXWV(0);
496 return;
497 }
498 wdivmod(wmul(wx,wy), wz, wq, wr);
499}
500
501static wideval_t
502wdiv(wideval_t wx, wideval_t wy)
503{
504#if WIDEVALUE_IS_WIDER
505 wideval_t q, dmy;
506 if (wdivmod0(wx, wy, &q, &dmy)) return q;
507#endif
508 return v2w(divv(w2v(wx), w2v(wy)));
509}
510
511static wideval_t
512wmod(wideval_t wx, wideval_t wy)
513{
514#if WIDEVALUE_IS_WIDER
515 wideval_t r, dmy;
516 if (wdivmod0(wx, wy, &dmy, &r)) return r;
517#endif
518 return v2w(modv(w2v(wx), w2v(wy)));
519}
520
521static VALUE
522num_exact_check(VALUE v)
523{
524 VALUE tmp;
525
526 switch (TYPE(v)) {
527 case T_FIXNUM:
528 case T_BIGNUM:
529 tmp = v;
530 break;
531
532 case T_RATIONAL:
533 tmp = rb_rational_canonicalize(v);
534 break;
535
536 default:
537 if (!UNDEF_P(tmp = rb_check_funcall(v, idTo_r, 0, NULL))) {
538 /* test to_int method availability to reject non-Numeric
539 * objects such as String, Time, etc which have to_r method. */
540 if (!rb_respond_to(v, idTo_int)) {
541 /* FALLTHROUGH */
542 }
543 else if (RB_INTEGER_TYPE_P(tmp)) {
544 break;
545 }
546 else if (RB_TYPE_P(tmp, T_RATIONAL)) {
547 tmp = rb_rational_canonicalize(tmp);
548 break;
549 }
550 }
551 else if (!NIL_P(tmp = rb_check_to_int(v))) {
552 return tmp;
553 }
554
555 case T_NIL:
556 case T_STRING:
557 return Qnil;
558 }
559 ASSUME(!NIL_P(tmp));
560 return tmp;
561}
562
563NORETURN(static void num_exact_fail(VALUE v));
564static void
565num_exact_fail(VALUE v)
566{
567 rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into an exact number",
568 rb_obj_class(v));
569}
570
571static VALUE
572num_exact(VALUE v)
573{
574 VALUE num = num_exact_check(v);
575 if (NIL_P(num)) num_exact_fail(v);
576 return num;
577}
578
579/* time_t */
580
581/* TIME_SCALE should be 10000... */
582static const int TIME_SCALE_NUMDIGITS = rb_strlen_lit(STRINGIZE(TIME_SCALE)) - 1;
583
584static wideval_t
585rb_time_magnify(wideval_t w)
586{
587 return wmul(w, WINT2FIXWV(TIME_SCALE));
588}
589
590static VALUE
591rb_time_unmagnify_to_rational(wideval_t w)
592{
593 return quor(w2v(w), INT2FIX(TIME_SCALE));
594}
595
596static wideval_t
597rb_time_unmagnify(wideval_t w)
598{
599 return v2w(rb_time_unmagnify_to_rational(w));
600}
601
602static VALUE
603rb_time_unmagnify_to_float(wideval_t w)
604{
605 VALUE v;
606#if WIDEVALUE_IS_WIDER
607 if (FIXWV_P(w)) {
608 wideint_t a, b, c;
609 a = FIXWV2WINT(w);
610 b = TIME_SCALE;
611 c = a / b;
612 if (c * b == a) {
613 return DBL2NUM((double)c);
614 }
615 v = DBL2NUM((double)FIXWV2WINT(w));
616 return quov(v, DBL2NUM(TIME_SCALE));
617 }
618#endif
619 v = w2v(w);
620 if (RB_TYPE_P(v, T_RATIONAL))
621 return rb_Float(quov(v, INT2FIX(TIME_SCALE)));
622 else
623 return quov(v, DBL2NUM(TIME_SCALE));
624}
625
626static void
627split_second(wideval_t timew, wideval_t *timew_p, VALUE *subsecx_p)
628{
629 wideval_t q, r;
630 wdivmod(timew, WINT2FIXWV(TIME_SCALE), &q, &r);
631 *timew_p = q;
632 *subsecx_p = w2v(r);
633}
634
635static wideval_t
636timet2wv(time_t t)
637{
638#if WIDEVALUE_IS_WIDER
639 if (TIMET_MIN == 0) {
640 uwideint_t wi = (uwideint_t)t;
641 if (wi <= FIXWV_MAX) {
642 return WINT2FIXWV(wi);
643 }
644 }
645 else {
646 wideint_t wi = (wideint_t)t;
647 if (FIXWV_MIN <= wi && wi <= FIXWV_MAX) {
648 return WINT2FIXWV(wi);
649 }
650 }
651#endif
652 return v2w(TIMET2NUM(t));
653}
654#define TIMET2WV(t) timet2wv(t)
655
656static time_t
657wv2timet(wideval_t w)
658{
659#if WIDEVALUE_IS_WIDER
660 if (FIXWV_P(w)) {
661 wideint_t wi = FIXWV2WINT(w);
662 if (TIMET_MIN == 0) {
663 if (wi < 0)
664 rb_raise(rb_eRangeError, "negative value to convert into 'time_t'");
665 if (TIMET_MAX < (uwideint_t)wi)
666 rb_raise(rb_eRangeError, "too big to convert into 'time_t'");
667 }
668 else {
669 if (wi < TIMET_MIN || TIMET_MAX < wi)
670 rb_raise(rb_eRangeError, "too big to convert into 'time_t'");
671 }
672 return (time_t)wi;
673 }
674#endif
675 return NUM2TIMET(w2v(w));
676}
677#define WV2TIMET(t) wv2timet(t)
678
680static VALUE rb_cTimeTM;
681
682static int obj2int(VALUE obj);
683static uint32_t obj2ubits(VALUE obj, unsigned int bits);
684static VALUE obj2vint(VALUE obj);
685static uint32_t month_arg(VALUE arg);
686static VALUE validate_utc_offset(VALUE utc_offset);
687static VALUE validate_zone_name(VALUE zone_name);
688static void validate_vtm(struct vtm *vtm);
689static void vtm_add_day(struct vtm *vtm, int day);
690static uint32_t obj2subsecx(VALUE obj, VALUE *subsecx);
691
692static VALUE time_gmtime(VALUE);
693static VALUE time_localtime(VALUE);
694static VALUE time_fixoff(VALUE);
695static VALUE time_zonelocal(VALUE time, VALUE off);
696
697static time_t timegm_noleapsecond(struct tm *tm);
698static int tmcmp(struct tm *a, struct tm *b);
699static int vtmcmp(struct vtm *a, struct vtm *b);
700static const char *find_time_t(struct tm *tptr, int utc_p, time_t *tp);
701
702static struct vtm *localtimew(wideval_t timew, struct vtm *result);
703
704static int leap_year_p(long y);
705#define leap_year_v_p(y) leap_year_p(NUM2LONG(modv((y), INT2FIX(400))))
706
707static VALUE tm_from_time(VALUE klass, VALUE time);
708
709bool ruby_tz_uptodate_p;
710
711#ifdef _WIN32
712enum {tzkey_max = numberof(((DYNAMIC_TIME_ZONE_INFORMATION *)NULL)->TimeZoneKeyName)};
713static struct {
714 char use_tzkey;
715 char name[tzkey_max * 4 + 1];
716} w32_tz;
717
718static char *
719get_tzname(int dst)
720{
721 if (w32_tz.use_tzkey) {
722 if (w32_tz.name[0]) {
723 return w32_tz.name;
724 }
725 else {
726 /*
727 * Use GetDynamicTimeZoneInformation::TimeZoneKeyName, Windows
728 * time zone ID, which is not localized because it is the key
729 * for "Dynamic DST" keys under the "Time Zones" registry.
730 * Available since Windows Vista and Windows Server 2008.
731 */
732 DYNAMIC_TIME_ZONE_INFORMATION tzi;
733 WCHAR *const wtzkey = tzi.TimeZoneKeyName;
734 DWORD tzret = GetDynamicTimeZoneInformation(&tzi);
735 if (tzret != TIME_ZONE_ID_INVALID && *wtzkey) {
736 int wlen = (int)wcsnlen(wtzkey, tzkey_max);
737 int clen = WideCharToMultiByte(CP_UTF8, 0, wtzkey, wlen,
738 w32_tz.name, sizeof(w32_tz.name) - 1,
739 NULL, NULL);
740 w32_tz.name[clen] = '\0';
741 return w32_tz.name;
742 }
743 }
744 }
745 return _tzname[_daylight && dst];
746}
747#endif
748
749void
750ruby_reset_timezone(const char *val)
751{
752 ruby_tz_uptodate_p = false;
753#ifdef _WIN32
754 w32_tz.use_tzkey = !val || !*val;
755#endif
756 ruby_reset_leap_second_info();
757}
758
759static void
760update_tz(void)
761{
762 if (ruby_tz_uptodate_p) return;
763 ruby_tz_uptodate_p = true;
764 tzset();
765}
766
767static struct tm *
768rb_localtime_r(const time_t *t, struct tm *result)
769{
770#if defined __APPLE__ && defined __LP64__
771 if (*t != (time_t)(int)*t) return NULL;
772#endif
773 update_tz();
774#ifdef HAVE_GMTIME_R
775 result = localtime_r(t, result);
776#else
777 {
778 struct tm *tmp = localtime(t);
779 if (tmp) *result = *tmp;
780 }
781#endif
782#if defined(HAVE_MKTIME) && defined(LOCALTIME_OVERFLOW_PROBLEM)
783 if (result) {
784 long gmtoff1 = 0;
785 long gmtoff2 = 0;
786 struct tm tmp = *result;
787 time_t t2;
788 t2 = mktime(&tmp);
789# if defined(HAVE_STRUCT_TM_TM_GMTOFF)
790 gmtoff1 = result->tm_gmtoff;
791 gmtoff2 = tmp.tm_gmtoff;
792# endif
793 if (*t + gmtoff1 != t2 + gmtoff2)
794 result = NULL;
795 }
796#endif
797 return result;
798}
799#define LOCALTIME(tm, result) rb_localtime_r((tm), &(result))
800
801#ifndef HAVE_STRUCT_TM_TM_GMTOFF
802static struct tm *
803rb_gmtime_r(const time_t *t, struct tm *result)
804{
805#ifdef HAVE_GMTIME_R
806 result = gmtime_r(t, result);
807#else
808 struct tm *tmp = gmtime(t);
809 if (tmp) *result = *tmp;
810#endif
811#if defined(HAVE_TIMEGM) && defined(LOCALTIME_OVERFLOW_PROBLEM)
812 if (result && *t != timegm(result)) {
813 return NULL;
814 }
815#endif
816 return result;
817}
818# define GMTIME(tm, result) rb_gmtime_r((tm), &(result))
819#endif
820
821static const int16_t common_year_yday_offset[] = {
822 -1,
823 -1 + 31,
824 -1 + 31 + 28,
825 -1 + 31 + 28 + 31,
826 -1 + 31 + 28 + 31 + 30,
827 -1 + 31 + 28 + 31 + 30 + 31,
828 -1 + 31 + 28 + 31 + 30 + 31 + 30,
829 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31,
830 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
831 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
832 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
833 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
834 /* 1 2 3 4 5 6 7 8 9 10 11 */
835};
836static const int16_t leap_year_yday_offset[] = {
837 -1,
838 -1 + 31,
839 -1 + 31 + 29,
840 -1 + 31 + 29 + 31,
841 -1 + 31 + 29 + 31 + 30,
842 -1 + 31 + 29 + 31 + 30 + 31,
843 -1 + 31 + 29 + 31 + 30 + 31 + 30,
844 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31,
845 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
846 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
847 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
848 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
849 /* 1 2 3 4 5 6 7 8 9 10 11 */
850};
851
852static const int8_t common_year_days_in_month[] = {
853 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
854};
855static const int8_t leap_year_days_in_month[] = {
856 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
857};
858
859#define days_in_month_of(leap) ((leap) ? leap_year_days_in_month : common_year_days_in_month)
860#define days_in_month_in(y) days_in_month_of(leap_year_p(y))
861#define days_in_month_in_v(y) days_in_month_of(leap_year_v_p(y))
862
863#define M28(m) \
864 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
865 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
866 (m),(m),(m),(m),(m),(m),(m),(m)
867#define M29(m) \
868 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
869 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
870 (m),(m),(m),(m),(m),(m),(m),(m),(m)
871#define M30(m) \
872 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
873 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
874 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m)
875#define M31(m) \
876 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
877 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), \
878 (m),(m),(m),(m),(m),(m),(m),(m),(m),(m), (m)
879
880static const uint8_t common_year_mon_of_yday[] = {
881 M31(1), M28(2), M31(3), M30(4), M31(5), M30(6),
882 M31(7), M31(8), M30(9), M31(10), M30(11), M31(12)
883};
884static const uint8_t leap_year_mon_of_yday[] = {
885 M31(1), M29(2), M31(3), M30(4), M31(5), M30(6),
886 M31(7), M31(8), M30(9), M31(10), M30(11), M31(12)
887};
888
889#undef M28
890#undef M29
891#undef M30
892#undef M31
893
894#define D28 \
895 1,2,3,4,5,6,7,8,9, \
896 10,11,12,13,14,15,16,17,18,19, \
897 20,21,22,23,24,25,26,27,28
898#define D29 \
899 1,2,3,4,5,6,7,8,9, \
900 10,11,12,13,14,15,16,17,18,19, \
901 20,21,22,23,24,25,26,27,28,29
902#define D30 \
903 1,2,3,4,5,6,7,8,9, \
904 10,11,12,13,14,15,16,17,18,19, \
905 20,21,22,23,24,25,26,27,28,29,30
906#define D31 \
907 1,2,3,4,5,6,7,8,9, \
908 10,11,12,13,14,15,16,17,18,19, \
909 20,21,22,23,24,25,26,27,28,29,30,31
910
911static const uint8_t common_year_mday_of_yday[] = {
912 /* 1 2 3 4 5 6 7 8 9 10 11 12 */
913 D31, D28, D31, D30, D31, D30, D31, D31, D30, D31, D30, D31
914};
915static const uint8_t leap_year_mday_of_yday[] = {
916 D31, D29, D31, D30, D31, D30, D31, D31, D30, D31, D30, D31
917};
918
919#undef D28
920#undef D29
921#undef D30
922#undef D31
923
924static int
925calc_tm_yday(long tm_year, int tm_mon, int tm_mday)
926{
927 int tm_year_mod400 = (int)MOD(tm_year, 400);
928 int tm_yday = tm_mday;
929
930 if (leap_year_p(tm_year_mod400 + 1900))
931 tm_yday += leap_year_yday_offset[tm_mon];
932 else
933 tm_yday += common_year_yday_offset[tm_mon];
934
935 return tm_yday;
936}
937
938static wideval_t
939timegmw_noleapsecond(struct vtm *vtm)
940{
941 VALUE year1900;
942 VALUE q400, r400;
943 int year_mod400;
944 int yday;
945 long days_in400;
946 VALUE vdays, ret;
947 wideval_t wret;
948
949 year1900 = subv(vtm->year, INT2FIX(1900));
950
951 divmodv(year1900, INT2FIX(400), &q400, &r400);
952 year_mod400 = NUM2INT(r400);
953
954 yday = calc_tm_yday(year_mod400, vtm->mon-1, vtm->mday);
955
956 /*
957 * `Seconds Since the Epoch' in SUSv3:
958 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
959 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
960 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
961 */
962 ret = LONG2NUM(vtm->sec
963 + vtm->min*60
964 + vtm->hour*3600);
965 days_in400 = yday
966 - 70*365
967 + DIV(year_mod400 - 69, 4)
968 - DIV(year_mod400 - 1, 100)
969 + (year_mod400 + 299) / 400;
970 vdays = LONG2NUM(days_in400);
971 vdays = addv(vdays, mulv(q400, INT2FIX(97)));
972 vdays = addv(vdays, mulv(year1900, INT2FIX(365)));
973 wret = wadd(rb_time_magnify(v2w(ret)), wmul(rb_time_magnify(v2w(vdays)), WINT2FIXWV(86400)));
974 wret = wadd(wret, v2w(vtm->subsecx));
975
976 return wret;
977}
978
979static VALUE
980zone_str(const char *zone)
981{
982 const char *p;
983 int ascii_only = 1;
984 VALUE str;
985 size_t len;
986
987 if (zone == NULL) {
988 return rb_fstring_lit("(NO-TIMEZONE-ABBREVIATION)");
989 }
990
991 for (p = zone; *p; p++) {
992 if (!ISASCII(*p)) {
993 ascii_only = 0;
994 p += strlen(p);
995 break;
996 }
997 }
998 len = p - zone;
999 if (ascii_only) {
1000 str = rb_usascii_str_new(zone, len);
1001 }
1002 else {
1003#ifdef _WIN32
1004 str = rb_utf8_str_new(zone, len);
1005 /* until we move to UTF-8 on Windows completely */
1006 str = rb_str_export_locale(str);
1007#else
1008 str = rb_enc_str_new(zone, len, rb_locale_encoding());
1009#endif
1010 }
1011 return rb_fstring(str);
1012}
1013
1014static void
1015gmtimew_noleapsecond(wideval_t timew, struct vtm *vtm)
1016{
1017 VALUE v;
1018 int n, x, y;
1019 int wday;
1020 VALUE timev;
1021 wideval_t timew2, w, w2;
1022 VALUE subsecx;
1023
1024 vtm->isdst = 0;
1025
1026 split_second(timew, &timew2, &subsecx);
1027 vtm->subsecx = subsecx;
1028
1029 wdivmod(timew2, WINT2FIXWV(86400), &w2, &w);
1030 timev = w2v(w2);
1031 v = w2v(w);
1032
1033 wday = NUM2INT(modv(timev, INT2FIX(7)));
1034 vtm->wday = (wday + 4) % 7;
1035
1036 n = NUM2INT(v);
1037 vtm->sec = n % 60; n = n / 60;
1038 vtm->min = n % 60; n = n / 60;
1039 vtm->hour = n;
1040
1041 /* 97 leap days in the 400 year cycle */
1042 divmodv(timev, INT2FIX(400*365 + 97), &timev, &v);
1043 vtm->year = mulv(timev, INT2FIX(400));
1044
1045 /* n is the days in the 400 year cycle.
1046 * the start of the cycle is 1970-01-01. */
1047
1048 n = NUM2INT(v);
1049 y = 1970;
1050
1051 /* 30 years including 7 leap days (1972, 1976, ... 1996),
1052 * 31 days in January 2000 and
1053 * 29 days in February 2000
1054 * from 1970-01-01 to 2000-02-29 */
1055 if (30*365+7+31+29-1 <= n) {
1056 /* 2000-02-29 or after */
1057 if (n < 31*365+8) {
1058 /* 2000-02-29 to 2000-12-31 */
1059 y += 30;
1060 n -= 30*365+7;
1061 goto found;
1062 }
1063 else {
1064 /* 2001-01-01 or after */
1065 n -= 1;
1066 }
1067 }
1068
1069 x = n / (365*100 + 24);
1070 n = n % (365*100 + 24);
1071 y += x * 100;
1072 if (30*365+7+31+29-1 <= n) {
1073 if (n < 31*365+7) {
1074 y += 30;
1075 n -= 30*365+7;
1076 goto found;
1077 }
1078 else
1079 n += 1;
1080 }
1081
1082 x = n / (365*4 + 1);
1083 n = n % (365*4 + 1);
1084 y += x * 4;
1085 if (365*2+31+29-1 <= n) {
1086 if (n < 365*2+366) {
1087 y += 2;
1088 n -= 365*2;
1089 goto found;
1090 }
1091 else
1092 n -= 1;
1093 }
1094
1095 x = n / 365;
1096 n = n % 365;
1097 y += x;
1098
1099 found:
1100 vtm->yday = n+1;
1101 vtm->year = addv(vtm->year, INT2NUM(y));
1102
1103 if (leap_year_p(y)) {
1104 vtm->mon = leap_year_mon_of_yday[n];
1105 vtm->mday = leap_year_mday_of_yday[n];
1106 }
1107 else {
1108 vtm->mon = common_year_mon_of_yday[n];
1109 vtm->mday = common_year_mday_of_yday[n];
1110 }
1111
1112 vtm->utc_offset = INT2FIX(0);
1113 vtm->zone = str_utc;
1114}
1115
1116static struct tm *
1117gmtime_with_leapsecond(const time_t *timep, struct tm *result)
1118{
1119#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1120 /* 4.4BSD counts leap seconds only with localtime, not with gmtime. */
1121 struct tm *t;
1122 int sign;
1123 int gmtoff_sec, gmtoff_min, gmtoff_hour, gmtoff_day;
1124 long gmtoff;
1125 t = LOCALTIME(timep, *result);
1126 if (t == NULL)
1127 return NULL;
1128
1129 /* subtract gmtoff */
1130 if (t->tm_gmtoff < 0) {
1131 sign = 1;
1132 gmtoff = -t->tm_gmtoff;
1133 }
1134 else {
1135 sign = -1;
1136 gmtoff = t->tm_gmtoff;
1137 }
1138 gmtoff_sec = (int)(gmtoff % 60);
1139 gmtoff = gmtoff / 60;
1140 gmtoff_min = (int)(gmtoff % 60);
1141 gmtoff = gmtoff / 60;
1142 gmtoff_hour = (int)gmtoff; /* <= 12 */
1143
1144 gmtoff_sec *= sign;
1145 gmtoff_min *= sign;
1146 gmtoff_hour *= sign;
1147
1148 gmtoff_day = 0;
1149
1150 if (gmtoff_sec) {
1151 /* If gmtoff_sec == 0, don't change result->tm_sec.
1152 * It may be 60 which is a leap second. */
1153 result->tm_sec += gmtoff_sec;
1154 if (result->tm_sec < 0) {
1155 result->tm_sec += 60;
1156 gmtoff_min -= 1;
1157 }
1158 if (60 <= result->tm_sec) {
1159 result->tm_sec -= 60;
1160 gmtoff_min += 1;
1161 }
1162 }
1163 if (gmtoff_min) {
1164 result->tm_min += gmtoff_min;
1165 if (result->tm_min < 0) {
1166 result->tm_min += 60;
1167 gmtoff_hour -= 1;
1168 }
1169 if (60 <= result->tm_min) {
1170 result->tm_min -= 60;
1171 gmtoff_hour += 1;
1172 }
1173 }
1174 if (gmtoff_hour) {
1175 result->tm_hour += gmtoff_hour;
1176 if (result->tm_hour < 0) {
1177 result->tm_hour += 24;
1178 gmtoff_day = -1;
1179 }
1180 if (24 <= result->tm_hour) {
1181 result->tm_hour -= 24;
1182 gmtoff_day = 1;
1183 }
1184 }
1185
1186 if (gmtoff_day) {
1187 if (gmtoff_day < 0) {
1188 if (result->tm_yday == 0) {
1189 result->tm_mday = 31;
1190 result->tm_mon = 11; /* December */
1191 result->tm_year--;
1192 result->tm_yday = leap_year_p(result->tm_year + 1900) ? 365 : 364;
1193 }
1194 else if (result->tm_mday == 1) {
1195 const int8_t *days_in_month = days_in_month_in(result->tm_year + 1900);
1196 result->tm_mon--;
1197 result->tm_mday = days_in_month[result->tm_mon];
1198 result->tm_yday--;
1199 }
1200 else {
1201 result->tm_mday--;
1202 result->tm_yday--;
1203 }
1204 result->tm_wday = (result->tm_wday + 6) % 7;
1205 }
1206 else {
1207 int leap = leap_year_p(result->tm_year + 1900);
1208 if (result->tm_yday == (leap ? 365 : 364)) {
1209 result->tm_year++;
1210 result->tm_mon = 0; /* January */
1211 result->tm_mday = 1;
1212 result->tm_yday = 0;
1213 }
1214 else if (result->tm_mday == days_in_month_of(leap)[result->tm_mon]) {
1215 result->tm_mon++;
1216 result->tm_mday = 1;
1217 result->tm_yday++;
1218 }
1219 else {
1220 result->tm_mday++;
1221 result->tm_yday++;
1222 }
1223 result->tm_wday = (result->tm_wday + 1) % 7;
1224 }
1225 }
1226 result->tm_isdst = 0;
1227 result->tm_gmtoff = 0;
1228#if defined(HAVE_TM_ZONE)
1229 result->tm_zone = (char *)"UTC";
1230#endif
1231 return result;
1232#else
1233 return GMTIME(timep, *result);
1234#endif
1235}
1236
1237static long this_year = 0;
1238static time_t known_leap_seconds_limit;
1239static int number_of_leap_seconds_known;
1240
1241static void
1242init_leap_second_info(void)
1243{
1244 /*
1245 * leap seconds are determined by IERS.
1246 * It is announced 6 months before the leap second.
1247 * So no one knows leap seconds in the future after the next year.
1248 */
1249 if (this_year == 0) {
1250 time_t now;
1251 struct tm *tm, result;
1252 struct vtm vtm;
1253 wideval_t timew;
1254 now = time(NULL);
1255#ifdef HAVE_GMTIME_R
1256 gmtime_r(&now, &result);
1257#else
1258 gmtime(&now);
1259#endif
1260 tm = gmtime_with_leapsecond(&now, &result);
1261 if (!tm) return;
1262 this_year = tm->tm_year;
1263
1264 if (TIMET_MAX - now < (time_t)(366*86400))
1265 known_leap_seconds_limit = TIMET_MAX;
1266 else
1267 known_leap_seconds_limit = now + (time_t)(366*86400);
1268
1269 if (!gmtime_with_leapsecond(&known_leap_seconds_limit, &result))
1270 return;
1271
1272 vtm.year = LONG2NUM(result.tm_year + 1900);
1273 vtm.mon = result.tm_mon + 1;
1274 vtm.mday = result.tm_mday;
1275 vtm.hour = result.tm_hour;
1276 vtm.min = result.tm_min;
1277 vtm.sec = result.tm_sec;
1278 vtm.subsecx = INT2FIX(0);
1279 vtm.utc_offset = INT2FIX(0);
1280
1281 timew = timegmw_noleapsecond(&vtm);
1282
1283 number_of_leap_seconds_known = NUM2INT(w2v(wsub(TIMET2WV(known_leap_seconds_limit), rb_time_unmagnify(timew))));
1284 }
1285}
1286
1287/* Use this if you want to re-run init_leap_second_info() */
1288void
1289ruby_reset_leap_second_info(void)
1290{
1291 this_year = 0;
1292}
1293
1294static wideval_t
1295timegmw(struct vtm *vtm)
1296{
1297 wideval_t timew;
1298 struct tm tm;
1299 time_t t;
1300 const char *errmsg;
1301
1302 /* The first leap second is 1972-06-30 23:59:60 UTC.
1303 * No leap seconds before. */
1304 if (gt(INT2FIX(1972), vtm->year))
1305 return timegmw_noleapsecond(vtm);
1306
1307 init_leap_second_info();
1308
1309 timew = timegmw_noleapsecond(vtm);
1310
1311
1312 if (number_of_leap_seconds_known == 0) {
1313 /* When init_leap_second_info() is executed, the timezone doesn't have
1314 * leap second information. Disable leap second for calculating gmtime.
1315 */
1316 return timew;
1317 }
1318 else if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
1319 return wadd(timew, rb_time_magnify(WINT2WV(number_of_leap_seconds_known)));
1320 }
1321
1322 tm.tm_year = rb_long2int(NUM2LONG(vtm->year) - 1900);
1323 tm.tm_mon = vtm->mon - 1;
1324 tm.tm_mday = vtm->mday;
1325 tm.tm_hour = vtm->hour;
1326 tm.tm_min = vtm->min;
1327 tm.tm_sec = vtm->sec;
1328 tm.tm_isdst = 0;
1329
1330 errmsg = find_time_t(&tm, 1, &t);
1331 if (errmsg)
1332 rb_raise(rb_eArgError, "%s", errmsg);
1333 return wadd(rb_time_magnify(TIMET2WV(t)), v2w(vtm->subsecx));
1334}
1335
1336static struct vtm *
1337gmtimew(wideval_t timew, struct vtm *result)
1338{
1339 time_t t;
1340 struct tm tm;
1341 VALUE subsecx;
1342 wideval_t timew2;
1343
1344 if (wlt(timew, WINT2FIXWV(0))) {
1345 gmtimew_noleapsecond(timew, result);
1346 return result;
1347 }
1348
1349 init_leap_second_info();
1350
1351 if (number_of_leap_seconds_known == 0) {
1352 /* When init_leap_second_info() is executed, the timezone doesn't have
1353 * leap second information. Disable leap second for calculating gmtime.
1354 */
1355 gmtimew_noleapsecond(timew, result);
1356 return result;
1357 }
1358 else if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
1359 timew = wsub(timew, rb_time_magnify(WINT2WV(number_of_leap_seconds_known)));
1360 gmtimew_noleapsecond(timew, result);
1361 return result;
1362 }
1363
1364 split_second(timew, &timew2, &subsecx);
1365
1366 t = WV2TIMET(timew2);
1367 if (!gmtime_with_leapsecond(&t, &tm))
1368 return NULL;
1369
1370 result->year = LONG2NUM((long)tm.tm_year + 1900);
1371 result->mon = tm.tm_mon + 1;
1372 result->mday = tm.tm_mday;
1373 result->hour = tm.tm_hour;
1374 result->min = tm.tm_min;
1375 result->sec = tm.tm_sec;
1376 result->subsecx = subsecx;
1377 result->utc_offset = INT2FIX(0);
1378 result->wday = tm.tm_wday;
1379 result->yday = tm.tm_yday+1;
1380 result->isdst = tm.tm_isdst;
1381
1382 return result;
1383}
1384
1385#define GMTIMEW(w, v) \
1386 (gmtimew(w, v) ? (void)0 : rb_raise(rb_eArgError, "gmtime error"))
1387
1388static struct tm *localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, VALUE *zone);
1389
1390/*
1391 * The idea, extrapolate localtime() function, is borrowed from Perl:
1392 * http://web.archive.org/web/20080211114141/http://use.perl.org/articles/08/02/07/197204.shtml
1393 *
1394 * compat_common_month_table is generated by the following program.
1395 * This table finds the last month which starts at the same day of a week.
1396 * The year 2037 is not used because:
1397 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=522949
1398 *
1399 * #!/usr/bin/ruby
1400 *
1401 * require 'date'
1402 *
1403 * h = {}
1404 * 2036.downto(2010) {|y|
1405 * 1.upto(12) {|m|
1406 * next if m == 2 && y % 4 == 0
1407 * d = Date.new(y,m,1)
1408 * h[m] ||= {}
1409 * h[m][d.wday] ||= y
1410 * }
1411 * }
1412 *
1413 * 1.upto(12) {|m|
1414 * print "{"
1415 * 0.upto(6) {|w|
1416 * y = h[m][w]
1417 * print " #{y},"
1418 * }
1419 * puts "},"
1420 * }
1421 *
1422 */
1423static const int compat_common_month_table[12][7] = {
1424 /* Sun Mon Tue Wed Thu Fri Sat */
1425 { 2034, 2035, 2036, 2031, 2032, 2027, 2033 }, /* January */
1426 { 2026, 2027, 2033, 2034, 2035, 2030, 2031 }, /* February */
1427 { 2026, 2032, 2033, 2034, 2035, 2030, 2036 }, /* March */
1428 { 2035, 2030, 2036, 2026, 2032, 2033, 2034 }, /* April */
1429 { 2033, 2034, 2035, 2030, 2036, 2026, 2032 }, /* May */
1430 { 2036, 2026, 2032, 2033, 2034, 2035, 2030 }, /* June */
1431 { 2035, 2030, 2036, 2026, 2032, 2033, 2034 }, /* July */
1432 { 2032, 2033, 2034, 2035, 2030, 2036, 2026 }, /* August */
1433 { 2030, 2036, 2026, 2032, 2033, 2034, 2035 }, /* September */
1434 { 2034, 2035, 2030, 2036, 2026, 2032, 2033 }, /* October */
1435 { 2026, 2032, 2033, 2034, 2035, 2030, 2036 }, /* November */
1436 { 2030, 2036, 2026, 2032, 2033, 2034, 2035 }, /* December */
1437};
1438
1439/*
1440 * compat_leap_month_table is generated by following program.
1441 *
1442 * #!/usr/bin/ruby
1443 *
1444 * require 'date'
1445 *
1446 * h = {}
1447 * 2037.downto(2010) {|y|
1448 * 1.upto(12) {|m|
1449 * next unless m == 2 && y % 4 == 0
1450 * d = Date.new(y,m,1)
1451 * h[m] ||= {}
1452 * h[m][d.wday] ||= y
1453 * }
1454 * }
1455 *
1456 * 2.upto(2) {|m|
1457 * 0.upto(6) {|w|
1458 * y = h[m][w]
1459 * print " #{y},"
1460 * }
1461 * puts
1462 * }
1463 */
1464static const int compat_leap_month_table[7] = {
1465/* Sun Mon Tue Wed Thu Fri Sat */
1466 2032, 2016, 2028, 2012, 2024, 2036, 2020, /* February */
1467};
1468
1469static int
1470calc_wday(int year_mod400, int month, int day)
1471{
1472 int a, y, m;
1473 int wday;
1474
1475 a = (14 - month) / 12;
1476 y = year_mod400 + 4800 - a;
1477 m = month + 12 * a - 3;
1478 wday = day + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 + 2;
1479 wday = wday % 7;
1480 return wday;
1481}
1482
1483static VALUE
1484guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, VALUE *zone_ret)
1485{
1486 struct tm tm;
1487 long gmtoff;
1488 VALUE zone;
1489 time_t t;
1490 struct vtm vtm2;
1491 VALUE timev;
1492 int year_mod400, wday;
1493
1494 /* Daylight Saving Time was introduced in 1916.
1495 * So we don't need to care about DST before that. */
1496 if (lt(vtm_utc->year, INT2FIX(1916))) {
1497 VALUE off = INT2FIX(0);
1498 int isdst = 0;
1499 zone = str_utc;
1500
1501# if defined(NEGATIVE_TIME_T)
1502# if SIZEOF_TIME_T <= 4
1503 /* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t. */
1504# define THE_TIME_OLD_ENOUGH ((time_t)0x80000000)
1505# else
1506 /* Since the Royal Greenwich Observatory was commissioned in 1675,
1507 no timezone defined using GMT at 1600. */
1508# define THE_TIME_OLD_ENOUGH ((time_t)(1600-1970)*366*24*60*60)
1509# endif
1510 if (localtime_with_gmtoff_zone((t = THE_TIME_OLD_ENOUGH, &t), &tm, &gmtoff, &zone)) {
1511 off = LONG2FIX(gmtoff);
1512 isdst = tm.tm_isdst;
1513 }
1514 else
1515# endif
1516 /* 1970-01-01 00:00:00 UTC : The Unix epoch - the oldest time in portable time_t. */
1517 if (localtime_with_gmtoff_zone((t = 0, &t), &tm, &gmtoff, &zone)) {
1518 off = LONG2FIX(gmtoff);
1519 isdst = tm.tm_isdst;
1520 }
1521
1522 if (isdst_ret)
1523 *isdst_ret = isdst;
1524 if (zone_ret)
1525 *zone_ret = zone;
1526 return off;
1527 }
1528
1529 /* It is difficult to guess the future. */
1530
1531 vtm2 = *vtm_utc;
1532
1533 /* guess using a year before 2038. */
1534 year_mod400 = NUM2INT(modv(vtm_utc->year, INT2FIX(400)));
1535 wday = calc_wday(year_mod400, vtm_utc->mon, 1);
1536 if (vtm_utc->mon == 2 && leap_year_p(year_mod400))
1537 vtm2.year = INT2FIX(compat_leap_month_table[wday]);
1538 else
1539 vtm2.year = INT2FIX(compat_common_month_table[vtm_utc->mon-1][wday]);
1540
1541 timev = w2v(rb_time_unmagnify(timegmw(&vtm2)));
1542 t = NUM2TIMET(timev);
1543 zone = str_utc;
1544 if (localtime_with_gmtoff_zone(&t, &tm, &gmtoff, &zone)) {
1545 if (isdst_ret)
1546 *isdst_ret = tm.tm_isdst;
1547 if (zone_ret)
1548 *zone_ret = zone;
1549 return LONG2FIX(gmtoff);
1550 }
1551
1552 {
1553 /* Use the current time offset as a last resort. */
1554 static time_t now = 0;
1555 static long now_gmtoff = 0;
1556 static int now_isdst = 0;
1557 static VALUE now_zone;
1558 if (now == 0) {
1559 VALUE zone;
1560 now = time(NULL);
1561 localtime_with_gmtoff_zone(&now, &tm, &now_gmtoff, &zone);
1562 now_isdst = tm.tm_isdst;
1563 zone = rb_fstring(zone);
1564 rb_vm_register_global_object(zone);
1565 now_zone = zone;
1566 }
1567 if (isdst_ret)
1568 *isdst_ret = now_isdst;
1569 if (zone_ret)
1570 *zone_ret = now_zone;
1571 return LONG2FIX(now_gmtoff);
1572 }
1573}
1574
1575static VALUE
1576small_vtm_sub(struct vtm *vtm1, struct vtm *vtm2)
1577{
1578 int off;
1579
1580 off = vtm1->sec - vtm2->sec;
1581 off += (vtm1->min - vtm2->min) * 60;
1582 off += (vtm1->hour - vtm2->hour) * 3600;
1583 if (ne(vtm1->year, vtm2->year))
1584 off += lt(vtm1->year, vtm2->year) ? -24*3600 : 24*3600;
1585 else if (vtm1->mon != vtm2->mon)
1586 off += vtm1->mon < vtm2->mon ? -24*3600 : 24*3600;
1587 else if (vtm1->mday != vtm2->mday)
1588 off += vtm1->mday < vtm2->mday ? -24*3600 : 24*3600;
1589
1590 return INT2FIX(off);
1591}
1592
1593static wideval_t
1594timelocalw(struct vtm *vtm)
1595{
1596 time_t t;
1597 struct tm tm;
1598 VALUE v;
1599 wideval_t timew1, timew2;
1600 struct vtm vtm1, vtm2;
1601 int n;
1602
1603 if (FIXNUM_P(vtm->year)) {
1604 long l = FIX2LONG(vtm->year) - 1900;
1605 if (l < INT_MIN || INT_MAX < l)
1606 goto no_localtime;
1607 tm.tm_year = (int)l;
1608 }
1609 else {
1610 v = subv(vtm->year, INT2FIX(1900));
1611 if (lt(v, INT2NUM(INT_MIN)) || lt(INT2NUM(INT_MAX), v))
1612 goto no_localtime;
1613 tm.tm_year = NUM2INT(v);
1614 }
1615
1616 tm.tm_mon = vtm->mon-1;
1617 tm.tm_mday = vtm->mday;
1618 tm.tm_hour = vtm->hour;
1619 tm.tm_min = vtm->min;
1620 tm.tm_sec = vtm->sec;
1621 tm.tm_isdst = vtm->isdst == VTM_ISDST_INITVAL ? -1 : vtm->isdst;
1622
1623 if (find_time_t(&tm, 0, &t))
1624 goto no_localtime;
1625 return wadd(rb_time_magnify(TIMET2WV(t)), v2w(vtm->subsecx));
1626
1627 no_localtime:
1628 timew1 = timegmw(vtm);
1629
1630 if (!localtimew(timew1, &vtm1))
1631 rb_raise(rb_eArgError, "localtimew error");
1632
1633 n = vtmcmp(vtm, &vtm1);
1634 if (n == 0) {
1635 timew1 = wsub(timew1, rb_time_magnify(WINT2FIXWV(12*3600)));
1636 if (!localtimew(timew1, &vtm1))
1637 rb_raise(rb_eArgError, "localtimew error");
1638 n = 1;
1639 }
1640
1641 if (n < 0) {
1642 timew2 = timew1;
1643 vtm2 = vtm1;
1644 timew1 = wsub(timew1, rb_time_magnify(WINT2FIXWV(24*3600)));
1645 if (!localtimew(timew1, &vtm1))
1646 rb_raise(rb_eArgError, "localtimew error");
1647 }
1648 else {
1649 timew2 = wadd(timew1, rb_time_magnify(WINT2FIXWV(24*3600)));
1650 if (!localtimew(timew2, &vtm2))
1651 rb_raise(rb_eArgError, "localtimew error");
1652 }
1653 timew1 = wadd(timew1, rb_time_magnify(v2w(small_vtm_sub(vtm, &vtm1))));
1654 timew2 = wadd(timew2, rb_time_magnify(v2w(small_vtm_sub(vtm, &vtm2))));
1655
1656 if (weq(timew1, timew2))
1657 return timew1;
1658
1659 if (!localtimew(timew1, &vtm1))
1660 rb_raise(rb_eArgError, "localtimew error");
1661 if (vtm->hour != vtm1.hour || vtm->min != vtm1.min || vtm->sec != vtm1.sec)
1662 return timew2;
1663
1664 if (!localtimew(timew2, &vtm2))
1665 rb_raise(rb_eArgError, "localtimew error");
1666 if (vtm->hour != vtm2.hour || vtm->min != vtm2.min || vtm->sec != vtm2.sec)
1667 return timew1;
1668
1669 if (vtm->isdst)
1670 return lt(vtm1.utc_offset, vtm2.utc_offset) ? timew2 : timew1;
1671 else
1672 return lt(vtm1.utc_offset, vtm2.utc_offset) ? timew1 : timew2;
1673}
1674
1675static struct tm *
1676localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, VALUE *zone)
1677{
1678 struct tm tm;
1679
1680 if (LOCALTIME(t, tm)) {
1681#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1682 *gmtoff = tm.tm_gmtoff;
1683#else
1684 struct tm *u, *l;
1685 long off;
1686 struct tm tmbuf;
1687 l = &tm;
1688 u = GMTIME(t, tmbuf);
1689 if (!u)
1690 return NULL;
1691 if (l->tm_year != u->tm_year)
1692 off = l->tm_year < u->tm_year ? -1 : 1;
1693 else if (l->tm_mon != u->tm_mon)
1694 off = l->tm_mon < u->tm_mon ? -1 : 1;
1695 else if (l->tm_mday != u->tm_mday)
1696 off = l->tm_mday < u->tm_mday ? -1 : 1;
1697 else
1698 off = 0;
1699 off = off * 24 + l->tm_hour - u->tm_hour;
1700 off = off * 60 + l->tm_min - u->tm_min;
1701 off = off * 60 + l->tm_sec - u->tm_sec;
1702 *gmtoff = off;
1703#endif
1704
1705 if (zone) {
1706#if defined(HAVE_TM_ZONE)
1707 *zone = zone_str(tm.tm_zone);
1708#elif defined(_WIN32)
1709 *zone = zone_str(get_tzname(tm.tm_isdst));
1710#elif defined(HAVE_TZNAME) && defined(HAVE_DAYLIGHT)
1711 /* this needs tzset or localtime, instead of localtime_r */
1712 *zone = zone_str(tzname[daylight && tm.tm_isdst]);
1713#else
1714 {
1715 char buf[64];
1716 strftime(buf, sizeof(buf), "%Z", &tm);
1717 *zone = zone_str(buf);
1718 }
1719#endif
1720 }
1721
1722 *result = tm;
1723 return result;
1724 }
1725 return NULL;
1726}
1727
1728static int
1729timew_out_of_timet_range(wideval_t timew)
1730{
1731 VALUE timexv;
1732#if WIDEVALUE_IS_WIDER && SIZEOF_TIME_T < SIZEOF_INT64_T
1733 if (FIXWV_P(timew)) {
1734 wideint_t t = FIXWV2WINT(timew);
1735 if (t < TIME_SCALE * (wideint_t)TIMET_MIN ||
1736 TIME_SCALE * (1 + (wideint_t)TIMET_MAX) <= t)
1737 return 1;
1738 return 0;
1739 }
1740#endif
1741#if SIZEOF_TIME_T == SIZEOF_INT64_T
1742 if (FIXWV_P(timew)) {
1743 wideint_t t = FIXWV2WINT(timew);
1744 if (~(time_t)0 <= 0) {
1745 return 0;
1746 }
1747 else {
1748 if (t < 0)
1749 return 1;
1750 return 0;
1751 }
1752 }
1753#endif
1754 timexv = w2v(timew);
1755 if (lt(timexv, mulv(INT2FIX(TIME_SCALE), TIMET2NUM(TIMET_MIN))) ||
1756 le(mulv(INT2FIX(TIME_SCALE), addv(TIMET2NUM(TIMET_MAX), INT2FIX(1))), timexv))
1757 return 1;
1758 return 0;
1759}
1760
1761static struct vtm *
1762localtimew(wideval_t timew, struct vtm *result)
1763{
1764 VALUE subsecx, offset;
1765 VALUE zone;
1766 int isdst;
1767
1768 if (!timew_out_of_timet_range(timew)) {
1769 time_t t;
1770 struct tm tm;
1771 long gmtoff;
1772 wideval_t timew2;
1773
1774 split_second(timew, &timew2, &subsecx);
1775
1776 t = WV2TIMET(timew2);
1777
1778 if (localtime_with_gmtoff_zone(&t, &tm, &gmtoff, &zone)) {
1779 result->year = LONG2NUM((long)tm.tm_year + 1900);
1780 result->mon = tm.tm_mon + 1;
1781 result->mday = tm.tm_mday;
1782 result->hour = tm.tm_hour;
1783 result->min = tm.tm_min;
1784 result->sec = tm.tm_sec;
1785 result->subsecx = subsecx;
1786 result->wday = tm.tm_wday;
1787 result->yday = tm.tm_yday+1;
1788 result->isdst = tm.tm_isdst;
1789 result->utc_offset = LONG2NUM(gmtoff);
1790 result->zone = zone;
1791 return result;
1792 }
1793 }
1794
1795 if (!gmtimew(timew, result))
1796 return NULL;
1797
1798 offset = guess_local_offset(result, &isdst, &zone);
1799
1800 if (!gmtimew(wadd(timew, rb_time_magnify(v2w(offset))), result))
1801 return NULL;
1802
1803 result->utc_offset = offset;
1804 result->isdst = isdst;
1805 result->zone = zone;
1806
1807 return result;
1808}
1809
1810#define TIME_TZMODE_LOCALTIME 0
1811#define TIME_TZMODE_UTC 1
1812#define TIME_TZMODE_FIXOFF 2
1813#define TIME_TZMODE_UNINITIALIZED 3
1814
1816 wideval_t timew; /* time_t value * TIME_SCALE. possibly Rational. */
1817 struct vtm vtm;
1818};
1819
1820#define GetTimeval(obj, tobj) ((tobj) = get_timeval(obj))
1821#define GetNewTimeval(obj, tobj) ((tobj) = get_new_timeval(obj))
1822
1823#define IsTimeval(obj) rb_typeddata_is_kind_of((obj), &time_data_type)
1824#define TIME_INIT_P(tobj) ((tobj)->vtm.tzmode != TIME_TZMODE_UNINITIALIZED)
1825
1826#define TZMODE_UTC_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_UTC)
1827#define TZMODE_SET_UTC(tobj) ((tobj)->vtm.tzmode = TIME_TZMODE_UTC)
1828
1829#define TZMODE_LOCALTIME_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_LOCALTIME)
1830#define TZMODE_SET_LOCALTIME(tobj) ((tobj)->vtm.tzmode = TIME_TZMODE_LOCALTIME)
1831
1832#define TZMODE_FIXOFF_P(tobj) ((tobj)->vtm.tzmode == TIME_TZMODE_FIXOFF)
1833#define TZMODE_SET_FIXOFF(time, tobj, off) do { \
1834 (tobj)->vtm.tzmode = TIME_TZMODE_FIXOFF; \
1835 RB_OBJ_WRITE_UNALIGNED(time, &(tobj)->vtm.utc_offset, off); \
1836} while (0)
1837
1838#define TZMODE_COPY(tobj1, tobj2) \
1839 ((tobj1)->vtm.tzmode = (tobj2)->vtm.tzmode, \
1840 (tobj1)->vtm.utc_offset = (tobj2)->vtm.utc_offset, \
1841 (tobj1)->vtm.zone = (tobj2)->vtm.zone)
1842
1843static int zone_localtime(VALUE zone, VALUE time);
1844static VALUE time_get_tm(VALUE, struct time_object *);
1845#define MAKE_TM(time, tobj) \
1846 do { \
1847 if ((tobj)->vtm.tm_got == 0) { \
1848 time_get_tm((time), (tobj)); \
1849 } \
1850 } while (0)
1851#define MAKE_TM_ENSURE(time, tobj, cond) \
1852 do { \
1853 MAKE_TM(time, tobj); \
1854 if (!(cond)) { \
1855 force_make_tm(time, tobj); \
1856 } \
1857 } while (0)
1858
1859static void
1860time_set_timew(VALUE time, struct time_object *tobj, wideval_t timew)
1861{
1862 tobj->timew = timew;
1863 if (!FIXWV_P(timew)) {
1864 RB_OBJ_WRITTEN(time, Qnil, w2v(timew));
1865 }
1866}
1867
1868static void
1869time_set_vtm(VALUE time, struct time_object *tobj, struct vtm vtm)
1870{
1871 tobj->vtm = vtm;
1872
1873 RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.year);
1874 RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.subsecx);
1875 RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.utc_offset);
1876 RB_OBJ_WRITTEN(time, Qnil, tobj->vtm.zone);
1877}
1878
1879static inline void
1880force_make_tm(VALUE time, struct time_object *tobj)
1881{
1882 VALUE zone = tobj->vtm.zone;
1883 if (!NIL_P(zone) && zone != str_empty && zone != str_utc) {
1884 if (zone_localtime(zone, time)) return;
1885 }
1886 tobj->vtm.tm_got = 0;
1887 time_get_tm(time, tobj);
1888}
1889
1890static void
1891time_mark(void *ptr)
1892{
1893 struct time_object *tobj = ptr;
1894 if (!FIXWV_P(tobj->timew)) {
1895 rb_gc_mark_movable(w2v(tobj->timew));
1896 }
1897 rb_gc_mark_movable(tobj->vtm.year);
1898 rb_gc_mark_movable(tobj->vtm.subsecx);
1899 rb_gc_mark_movable(tobj->vtm.utc_offset);
1900 rb_gc_mark_movable(tobj->vtm.zone);
1901}
1902
1903static void
1904time_compact(void *ptr)
1905{
1906 struct time_object *tobj = ptr;
1907 if (!FIXWV_P(tobj->timew)) {
1908 WIDEVAL_GET(tobj->timew) = WIDEVAL_WRAP(rb_gc_location(w2v(tobj->timew)));
1909 }
1910
1911 tobj->vtm.year = rb_gc_location(tobj->vtm.year);
1912 tobj->vtm.subsecx = rb_gc_location(tobj->vtm.subsecx);
1913 tobj->vtm.utc_offset = rb_gc_location(tobj->vtm.utc_offset);
1914 tobj->vtm.zone = rb_gc_location(tobj->vtm.zone);
1915}
1916
1917static const rb_data_type_t time_data_type = {
1918 .wrap_struct_name = "time",
1919 .function = {
1920 .dmark = time_mark,
1921 .dfree = RUBY_TYPED_DEFAULT_FREE,
1922 .dsize = NULL,
1923 .dcompact = time_compact,
1924 },
1925 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
1926};
1927
1928static VALUE
1929time_s_alloc(VALUE klass)
1930{
1931 VALUE obj;
1932 struct time_object *tobj;
1933
1934 obj = TypedData_Make_Struct(klass, struct time_object, &time_data_type, tobj);
1935 tobj->vtm.tzmode = TIME_TZMODE_UNINITIALIZED;
1936 tobj->vtm.tm_got = 0;
1937 time_set_timew(obj, tobj, WINT2FIXWV(0));
1938 tobj->vtm.zone = Qnil;
1939
1940 return obj;
1941}
1942
1943static struct time_object *
1944get_timeval(VALUE obj)
1945{
1946 struct time_object *tobj;
1947 TypedData_Get_Struct(obj, struct time_object, &time_data_type, tobj);
1948 if (!TIME_INIT_P(tobj)) {
1949 rb_raise(rb_eTypeError, "uninitialized %"PRIsVALUE, rb_obj_class(obj));
1950 }
1951 return tobj;
1952}
1953
1954static struct time_object *
1955get_new_timeval(VALUE obj)
1956{
1957 struct time_object *tobj;
1958 TypedData_Get_Struct(obj, struct time_object, &time_data_type, tobj);
1959 if (TIME_INIT_P(tobj)) {
1960 rb_raise(rb_eTypeError, "already initialized %"PRIsVALUE, rb_obj_class(obj));
1961 }
1962 return tobj;
1963}
1964
1965static void
1966time_modify(VALUE time)
1967{
1968 rb_check_frozen(time);
1969}
1970
1971static wideval_t
1972timenano2timew(wideint_t sec, long nsec)
1973{
1974 wideval_t timew;
1975
1976 timew = rb_time_magnify(WINT2WV(sec));
1977 if (nsec)
1978 timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000));
1979 return timew;
1980}
1981
1982static struct timespec
1983timew2timespec(wideval_t timew)
1984{
1985 VALUE subsecx;
1986 struct timespec ts;
1987 wideval_t timew2;
1988
1989 if (timew_out_of_timet_range(timew))
1990 rb_raise(rb_eArgError, "time out of system range");
1991 split_second(timew, &timew2, &subsecx);
1992 ts.tv_sec = WV2TIMET(timew2);
1993 ts.tv_nsec = NUM2LONG(mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE)));
1994 return ts;
1995}
1996
1997static struct timespec *
1998timew2timespec_exact(wideval_t timew, struct timespec *ts)
1999{
2000 VALUE subsecx;
2001 wideval_t timew2;
2002 VALUE nsecv;
2003
2004 if (timew_out_of_timet_range(timew))
2005 return NULL;
2006 split_second(timew, &timew2, &subsecx);
2007 ts->tv_sec = WV2TIMET(timew2);
2008 nsecv = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
2009 if (!FIXNUM_P(nsecv))
2010 return NULL;
2011 ts->tv_nsec = NUM2LONG(nsecv);
2012 return ts;
2013}
2014
2015void
2017{
2018#ifdef HAVE_CLOCK_GETTIME
2019 if (clock_gettime(CLOCK_REALTIME, ts) == -1) {
2020 rb_sys_fail("clock_gettime");
2021 }
2022#else
2023 {
2024 struct timeval tv;
2025 if (gettimeofday(&tv, 0) < 0) {
2026 rb_sys_fail("gettimeofday");
2027 }
2028 ts->tv_sec = tv.tv_sec;
2029 ts->tv_nsec = tv.tv_usec * 1000;
2030 }
2031#endif
2032}
2033
2034/*
2035 * Sets the current time information into _time_.
2036 * Returns _time_.
2037 */
2038static VALUE
2039time_init_now(rb_execution_context_t *ec, VALUE time, VALUE zone)
2040{
2041 struct time_object *tobj;
2042 struct timespec ts;
2043
2044 time_modify(time);
2045 GetNewTimeval(time, tobj);
2046 TZMODE_SET_LOCALTIME(tobj);
2047 tobj->vtm.tm_got=0;
2048 rb_timespec_now(&ts);
2049 time_set_timew(time, tobj, timenano2timew(ts.tv_sec, ts.tv_nsec));
2050
2051 if (!NIL_P(zone)) {
2052 time_zonelocal(time, zone);
2053 }
2054 return time;
2055}
2056
2057static VALUE
2058time_s_now(rb_execution_context_t *ec, VALUE klass, VALUE zone)
2059{
2060 VALUE t = time_s_alloc(klass);
2061 return time_init_now(ec, t, zone);
2062}
2063
2064static VALUE
2065time_set_utc_offset(VALUE time, VALUE off)
2066{
2067 struct time_object *tobj;
2068 off = num_exact(off);
2069
2070 time_modify(time);
2071 GetTimeval(time, tobj);
2072
2073 tobj->vtm.tm_got = 0;
2074 tobj->vtm.zone = Qnil;
2075 TZMODE_SET_FIXOFF(time, tobj, off);
2076
2077 return time;
2078}
2079
2080static void
2081vtm_add_offset(struct vtm *vtm, VALUE off, int sign)
2082{
2083 VALUE subsec, v;
2084 int sec, min, hour;
2085 int day;
2086
2087 if (lt(off, INT2FIX(0))) {
2088 sign = -sign;
2089 off = neg(off);
2090 }
2091 divmodv(off, INT2FIX(1), &off, &subsec);
2092 divmodv(off, INT2FIX(60), &off, &v);
2093 sec = NUM2INT(v);
2094 divmodv(off, INT2FIX(60), &off, &v);
2095 min = NUM2INT(v);
2096 divmodv(off, INT2FIX(24), &off, &v);
2097 hour = NUM2INT(v);
2098
2099 if (sign < 0) {
2100 subsec = neg(subsec);
2101 sec = -sec;
2102 min = -min;
2103 hour = -hour;
2104 }
2105
2106 day = 0;
2107
2108 if (!rb_equal(subsec, INT2FIX(0))) {
2109 vtm->subsecx = addv(vtm->subsecx, w2v(rb_time_magnify(v2w(subsec))));
2110 if (lt(vtm->subsecx, INT2FIX(0))) {
2111 vtm->subsecx = addv(vtm->subsecx, INT2FIX(TIME_SCALE));
2112 sec -= 1;
2113 }
2114 if (le(INT2FIX(TIME_SCALE), vtm->subsecx)) {
2115 vtm->subsecx = subv(vtm->subsecx, INT2FIX(TIME_SCALE));
2116 sec += 1;
2117 }
2118 }
2119 if (sec) {
2120 /* If sec + subsec == 0, don't change vtm->sec.
2121 * It may be 60 which is a leap second. */
2122 sec += vtm->sec;
2123 if (sec < 0) {
2124 sec += 60;
2125 min -= 1;
2126 }
2127 if (60 <= sec) {
2128 sec -= 60;
2129 min += 1;
2130 }
2131 vtm->sec = sec;
2132 }
2133 if (min) {
2134 min += vtm->min;
2135 if (min < 0) {
2136 min += 60;
2137 hour -= 1;
2138 }
2139 if (60 <= min) {
2140 min -= 60;
2141 hour += 1;
2142 }
2143 vtm->min = min;
2144 }
2145 if (hour) {
2146 hour += vtm->hour;
2147 if (hour < 0) {
2148 hour += 24;
2149 day = -1;
2150 }
2151 if (24 <= hour) {
2152 hour -= 24;
2153 day = 1;
2154 }
2155 vtm->hour = hour;
2156 }
2157
2158 vtm_add_day(vtm, day);
2159}
2160
2161static void
2162vtm_add_day(struct vtm *vtm, int day)
2163{
2164 if (day) {
2165 if (day < 0) {
2166 if (vtm->mon == 1 && vtm->mday == 1) {
2167 vtm->mday = 31;
2168 vtm->mon = 12; /* December */
2169 vtm->year = subv(vtm->year, INT2FIX(1));
2170 if (vtm->yday != 0)
2171 vtm->yday = leap_year_v_p(vtm->year) ? 366 : 365;
2172 }
2173 else if (vtm->mday == 1) {
2174 const int8_t *days_in_month = days_in_month_in_v(vtm->year);
2175 vtm->mon--;
2176 vtm->mday = days_in_month[vtm->mon-1];
2177 if (vtm->yday != 0) vtm->yday--;
2178 }
2179 else {
2180 vtm->mday--;
2181 if (vtm->yday != 0) vtm->yday--;
2182 }
2183 if (vtm->wday != VTM_WDAY_INITVAL) vtm->wday = (vtm->wday + 6) % 7;
2184 }
2185 else {
2186 int leap = leap_year_v_p(vtm->year);
2187 if (vtm->mon == 12 && vtm->mday == 31) {
2188 vtm->year = addv(vtm->year, INT2FIX(1));
2189 vtm->mon = 1; /* January */
2190 vtm->mday = 1;
2191 vtm->yday = 1;
2192 }
2193 else if (vtm->mday == days_in_month_of(leap)[vtm->mon-1]) {
2194 vtm->mon++;
2195 vtm->mday = 1;
2196 if (vtm->yday != 0) vtm->yday++;
2197 }
2198 else {
2199 vtm->mday++;
2200 if (vtm->yday != 0) vtm->yday++;
2201 }
2202 if (vtm->wday != VTM_WDAY_INITVAL) vtm->wday = (vtm->wday + 1) % 7;
2203 }
2204 }
2205}
2206
2207static int
2208maybe_tzobj_p(VALUE obj)
2209{
2210 if (NIL_P(obj)) return FALSE;
2211 if (RB_INTEGER_TYPE_P(obj)) return FALSE;
2212 if (RB_TYPE_P(obj, T_STRING)) return FALSE;
2213 return TRUE;
2214}
2215
2216NORETURN(static void invalid_utc_offset(VALUE));
2217static void
2218invalid_utc_offset(VALUE zone)
2219{
2220 rb_raise(rb_eArgError, "\"+HH:MM\", \"-HH:MM\", \"UTC\" or "
2221 "\"A\"..\"I\",\"K\"..\"Z\" expected for utc_offset: %"PRIsVALUE,
2222 zone);
2223}
2224
2225#define have_2digits(ptr) (ISDIGIT((ptr)[0]) && ISDIGIT((ptr)[1]))
2226#define num_from_2digits(ptr) ((ptr)[0] * 10 + (ptr)[1] - '0' * 11)
2227
2228static VALUE
2229utc_offset_arg(VALUE arg)
2230{
2231 VALUE tmp;
2232 if (!NIL_P(tmp = rb_check_string_type(arg))) {
2233 int n = 0;
2234 const char *s = RSTRING_PTR(tmp), *min = NULL, *sec = NULL;
2235 if (!rb_enc_str_asciicompat_p(tmp)) {
2236 goto invalid_utc_offset;
2237 }
2238 switch (RSTRING_LEN(tmp)) {
2239 case 1:
2240 if (s[0] == 'Z') {
2241 return UTC_ZONE;
2242 }
2243 /* Military Time Zone Names */
2244 if (s[0] >= 'A' && s[0] <= 'I') {
2245 n = (int)s[0] - 'A' + 1;
2246 }
2247 /* No 'J' zone */
2248 else if (s[0] >= 'K' && s[0] <= 'M') {
2249 n = (int)s[0] - 'A';
2250 }
2251 else if (s[0] >= 'N' && s[0] <= 'Y') {
2252 n = 'M' - (int)s[0];
2253 }
2254 else {
2255 goto invalid_utc_offset;
2256 }
2257 n *= 3600;
2258 return INT2FIX(n);
2259 case 3:
2260 if (STRNCASECMP("UTC", s, 3) == 0) {
2261 return UTC_ZONE;
2262 }
2263 break; /* +HH */
2264 case 7: /* +HHMMSS */
2265 sec = s+5;
2266 /* fallthrough */
2267 case 5: /* +HHMM */
2268 min = s+3;
2269 break;
2270 case 9: /* +HH:MM:SS */
2271 if (s[6] != ':') goto invalid_utc_offset;
2272 sec = s+7;
2273 /* fallthrough */
2274 case 6: /* +HH:MM */
2275 if (s[3] != ':') goto invalid_utc_offset;
2276 min = s+4;
2277 break;
2278 default:
2279 goto invalid_utc_offset;
2280 }
2281 if (sec) {
2282 if (!have_2digits(sec)) goto invalid_utc_offset;
2283 if (sec[0] > '5') goto invalid_utc_offset;
2284 n += num_from_2digits(sec);
2285 ASSUME(min);
2286 }
2287 if (min) {
2288 if (!have_2digits(min)) goto invalid_utc_offset;
2289 if (min[0] > '5') goto invalid_utc_offset;
2290 n += num_from_2digits(min) * 60;
2291 }
2292 if (s[0] != '+' && s[0] != '-') goto invalid_utc_offset;
2293 if (!have_2digits(s+1)) goto invalid_utc_offset;
2294 n += num_from_2digits(s+1) * 3600;
2295 if (s[0] == '-') {
2296 if (n == 0) return UTC_ZONE;
2297 n = -n;
2298 }
2299 return INT2FIX(n);
2300 }
2301 else {
2302 return num_exact(arg);
2303 }
2304 invalid_utc_offset:
2305 return Qnil;
2306}
2307
2308static void
2309zone_set_offset(VALUE zone, struct time_object *tobj,
2310 wideval_t tlocal, wideval_t tutc, VALUE time)
2311{
2312 /* tlocal and tutc must be unmagnified and in seconds */
2313 wideval_t w = wsub(tlocal, tutc);
2314 VALUE off = w2v(w);
2315 validate_utc_offset(off);
2316 RB_OBJ_WRITE(time, &tobj->vtm.utc_offset, off);
2317 RB_OBJ_WRITE(time, &tobj->vtm.zone, zone);
2318 TZMODE_SET_LOCALTIME(tobj);
2319}
2320
2321static wideval_t
2322extract_time(VALUE time)
2323{
2324 wideval_t t;
2325 const ID id_to_i = idTo_i;
2326
2327#define EXTRACT_TIME() do { \
2328 t = NUM2WV(AREF(to_i)); \
2329 } while (0)
2330
2331 if (rb_typeddata_is_kind_of(time, &time_data_type)) {
2332 struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
2333
2334 time_gmtime(time); /* ensure tm got */
2335 t = rb_time_unmagnify(tobj->timew);
2336
2337 RB_GC_GUARD(time);
2338 }
2339 else if (RB_TYPE_P(time, T_STRUCT)) {
2340#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2341 EXTRACT_TIME();
2342#undef AREF
2343 }
2344 else {
2345#define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2346 EXTRACT_TIME();
2347#undef AREF
2348 }
2349#undef EXTRACT_TIME
2350
2351 return t;
2352}
2353
2354static wideval_t
2355extract_vtm(VALUE time, VALUE orig_time, struct time_object *orig_tobj, VALUE subsecx)
2356{
2357 wideval_t t;
2358 const ID id_to_i = idTo_i;
2359 struct vtm *vtm = &orig_tobj->vtm;
2360
2361#define EXTRACT_VTM() do { \
2362 VALUE subsecx; \
2363 vtm->year = obj2vint(AREF(year)); \
2364 vtm->mon = month_arg(AREF(mon)); \
2365 vtm->mday = obj2ubits(AREF(mday), 5); \
2366 vtm->hour = obj2ubits(AREF(hour), 5); \
2367 vtm->min = obj2ubits(AREF(min), 6); \
2368 vtm->sec = obj2subsecx(AREF(sec), &subsecx); \
2369 vtm->isdst = RTEST(AREF(isdst)); \
2370 vtm->utc_offset = Qnil; \
2371 t = NUM2WV(AREF(to_i)); \
2372 } while (0)
2373
2374 if (rb_typeddata_is_kind_of(time, &time_data_type)) {
2375 struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
2376
2377 time_get_tm(time, tobj);
2378 time_set_vtm(orig_time, orig_tobj, tobj->vtm);
2379 t = rb_time_unmagnify(tobj->timew);
2380 if (TZMODE_FIXOFF_P(tobj) && vtm->utc_offset != INT2FIX(0))
2381 t = wadd(t, v2w(vtm->utc_offset));
2382
2383 RB_GC_GUARD(time);
2384 }
2385 else if (RB_TYPE_P(time, T_STRUCT)) {
2386#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2387 EXTRACT_VTM();
2388#undef AREF
2389 }
2390 else if (rb_integer_type_p(time)) {
2391 t = v2w(time);
2392 struct vtm temp_vtm = *vtm;
2393 GMTIMEW(rb_time_magnify(t), &temp_vtm);
2394 time_set_vtm(orig_time, orig_tobj, temp_vtm);
2395 }
2396 else {
2397#define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2398 EXTRACT_VTM();
2399#undef AREF
2400 }
2401#undef EXTRACT_VTM
2402
2403 RB_OBJ_WRITE_UNALIGNED(orig_time, &vtm->subsecx, subsecx);
2404
2405 validate_vtm(vtm);
2406 return t;
2407}
2408
2409static void
2410zone_set_dst(VALUE zone, struct time_object *tobj, VALUE tm)
2411{
2412 ID id_dst_p;
2413 VALUE dst;
2414 CONST_ID(id_dst_p, "dst?");
2415 dst = rb_check_funcall(zone, id_dst_p, 1, &tm);
2416 tobj->vtm.isdst = (!UNDEF_P(dst) && RTEST(dst));
2417}
2418
2419static int
2420zone_timelocal(VALUE zone, VALUE time)
2421{
2422 VALUE utc, tm;
2423 struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
2424 wideval_t t, s;
2425
2426 wdivmod(tobj->timew, WINT2FIXWV(TIME_SCALE), &t, &s);
2427 tm = tm_from_time(rb_cTimeTM, time);
2428 utc = rb_check_funcall(zone, id_local_to_utc, 1, &tm);
2429 if (UNDEF_P(utc)) return 0;
2430
2431 s = extract_time(utc);
2432 zone_set_offset(zone, tobj, t, s, time);
2433 s = rb_time_magnify(s);
2434 if (tobj->vtm.subsecx != INT2FIX(0)) {
2435 s = wadd(s, v2w(tobj->vtm.subsecx));
2436 }
2437 time_set_timew(time, tobj, s);
2438
2439 zone_set_dst(zone, tobj, tm);
2440
2441 RB_GC_GUARD(time);
2442
2443 return 1;
2444}
2445
2446static int
2447zone_localtime(VALUE zone, VALUE time)
2448{
2449 VALUE local, tm, subsecx;
2450 struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
2451 wideval_t t, s;
2452
2453 split_second(tobj->timew, &t, &subsecx);
2454 tm = tm_from_time(rb_cTimeTM, time);
2455
2456 local = rb_check_funcall(zone, id_utc_to_local, 1, &tm);
2457 if (UNDEF_P(local)) return 0;
2458
2459 s = extract_vtm(local, time, tobj, subsecx);
2460 tobj->vtm.tm_got = 1;
2461 zone_set_offset(zone, tobj, s, t, time);
2462 zone_set_dst(zone, tobj, tm);
2463
2464 RB_GC_GUARD(time);
2465
2466 return 1;
2467}
2468
2469static VALUE
2470find_timezone(VALUE time, VALUE zone)
2471{
2472 VALUE klass = CLASS_OF(time);
2473
2474 return rb_check_funcall_default(klass, id_find_timezone, 1, &zone, Qnil);
2475}
2476
2477/* Turn the special case 24:00:00 of already validated vtm into
2478 * 00:00:00 the next day */
2479static void
2480vtm_day_wraparound(struct vtm *vtm)
2481{
2482 if (vtm->hour < 24) return;
2483
2484 /* Assuming UTC and no care of DST, just reset hour and advance
2485 * date, not to discard the validated vtm. */
2486 vtm->hour = 0;
2487 vtm_add_day(vtm, 1);
2488}
2489
2490static VALUE time_init_vtm(VALUE time, struct vtm vtm, VALUE zone);
2491
2492/*
2493 * Sets the broken-out time information into _time_.
2494 * Returns _time_.
2495 */
2496static VALUE
2497time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday,
2498 VALUE hour, VALUE min, VALUE sec, VALUE zone)
2499{
2500 struct vtm vtm;
2501
2502 vtm.wday = VTM_WDAY_INITVAL;
2503 vtm.yday = 0;
2504 vtm.zone = str_empty;
2505
2506 vtm.year = obj2vint(year);
2507
2508 vtm.mon = NIL_P(mon) ? 1 : month_arg(mon);
2509
2510 vtm.mday = NIL_P(mday) ? 1 : obj2ubits(mday, 5);
2511
2512 vtm.hour = NIL_P(hour) ? 0 : obj2ubits(hour, 5);
2513
2514 vtm.min = NIL_P(min) ? 0 : obj2ubits(min, 6);
2515
2516 if (NIL_P(sec)) {
2517 vtm.sec = 0;
2518 vtm.subsecx = INT2FIX(0);
2519 }
2520 else {
2521 VALUE subsecx;
2522 vtm.sec = obj2subsecx(sec, &subsecx);
2523 vtm.subsecx = subsecx;
2524 }
2525
2526 return time_init_vtm(time, vtm, zone);
2527}
2528
2529static VALUE
2530time_init_vtm(VALUE time, struct vtm vtm, VALUE zone)
2531{
2532 VALUE utc = Qnil;
2533 struct time_object *tobj;
2534
2535 vtm.isdst = VTM_ISDST_INITVAL;
2536 vtm.utc_offset = Qnil;
2537 const VALUE arg = zone;
2538 if (!NIL_P(arg)) {
2539 zone = Qnil;
2540 if (arg == ID2SYM(rb_intern("dst")))
2541 vtm.isdst = 1;
2542 else if (arg == ID2SYM(rb_intern("std")))
2543 vtm.isdst = 0;
2544 else if (maybe_tzobj_p(arg))
2545 zone = arg;
2546 else if (!NIL_P(utc = utc_offset_arg(arg)))
2547 vtm.utc_offset = utc == UTC_ZONE ? INT2FIX(0) : utc;
2548 else if (NIL_P(zone = find_timezone(time, arg)))
2549 invalid_utc_offset(arg);
2550 }
2551
2552 validate_vtm(&vtm);
2553
2554 time_modify(time);
2555 GetNewTimeval(time, tobj);
2556
2557 if (!NIL_P(zone)) {
2558 time_set_timew(time, tobj, timegmw(&vtm));
2559 vtm_day_wraparound(&vtm);
2560 time_set_vtm(time, tobj, vtm);
2561 tobj->vtm.tm_got = 1;
2562 TZMODE_SET_LOCALTIME(tobj);
2563 if (zone_timelocal(zone, time)) {
2564 return time;
2565 }
2566 else if (NIL_P(vtm.utc_offset = utc_offset_arg(zone))) {
2567 if (NIL_P(zone = find_timezone(time, zone)) || !zone_timelocal(zone, time))
2568 invalid_utc_offset(arg);
2569 }
2570 }
2571
2572 if (utc == UTC_ZONE) {
2573 time_set_timew(time, tobj, timegmw(&vtm));
2574 vtm.isdst = 0; /* No DST in UTC */
2575 vtm_day_wraparound(&vtm);
2576 time_set_vtm(time, tobj, vtm);
2577 tobj->vtm.tm_got = 1;
2578 TZMODE_SET_UTC(tobj);
2579 return time;
2580 }
2581
2582 TZMODE_SET_LOCALTIME(tobj);
2583 tobj->vtm.tm_got=0;
2584
2585 if (!NIL_P(vtm.utc_offset)) {
2586 VALUE off = vtm.utc_offset;
2587 vtm_add_offset(&vtm, off, -1);
2588 vtm.utc_offset = Qnil;
2589 time_set_timew(time, tobj, timegmw(&vtm));
2590
2591 return time_set_utc_offset(time, off);
2592 }
2593 else {
2594 time_set_timew(time, tobj, timelocalw(&vtm));
2595
2596 return time_localtime(time);
2597 }
2598}
2599
2600static int
2601two_digits(const char *ptr, const char *end, const char **endp, const char *name)
2602{
2603 ssize_t len = end - ptr;
2604 if (len < 2 || !have_2digits(ptr) || ((len > 2) && ISDIGIT(ptr[2]))) {
2605 VALUE mesg = rb_sprintf("two digits %s is expected", name);
2606 if (ptr[-1] == '-' || ptr[-1] == ':') {
2607 rb_str_catf(mesg, " after '%c'", ptr[-1]);
2608 }
2609 rb_str_catf(mesg, ": %.*s", ((len > 10) ? 10 : (int)(end - ptr)) + 1, ptr - 1);
2610 rb_exc_raise(rb_exc_new_str(rb_eArgError, mesg));
2611 }
2612 *endp = ptr + 2;
2613 return num_from_2digits(ptr);
2614}
2615
2616static VALUE
2617parse_int(const char *ptr, const char *end, const char **endp, size_t *ndigits, bool sign)
2618{
2619 ssize_t len = (end - ptr);
2620 int flags = sign ? RB_INT_PARSE_SIGN : 0;
2621 return rb_int_parse_cstr(ptr, len, (char **)endp, ndigits, 10, flags);
2622}
2623
2624/*
2625 * Parses _str_ and sets the broken-out time information into _time_.
2626 * If _str_ is not a String, returns +nil+, otherwise returns _time_.
2627 */
2628static VALUE
2629time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone, VALUE precision)
2630{
2631 if (NIL_P(str = rb_check_string_type(str))) return Qnil;
2632 if (!rb_enc_str_asciicompat_p(str)) {
2633 rb_raise(rb_eArgError, "time string should have ASCII compatible encoding");
2634 }
2635
2636 const char *const begin = RSTRING_PTR(str);
2637 const char *const end = RSTRING_END(str);
2638 const char *ptr = begin;
2639 VALUE year = Qnil, subsec = Qnil;
2640 int mon = -1, mday = -1, hour = -1, min = -1, sec = -1;
2641 size_t ndigits;
2642 size_t prec = NIL_P(precision) ? SIZE_MAX : NUM2SIZET(precision);
2643
2644 if ((ptr < end) && (ISSPACE(*ptr) || ISSPACE(*(end-1)))) {
2645 rb_raise(rb_eArgError, "can't parse: %+"PRIsVALUE, str);
2646 }
2647 year = parse_int(ptr, end, &ptr, &ndigits, true);
2648 if (NIL_P(year)) {
2649 rb_raise(rb_eArgError, "can't parse: %+"PRIsVALUE, str);
2650 }
2651 else if (ndigits < 4) {
2652 rb_raise(rb_eArgError, "year must be 4 or more digits: %.*s", (int)ndigits, ptr - ndigits);
2653 }
2654 else if (ptr == end) {
2655 goto only_year;
2656 }
2657 do {
2658#define peekable_p(n) ((ptrdiff_t)(n) < (end - ptr))
2659#define peek_n(c, n) (peekable_p(n) && ((unsigned char)ptr[n] == (c)))
2660#define peek(c) peek_n(c, 0)
2661#define peekc_n(n) (peekable_p(n) ? (int)(unsigned char)ptr[n] : -1)
2662#define peekc() peekc_n(0)
2663#define expect_two_digits(x, bits) \
2664 (((unsigned int)(x = two_digits(ptr + 1, end, &ptr, #x)) > (1U << bits) - 1) ? \
2665 rb_raise(rb_eArgError, #x" out of range") : (void)0)
2666 if (!peek('-')) break;
2667 expect_two_digits(mon, 4);
2668 if (!peek('-')) break;
2669 expect_two_digits(mday, 5);
2670 if (!peek(' ') && !peek('T')) break;
2671 const char *const time_part = ptr + 1;
2672 if (!ISDIGIT(peekc_n(1))) break;
2673#define nofraction(x) \
2674 if (peek('.')) { \
2675 rb_raise(rb_eArgError, "fraction " #x " is not supported: %.*s", \
2676 (int)(ptr + 1 - time_part), time_part); \
2677 }
2678#define need_colon(x) \
2679 if (!peek(':')) { \
2680 rb_raise(rb_eArgError, "missing " #x " part: %.*s", \
2681 (int)(ptr + 1 - time_part), time_part); \
2682 }
2683 expect_two_digits(hour, 5);
2684 nofraction(hour);
2685 need_colon(min);
2686 expect_two_digits(min, 6);
2687 nofraction(min);
2688 need_colon(sec);
2689 expect_two_digits(sec, 6);
2690 if (peek('.')) {
2691 ptr++;
2692 for (ndigits = 0; ndigits < prec && ISDIGIT(peekc_n(ndigits)); ++ndigits);
2693 if (!ndigits) {
2694 int clen = rb_enc_precise_mbclen(ptr, end, rb_enc_get(str));
2695 if (clen < 0) clen = 0;
2696 rb_raise(rb_eArgError, "subsecond expected after dot: %.*s",
2697 (int)(ptr - time_part) + clen, time_part);
2698 }
2699 subsec = parse_int(ptr, ptr + ndigits, &ptr, &ndigits, false);
2700 if (NIL_P(subsec)) break;
2701 while (ptr < end && ISDIGIT(*ptr)) ptr++;
2702 }
2703 } while (0);
2704 while (ptr < end && ISSPACE(*ptr)) ptr++;
2705 const char *const zstr = ptr;
2706 while (ptr < end && !ISSPACE(*ptr)) ptr++;
2707 const char *const zend = ptr;
2708 while (ptr < end && ISSPACE(*ptr)) ptr++;
2709 if (ptr < end) {
2710 VALUE mesg = rb_str_new_cstr("can't parse at: ");
2711 rb_str_cat(mesg, ptr, end - ptr);
2712 rb_exc_raise(rb_exc_new_str(rb_eArgError, mesg));
2713 }
2714 if (zend > zstr) {
2715 zone = rb_str_subseq(str, zstr - begin, zend - zstr);
2716 }
2717 else if (hour == -1) {
2718 rb_raise(rb_eArgError, "no time information");
2719 }
2720 if (!NIL_P(subsec)) {
2721 /* subseconds is the last using ndigits */
2722 if (ndigits < (size_t)TIME_SCALE_NUMDIGITS) {
2723 VALUE mul = rb_int_positive_pow(10, TIME_SCALE_NUMDIGITS - ndigits);
2724 subsec = rb_int_mul(subsec, mul);
2725 }
2726 else if (ndigits > (size_t)TIME_SCALE_NUMDIGITS) {
2727 VALUE num = rb_int_positive_pow(10, ndigits - TIME_SCALE_NUMDIGITS);
2728 subsec = rb_rational_new(subsec, num);
2729 }
2730 }
2731
2732only_year:
2733 ;
2734
2735 struct vtm vtm = {
2736 .wday = VTM_WDAY_INITVAL,
2737 .yday = 0,
2738 .zone = str_empty,
2739 .year = year,
2740 .mon = (mon < 0) ? 1 : mon,
2741 .mday = (mday < 0) ? 1 : mday,
2742 .hour = (hour < 0) ? 0 : hour,
2743 .min = (min < 0) ? 0 : min,
2744 .sec = (sec < 0) ? 0 : sec,
2745 .subsecx = NIL_P(subsec) ? INT2FIX(0) : subsec,
2746 };
2747 return time_init_vtm(time, vtm, zone);
2748}
2749
2750static void
2751subsec_normalize(wideint_t *secp, long *subsecp, const long maxsubsec)
2752{
2753 wideint_t sec = *secp;
2754 long subsec = *subsecp;
2755 long sec2;
2756
2757 if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */
2758 sec2 = subsec / maxsubsec;
2759 if (WIDEINT_MAX - sec2 < sec) {
2760 rb_raise(rb_eRangeError, "out of Time range");
2761 }
2762 subsec -= sec2 * maxsubsec;
2763 sec += sec2;
2764 }
2765 else if (UNLIKELY(subsec < 0)) { /* subsec negative overflow */
2766 sec2 = NDIV(subsec, maxsubsec); /* negative div */
2767 if (sec < WIDEINT_MIN - sec2) {
2768 rb_raise(rb_eRangeError, "out of Time range");
2769 }
2770 subsec -= sec2 * maxsubsec;
2771 sec += sec2;
2772 }
2773 *secp = sec;
2774 *subsecp = subsec;
2775}
2776
2777#define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
2778#define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000)
2779
2780static VALUE
2781time_new_timew(VALUE klass, wideval_t timew)
2782{
2783 VALUE time = time_s_alloc(klass);
2784 struct time_object *tobj;
2785
2786 tobj = RTYPEDDATA_GET_DATA(time); /* skip type check */
2787 TZMODE_SET_LOCALTIME(tobj);
2788 time_set_timew(time, tobj, timew);
2789
2790 return time;
2791}
2792
2793static wideint_t
2794TIMETtoWIDEINT(time_t t)
2795{
2796#if SIZEOF_TIME_T * CHAR_BIT - (SIGNEDNESS_OF_TIME_T < 0) > \
2797 SIZEOF_WIDEINT * CHAR_BIT - 1
2798 /* compare in bit size without sign bit */
2799 if (t > WIDEINT_MAX) rb_raise(rb_eArgError, "out of Time range");
2800#endif
2801 return (wideint_t)t;
2802}
2803
2804VALUE
2805rb_time_new(time_t sec, long usec)
2806{
2807 wideint_t isec = TIMETtoWIDEINT(sec);
2808 time_usec_normalize(&isec, &usec);
2809 return time_new_timew(rb_cTime, timenano2timew(isec, usec * 1000));
2810}
2811
2812/* returns localtime time object */
2813VALUE
2814rb_time_nano_new(time_t sec, long nsec)
2815{
2816 wideint_t isec = TIMETtoWIDEINT(sec);
2817 time_nsec_normalize(&isec, &nsec);
2818 return time_new_timew(rb_cTime, timenano2timew(isec, nsec));
2819}
2820
2821VALUE
2822rb_time_timespec_new(const struct timespec *ts, int offset)
2823{
2824 struct time_object *tobj;
2825 VALUE time = rb_time_nano_new(ts->tv_sec, ts->tv_nsec);
2826
2827 if (-86400 < offset && offset < 86400) { /* fixoff */
2828 GetTimeval(time, tobj);
2829 TZMODE_SET_FIXOFF(time, tobj, INT2FIX(offset));
2830 }
2831 else if (offset == INT_MAX) { /* localtime */
2832 }
2833 else if (offset == INT_MAX-1) { /* UTC */
2834 GetTimeval(time, tobj);
2835 TZMODE_SET_UTC(tobj);
2836 }
2837 else {
2838 rb_raise(rb_eArgError, "utc_offset out of range");
2839 }
2840
2841 return time;
2842}
2843
2844VALUE
2846{
2847 VALUE time = time_new_timew(rb_cTime, rb_time_magnify(v2w(timev)));
2848
2849 if (!NIL_P(off)) {
2850 VALUE zone = off;
2851
2852 if (maybe_tzobj_p(zone)) {
2853 time_gmtime(time);
2854 if (zone_timelocal(zone, time)) return time;
2855 }
2856 if (NIL_P(off = utc_offset_arg(off))) {
2857 off = zone;
2858 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
2859 time_gmtime(time);
2860 if (!zone_timelocal(zone, time)) invalid_utc_offset(off);
2861 return time;
2862 }
2863 else if (off == UTC_ZONE) {
2864 return time_gmtime(time);
2865 }
2866
2867 validate_utc_offset(off);
2868 time_set_utc_offset(time, off);
2869 return time;
2870 }
2871
2872 return time;
2873}
2874
2875static struct timespec
2876time_timespec(VALUE num, int interval)
2877{
2878 struct timespec t;
2879 const char *const tstr = interval ? "time interval" : "time";
2880 VALUE i, f, ary;
2881
2882#ifndef NEGATIVE_TIME_T
2883# define arg_range_check(v) \
2884 (((v) < 0) ? \
2885 rb_raise(rb_eArgError, "%s must not be negative", tstr) : \
2886 (void)0)
2887#else
2888# define arg_range_check(v) \
2889 ((interval && (v) < 0) ? \
2890 rb_raise(rb_eArgError, "time interval must not be negative") : \
2891 (void)0)
2892#endif
2893
2894 if (FIXNUM_P(num)) {
2895 t.tv_sec = NUM2TIMET(num);
2896 arg_range_check(t.tv_sec);
2897 t.tv_nsec = 0;
2898 }
2899 else if (RB_FLOAT_TYPE_P(num)) {
2900 double x = RFLOAT_VALUE(num);
2901 arg_range_check(x);
2902 {
2903 double f, d;
2904
2905 d = modf(x, &f);
2906 if (d >= 0) {
2907 t.tv_nsec = (int)(d*1e9+0.5);
2908 if (t.tv_nsec >= 1000000000) {
2909 t.tv_nsec -= 1000000000;
2910 f += 1;
2911 }
2912 }
2913 else if ((t.tv_nsec = (int)(-d*1e9+0.5)) > 0) {
2914 t.tv_nsec = 1000000000 - t.tv_nsec;
2915 f -= 1;
2916 }
2917 t.tv_sec = (time_t)f;
2918 if (f != t.tv_sec) {
2919 rb_raise(rb_eRangeError, "%f out of Time range", x);
2920 }
2921 }
2922 }
2923 else if (RB_BIGNUM_TYPE_P(num)) {
2924 t.tv_sec = NUM2TIMET(num);
2925 arg_range_check(t.tv_sec);
2926 t.tv_nsec = 0;
2927 }
2928 else {
2929 i = INT2FIX(1);
2930 ary = rb_check_funcall(num, id_divmod, 1, &i);
2931 if (!UNDEF_P(ary) && !NIL_P(ary = rb_check_array_type(ary))) {
2932 i = rb_ary_entry(ary, 0);
2933 f = rb_ary_entry(ary, 1);
2934 t.tv_sec = NUM2TIMET(i);
2935 arg_range_check(t.tv_sec);
2936 f = rb_funcall(f, '*', 1, INT2FIX(1000000000));
2937 t.tv_nsec = NUM2LONG(f);
2938 }
2939 else {
2940 rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into %s",
2941 rb_obj_class(num), tstr);
2942 }
2943 }
2944 return t;
2945#undef arg_range_check
2946}
2947
2948static struct timeval
2949time_timeval(VALUE num, int interval)
2950{
2951 struct timespec ts;
2952 struct timeval tv;
2953
2954 ts = time_timespec(num, interval);
2955 tv.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
2956 tv.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
2957
2958 return tv;
2959}
2960
2961struct timeval
2963{
2964 return time_timeval(num, TRUE);
2965}
2966
2967struct timeval
2969{
2970 struct time_object *tobj;
2971 struct timeval t;
2972 struct timespec ts;
2973
2974 if (IsTimeval(time)) {
2975 GetTimeval(time, tobj);
2976 ts = timew2timespec(tobj->timew);
2977 t.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
2978 t.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
2979 return t;
2980 }
2981 return time_timeval(time, FALSE);
2982}
2983
2984struct timespec
2986{
2987 struct time_object *tobj;
2988 struct timespec t;
2989
2990 if (IsTimeval(time)) {
2991 GetTimeval(time, tobj);
2992 t = timew2timespec(tobj->timew);
2993 return t;
2994 }
2995 return time_timespec(time, FALSE);
2996}
2997
2998struct timespec
3000{
3001 return time_timespec(num, TRUE);
3002}
3003
3004static int
3005get_scale(VALUE unit)
3006{
3007 if (unit == ID2SYM(id_nanosecond) || unit == ID2SYM(id_nsec)) {
3008 return 1000000000;
3009 }
3010 else if (unit == ID2SYM(id_microsecond) || unit == ID2SYM(id_usec)) {
3011 return 1000000;
3012 }
3013 else if (unit == ID2SYM(id_millisecond)) {
3014 return 1000;
3015 }
3016 else {
3017 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
3018 }
3019}
3020
3021static VALUE
3022time_s_at(rb_execution_context_t *ec, VALUE klass, VALUE time, VALUE subsec, VALUE unit, VALUE zone)
3023{
3024 VALUE t;
3025 wideval_t timew;
3026
3027 if (subsec) {
3028 int scale = get_scale(unit);
3029 time = num_exact(time);
3030 t = num_exact(subsec);
3031 timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, scale));
3032 t = time_new_timew(klass, timew);
3033 }
3034 else if (IsTimeval(time)) {
3035 struct time_object *tobj, *tobj2;
3036 GetTimeval(time, tobj);
3037 t = time_new_timew(klass, tobj->timew);
3038 GetTimeval(t, tobj2);
3039 TZMODE_COPY(tobj2, tobj);
3040 }
3041 else {
3042 timew = rb_time_magnify(v2w(num_exact(time)));
3043 t = time_new_timew(klass, timew);
3044 }
3045 if (!NIL_P(zone)) {
3046 time_zonelocal(t, zone);
3047 }
3048
3049 return t;
3050}
3051
3052static VALUE
3053time_s_at1(rb_execution_context_t *ec, VALUE klass, VALUE time)
3054{
3055 return time_s_at(ec, klass, time, Qfalse, ID2SYM(id_microsecond), Qnil);
3056}
3057
3058static const char months[][4] = {
3059 "jan", "feb", "mar", "apr", "may", "jun",
3060 "jul", "aug", "sep", "oct", "nov", "dec",
3061};
3062
3063static int
3064obj2int(VALUE obj)
3065{
3066 if (RB_TYPE_P(obj, T_STRING)) {
3067 obj = rb_str_to_inum(obj, 10, TRUE);
3068 }
3069
3070 return NUM2INT(obj);
3071}
3072
3073/* bits should be 0 <= x <= 31 */
3074static uint32_t
3075obj2ubits(VALUE obj, unsigned int bits)
3076{
3077 const unsigned int usable_mask = (1U << bits) - 1;
3078 unsigned int rv = (unsigned int)obj2int(obj);
3079
3080 if ((rv & usable_mask) != rv)
3081 rb_raise(rb_eArgError, "argument out of range");
3082 return (uint32_t)rv;
3083}
3084
3085static VALUE
3086obj2vint(VALUE obj)
3087{
3088 if (RB_TYPE_P(obj, T_STRING)) {
3089 obj = rb_str_to_inum(obj, 10, TRUE);
3090 }
3091 else {
3092 obj = rb_to_int(obj);
3093 }
3094
3095 return obj;
3096}
3097
3098static uint32_t
3099obj2subsecx(VALUE obj, VALUE *subsecx)
3100{
3101 VALUE subsec;
3102
3103 if (RB_TYPE_P(obj, T_STRING)) {
3104 obj = rb_str_to_inum(obj, 10, TRUE);
3105 *subsecx = INT2FIX(0);
3106 }
3107 else {
3108 divmodv(num_exact(obj), INT2FIX(1), &obj, &subsec);
3109 *subsecx = w2v(rb_time_magnify(v2w(subsec)));
3110 }
3111 return obj2ubits(obj, 6); /* vtm->sec */
3112}
3113
3114static VALUE
3115usec2subsecx(VALUE obj)
3116{
3117 if (RB_TYPE_P(obj, T_STRING)) {
3118 obj = rb_str_to_inum(obj, 10, TRUE);
3119 }
3120
3121 return mulquov(num_exact(obj), INT2FIX(TIME_SCALE), INT2FIX(1000000));
3122}
3123
3124static uint32_t
3125month_arg(VALUE arg)
3126{
3127 int i, mon;
3128
3129 if (FIXNUM_P(arg)) {
3130 return obj2ubits(arg, 4);
3131 }
3132
3133 mon = 0;
3134 VALUE s = rb_check_string_type(arg);
3135 if (!NIL_P(s) && RSTRING_LEN(s) > 0) {
3136 arg = s;
3137 for (i=0; i<12; i++) {
3138 if (RSTRING_LEN(s) == 3 &&
3139 STRNCASECMP(months[i], RSTRING_PTR(s), 3) == 0) {
3140 mon = i+1;
3141 break;
3142 }
3143 }
3144 }
3145 if (mon == 0) {
3146 mon = obj2ubits(arg, 4);
3147 }
3148 return mon;
3149}
3150
3151static VALUE
3152validate_utc_offset(VALUE utc_offset)
3153{
3154 if (le(utc_offset, INT2FIX(-86400)) || ge(utc_offset, INT2FIX(86400)))
3155 rb_raise(rb_eArgError, "utc_offset out of range");
3156 return utc_offset;
3157}
3158
3159static VALUE
3160validate_zone_name(VALUE zone_name)
3161{
3162 StringValueCStr(zone_name);
3163 return zone_name;
3164}
3165
3166static void
3167validate_vtm(struct vtm *vtm)
3168{
3169#define validate_vtm_range(mem, b, e) \
3170 ((vtm->mem < b || vtm->mem > e) ? \
3171 rb_raise(rb_eArgError, #mem" out of range") : (void)0)
3172 validate_vtm_range(mon, 1, 12);
3173 validate_vtm_range(mday, 1, 31);
3174 validate_vtm_range(hour, 0, 24);
3175 validate_vtm_range(min, 0, (vtm->hour == 24 ? 0 : 59));
3176 validate_vtm_range(sec, 0, (vtm->hour == 24 ? 0 : 60));
3177 if (lt(vtm->subsecx, INT2FIX(0)) || ge(vtm->subsecx, INT2FIX(TIME_SCALE)))
3178 rb_raise(rb_eArgError, "subsecx out of range");
3179 if (!NIL_P(vtm->utc_offset)) validate_utc_offset(vtm->utc_offset);
3180#undef validate_vtm_range
3181}
3182
3183static void
3184time_arg(int argc, const VALUE *argv, struct vtm *vtm)
3185{
3186 VALUE v[8];
3187 VALUE subsecx = INT2FIX(0);
3188
3189 vtm->year = INT2FIX(0);
3190 vtm->mon = 0;
3191 vtm->mday = 0;
3192 vtm->hour = 0;
3193 vtm->min = 0;
3194 vtm->sec = 0;
3195 vtm->subsecx = INT2FIX(0);
3196 vtm->utc_offset = Qnil;
3197 vtm->wday = 0;
3198 vtm->yday = 0;
3199 vtm->isdst = 0;
3200 vtm->zone = str_empty;
3201
3202 if (argc == 10) {
3203 v[0] = argv[5];
3204 v[1] = argv[4];
3205 v[2] = argv[3];
3206 v[3] = argv[2];
3207 v[4] = argv[1];
3208 v[5] = argv[0];
3209 v[6] = Qnil;
3210 vtm->isdst = RTEST(argv[8]) ? 1 : 0;
3211 }
3212 else {
3213 rb_scan_args(argc, argv, "17", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6],&v[7]);
3214 /* v[6] may be usec or zone (parsedate) */
3215 /* v[7] is wday (parsedate; ignored) */
3216 vtm->wday = VTM_WDAY_INITVAL;
3217 vtm->isdst = VTM_ISDST_INITVAL;
3218 }
3219
3220 vtm->year = obj2vint(v[0]);
3221
3222 if (NIL_P(v[1])) {
3223 vtm->mon = 1;
3224 }
3225 else {
3226 vtm->mon = month_arg(v[1]);
3227 }
3228
3229 if (NIL_P(v[2])) {
3230 vtm->mday = 1;
3231 }
3232 else {
3233 vtm->mday = obj2ubits(v[2], 5);
3234 }
3235
3236 /* normalize month-mday */
3237 switch (vtm->mon) {
3238 case 2:
3239 {
3240 /* this drops higher bits but it's not a problem to calc leap year */
3241 unsigned int mday2 = leap_year_v_p(vtm->year) ? 29 : 28;
3242 if (vtm->mday > mday2) {
3243 vtm->mday -= mday2;
3244 vtm->mon++;
3245 }
3246 }
3247 break;
3248 case 4:
3249 case 6:
3250 case 9:
3251 case 11:
3252 if (vtm->mday == 31) {
3253 vtm->mon++;
3254 vtm->mday = 1;
3255 }
3256 break;
3257 }
3258
3259 vtm->hour = NIL_P(v[3])?0:obj2ubits(v[3], 5);
3260
3261 vtm->min = NIL_P(v[4])?0:obj2ubits(v[4], 6);
3262
3263 if (!NIL_P(v[6]) && argc == 7) {
3264 vtm->sec = NIL_P(v[5])?0:obj2ubits(v[5],6);
3265 subsecx = usec2subsecx(v[6]);
3266 }
3267 else {
3268 /* when argc == 8, v[6] is timezone, but ignored */
3269 if (NIL_P(v[5])) {
3270 vtm->sec = 0;
3271 }
3272 else {
3273 vtm->sec = obj2subsecx(v[5], &subsecx);
3274 }
3275 }
3276 vtm->subsecx = subsecx;
3277
3278 validate_vtm(vtm);
3279 RB_GC_GUARD(subsecx);
3280}
3281
3282static int
3283leap_year_p(long y)
3284{
3285 /* TODO:
3286 * ensure about negative years in proleptic Gregorian calendar.
3287 */
3288 unsigned long uy = (unsigned long)(LIKELY(y >= 0) ? y : -y);
3289
3290 if (LIKELY(uy % 4 != 0)) return 0;
3291
3292 unsigned long century = uy / 100;
3293 if (LIKELY(uy != century * 100)) return 1;
3294 return century % 4 == 0;
3295}
3296
3297static time_t
3298timegm_noleapsecond(struct tm *tm)
3299{
3300 long tm_year = tm->tm_year;
3301 int tm_yday = calc_tm_yday(tm->tm_year, tm->tm_mon, tm->tm_mday);
3302
3303 /*
3304 * `Seconds Since the Epoch' in SUSv3:
3305 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
3306 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
3307 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
3308 */
3309 return tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 +
3310 (time_t)(tm_yday +
3311 (tm_year-70)*365 +
3312 DIV(tm_year-69,4) -
3313 DIV(tm_year-1,100) +
3314 DIV(tm_year+299,400))*86400;
3315}
3316
3317#if 0
3318#define DEBUG_FIND_TIME_NUMGUESS
3319#define DEBUG_GUESSRANGE
3320#endif
3321
3322static const bool debug_guessrange =
3323#ifdef DEBUG_GUESSRANGE
3324 true;
3325#else
3326 false;
3327#endif
3328
3329#define DEBUG_REPORT_GUESSRANGE \
3330 (debug_guessrange ? debug_report_guessrange(guess_lo, guess_hi) : (void)0)
3331
3332static inline void
3333debug_report_guessrange(time_t guess_lo, time_t guess_hi)
3334{
3335 time_t guess_diff = guess_hi - guess_lo;
3336 fprintf(stderr, "find time guess range: %"PRI_TIMET_PREFIX"d - "
3337 "%"PRI_TIMET_PREFIX"d : %"PRI_TIMET_PREFIX"u\n",
3338 guess_lo, guess_hi, guess_diff);
3339}
3340
3341static const bool debug_find_time_numguess =
3342#ifdef DEBUG_FIND_TIME_NUMGUESS
3343 true;
3344#else
3345 false;
3346#endif
3347
3348#define DEBUG_FIND_TIME_NUMGUESS_INC \
3349 (void)(debug_find_time_numguess && find_time_numguess++),
3350static unsigned long long find_time_numguess;
3351
3352static VALUE
3353find_time_numguess_getter(ID name, VALUE *data)
3354{
3355 unsigned long long *numguess = (void *)data;
3356 return ULL2NUM(*numguess);
3357}
3358
3359static const char *
3360find_time_t(struct tm *tptr, int utc_p, time_t *tp)
3361{
3362 time_t guess, guess0, guess_lo, guess_hi;
3363 struct tm *tm, tm0, tm_lo, tm_hi;
3364 int d;
3365 int find_dst;
3366 struct tm result;
3367 int status;
3368 int tptr_tm_yday;
3369
3370#define GUESS(p) (DEBUG_FIND_TIME_NUMGUESS_INC (utc_p ? gmtime_with_leapsecond((p), &result) : LOCALTIME((p), result)))
3371
3372 guess_lo = TIMET_MIN;
3373 guess_hi = TIMET_MAX;
3374
3375 find_dst = 0 < tptr->tm_isdst;
3376
3377 /* /etc/localtime might be changed. reload it. */
3378 update_tz();
3379
3380 tm0 = *tptr;
3381 if (tm0.tm_mon < 0) {
3382 tm0.tm_mon = 0;
3383 tm0.tm_mday = 1;
3384 tm0.tm_hour = 0;
3385 tm0.tm_min = 0;
3386 tm0.tm_sec = 0;
3387 }
3388 else if (11 < tm0.tm_mon) {
3389 tm0.tm_mon = 11;
3390 tm0.tm_mday = 31;
3391 tm0.tm_hour = 23;
3392 tm0.tm_min = 59;
3393 tm0.tm_sec = 60;
3394 }
3395 else if (tm0.tm_mday < 1) {
3396 tm0.tm_mday = 1;
3397 tm0.tm_hour = 0;
3398 tm0.tm_min = 0;
3399 tm0.tm_sec = 0;
3400 }
3401 else if ((d = days_in_month_in(1900 + tm0.tm_year)[tm0.tm_mon]) < tm0.tm_mday) {
3402 tm0.tm_mday = d;
3403 tm0.tm_hour = 23;
3404 tm0.tm_min = 59;
3405 tm0.tm_sec = 60;
3406 }
3407 else if (tm0.tm_hour < 0) {
3408 tm0.tm_hour = 0;
3409 tm0.tm_min = 0;
3410 tm0.tm_sec = 0;
3411 }
3412 else if (23 < tm0.tm_hour) {
3413 tm0.tm_hour = 23;
3414 tm0.tm_min = 59;
3415 tm0.tm_sec = 60;
3416 }
3417 else if (tm0.tm_min < 0) {
3418 tm0.tm_min = 0;
3419 tm0.tm_sec = 0;
3420 }
3421 else if (59 < tm0.tm_min) {
3422 tm0.tm_min = 59;
3423 tm0.tm_sec = 60;
3424 }
3425 else if (tm0.tm_sec < 0) {
3426 tm0.tm_sec = 0;
3427 }
3428 else if (60 < tm0.tm_sec) {
3429 tm0.tm_sec = 60;
3430 }
3431
3432 DEBUG_REPORT_GUESSRANGE;
3433 guess0 = guess = timegm_noleapsecond(&tm0);
3434 tm = GUESS(&guess);
3435 if (tm) {
3436 d = tmcmp(tptr, tm);
3437 if (d == 0) { goto found; }
3438 if (d < 0) {
3439 guess_hi = guess;
3440 guess -= 24 * 60 * 60;
3441 }
3442 else {
3443 guess_lo = guess;
3444 guess += 24 * 60 * 60;
3445 }
3446 DEBUG_REPORT_GUESSRANGE;
3447 if (guess_lo < guess && guess < guess_hi && (tm = GUESS(&guess)) != NULL) {
3448 d = tmcmp(tptr, tm);
3449 if (d == 0) { goto found; }
3450 if (d < 0)
3451 guess_hi = guess;
3452 else
3453 guess_lo = guess;
3454 DEBUG_REPORT_GUESSRANGE;
3455 }
3456 }
3457
3458 tm = GUESS(&guess_lo);
3459 if (!tm) goto error;
3460 d = tmcmp(tptr, tm);
3461 if (d < 0) goto out_of_range;
3462 if (d == 0) { guess = guess_lo; goto found; }
3463 tm_lo = *tm;
3464
3465 tm = GUESS(&guess_hi);
3466 if (!tm) goto error;
3467 d = tmcmp(tptr, tm);
3468 if (d > 0) goto out_of_range;
3469 if (d == 0) { guess = guess_hi; goto found; }
3470 tm_hi = *tm;
3471
3472 DEBUG_REPORT_GUESSRANGE;
3473
3474 status = 1;
3475
3476 while (guess_lo + 1 < guess_hi) {
3477 binsearch:
3478 if (status == 0) {
3479 guess = guess_lo / 2 + guess_hi / 2;
3480 if (guess <= guess_lo)
3481 guess = guess_lo + 1;
3482 else if (guess >= guess_hi)
3483 guess = guess_hi - 1;
3484 status = 1;
3485 }
3486 else {
3487 if (status == 1) {
3488 time_t guess0_hi = timegm_noleapsecond(&tm_hi);
3489 guess = guess_hi - (guess0_hi - guess0);
3490 if (guess == guess_hi) /* hh:mm:60 tends to cause this condition. */
3491 guess--;
3492 status = 2;
3493 }
3494 else if (status == 2) {
3495 time_t guess0_lo = timegm_noleapsecond(&tm_lo);
3496 guess = guess_lo + (guess0 - guess0_lo);
3497 if (guess == guess_lo)
3498 guess++;
3499 status = 0;
3500 }
3501 if (guess <= guess_lo || guess_hi <= guess) {
3502 /* Previous guess is invalid. try binary search. */
3503 if (debug_guessrange) {
3504 if (guess <= guess_lo) {
3505 fprintf(stderr, "too small guess: %"PRI_TIMET_PREFIX"d"\
3506 " <= %"PRI_TIMET_PREFIX"d\n", guess, guess_lo);
3507 }
3508 if (guess_hi <= guess) {
3509 fprintf(stderr, "too big guess: %"PRI_TIMET_PREFIX"d"\
3510 " <= %"PRI_TIMET_PREFIX"d\n", guess_hi, guess);
3511 }
3512 }
3513 status = 0;
3514 goto binsearch;
3515 }
3516 }
3517
3518 tm = GUESS(&guess);
3519 if (!tm) goto error;
3520
3521 d = tmcmp(tptr, tm);
3522
3523 if (d < 0) {
3524 guess_hi = guess;
3525 tm_hi = *tm;
3526 DEBUG_REPORT_GUESSRANGE;
3527 }
3528 else if (d > 0) {
3529 guess_lo = guess;
3530 tm_lo = *tm;
3531 DEBUG_REPORT_GUESSRANGE;
3532 }
3533 else {
3534 goto found;
3535 }
3536 }
3537
3538 /* Given argument has no corresponding time_t. Let's extrapolate. */
3539 /*
3540 * `Seconds Since the Epoch' in SUSv3:
3541 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
3542 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
3543 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
3544 */
3545
3546 tptr_tm_yday = calc_tm_yday(tptr->tm_year, tptr->tm_mon, tptr->tm_mday);
3547
3548 *tp = guess_lo +
3549 ((tptr->tm_year - tm_lo.tm_year) * 365 +
3550 DIV((tptr->tm_year-69), 4) -
3551 DIV((tptr->tm_year-1), 100) +
3552 DIV((tptr->tm_year+299), 400) -
3553 DIV((tm_lo.tm_year-69), 4) +
3554 DIV((tm_lo.tm_year-1), 100) -
3555 DIV((tm_lo.tm_year+299), 400) +
3556 tptr_tm_yday -
3557 tm_lo.tm_yday) * 86400 +
3558 (tptr->tm_hour - tm_lo.tm_hour) * 3600 +
3559 (tptr->tm_min - tm_lo.tm_min) * 60 +
3560 (tptr->tm_sec - (tm_lo.tm_sec == 60 ? 59 : tm_lo.tm_sec));
3561
3562 return NULL;
3563
3564 found:
3565 if (!utc_p) {
3566 /* If localtime is nonmonotonic, another result may exist. */
3567 time_t guess2;
3568 if (find_dst) {
3569 guess2 = guess - 2 * 60 * 60;
3570 tm = LOCALTIME(&guess2, result);
3571 if (tm) {
3572 if (tptr->tm_hour != (tm->tm_hour + 2) % 24 ||
3573 tptr->tm_min != tm->tm_min ||
3574 tptr->tm_sec != tm->tm_sec) {
3575 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
3576 (tm->tm_min - tptr->tm_min) * 60 +
3577 (tm->tm_sec - tptr->tm_sec);
3578 if (tptr->tm_mday != tm->tm_mday)
3579 guess2 += 24 * 60 * 60;
3580 if (guess != guess2) {
3581 tm = LOCALTIME(&guess2, result);
3582 if (tm && tmcmp(tptr, tm) == 0) {
3583 if (guess < guess2)
3584 *tp = guess;
3585 else
3586 *tp = guess2;
3587 return NULL;
3588 }
3589 }
3590 }
3591 }
3592 }
3593 else {
3594 guess2 = guess + 2 * 60 * 60;
3595 tm = LOCALTIME(&guess2, result);
3596 if (tm) {
3597 if ((tptr->tm_hour + 2) % 24 != tm->tm_hour ||
3598 tptr->tm_min != tm->tm_min ||
3599 tptr->tm_sec != tm->tm_sec) {
3600 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
3601 (tm->tm_min - tptr->tm_min) * 60 +
3602 (tm->tm_sec - tptr->tm_sec);
3603 if (tptr->tm_mday != tm->tm_mday)
3604 guess2 -= 24 * 60 * 60;
3605 if (guess != guess2) {
3606 tm = LOCALTIME(&guess2, result);
3607 if (tm && tmcmp(tptr, tm) == 0) {
3608 if (guess < guess2)
3609 *tp = guess2;
3610 else
3611 *tp = guess;
3612 return NULL;
3613 }
3614 }
3615 }
3616 }
3617 }
3618 }
3619 *tp = guess;
3620 return NULL;
3621
3622 out_of_range:
3623 return "time out of range";
3624
3625 error:
3626 return "gmtime/localtime error";
3627}
3628
3629static int
3630vtmcmp(struct vtm *a, struct vtm *b)
3631{
3632 if (ne(a->year, b->year))
3633 return lt(a->year, b->year) ? -1 : 1;
3634 else if (a->mon != b->mon)
3635 return a->mon < b->mon ? -1 : 1;
3636 else if (a->mday != b->mday)
3637 return a->mday < b->mday ? -1 : 1;
3638 else if (a->hour != b->hour)
3639 return a->hour < b->hour ? -1 : 1;
3640 else if (a->min != b->min)
3641 return a->min < b->min ? -1 : 1;
3642 else if (a->sec != b->sec)
3643 return a->sec < b->sec ? -1 : 1;
3644 else if (ne(a->subsecx, b->subsecx))
3645 return lt(a->subsecx, b->subsecx) ? -1 : 1;
3646 else
3647 return 0;
3648}
3649
3650static int
3651tmcmp(struct tm *a, struct tm *b)
3652{
3653 if (a->tm_year != b->tm_year)
3654 return a->tm_year < b->tm_year ? -1 : 1;
3655 else if (a->tm_mon != b->tm_mon)
3656 return a->tm_mon < b->tm_mon ? -1 : 1;
3657 else if (a->tm_mday != b->tm_mday)
3658 return a->tm_mday < b->tm_mday ? -1 : 1;
3659 else if (a->tm_hour != b->tm_hour)
3660 return a->tm_hour < b->tm_hour ? -1 : 1;
3661 else if (a->tm_min != b->tm_min)
3662 return a->tm_min < b->tm_min ? -1 : 1;
3663 else if (a->tm_sec != b->tm_sec)
3664 return a->tm_sec < b->tm_sec ? -1 : 1;
3665 else
3666 return 0;
3667}
3668
3669/*
3670 * call-seq:
3671 * Time.utc(year, month = 1, mday = 1, hour = 0, min = 0, sec = 0, usec = 0) -> new_time
3672 * Time.utc(sec, min, hour, mday, month, year, dummy, dummy, dummy, dummy) -> new_time
3673 *
3674 * Returns a new +Time+ object based the on given arguments,
3675 * in the UTC timezone.
3676 *
3677 * With one to seven arguments given,
3678 * the arguments are interpreted as in the first calling sequence above:
3679 *
3680 * Time.utc(year, month = 1, mday = 1, hour = 0, min = 0, sec = 0, usec = 0)
3681 *
3682 * Examples:
3683 *
3684 * Time.utc(2000) # => 2000-01-01 00:00:00 UTC
3685 * Time.utc(-2000) # => -2000-01-01 00:00:00 UTC
3686 *
3687 * There are no minimum and maximum values for the required argument +year+.
3688 *
3689 * For the optional arguments:
3690 *
3691 * - +month+: Month in range (1..12), or case-insensitive
3692 * 3-letter month name:
3693 *
3694 * Time.utc(2000, 1) # => 2000-01-01 00:00:00 UTC
3695 * Time.utc(2000, 12) # => 2000-12-01 00:00:00 UTC
3696 * Time.utc(2000, 'jan') # => 2000-01-01 00:00:00 UTC
3697 * Time.utc(2000, 'JAN') # => 2000-01-01 00:00:00 UTC
3698 *
3699 * - +mday+: Month day in range(1..31):
3700 *
3701 * Time.utc(2000, 1, 1) # => 2000-01-01 00:00:00 UTC
3702 * Time.utc(2000, 1, 31) # => 2000-01-31 00:00:00 UTC
3703 *
3704 * - +hour+: Hour in range (0..23), or 24 if +min+, +sec+, and +usec+
3705 * are zero:
3706 *
3707 * Time.utc(2000, 1, 1, 0) # => 2000-01-01 00:00:00 UTC
3708 * Time.utc(2000, 1, 1, 23) # => 2000-01-01 23:00:00 UTC
3709 * Time.utc(2000, 1, 1, 24) # => 2000-01-02 00:00:00 UTC
3710 *
3711 * - +min+: Minute in range (0..59):
3712 *
3713 * Time.utc(2000, 1, 1, 0, 0) # => 2000-01-01 00:00:00 UTC
3714 * Time.utc(2000, 1, 1, 0, 59) # => 2000-01-01 00:59:00 UTC
3715 *
3716 * - +sec+: Second in range (0..59), or 60 if +usec+ is zero:
3717 *
3718 * Time.utc(2000, 1, 1, 0, 0, 0) # => 2000-01-01 00:00:00 UTC
3719 * Time.utc(2000, 1, 1, 0, 0, 59) # => 2000-01-01 00:00:59 UTC
3720 * Time.utc(2000, 1, 1, 0, 0, 60) # => 2000-01-01 00:01:00 UTC
3721 *
3722 * - +usec+: Microsecond in range (0..999999):
3723 *
3724 * Time.utc(2000, 1, 1, 0, 0, 0, 0) # => 2000-01-01 00:00:00 UTC
3725 * Time.utc(2000, 1, 1, 0, 0, 0, 999999) # => 2000-01-01 00:00:00.999999 UTC
3726 *
3727 * The values may be:
3728 *
3729 * - Integers, as above.
3730 * - Numerics convertible to integers:
3731 *
3732 * Time.utc(Float(0.0), Rational(1, 1), 1.0, 0.0, 0.0, 0.0, 0.0)
3733 * # => 0000-01-01 00:00:00 UTC
3734 *
3735 * - String integers:
3736 *
3737 * a = %w[0 1 1 0 0 0 0 0]
3738 * # => ["0", "1", "1", "0", "0", "0", "0", "0"]
3739 * Time.utc(*a) # => 0000-01-01 00:00:00 UTC
3740 *
3741 * When exactly ten arguments are given,
3742 * the arguments are interpreted as in the second calling sequence above:
3743 *
3744 * Time.utc(sec, min, hour, mday, month, year, dummy, dummy, dummy, dummy)
3745 *
3746 * where the +dummy+ arguments are ignored:
3747 *
3748 * a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3749 * # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3750 * Time.utc(*a) # => 0005-04-03 02:01:00 UTC
3751 *
3752 * This form is useful for creating a +Time+ object from a 10-element
3753 * array returned by Time.to_a:
3754 *
3755 * t = Time.new(2000, 1, 2, 3, 4, 5, 6) # => 2000-01-02 03:04:05 +000006
3756 * a = t.to_a # => [5, 4, 3, 2, 1, 2000, 0, 2, false, nil]
3757 * Time.utc(*a) # => 2000-01-02 03:04:05 UTC
3758 *
3759 * The two forms have their first six arguments in common,
3760 * though in different orders;
3761 * the ranges of these common arguments are the same for both forms; see above.
3762 *
3763 * Raises an exception if the number of arguments is eight, nine,
3764 * or greater than ten.
3765 *
3766 * Related: Time.local.
3767 *
3768 */
3769static VALUE
3770time_s_mkutc(int argc, VALUE *argv, VALUE klass)
3771{
3772 struct vtm vtm;
3773
3774 time_arg(argc, argv, &vtm);
3775 return time_gmtime(time_new_timew(klass, timegmw(&vtm)));
3776}
3777
3778/*
3779 * call-seq:
3780 * Time.local(year, month = 1, mday = 1, hour = 0, min = 0, sec = 0, usec = 0) -> new_time
3781 * Time.local(sec, min, hour, mday, month, year, dummy, dummy, dummy, dummy) -> new_time
3782 *
3783 * Like Time.utc, except that the returned +Time+ object
3784 * has the local timezone, not the UTC timezone:
3785 *
3786 * # With seven arguments.
3787 * Time.local(0, 1, 2, 3, 4, 5, 6)
3788 * # => 0000-01-02 03:04:05.000006 -0600
3789 * # With exactly ten arguments.
3790 * Time.local(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
3791 * # => 0005-04-03 02:01:00 -0600
3792 *
3793 */
3794
3795static VALUE
3796time_s_mktime(int argc, VALUE *argv, VALUE klass)
3797{
3798 struct vtm vtm;
3799
3800 time_arg(argc, argv, &vtm);
3801 return time_localtime(time_new_timew(klass, timelocalw(&vtm)));
3802}
3803
3804/*
3805 * call-seq:
3806 * to_i -> integer
3807 *
3808 * Returns the value of +self+ as integer
3809 * {Epoch seconds}[rdoc-ref:Time@Epoch+Seconds];
3810 * subseconds are truncated (not rounded):
3811 *
3812 * Time.utc(1970, 1, 1, 0, 0, 0).to_i # => 0
3813 * Time.utc(1970, 1, 1, 0, 0, 0, 999999).to_i # => 0
3814 * Time.utc(1950, 1, 1, 0, 0, 0).to_i # => -631152000
3815 * Time.utc(1990, 1, 1, 0, 0, 0).to_i # => 631152000
3816 *
3817 * Related: Time#to_f Time#to_r.
3818 */
3819
3820static VALUE
3821time_to_i(VALUE time)
3822{
3823 struct time_object *tobj;
3824
3825 GetTimeval(time, tobj);
3826 return w2v(wdiv(tobj->timew, WINT2FIXWV(TIME_SCALE)));
3827}
3828
3829/*
3830 * call-seq:
3831 * to_f -> float
3832 *
3833 * Returns the value of +self+ as a Float number
3834 * {Epoch seconds}[rdoc-ref:Time@Epoch+Seconds];
3835 * subseconds are included.
3836 *
3837 * The stored value of +self+ is a
3838 * {Rational}[rdoc-ref:Rational@#method-i-to_f],
3839 * which means that the returned value may be approximate:
3840 *
3841 * Time.utc(1970, 1, 1, 0, 0, 0).to_f # => 0.0
3842 * Time.utc(1970, 1, 1, 0, 0, 0, 999999).to_f # => 0.999999
3843 * Time.utc(1950, 1, 1, 0, 0, 0).to_f # => -631152000.0
3844 * Time.utc(1990, 1, 1, 0, 0, 0).to_f # => 631152000.0
3845 *
3846 * Related: Time#to_i, Time#to_r.
3847 */
3848
3849static VALUE
3850time_to_f(VALUE time)
3851{
3852 struct time_object *tobj;
3853
3854 GetTimeval(time, tobj);
3855 return rb_Float(rb_time_unmagnify_to_float(tobj->timew));
3856}
3857
3858/*
3859 * call-seq:
3860 * to_r -> rational
3861 *
3862 * Returns the value of +self+ as a Rational exact number of
3863 * {Epoch seconds}[rdoc-ref:Time@Epoch+Seconds];
3864 *
3865 * Time.now.to_r # => (16571402750320203/10000000)
3866 *
3867 * Related: Time#to_f, Time#to_i.
3868 */
3869
3870static VALUE
3871time_to_r(VALUE time)
3872{
3873 struct time_object *tobj;
3874 VALUE v;
3875
3876 GetTimeval(time, tobj);
3877 v = rb_time_unmagnify_to_rational(tobj->timew);
3878 if (!RB_TYPE_P(v, T_RATIONAL)) {
3879 v = rb_Rational1(v);
3880 }
3881 return v;
3882}
3883
3884/*
3885 * call-seq:
3886 * usec -> integer
3887 *
3888 * Returns the number of microseconds in the subseconds part of +self+
3889 * in the range (0..999_999);
3890 * lower-order digits are truncated, not rounded:
3891 *
3892 * t = Time.now # => 2022-07-11 14:59:47.5484697 -0500
3893 * t.usec # => 548469
3894 *
3895 * Related: Time#subsec (returns exact subseconds).
3896 */
3897
3898static VALUE
3899time_usec(VALUE time)
3900{
3901 struct time_object *tobj;
3902 wideval_t w, q, r;
3903
3904 GetTimeval(time, tobj);
3905
3906 w = wmod(tobj->timew, WINT2WV(TIME_SCALE));
3907 wmuldivmod(w, WINT2FIXWV(1000000), WINT2FIXWV(TIME_SCALE), &q, &r);
3908 return rb_to_int(w2v(q));
3909}
3910
3911/*
3912 * call-seq:
3913 * nsec -> integer
3914 *
3915 * Returns the number of nanoseconds in the subseconds part of +self+
3916 * in the range (0..999_999_999);
3917 * lower-order digits are truncated, not rounded:
3918 *
3919 * t = Time.now # => 2022-07-11 15:04:53.3219637 -0500
3920 * t.nsec # => 321963700
3921 *
3922 * Related: Time#subsec (returns exact subseconds).
3923 */
3924
3925static VALUE
3926time_nsec(VALUE time)
3927{
3928 struct time_object *tobj;
3929
3930 GetTimeval(time, tobj);
3931 return rb_to_int(w2v(wmulquoll(wmod(tobj->timew, WINT2WV(TIME_SCALE)), 1000000000, TIME_SCALE)));
3932}
3933
3934/*
3935 * call-seq:
3936 * subsec -> numeric
3937 *
3938 * Returns the exact subseconds for +self+ as a Numeric
3939 * (Integer or Rational):
3940 *
3941 * t = Time.now # => 2022-07-11 15:11:36.8490302 -0500
3942 * t.subsec # => (4245151/5000000)
3943 *
3944 * If the subseconds is zero, returns integer zero:
3945 *
3946 * t = Time.new(2000, 1, 1, 2, 3, 4) # => 2000-01-01 02:03:04 -0600
3947 * t.subsec # => 0
3948 *
3949 */
3950
3951static VALUE
3952time_subsec(VALUE time)
3953{
3954 struct time_object *tobj;
3955
3956 GetTimeval(time, tobj);
3957 return quov(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE));
3958}
3959
3960/*
3961 * call-seq:
3962 * self <=> other_time -> -1, 0, +1, or nil
3963 *
3964 * Compares +self+ with +other_time+; returns:
3965 *
3966 * - +-1+, if +self+ is less than +other_time+.
3967 * - +0+, if +self+ is equal to +other_time+.
3968 * - +1+, if +self+ is greater then +other_time+.
3969 * - +nil+, if +self+ and +other_time+ are incomparable.
3970 *
3971 * Examples:
3972 *
3973 * t = Time.now # => 2007-11-19 08:12:12 -0600
3974 * t2 = t + 2592000 # => 2007-12-19 08:12:12 -0600
3975 * t <=> t2 # => -1
3976 * t2 <=> t # => 1
3977 *
3978 * t = Time.now # => 2007-11-19 08:13:38 -0600
3979 * t2 = t + 0.1 # => 2007-11-19 08:13:38 -0600
3980 * t.nsec # => 98222999
3981 * t2.nsec # => 198222999
3982 * t <=> t2 # => -1
3983 * t2 <=> t # => 1
3984 * t <=> t # => 0
3985 *
3986 */
3987
3988static VALUE
3989time_cmp(VALUE time1, VALUE time2)
3990{
3991 struct time_object *tobj1, *tobj2;
3992 int n;
3993
3994 GetTimeval(time1, tobj1);
3995 if (IsTimeval(time2)) {
3996 GetTimeval(time2, tobj2);
3997 n = wcmp(tobj1->timew, tobj2->timew);
3998 }
3999 else {
4000 return rb_invcmp(time1, time2);
4001 }
4002 if (n == 0) return INT2FIX(0);
4003 if (n > 0) return INT2FIX(1);
4004 return INT2FIX(-1);
4005}
4006
4007/*
4008 * call-seq:
4009 * eql?(other_time)
4010 *
4011 * Returns +true+ if +self+ and +other_time+ are
4012 * both +Time+ objects with the exact same time value.
4013 */
4014
4015static VALUE
4016time_eql(VALUE time1, VALUE time2)
4017{
4018 struct time_object *tobj1, *tobj2;
4019
4020 GetTimeval(time1, tobj1);
4021 if (IsTimeval(time2)) {
4022 GetTimeval(time2, tobj2);
4023 return rb_equal(w2v(tobj1->timew), w2v(tobj2->timew));
4024 }
4025 return Qfalse;
4026}
4027
4028/*
4029 * call-seq:
4030 * utc? -> true or false
4031 *
4032 * Returns +true+ if +self+ represents a time in UTC (GMT):
4033 *
4034 * now = Time.now
4035 * # => 2022-08-18 10:24:13.5398485 -0500
4036 * now.utc? # => false
4037 * now.getutc.utc? # => true
4038 * utc = Time.utc(2000, 1, 1, 20, 15, 1)
4039 * # => 2000-01-01 20:15:01 UTC
4040 * utc.utc? # => true
4041 *
4042 * +Time+ objects created with these methods are considered to be in
4043 * UTC:
4044 *
4045 * * Time.utc
4046 * * Time#utc
4047 * * Time#getutc
4048 *
4049 * Objects created in other ways will not be treated as UTC even if
4050 * the environment variable "TZ" is "UTC".
4051 *
4052 * Related: Time.utc.
4053 */
4054
4055static VALUE
4056time_utc_p(VALUE time)
4057{
4058 struct time_object *tobj;
4059
4060 GetTimeval(time, tobj);
4061 return RBOOL(TZMODE_UTC_P(tobj));
4062}
4063
4064/*
4065 * call-seq:
4066 * hash -> integer
4067 *
4068 * Returns the integer hash code for +self+.
4069 *
4070 * Related: Object#hash.
4071 */
4072
4073static VALUE
4074time_hash(VALUE time)
4075{
4076 struct time_object *tobj;
4077
4078 GetTimeval(time, tobj);
4079 return rb_hash(w2v(tobj->timew));
4080}
4081
4082/* :nodoc: */
4083static VALUE
4084time_init_copy(VALUE copy, VALUE time)
4085{
4086 struct time_object *tobj, *tcopy;
4087
4088 if (!OBJ_INIT_COPY(copy, time)) return copy;
4089 GetTimeval(time, tobj);
4090 GetNewTimeval(copy, tcopy);
4091
4092 time_set_timew(copy, tcopy, tobj->timew);
4093 time_set_vtm(copy, tcopy, tobj->vtm);
4094
4095 return copy;
4096}
4097
4098static VALUE
4099time_dup(VALUE time)
4100{
4101 VALUE dup = time_s_alloc(rb_obj_class(time));
4102 time_init_copy(dup, time);
4103 return dup;
4104}
4105
4106static VALUE
4107time_localtime(VALUE time)
4108{
4109 struct time_object *tobj;
4110 struct vtm vtm;
4111 VALUE zone;
4112
4113 GetTimeval(time, tobj);
4114 if (TZMODE_LOCALTIME_P(tobj)) {
4115 if (tobj->vtm.tm_got)
4116 return time;
4117 }
4118 else {
4119 time_modify(time);
4120 }
4121
4122 zone = tobj->vtm.zone;
4123 if (maybe_tzobj_p(zone) && zone_localtime(zone, time)) {
4124 return time;
4125 }
4126
4127 if (!localtimew(tobj->timew, &vtm))
4128 rb_raise(rb_eArgError, "localtime error");
4129 time_set_vtm(time, tobj, vtm);
4130
4131 tobj->vtm.tm_got = 1;
4132 TZMODE_SET_LOCALTIME(tobj);
4133 return time;
4134}
4135
4136static VALUE
4137time_zonelocal(VALUE time, VALUE off)
4138{
4139 VALUE zone = off;
4140 if (zone_localtime(zone, time)) return time;
4141
4142 if (NIL_P(off = utc_offset_arg(off))) {
4143 off = zone;
4144 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
4145 if (!zone_localtime(zone, time)) invalid_utc_offset(off);
4146 return time;
4147 }
4148 else if (off == UTC_ZONE) {
4149 return time_gmtime(time);
4150 }
4151 validate_utc_offset(off);
4152
4153 time_set_utc_offset(time, off);
4154 return time_fixoff(time);
4155}
4156
4157/*
4158 * call-seq:
4159 * localtime -> self or new_time
4160 * localtime(zone) -> new_time
4161 *
4162 * With no argument given:
4163 *
4164 * - Returns +self+ if +self+ is a local time.
4165 * - Otherwise returns a new +Time+ in the user's local timezone:
4166 *
4167 * t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
4168 * t.localtime # => 2000-01-01 14:15:01 -0600
4169 *
4170 * With argument +zone+ given,
4171 * returns the new +Time+ object created by converting
4172 * +self+ to the given time zone:
4173 *
4174 * t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC
4175 * t.localtime("-09:00") # => 2000-01-01 11:15:01 -0900
4176 *
4177 * For forms of argument +zone+, see
4178 * {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers].
4179 *
4180 */
4181
4182static VALUE
4183time_localtime_m(int argc, VALUE *argv, VALUE time)
4184{
4185 VALUE off;
4186
4187 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
4188 return time_zonelocal(time, off);
4189 }
4190
4191 return time_localtime(time);
4192}
4193
4194/*
4195 * call-seq:
4196 * utc -> self
4197 *
4198 * Returns +self+, converted to the UTC timezone:
4199 *
4200 * t = Time.new(2000) # => 2000-01-01 00:00:00 -0600
4201 * t.utc? # => false
4202 * t.utc # => 2000-01-01 06:00:00 UTC
4203 * t.utc? # => true
4204 *
4205 * Related: Time#getutc (returns a new converted +Time+ object).
4206 */
4207
4208static VALUE
4209time_gmtime(VALUE time)
4210{
4211 struct time_object *tobj;
4212 struct vtm vtm;
4213
4214 GetTimeval(time, tobj);
4215 if (TZMODE_UTC_P(tobj)) {
4216 if (tobj->vtm.tm_got)
4217 return time;
4218 }
4219 else {
4220 time_modify(time);
4221 }
4222
4223 vtm.zone = str_utc;
4224 GMTIMEW(tobj->timew, &vtm);
4225 time_set_vtm(time, tobj, vtm);
4226
4227 tobj->vtm.tm_got = 1;
4228 TZMODE_SET_UTC(tobj);
4229 return time;
4230}
4231
4232static VALUE
4233time_fixoff(VALUE time)
4234{
4235 struct time_object *tobj;
4236 struct vtm vtm;
4237 VALUE off, zone;
4238
4239 GetTimeval(time, tobj);
4240 if (TZMODE_FIXOFF_P(tobj)) {
4241 if (tobj->vtm.tm_got)
4242 return time;
4243 }
4244 else {
4245 time_modify(time);
4246 }
4247
4248 if (TZMODE_FIXOFF_P(tobj))
4249 off = tobj->vtm.utc_offset;
4250 else
4251 off = INT2FIX(0);
4252
4253 GMTIMEW(tobj->timew, &vtm);
4254
4255 zone = tobj->vtm.zone;
4256 vtm_add_offset(&vtm, off, +1);
4257
4258 time_set_vtm(time, tobj, vtm);
4259 RB_OBJ_WRITE_UNALIGNED(time, &tobj->vtm.zone, zone);
4260
4261 tobj->vtm.tm_got = 1;
4262 TZMODE_SET_FIXOFF(time, tobj, off);
4263 return time;
4264}
4265
4266/*
4267 * call-seq:
4268 * getlocal(zone = nil) -> new_time
4269 *
4270 * Returns a new +Time+ object representing the value of +self+
4271 * converted to a given timezone;
4272 * if +zone+ is +nil+, the local timezone is used:
4273 *
4274 * t = Time.utc(2000) # => 2000-01-01 00:00:00 UTC
4275 * t.getlocal # => 1999-12-31 18:00:00 -0600
4276 * t.getlocal('+12:00') # => 2000-01-01 12:00:00 +1200
4277 *
4278 * For forms of argument +zone+, see
4279 * {Timezone Specifiers}[rdoc-ref:Time@Timezone+Specifiers].
4280 *
4281 */
4282
4283static VALUE
4284time_getlocaltime(int argc, VALUE *argv, VALUE time)
4285{
4286 VALUE off;
4287
4288 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
4289 VALUE zone = off;
4290 if (maybe_tzobj_p(zone)) {
4291 VALUE t = time_dup(time);
4292 if (zone_localtime(off, t)) return t;
4293 }
4294
4295 if (NIL_P(off = utc_offset_arg(off))) {
4296 off = zone;
4297 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
4298 time = time_dup(time);
4299 if (!zone_localtime(zone, time)) invalid_utc_offset(off);
4300 return time;
4301 }
4302 else if (off == UTC_ZONE) {
4303 return time_gmtime(time_dup(time));
4304 }
4305 validate_utc_offset(off);
4306
4307 time = time_dup(time);
4308 time_set_utc_offset(time, off);
4309 return time_fixoff(time);
4310 }
4311
4312 return time_localtime(time_dup(time));
4313}
4314
4315/*
4316 * call-seq:
4317 * getutc -> new_time
4318 *
4319 * Returns a new +Time+ object representing the value of +self+
4320 * converted to the UTC timezone:
4321 *
4322 * local = Time.local(2000) # => 2000-01-01 00:00:00 -0600
4323 * local.utc? # => false
4324 * utc = local.getutc # => 2000-01-01 06:00:00 UTC
4325 * utc.utc? # => true
4326 * utc == local # => true
4327 *
4328 */
4329
4330static VALUE
4331time_getgmtime(VALUE time)
4332{
4333 return time_gmtime(time_dup(time));
4334}
4335
4336static VALUE
4337time_get_tm(VALUE time, struct time_object *tobj)
4338{
4339 if (TZMODE_UTC_P(tobj)) return time_gmtime(time);
4340 if (TZMODE_FIXOFF_P(tobj)) return time_fixoff(time);
4341 return time_localtime(time);
4342}
4343
4344static VALUE strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc);
4345#define strftimev(fmt, time, enc) strftime_cstr((fmt), rb_strlen_lit(fmt), (time), (enc))
4346
4347/*
4348 * call-seq:
4349 * ctime -> string
4350 *
4351 * Returns a string representation of +self+,
4352 * formatted by <tt>strftime('%a %b %e %T %Y')</tt>
4353 * or its shorthand version <tt>strftime('%c')</tt>;
4354 * see {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc]:
4355 *
4356 * t = Time.new(2000, 12, 31, 23, 59, 59, 0.5)
4357 * t.ctime # => "Sun Dec 31 23:59:59 2000"
4358 * t.strftime('%a %b %e %T %Y') # => "Sun Dec 31 23:59:59 2000"
4359 * t.strftime('%c') # => "Sun Dec 31 23:59:59 2000"
4360 *
4361 * Related: Time#to_s, Time#inspect:
4362 *
4363 * t.inspect # => "2000-12-31 23:59:59.5 +000001"
4364 * t.to_s # => "2000-12-31 23:59:59 +0000"
4365 *
4366 */
4367
4368static VALUE
4369time_asctime(VALUE time)
4370{
4371 return strftimev("%a %b %e %T %Y", time, rb_usascii_encoding());
4372}
4373
4374/*
4375 * call-seq:
4376 * to_s -> string
4377 *
4378 * Returns a string representation of +self+, without subseconds:
4379 *
4380 * t = Time.new(2000, 12, 31, 23, 59, 59, 0.5)
4381 * t.to_s # => "2000-12-31 23:59:59 +0000"
4382 *
4383 * Related: Time#ctime, Time#inspect:
4384 *
4385 * t.ctime # => "Sun Dec 31 23:59:59 2000"
4386 * t.inspect # => "2000-12-31 23:59:59.5 +000001"
4387 *
4388 */
4389
4390static VALUE
4391time_to_s(VALUE time)
4392{
4393 struct time_object *tobj;
4394
4395 GetTimeval(time, tobj);
4396 if (TZMODE_UTC_P(tobj))
4397 return strftimev("%Y-%m-%d %H:%M:%S UTC", time, rb_usascii_encoding());
4398 else
4399 return strftimev("%Y-%m-%d %H:%M:%S %z", time, rb_usascii_encoding());
4400}
4401
4402/*
4403 * call-seq:
4404 * inspect -> string
4405 *
4406 * Returns a string representation of +self+ with subseconds:
4407 *
4408 * t = Time.new(2000, 12, 31, 23, 59, 59, 0.5)
4409 * t.inspect # => "2000-12-31 23:59:59.5 +000001"
4410 *
4411 * Related: Time#ctime, Time#to_s:
4412 *
4413 * t.ctime # => "Sun Dec 31 23:59:59 2000"
4414 * t.to_s # => "2000-12-31 23:59:59 +0000"
4415 *
4416 */
4417
4418static VALUE
4419time_inspect(VALUE time)
4420{
4421 struct time_object *tobj;
4422 VALUE str, subsec;
4423
4424 GetTimeval(time, tobj);
4425 str = strftimev("%Y-%m-%d %H:%M:%S", time, rb_usascii_encoding());
4426 subsec = w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE)));
4427 if (subsec == INT2FIX(0)) {
4428 }
4429 else if (FIXNUM_P(subsec) && FIX2LONG(subsec) < TIME_SCALE) {
4430 long len;
4431 rb_str_catf(str, ".%09ld", FIX2LONG(subsec));
4432 for (len=RSTRING_LEN(str); RSTRING_PTR(str)[len-1] == '0' && len > 0; len--)
4433 ;
4434 rb_str_resize(str, len);
4435 }
4436 else {
4437 rb_str_cat_cstr(str, " ");
4438 subsec = quov(subsec, INT2FIX(TIME_SCALE));
4439 rb_str_concat(str, rb_obj_as_string(subsec));
4440 }
4441 if (TZMODE_UTC_P(tobj)) {
4442 rb_str_cat_cstr(str, " UTC");
4443 }
4444 else {
4445 /* ?TODO: subsecond offset */
4446 long off = NUM2LONG(rb_funcall(tobj->vtm.utc_offset, rb_intern("round"), 0));
4447 char sign = (off < 0) ? (off = -off, '-') : '+';
4448 int sec = off % 60;
4449 int min = (off /= 60) % 60;
4450 off /= 60;
4451 rb_str_catf(str, " %c%.2d%.2d", sign, (int)off, min);
4452 if (sec) rb_str_catf(str, "%.2d", sec);
4453 }
4454 return str;
4455}
4456
4457static VALUE
4458time_add0(VALUE klass, const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4459{
4460 VALUE result;
4461 struct time_object *result_tobj;
4462
4463 offset = num_exact(offset);
4464 if (sign < 0)
4465 result = time_new_timew(klass, wsub(tobj->timew, rb_time_magnify(v2w(offset))));
4466 else
4467 result = time_new_timew(klass, wadd(tobj->timew, rb_time_magnify(v2w(offset))));
4468 GetTimeval(result, result_tobj);
4469 TZMODE_COPY(result_tobj, tobj);
4470
4471 return result;
4472}
4473
4474static VALUE
4475time_add(const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4476{
4477 return time_add0(rb_cTime, tobj, torig, offset, sign);
4478}
4479
4480/*
4481 * call-seq:
4482 * self + numeric -> new_time
4483 *
4484 * Returns a new +Time+ object whose value is the sum of the numeric value
4485 * of +self+ and the given +numeric+:
4486 *
4487 * t = Time.new(2000) # => 2000-01-01 00:00:00 -0600
4488 * t + (60 * 60 * 24) # => 2000-01-02 00:00:00 -0600
4489 * t + 0.5 # => 2000-01-01 00:00:00.5 -0600
4490 *
4491 * Related: Time#-.
4492 */
4493
4494static VALUE
4495time_plus(VALUE time1, VALUE time2)
4496{
4497 struct time_object *tobj;
4498 GetTimeval(time1, tobj);
4499
4500 if (IsTimeval(time2)) {
4501 rb_raise(rb_eTypeError, "time + time?");
4502 }
4503 return time_add(tobj, time1, time2, 1);
4504}
4505
4506/*
4507 * call-seq:
4508 * self - numeric -> new_time
4509 * self - other_time -> float
4510 *
4511 * When +numeric+ is given,
4512 * returns a new +Time+ object whose value is the difference
4513 * of the numeric value of +self+ and +numeric+:
4514 *
4515 * t = Time.new(2000) # => 2000-01-01 00:00:00 -0600
4516 * t - (60 * 60 * 24) # => 1999-12-31 00:00:00 -0600
4517 * t - 0.5 # => 1999-12-31 23:59:59.5 -0600
4518 *
4519 * When +other_time+ is given,
4520 * returns a Float whose value is the difference
4521 * of the numeric values of +self+ and +other_time+ in seconds:
4522 *
4523 * t - t # => 0.0
4524 *
4525 * Related: Time#+.
4526 */
4527
4528static VALUE
4529time_minus(VALUE time1, VALUE time2)
4530{
4531 struct time_object *tobj;
4532
4533 GetTimeval(time1, tobj);
4534 if (IsTimeval(time2)) {
4535 struct time_object *tobj2;
4536
4537 GetTimeval(time2, tobj2);
4538 return rb_Float(rb_time_unmagnify_to_float(wsub(tobj->timew, tobj2->timew)));
4539 }
4540 return time_add(tobj, time1, time2, -1);
4541}
4542
4543static VALUE
4544ndigits_denominator(VALUE ndigits)
4545{
4546 long nd = NUM2LONG(ndigits);
4547
4548 if (nd < 0) {
4549 rb_raise(rb_eArgError, "negative ndigits given");
4550 }
4551 if (nd == 0) {
4552 return INT2FIX(1);
4553 }
4554 return rb_rational_new(INT2FIX(1),
4555 rb_int_positive_pow(10, (unsigned long)nd));
4556}
4557
4558/*
4559 * call-seq:
4560 * round(ndigits = 0) -> new_time
4561 *
4562 * Returns a new +Time+ object whose numeric value is that of +self+,
4563 * with its seconds value rounded to precision +ndigits+:
4564 *
4565 * t = Time.utc(2010, 3, 30, 5, 43, 25.123456789r)
4566 * t # => 2010-03-30 05:43:25.123456789 UTC
4567 * t.round # => 2010-03-30 05:43:25 UTC
4568 * t.round(0) # => 2010-03-30 05:43:25 UTC
4569 * t.round(1) # => 2010-03-30 05:43:25.1 UTC
4570 * t.round(2) # => 2010-03-30 05:43:25.12 UTC
4571 * t.round(3) # => 2010-03-30 05:43:25.123 UTC
4572 * t.round(4) # => 2010-03-30 05:43:25.1235 UTC
4573 *
4574 * t = Time.utc(1999, 12,31, 23, 59, 59)
4575 * t # => 1999-12-31 23:59:59 UTC
4576 * (t + 0.4).round # => 1999-12-31 23:59:59 UTC
4577 * (t + 0.49).round # => 1999-12-31 23:59:59 UTC
4578 * (t + 0.5).round # => 2000-01-01 00:00:00 UTC
4579 * (t + 1.4).round # => 2000-01-01 00:00:00 UTC
4580 * (t + 1.49).round # => 2000-01-01 00:00:00 UTC
4581 * (t + 1.5).round # => 2000-01-01 00:00:01 UTC
4582 *
4583 * Related: Time#ceil, Time#floor.
4584 */
4585
4586static VALUE
4587time_round(int argc, VALUE *argv, VALUE time)
4588{
4589 VALUE ndigits, v, den;
4590 struct time_object *tobj;
4591
4592 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4593 den = INT2FIX(1);
4594 else
4595 den = ndigits_denominator(ndigits);
4596
4597 GetTimeval(time, tobj);
4598 v = w2v(rb_time_unmagnify(tobj->timew));
4599
4600 v = modv(v, den);
4601 if (lt(v, quov(den, INT2FIX(2))))
4602 return time_add(tobj, time, v, -1);
4603 else
4604 return time_add(tobj, time, subv(den, v), 1);
4605}
4606
4607/*
4608 * call-seq:
4609 * floor(ndigits = 0) -> new_time
4610 *
4611 * Returns a new +Time+ object whose numerical value
4612 * is less than or equal to +self+ with its seconds
4613 * truncated to precision +ndigits+:
4614 *
4615 * t = Time.utc(2010, 3, 30, 5, 43, 25.123456789r)
4616 * t # => 2010-03-30 05:43:25.123456789 UTC
4617 * t.floor # => 2010-03-30 05:43:25 UTC
4618 * t.floor(2) # => 2010-03-30 05:43:25.12 UTC
4619 * t.floor(4) # => 2010-03-30 05:43:25.1234 UTC
4620 * t.floor(6) # => 2010-03-30 05:43:25.123456 UTC
4621 * t.floor(8) # => 2010-03-30 05:43:25.12345678 UTC
4622 * t.floor(10) # => 2010-03-30 05:43:25.123456789 UTC
4623 *
4624 * t = Time.utc(1999, 12, 31, 23, 59, 59)
4625 * t # => 1999-12-31 23:59:59 UTC
4626 * (t + 0.4).floor # => 1999-12-31 23:59:59 UTC
4627 * (t + 0.9).floor # => 1999-12-31 23:59:59 UTC
4628 * (t + 1.4).floor # => 2000-01-01 00:00:00 UTC
4629 * (t + 1.9).floor # => 2000-01-01 00:00:00 UTC
4630 *
4631 * Related: Time#ceil, Time#round.
4632 */
4633
4634static VALUE
4635time_floor(int argc, VALUE *argv, VALUE time)
4636{
4637 VALUE ndigits, v, den;
4638 struct time_object *tobj;
4639
4640 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4641 den = INT2FIX(1);
4642 else
4643 den = ndigits_denominator(ndigits);
4644
4645 GetTimeval(time, tobj);
4646 v = w2v(rb_time_unmagnify(tobj->timew));
4647
4648 v = modv(v, den);
4649 return time_add(tobj, time, v, -1);
4650}
4651
4652/*
4653 * call-seq:
4654 * ceil(ndigits = 0) -> new_time
4655 *
4656 * Returns a new +Time+ object whose numerical value
4657 * is greater than or equal to +self+ with its seconds
4658 * truncated to precision +ndigits+:
4659 *
4660 * t = Time.utc(2010, 3, 30, 5, 43, 25.123456789r)
4661 * t # => 2010-03-30 05:43:25.123456789 UTC
4662 * t.ceil # => 2010-03-30 05:43:26 UTC
4663 * t.ceil(2) # => 2010-03-30 05:43:25.13 UTC
4664 * t.ceil(4) # => 2010-03-30 05:43:25.1235 UTC
4665 * t.ceil(6) # => 2010-03-30 05:43:25.123457 UTC
4666 * t.ceil(8) # => 2010-03-30 05:43:25.12345679 UTC
4667 * t.ceil(10) # => 2010-03-30 05:43:25.123456789 UTC
4668 *
4669 * t = Time.utc(1999, 12, 31, 23, 59, 59)
4670 * t # => 1999-12-31 23:59:59 UTC
4671 * (t + 0.4).ceil # => 2000-01-01 00:00:00 UTC
4672 * (t + 0.9).ceil # => 2000-01-01 00:00:00 UTC
4673 * (t + 1.4).ceil # => 2000-01-01 00:00:01 UTC
4674 * (t + 1.9).ceil # => 2000-01-01 00:00:01 UTC
4675 *
4676 * Related: Time#floor, Time#round.
4677 */
4678
4679static VALUE
4680time_ceil(int argc, VALUE *argv, VALUE time)
4681{
4682 VALUE ndigits, v, den;
4683 struct time_object *tobj;
4684
4685 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4686 den = INT2FIX(1);
4687 else
4688 den = ndigits_denominator(ndigits);
4689
4690 GetTimeval(time, tobj);
4691 v = w2v(rb_time_unmagnify(tobj->timew));
4692
4693 v = modv(v, den);
4694 if (!rb_equal(v, INT2FIX(0))) {
4695 v = subv(den, v);
4696 }
4697 return time_add(tobj, time, v, 1);
4698}
4699
4700/*
4701 * call-seq:
4702 * sec -> integer
4703 *
4704 * Returns the integer second of the minute for +self+,
4705 * in range (0..60):
4706 *
4707 * t = Time.new(2000, 1, 2, 3, 4, 5, 6)
4708 * # => 2000-01-02 03:04:05 +000006
4709 * t.sec # => 5
4710 *
4711 * Note: the second value may be 60 when there is a
4712 * {leap second}[https://en.wikipedia.org/wiki/Leap_second].
4713 *
4714 * Related: Time#year, Time#mon, Time#min.
4715 */
4716
4717static VALUE
4718time_sec(VALUE time)
4719{
4720 struct time_object *tobj;
4721
4722 GetTimeval(time, tobj);
4723 MAKE_TM(time, tobj);
4724 return INT2FIX(tobj->vtm.sec);
4725}
4726
4727/*
4728 * call-seq:
4729 * min -> integer
4730 *
4731 * Returns the integer minute of the hour for +self+,
4732 * in range (0..59):
4733 *
4734 * t = Time.new(2000, 1, 2, 3, 4, 5, 6)
4735 * # => 2000-01-02 03:04:05 +000006
4736 * t.min # => 4
4737 *
4738 * Related: Time#year, Time#mon, Time#sec.
4739 */
4740
4741static VALUE
4742time_min(VALUE time)
4743{
4744 struct time_object *tobj;
4745
4746 GetTimeval(time, tobj);
4747 MAKE_TM(time, tobj);
4748 return INT2FIX(tobj->vtm.min);
4749}
4750
4751/*
4752 * call-seq:
4753 * hour -> integer
4754 *
4755 * Returns the integer hour of the day for +self+,
4756 * in range (0..23):
4757 *
4758 * t = Time.new(2000, 1, 2, 3, 4, 5, 6)
4759 * # => 2000-01-02 03:04:05 +000006
4760 * t.hour # => 3
4761 *
4762 * Related: Time#year, Time#mon, Time#min.
4763 */
4764
4765static VALUE
4766time_hour(VALUE time)
4767{
4768 struct time_object *tobj;
4769
4770 GetTimeval(time, tobj);
4771 MAKE_TM(time, tobj);
4772 return INT2FIX(tobj->vtm.hour);
4773}
4774
4775/*
4776 * call-seq:
4777 * mday -> integer
4778 *
4779 * Returns the integer day of the month for +self+,
4780 * in range (1..31):
4781 *
4782 * t = Time.new(2000, 1, 2, 3, 4, 5, 6)
4783 * # => 2000-01-02 03:04:05 +000006
4784 * t.mday # => 2
4785 *
4786 * Related: Time#year, Time#hour, Time#min.
4787 */
4788
4789static VALUE
4790time_mday(VALUE time)
4791{
4792 struct time_object *tobj;
4793
4794 GetTimeval(time, tobj);
4795 MAKE_TM(time, tobj);
4796 return INT2FIX(tobj->vtm.mday);
4797}
4798
4799/*
4800 * call-seq:
4801 * mon -> integer
4802 *
4803 * Returns the integer month of the year for +self+,
4804 * in range (1..12):
4805 *
4806 * t = Time.new(2000, 1, 2, 3, 4, 5, 6)
4807 * # => 2000-01-02 03:04:05 +000006
4808 * t.mon # => 1
4809 *
4810 * Related: Time#year, Time#hour, Time#min.
4811 */
4812
4813static VALUE
4814time_mon(VALUE time)
4815{
4816 struct time_object *tobj;
4817
4818 GetTimeval(time, tobj);
4819 MAKE_TM(time, tobj);
4820 return INT2FIX(tobj->vtm.mon);
4821}
4822
4823/*
4824 * call-seq:
4825 * year -> integer
4826 *
4827 * Returns the integer year for +self+:
4828 *
4829 * t = Time.new(2000, 1, 2, 3, 4, 5, 6)
4830 * # => 2000-01-02 03:04:05 +000006
4831 * t.year # => 2000
4832 *
4833 * Related: Time#mon, Time#hour, Time#min.
4834 */
4835
4836static VALUE
4837time_year(VALUE time)
4838{
4839 struct time_object *tobj;
4840
4841 GetTimeval(time, tobj);
4842 MAKE_TM(time, tobj);
4843 return tobj->vtm.year;
4844}
4845
4846/*
4847 * call-seq:
4848 * wday -> integer
4849 *
4850 * Returns the integer day of the week for +self+,
4851 * in range (0..6), with Sunday as zero.
4852 *
4853 * t = Time.new(2000, 1, 2, 3, 4, 5, 6)
4854 * # => 2000-01-02 03:04:05 +000006
4855 * t.wday # => 0
4856 * t.sunday? # => true
4857 *
4858 * Related: Time#year, Time#hour, Time#min.
4859 */
4860
4861static VALUE
4862time_wday(VALUE time)
4863{
4864 struct time_object *tobj;
4865
4866 GetTimeval(time, tobj);
4867 MAKE_TM_ENSURE(time, tobj, tobj->vtm.wday != VTM_WDAY_INITVAL);
4868 return INT2FIX((int)tobj->vtm.wday);
4869}
4870
4871#define wday_p(n) {\
4872 return RBOOL(time_wday(time) == INT2FIX(n)); \
4873}
4874
4875/*
4876 * call-seq:
4877 * sunday? -> true or false
4878 *
4879 * Returns +true+ if +self+ represents a Sunday, +false+ otherwise:
4880 *
4881 * t = Time.utc(2000, 1, 2) # => 2000-01-02 00:00:00 UTC
4882 * t.sunday? # => true
4883 *
4884 * Related: Time#monday?, Time#tuesday?, Time#wednesday?.
4885 */
4886
4887static VALUE
4888time_sunday(VALUE time)
4889{
4890 wday_p(0);
4891}
4892
4893/*
4894 * call-seq:
4895 * monday? -> true or false
4896 *
4897 * Returns +true+ if +self+ represents a Monday, +false+ otherwise:
4898 *
4899 * t = Time.utc(2000, 1, 3) # => 2000-01-03 00:00:00 UTC
4900 * t.monday? # => true
4901 *
4902 * Related: Time#tuesday?, Time#wednesday?, Time#thursday?.
4903 */
4904
4905static VALUE
4906time_monday(VALUE time)
4907{
4908 wday_p(1);
4909}
4910
4911/*
4912 * call-seq:
4913 * tuesday? -> true or false
4914 *
4915 * Returns +true+ if +self+ represents a Tuesday, +false+ otherwise:
4916 *
4917 * t = Time.utc(2000, 1, 4) # => 2000-01-04 00:00:00 UTC
4918 * t.tuesday? # => true
4919 *
4920 * Related: Time#wednesday?, Time#thursday?, Time#friday?.
4921 */
4922
4923static VALUE
4924time_tuesday(VALUE time)
4925{
4926 wday_p(2);
4927}
4928
4929/*
4930 * call-seq:
4931 * wednesday? -> true or false
4932 *
4933 * Returns +true+ if +self+ represents a Wednesday, +false+ otherwise:
4934 *
4935 * t = Time.utc(2000, 1, 5) # => 2000-01-05 00:00:00 UTC
4936 * t.wednesday? # => true
4937 *
4938 * Related: Time#thursday?, Time#friday?, Time#saturday?.
4939 */
4940
4941static VALUE
4942time_wednesday(VALUE time)
4943{
4944 wday_p(3);
4945}
4946
4947/*
4948 * call-seq:
4949 * thursday? -> true or false
4950 *
4951 * Returns +true+ if +self+ represents a Thursday, +false+ otherwise:
4952 *
4953 * t = Time.utc(2000, 1, 6) # => 2000-01-06 00:00:00 UTC
4954 * t.thursday? # => true
4955 *
4956 * Related: Time#friday?, Time#saturday?, Time#sunday?.
4957 */
4958
4959static VALUE
4960time_thursday(VALUE time)
4961{
4962 wday_p(4);
4963}
4964
4965/*
4966 * call-seq:
4967 * friday? -> true or false
4968 *
4969 * Returns +true+ if +self+ represents a Friday, +false+ otherwise:
4970 *
4971 * t = Time.utc(2000, 1, 7) # => 2000-01-07 00:00:00 UTC
4972 * t.friday? # => true
4973 *
4974 * Related: Time#saturday?, Time#sunday?, Time#monday?.
4975 */
4976
4977static VALUE
4978time_friday(VALUE time)
4979{
4980 wday_p(5);
4981}
4982
4983/*
4984 * call-seq:
4985 * saturday? -> true or false
4986 *
4987 * Returns +true+ if +self+ represents a Saturday, +false+ otherwise:
4988 *
4989 * t = Time.utc(2000, 1, 1) # => 2000-01-01 00:00:00 UTC
4990 * t.saturday? # => true
4991 *
4992 * Related: Time#sunday?, Time#monday?, Time#tuesday?.
4993 */
4994
4995static VALUE
4996time_saturday(VALUE time)
4997{
4998 wday_p(6);
4999}
5000
5001/*
5002 * call-seq:
5003 * yday -> integer
5004 *
5005 * Returns the integer day of the year of +self+, in range (1..366).
5006 *
5007 * Time.new(2000, 1, 1).yday # => 1
5008 * Time.new(2000, 12, 31).yday # => 366
5009 */
5010
5011static VALUE
5012time_yday(VALUE time)
5013{
5014 struct time_object *tobj;
5015
5016 GetTimeval(time, tobj);
5017 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
5018 return INT2FIX(tobj->vtm.yday);
5019}
5020
5021/*
5022 * call-seq:
5023 * dst? -> true or false
5024 *
5025 * Returns +true+ if +self+ is in daylight saving time, +false+ otherwise:
5026 *
5027 * t = Time.local(2000, 1, 1) # => 2000-01-01 00:00:00 -0600
5028 * t.zone # => "Central Standard Time"
5029 * t.dst? # => false
5030 * t = Time.local(2000, 7, 1) # => 2000-07-01 00:00:00 -0500
5031 * t.zone # => "Central Daylight Time"
5032 * t.dst? # => true
5033 *
5034 */
5035
5036static VALUE
5037time_isdst(VALUE time)
5038{
5039 struct time_object *tobj;
5040
5041 GetTimeval(time, tobj);
5042 MAKE_TM(time, tobj);
5043 if (tobj->vtm.isdst == VTM_ISDST_INITVAL) {
5044 rb_raise(rb_eRuntimeError, "isdst is not set yet");
5045 }
5046 return RBOOL(tobj->vtm.isdst);
5047}
5048
5049/*
5050 * call-seq:
5051 * time.zone -> string or timezone
5052 *
5053 * Returns the string name of the time zone for +self+:
5054 *
5055 * Time.utc(2000, 1, 1).zone # => "UTC"
5056 * Time.new(2000, 1, 1).zone # => "Central Standard Time"
5057 */
5058
5059static VALUE
5060time_zone(VALUE time)
5061{
5062 struct time_object *tobj;
5063 VALUE zone;
5064
5065 GetTimeval(time, tobj);
5066 MAKE_TM(time, tobj);
5067
5068 if (TZMODE_UTC_P(tobj)) {
5069 return rb_usascii_str_new_cstr("UTC");
5070 }
5071 zone = tobj->vtm.zone;
5072 if (NIL_P(zone))
5073 return Qnil;
5074
5075 if (RB_TYPE_P(zone, T_STRING))
5076 zone = rb_str_dup(zone);
5077 return zone;
5078}
5079
5080/*
5081 * call-seq:
5082 * utc_offset -> integer
5083 *
5084 * Returns the offset in seconds between the timezones of UTC and +self+:
5085 *
5086 * Time.utc(2000, 1, 1).utc_offset # => 0
5087 * Time.local(2000, 1, 1).utc_offset # => -21600 # -6*3600, or minus six hours.
5088 *
5089 */
5090
5091VALUE
5093{
5094 struct time_object *tobj;
5095
5096 GetTimeval(time, tobj);
5097
5098 if (TZMODE_UTC_P(tobj)) {
5099 return INT2FIX(0);
5100 }
5101 else {
5102 MAKE_TM(time, tobj);
5103 return tobj->vtm.utc_offset;
5104 }
5105}
5106
5107/*
5108 * call-seq:
5109 * to_a -> array
5110 *
5111 * Returns a 10-element array of values representing +self+:
5112 *
5113 * Time.utc(2000, 1, 1).to_a
5114 * # => [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"]
5115 * # [sec, min, hour, day, mon, year, wday, yday, dst?, zone]
5116 *
5117 * The returned array is suitable for use as an argument to Time.utc or Time.local
5118 * to create a new +Time+ object.
5119 *
5120 */
5121
5122static VALUE
5123time_to_a(VALUE time)
5124{
5125 struct time_object *tobj;
5126
5127 GetTimeval(time, tobj);
5128 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
5129 return rb_ary_new3(10,
5130 INT2FIX(tobj->vtm.sec),
5131 INT2FIX(tobj->vtm.min),
5132 INT2FIX(tobj->vtm.hour),
5133 INT2FIX(tobj->vtm.mday),
5134 INT2FIX(tobj->vtm.mon),
5135 tobj->vtm.year,
5136 INT2FIX(tobj->vtm.wday),
5137 INT2FIX(tobj->vtm.yday),
5138 RBOOL(tobj->vtm.isdst),
5139 time_zone(time));
5140}
5141
5142/*
5143 * call-seq:
5144 * deconstruct_keys(array_of_names_or_nil) -> hash
5145 *
5146 * Returns a hash of the name/value pairs, to use in pattern matching.
5147 * Possible keys are: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>,
5148 * <tt>:yday</tt>, <tt>:wday</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>,
5149 * <tt>:subsec</tt>, <tt>:dst</tt>, <tt>:zone</tt>.
5150 *
5151 * Possible usages:
5152 *
5153 * t = Time.utc(2022, 10, 5, 21, 25, 30)
5154 *
5155 * if t in wday: 3, day: ..7 # uses deconstruct_keys underneath
5156 * puts "first Wednesday of the month"
5157 * end
5158 * #=> prints "first Wednesday of the month"
5159 *
5160 * case t
5161 * in year: ...2022
5162 * puts "too old"
5163 * in month: ..9
5164 * puts "quarter 1-3"
5165 * in wday: 1..5, month:
5166 * puts "working day in month #{month}"
5167 * end
5168 * #=> prints "working day in month 10"
5169 *
5170 * Note that deconstruction by pattern can also be combined with class check:
5171 *
5172 * if t in Time(wday: 3, day: ..7)
5173 * puts "first Wednesday of the month"
5174 * end
5175 *
5176 */
5177static VALUE
5178time_deconstruct_keys(VALUE time, VALUE keys)
5179{
5180 struct time_object *tobj;
5181 VALUE h;
5182 long i;
5183
5184 GetTimeval(time, tobj);
5185 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
5186
5187 if (NIL_P(keys)) {
5188 h = rb_hash_new_with_size(11);
5189
5190 rb_hash_aset(h, sym_year, tobj->vtm.year);
5191 rb_hash_aset(h, sym_month, INT2FIX(tobj->vtm.mon));
5192 rb_hash_aset(h, sym_day, INT2FIX(tobj->vtm.mday));
5193 rb_hash_aset(h, sym_yday, INT2FIX(tobj->vtm.yday));
5194 rb_hash_aset(h, sym_wday, INT2FIX(tobj->vtm.wday));
5195 rb_hash_aset(h, sym_hour, INT2FIX(tobj->vtm.hour));
5196 rb_hash_aset(h, sym_min, INT2FIX(tobj->vtm.min));
5197 rb_hash_aset(h, sym_sec, INT2FIX(tobj->vtm.sec));
5198 rb_hash_aset(h, sym_subsec,
5199 quov(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE)));
5200 rb_hash_aset(h, sym_dst, RBOOL(tobj->vtm.isdst));
5201 rb_hash_aset(h, sym_zone, time_zone(time));
5202
5203 return h;
5204 }
5205 if (UNLIKELY(!RB_TYPE_P(keys, T_ARRAY))) {
5206 rb_raise(rb_eTypeError,
5207 "wrong argument type %"PRIsVALUE" (expected Array or nil)",
5208 rb_obj_class(keys));
5209
5210 }
5211
5212 h = rb_hash_new_with_size(RARRAY_LEN(keys));
5213
5214 for (i=0; i<RARRAY_LEN(keys); i++) {
5215 VALUE key = RARRAY_AREF(keys, i);
5216
5217 if (sym_year == key) rb_hash_aset(h, key, tobj->vtm.year);
5218 if (sym_month == key) rb_hash_aset(h, key, INT2FIX(tobj->vtm.mon));
5219 if (sym_day == key) rb_hash_aset(h, key, INT2FIX(tobj->vtm.mday));
5220 if (sym_yday == key) rb_hash_aset(h, key, INT2FIX(tobj->vtm.yday));
5221 if (sym_wday == key) rb_hash_aset(h, key, INT2FIX(tobj->vtm.wday));
5222 if (sym_hour == key) rb_hash_aset(h, key, INT2FIX(tobj->vtm.hour));
5223 if (sym_min == key) rb_hash_aset(h, key, INT2FIX(tobj->vtm.min));
5224 if (sym_sec == key) rb_hash_aset(h, key, INT2FIX(tobj->vtm.sec));
5225 if (sym_subsec == key) {
5226 rb_hash_aset(h, key, quov(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE)));
5227 }
5228 if (sym_dst == key) rb_hash_aset(h, key, RBOOL(tobj->vtm.isdst));
5229 if (sym_zone == key) rb_hash_aset(h, key, time_zone(time));
5230 }
5231 return h;
5232}
5233
5234static VALUE
5235rb_strftime_alloc(const char *format, size_t format_len, rb_encoding *enc,
5236 VALUE time, struct vtm *vtm, wideval_t timew, int gmt)
5237{
5238 VALUE timev = Qnil;
5239 struct timespec ts;
5240
5241 if (!timew2timespec_exact(timew, &ts))
5242 timev = w2v(rb_time_unmagnify(timew));
5243
5244 if (NIL_P(timev)) {
5245 return rb_strftime_timespec(format, format_len, enc, time, vtm, &ts, gmt);
5246 }
5247 else {
5248 return rb_strftime(format, format_len, enc, time, vtm, timev, gmt);
5249 }
5250}
5251
5252static VALUE
5253strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc)
5254{
5255 struct time_object *tobj;
5256 VALUE str;
5257
5258 GetTimeval(time, tobj);
5259 MAKE_TM(time, tobj);
5260 str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew, TZMODE_UTC_P(tobj));
5261 if (!str) rb_raise(rb_eArgError, "invalid format: %s", fmt);
5262 return str;
5263}
5264
5265/*
5266 * call-seq:
5267 * strftime(format_string) -> string
5268 *
5269 * Returns a string representation of +self+,
5270 * formatted according to the given string +format+.
5271 * See {Formats for Dates and Times}[rdoc-ref:strftime_formatting.rdoc].
5272 */
5273
5274static VALUE
5275time_strftime(VALUE time, VALUE format)
5276{
5277 struct time_object *tobj;
5278 const char *fmt;
5279 long len;
5280 rb_encoding *enc;
5281 VALUE tmp;
5282
5283 GetTimeval(time, tobj);
5284 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
5285 StringValue(format);
5286 if (!rb_enc_str_asciicompat_p(format)) {
5287 rb_raise(rb_eArgError, "format should have ASCII compatible encoding");
5288 }
5289 tmp = rb_str_tmp_frozen_acquire(format);
5290 fmt = RSTRING_PTR(tmp);
5291 len = RSTRING_LEN(tmp);
5292 enc = rb_enc_get(format);
5293 if (len == 0) {
5294 rb_warning("strftime called with empty format string");
5295 return rb_enc_str_new(0, 0, enc);
5296 }
5297 else {
5298 VALUE str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew,
5299 TZMODE_UTC_P(tobj));
5300 rb_str_tmp_frozen_release(format, tmp);
5301 if (!str) rb_raise(rb_eArgError, "invalid format: %"PRIsVALUE, format);
5302 return str;
5303 }
5304}
5305
5306/*
5307 * call-seq:
5308 * xmlschema(fraction_digits=0) -> string
5309 *
5310 * Returns a string which represents the time as a dateTime defined by XML
5311 * Schema:
5312 *
5313 * CCYY-MM-DDThh:mm:ssTZD
5314 * CCYY-MM-DDThh:mm:ss.sssTZD
5315 *
5316 * where TZD is Z or [+-]hh:mm.
5317 *
5318 * If self is a UTC time, Z is used as TZD. [+-]hh:mm is used otherwise.
5319 *
5320 * +fraction_digits+ specifies a number of digits to use for fractional
5321 * seconds. Its default value is 0.
5322 *
5323 * t = Time.now
5324 * t.xmlschema # => "2011-10-05T22:26:12-04:00"
5325 */
5326
5327static VALUE
5328time_xmlschema(int argc, VALUE *argv, VALUE time)
5329{
5330 long fraction_digits = 0;
5331 rb_check_arity(argc, 0, 1);
5332 if (argc > 0) {
5333 fraction_digits = NUM2LONG(argv[0]);
5334 if (fraction_digits < 0) {
5335 fraction_digits = 0;
5336 }
5337 }
5338
5339 struct time_object *tobj;
5340
5341 GetTimeval(time, tobj);
5342 MAKE_TM(time, tobj);
5343
5344 const long size_after_year = sizeof("-MM-DDTHH:MM:SS+ZH:ZM") + fraction_digits
5345 + (fraction_digits > 0);
5346 VALUE str;
5347 char *ptr;
5348
5349# define fill_digits_long(len, prec, n) \
5350 for (int fill_it = 1, written = snprintf(ptr, len, "%0*ld", prec, n); \
5351 fill_it; ptr += written, fill_it = 0)
5352
5353 if (FIXNUM_P(tobj->vtm.year)) {
5354 long year = FIX2LONG(tobj->vtm.year);
5355 int year_width = (year < 0) + rb_strlen_lit("YYYY");
5356 int w = (year >= -9999 && year <= 9999 ? year_width : (year < 0) + (int)DECIMAL_SIZE_OF(year));
5357 str = rb_usascii_str_new(0, w + size_after_year);
5358 ptr = RSTRING_PTR(str);
5359 fill_digits_long(w + 1, year_width, year) {
5360 if (year >= -9999 && year <= 9999) {
5361 RUBY_ASSERT(written == year_width);
5362 }
5363 else {
5364 RUBY_ASSERT(written >= year_width);
5365 RUBY_ASSERT(written <= w);
5366 }
5367 }
5368 }
5369 else {
5370 str = rb_int2str(tobj->vtm.year, 10);
5371 rb_str_modify_expand(str, size_after_year);
5372 ptr = RSTRING_END(str);
5373 }
5374
5375# define fill_2(c, n) (*ptr++ = c, *ptr++ = '0' + (n) / 10, *ptr++ = '0' + (n) % 10)
5376 fill_2('-', tobj->vtm.mon);
5377 fill_2('-', tobj->vtm.mday);
5378 fill_2('T', tobj->vtm.hour);
5379 fill_2(':', tobj->vtm.min);
5380 fill_2(':', tobj->vtm.sec);
5381
5382 if (fraction_digits > 0) {
5383 VALUE subsecx = tobj->vtm.subsecx;
5384 long subsec;
5385 int digits = -1;
5386 *ptr++ = '.';
5387 if (fraction_digits <= TIME_SCALE_NUMDIGITS) {
5388 digits = TIME_SCALE_NUMDIGITS - (int)fraction_digits;
5389 }
5390 else {
5391 long w = fraction_digits - TIME_SCALE_NUMDIGITS; /* > 0 */
5392 subsecx = mulv(subsecx, rb_int_positive_pow(10, (unsigned long)w));
5393 if (!RB_INTEGER_TYPE_P(subsecx)) { /* maybe Rational */
5394 subsecx = rb_Integer(subsecx);
5395 }
5396 if (FIXNUM_P(subsecx)) digits = 0;
5397 }
5398 if (digits >= 0 && fraction_digits < INT_MAX) {
5399 subsec = NUM2LONG(subsecx);
5400 if (digits > 0) subsec /= (long)pow(10, digits);
5401 fill_digits_long(fraction_digits + 1, (int)fraction_digits, subsec) {
5402 RUBY_ASSERT(written == (int)fraction_digits);
5403 }
5404 }
5405 else {
5406 subsecx = rb_int2str(subsecx, 10);
5407 long len = RSTRING_LEN(subsecx);
5408 if (fraction_digits > len) {
5409 memset(ptr, '0', fraction_digits - len);
5410 }
5411 else {
5412 len = fraction_digits;
5413 }
5414 ptr += fraction_digits;
5415 memcpy(ptr - len, RSTRING_PTR(subsecx), len);
5416 }
5417 }
5418
5419 if (TZMODE_UTC_P(tobj)) {
5420 *ptr = 'Z';
5421 ptr++;
5422 }
5423 else {
5424 long offset = NUM2LONG(rb_time_utc_offset(time));
5425 char sign = offset < 0 ? '-' : '+';
5426 if (offset < 0) offset = -offset;
5427 offset /= 60;
5428 fill_2(sign, offset / 60);
5429 fill_2(':', offset % 60);
5430 }
5431 const char *const start = RSTRING_PTR(str);
5432 rb_str_set_len(str, ptr - start); // We could skip coderange scanning as we know it's full ASCII.
5433 return str;
5434}
5435
5436int ruby_marshal_write_long(long x, char *buf);
5437
5438enum {base_dump_size = 8};
5439
5440/* :nodoc: */
5441static VALUE
5442time_mdump(VALUE time)
5443{
5444 struct time_object *tobj;
5445 unsigned long p, s;
5446 char buf[base_dump_size + sizeof(long) + 1];
5447 int i;
5448 VALUE str;
5449
5450 struct vtm vtm;
5451 long year;
5452 long usec, nsec;
5453 VALUE subsecx, nano, subnano, v, zone;
5454
5455 VALUE year_extend = Qnil;
5456 const int max_year = 1900+0xffff;
5457
5458 GetTimeval(time, tobj);
5459
5460 gmtimew(tobj->timew, &vtm);
5461
5462 if (FIXNUM_P(vtm.year)) {
5463 year = FIX2LONG(vtm.year);
5464 if (year > max_year) {
5465 year_extend = INT2FIX(year - max_year);
5466 year = max_year;
5467 }
5468 else if (year < 1900) {
5469 year_extend = LONG2NUM(1900 - year);
5470 year = 1900;
5471 }
5472 }
5473 else {
5474 if (rb_int_positive_p(vtm.year)) {
5475 year_extend = rb_int_minus(vtm.year, INT2FIX(max_year));
5476 year = max_year;
5477 }
5478 else {
5479 year_extend = rb_int_minus(INT2FIX(1900), vtm.year);
5480 year = 1900;
5481 }
5482 }
5483
5484 subsecx = vtm.subsecx;
5485
5486 nano = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
5487 divmodv(nano, INT2FIX(1), &v, &subnano);
5488 nsec = FIX2LONG(v);
5489 usec = nsec / 1000;
5490 nsec = nsec % 1000;
5491
5492 nano = addv(LONG2FIX(nsec), subnano);
5493
5494 p = 0x1UL << 31 | /* 1 */
5495 TZMODE_UTC_P(tobj) << 30 | /* 1 */
5496 (year-1900) << 14 | /* 16 */
5497 (vtm.mon-1) << 10 | /* 4 */
5498 vtm.mday << 5 | /* 5 */
5499 vtm.hour; /* 5 */
5500 s = (unsigned long)vtm.min << 26 | /* 6 */
5501 vtm.sec << 20 | /* 6 */
5502 usec; /* 20 */
5503
5504 for (i=0; i<4; i++) {
5505 buf[i] = (unsigned char)p;
5506 p = RSHIFT(p, 8);
5507 }
5508 for (i=4; i<8; i++) {
5509 buf[i] = (unsigned char)s;
5510 s = RSHIFT(s, 8);
5511 }
5512
5513 if (!NIL_P(year_extend)) {
5514 /*
5515 * Append extended year distance from 1900..(1900+0xffff). In
5516 * each cases, there is no sign as the value is positive. The
5517 * format is length (marshaled long) + little endian packed
5518 * binary (like as Integer).
5519 */
5520 size_t ysize = rb_absint_size(year_extend, NULL);
5521 char *p, *const buf_year_extend = buf + base_dump_size;
5522 if (ysize > LONG_MAX ||
5523 (i = ruby_marshal_write_long((long)ysize, buf_year_extend)) < 0) {
5524 rb_raise(rb_eArgError, "year too %s to marshal: %"PRIsVALUE" UTC",
5525 (year == 1900 ? "small" : "big"), vtm.year);
5526 }
5527 i += base_dump_size;
5528 str = rb_str_new(NULL, i + ysize);
5529 p = RSTRING_PTR(str);
5530 memcpy(p, buf, i);
5531 p += i;
5532 rb_integer_pack(year_extend, p, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
5533 }
5534 else {
5535 str = rb_str_new(buf, base_dump_size);
5536 }
5537 rb_copy_generic_ivar(str, time);
5538 if (!rb_equal(nano, INT2FIX(0))) {
5539 if (RB_TYPE_P(nano, T_RATIONAL)) {
5540 rb_ivar_set(str, id_nano_num, RRATIONAL(nano)->num);
5541 rb_ivar_set(str, id_nano_den, RRATIONAL(nano)->den);
5542 }
5543 else {
5544 rb_ivar_set(str, id_nano_num, nano);
5545 rb_ivar_set(str, id_nano_den, INT2FIX(1));
5546 }
5547 }
5548 if (nsec) { /* submicro is only for Ruby 1.9.1 compatibility */
5549 /*
5550 * submicro is formatted in fixed-point packed BCD (without sign).
5551 * It represent digits under microsecond.
5552 * For nanosecond resolution, 3 digits (2 bytes) are used.
5553 * However it can be longer.
5554 * Extra digits are ignored for loading.
5555 */
5556 char buf[2];
5557 int len = (int)sizeof(buf);
5558 buf[1] = (char)((nsec % 10) << 4);
5559 nsec /= 10;
5560 buf[0] = (char)(nsec % 10);
5561 nsec /= 10;
5562 buf[0] |= (char)((nsec % 10) << 4);
5563 if (buf[1] == 0)
5564 len = 1;
5565 rb_ivar_set(str, id_submicro, rb_str_new(buf, len));
5566 }
5567 if (!TZMODE_UTC_P(tobj)) {
5568 VALUE off = rb_time_utc_offset(time), div, mod;
5569 divmodv(off, INT2FIX(1), &div, &mod);
5570 if (rb_equal(mod, INT2FIX(0)))
5571 off = rb_Integer(div);
5572 rb_ivar_set(str, id_offset, off);
5573 }
5574 zone = tobj->vtm.zone;
5575 if (maybe_tzobj_p(zone)) {
5576 zone = rb_funcallv(zone, id_name, 0, 0);
5577 }
5578 rb_ivar_set(str, id_zone, zone);
5579 return str;
5580}
5581
5582/* :nodoc: */
5583static VALUE
5584time_dump(int argc, VALUE *argv, VALUE time)
5585{
5586 VALUE str;
5587
5588 rb_check_arity(argc, 0, 1);
5589 str = time_mdump(time);
5590
5591 return str;
5592}
5593
5594static VALUE
5595mload_findzone(VALUE arg)
5596{
5597 VALUE *argp = (VALUE *)arg;
5598 VALUE time = argp[0], zone = argp[1];
5599 return find_timezone(time, zone);
5600}
5601
5602static VALUE
5603mload_zone(VALUE time, VALUE zone)
5604{
5605 VALUE z, args[2];
5606 args[0] = time;
5607 args[1] = zone;
5608 z = rb_rescue(mload_findzone, (VALUE)args, 0, Qnil);
5609 if (NIL_P(z)) return rb_fstring(zone);
5610 if (RB_TYPE_P(z, T_STRING)) return rb_fstring(z);
5611 return z;
5612}
5613
5614long ruby_marshal_read_long(const char **buf, long len);
5615
5616/* :nodoc: */
5617static VALUE
5618time_mload(VALUE time, VALUE str)
5619{
5620 struct time_object *tobj;
5621 unsigned long p, s;
5622 time_t sec;
5623 long usec;
5624 unsigned char *buf;
5625 struct vtm vtm;
5626 int i, gmt;
5627 long nsec;
5628 VALUE submicro, nano_num, nano_den, offset, zone, year;
5629 wideval_t timew;
5630
5631 time_modify(time);
5632
5633#define get_attr(attr, iffound) \
5634 attr = rb_attr_delete(str, id_##attr); \
5635 if (!NIL_P(attr)) { \
5636 iffound; \
5637 }
5638
5639 get_attr(nano_num, {});
5640 get_attr(nano_den, {});
5641 get_attr(submicro, {});
5642 get_attr(offset, (offset = rb_rescue(validate_utc_offset, offset, 0, Qnil)));
5643 get_attr(zone, (zone = rb_rescue(validate_zone_name, zone, 0, Qnil)));
5644 get_attr(year, {});
5645
5646#undef get_attr
5647
5648 rb_copy_generic_ivar(time, str);
5649
5650 StringValue(str);
5651 buf = (unsigned char *)RSTRING_PTR(str);
5652 if (RSTRING_LEN(str) < base_dump_size) {
5653 goto invalid_format;
5654 }
5655
5656 p = s = 0;
5657 for (i=0; i<4; i++) {
5658 p |= (unsigned long)buf[i]<<(8*i);
5659 }
5660 for (i=4; i<8; i++) {
5661 s |= (unsigned long)buf[i]<<(8*(i-4));
5662 }
5663
5664 if ((p & (1UL<<31)) == 0) {
5665 gmt = 0;
5666 offset = Qnil;
5667 sec = p;
5668 usec = s;
5669 nsec = usec * 1000;
5670 timew = wadd(rb_time_magnify(TIMET2WV(sec)), wmulquoll(WINT2FIXWV(usec), TIME_SCALE, 1000000));
5671 }
5672 else {
5673 p &= ~(1UL<<31);
5674 gmt = (int)((p >> 30) & 0x1);
5675
5676 if (NIL_P(year)) {
5677 year = INT2FIX(((int)(p >> 14) & 0xffff) + 1900);
5678 }
5679 if (RSTRING_LEN(str) > base_dump_size) {
5680 long len = RSTRING_LEN(str) - base_dump_size;
5681 long ysize = 0;
5682 VALUE year_extend;
5683 const char *ybuf = (const char *)(buf += base_dump_size);
5684 ysize = ruby_marshal_read_long(&ybuf, len);
5685 len -= ybuf - (const char *)buf;
5686 if (ysize < 0 || ysize > len) goto invalid_format;
5687 year_extend = rb_integer_unpack(ybuf, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
5688 if (year == INT2FIX(1900)) {
5689 year = rb_int_minus(year, year_extend);
5690 }
5691 else {
5692 year = rb_int_plus(year, year_extend);
5693 }
5694 }
5695 unsigned int mon = ((int)(p >> 10) & 0xf); /* 0...12 */
5696 if (mon >= 12) {
5697 mon -= 12;
5698 year = addv(year, LONG2FIX(1));
5699 }
5700 vtm.year = year;
5701 vtm.mon = mon + 1;
5702 vtm.mday = (int)(p >> 5) & 0x1f;
5703 vtm.hour = (int) p & 0x1f;
5704 vtm.min = (int)(s >> 26) & 0x3f;
5705 vtm.sec = (int)(s >> 20) & 0x3f;
5706 vtm.utc_offset = INT2FIX(0);
5707 vtm.yday = vtm.wday = 0;
5708 vtm.isdst = 0;
5709 vtm.zone = str_empty;
5710
5711 usec = (long)(s & 0xfffff);
5712 nsec = usec * 1000;
5713
5714
5715 vtm.subsecx = mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000));
5716 if (nano_num != Qnil) {
5717 VALUE nano = quov(num_exact(nano_num), num_exact(nano_den));
5718 vtm.subsecx = addv(vtm.subsecx, mulquov(nano, INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
5719 }
5720 else if (submicro != Qnil) { /* for Ruby 1.9.1 compatibility */
5721 unsigned char *ptr;
5722 long len;
5723 int digit;
5724 ptr = (unsigned char*)StringValuePtr(submicro);
5725 len = RSTRING_LEN(submicro);
5726 nsec = 0;
5727 if (0 < len) {
5728 if (10 <= (digit = ptr[0] >> 4)) goto end_submicro;
5729 nsec += digit * 100;
5730 if (10 <= (digit = ptr[0] & 0xf)) goto end_submicro;
5731 nsec += digit * 10;
5732 }
5733 if (1 < len) {
5734 if (10 <= (digit = ptr[1] >> 4)) goto end_submicro;
5735 nsec += digit;
5736 }
5737 vtm.subsecx = addv(vtm.subsecx, mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
5738end_submicro: ;
5739 }
5740 timew = timegmw(&vtm);
5741 }
5742
5743 GetNewTimeval(time, tobj);
5744 TZMODE_SET_LOCALTIME(tobj);
5745 tobj->vtm.tm_got = 0;
5746 time_set_timew(time, tobj, timew);
5747
5748 if (gmt) {
5749 TZMODE_SET_UTC(tobj);
5750 }
5751 else if (!NIL_P(offset)) {
5752 time_set_utc_offset(time, offset);
5753 time_fixoff(time);
5754 }
5755 if (!NIL_P(zone)) {
5756 zone = mload_zone(time, zone);
5757 RB_OBJ_WRITE(time, &tobj->vtm.zone, zone);
5758 zone_localtime(zone, time);
5759 }
5760
5761 return time;
5762
5763 invalid_format:
5764 rb_raise(rb_eTypeError, "marshaled time format differ");
5766}
5767
5768/* :nodoc: */
5769static VALUE
5770time_load(VALUE klass, VALUE str)
5771{
5772 VALUE time = time_s_alloc(klass);
5773
5774 time_mload(time, str);
5775 return time;
5776}
5777
5778/* :nodoc:*/
5779/* Document-class: Time::tm
5780 *
5781 * A container class for timezone conversion.
5782 */
5783
5784/*
5785 * call-seq:
5786 * Time::tm.from_time(t) -> tm
5787 *
5788 * Creates new Time::tm object from a Time object.
5789 */
5790
5791static VALUE
5792tm_from_time(VALUE klass, VALUE time)
5793{
5794 struct time_object *tobj;
5795 struct vtm vtm, *v;
5796 VALUE tm;
5797 struct time_object *ttm;
5798
5799 GetTimeval(time, tobj);
5800 tm = time_s_alloc(klass);
5801 ttm = RTYPEDDATA_GET_DATA(tm);
5802 v = &vtm;
5803
5804 WIDEVALUE timew = tobj->timew;
5805 GMTIMEW(timew, v);
5806 time_set_timew(tm, ttm, wsub(timew, v->subsecx));
5807 v->subsecx = INT2FIX(0);
5808 v->zone = Qnil;
5809 time_set_vtm(tm, ttm, *v);
5810
5811 ttm->vtm.tm_got = 1;
5812 TZMODE_SET_UTC(ttm);
5813 return tm;
5814}
5815
5816/*
5817 * call-seq:
5818 * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm
5819 *
5820 * Creates new Time::tm object.
5821 */
5822
5823static VALUE
5824tm_initialize(int argc, VALUE *argv, VALUE time)
5825{
5826 struct vtm vtm;
5827 wideval_t t;
5828
5829 if (rb_check_arity(argc, 1, 7) > 6) argc = 6;
5830 time_arg(argc, argv, &vtm);
5831 t = timegmw(&vtm);
5832 struct time_object *tobj = RTYPEDDATA_GET_DATA(time);
5833 TZMODE_SET_UTC(tobj);
5834 time_set_timew(time, tobj, t);
5835 time_set_vtm(time, tobj, vtm);
5836
5837 return time;
5838}
5839
5840/* call-seq:
5841 * tm.to_time -> time
5842 *
5843 * Returns a new Time object.
5844 */
5845
5846static VALUE
5847tm_to_time(VALUE tm)
5848{
5849 struct time_object *torig = get_timeval(tm);
5850 VALUE dup = time_s_alloc(rb_cTime);
5851 struct time_object *tobj = RTYPEDDATA_GET_DATA(dup);
5852 *tobj = *torig;
5853 return dup;
5854}
5855
5856static VALUE
5857tm_plus(VALUE tm, VALUE offset)
5858{
5859 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, +1);
5860}
5861
5862static VALUE
5863tm_minus(VALUE tm, VALUE offset)
5864{
5865 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, -1);
5866}
5867
5868static VALUE
5869Init_tm(VALUE outer, const char *name)
5870{
5871 /* :stopdoc:*/
5872 VALUE tm;
5873 tm = rb_define_class_under(outer, name, rb_cObject);
5874 rb_define_alloc_func(tm, time_s_alloc);
5875 rb_define_method(tm, "sec", time_sec, 0);
5876 rb_define_method(tm, "min", time_min, 0);
5877 rb_define_method(tm, "hour", time_hour, 0);
5878 rb_define_method(tm, "mday", time_mday, 0);
5879 rb_define_method(tm, "day", time_mday, 0);
5880 rb_define_method(tm, "mon", time_mon, 0);
5881 rb_define_method(tm, "month", time_mon, 0);
5882 rb_define_method(tm, "year", time_year, 0);
5883 rb_define_method(tm, "isdst", time_isdst, 0);
5884 rb_define_method(tm, "dst?", time_isdst, 0);
5885 rb_define_method(tm, "zone", time_zone, 0);
5886 rb_define_method(tm, "gmtoff", rb_time_utc_offset, 0);
5887 rb_define_method(tm, "gmt_offset", rb_time_utc_offset, 0);
5888 rb_define_method(tm, "utc_offset", rb_time_utc_offset, 0);
5889 rb_define_method(tm, "utc?", time_utc_p, 0);
5890 rb_define_method(tm, "gmt?", time_utc_p, 0);
5891 rb_define_method(tm, "to_s", time_to_s, 0);
5892 rb_define_method(tm, "inspect", time_inspect, 0);
5893 rb_define_method(tm, "to_a", time_to_a, 0);
5894 rb_define_method(tm, "tv_sec", time_to_i, 0);
5895 rb_define_method(tm, "tv_usec", time_usec, 0);
5896 rb_define_method(tm, "usec", time_usec, 0);
5897 rb_define_method(tm, "tv_nsec", time_nsec, 0);
5898 rb_define_method(tm, "nsec", time_nsec, 0);
5899 rb_define_method(tm, "subsec", time_subsec, 0);
5900 rb_define_method(tm, "to_i", time_to_i, 0);
5901 rb_define_method(tm, "to_f", time_to_f, 0);
5902 rb_define_method(tm, "to_r", time_to_r, 0);
5903 rb_define_method(tm, "+", tm_plus, 1);
5904 rb_define_method(tm, "-", tm_minus, 1);
5905 rb_define_method(tm, "initialize", tm_initialize, -1);
5906 rb_define_method(tm, "utc", tm_to_time, 0);
5907 rb_alias(tm, rb_intern_const("to_time"), rb_intern_const("utc"));
5908 rb_define_singleton_method(tm, "from_time", tm_from_time, 1);
5909 /* :startdoc:*/
5910
5911 return tm;
5912}
5913
5914VALUE
5915rb_time_zone_abbreviation(VALUE zone, VALUE time)
5916{
5917 VALUE tm, abbr, strftime_args[2];
5918
5919 abbr = rb_check_string_type(zone);
5920 if (!NIL_P(abbr)) return abbr;
5921
5922 tm = tm_from_time(rb_cTimeTM, time);
5923 abbr = rb_check_funcall(zone, rb_intern("abbr"), 1, &tm);
5924 if (!UNDEF_P(abbr)) {
5925 goto found;
5926 }
5927#ifdef SUPPORT_TZINFO_ZONE_ABBREVIATION
5928 abbr = rb_check_funcall(zone, rb_intern("period_for_utc"), 1, &tm);
5929 if (!UNDEF_P(abbr)) {
5930 abbr = rb_funcallv(abbr, rb_intern("abbreviation"), 0, 0);
5931 goto found;
5932 }
5933#endif
5934 strftime_args[0] = rb_fstring_lit("%Z");
5935 strftime_args[1] = tm;
5936 abbr = rb_check_funcall(zone, rb_intern("strftime"), 2, strftime_args);
5937 if (!UNDEF_P(abbr)) {
5938 goto found;
5939 }
5940 abbr = rb_check_funcall_default(zone, idName, 0, 0, Qnil);
5941 found:
5942 return rb_obj_as_string(abbr);
5943}
5944
5945//
5946void
5947Init_Time(void)
5948{
5949#ifdef _WIN32
5950 ruby_reset_timezone(getenv("TZ"));
5951#endif
5952
5953 id_submicro = rb_intern_const("submicro");
5954 id_nano_num = rb_intern_const("nano_num");
5955 id_nano_den = rb_intern_const("nano_den");
5956 id_offset = rb_intern_const("offset");
5957 id_zone = rb_intern_const("zone");
5958 id_nanosecond = rb_intern_const("nanosecond");
5959 id_microsecond = rb_intern_const("microsecond");
5960 id_millisecond = rb_intern_const("millisecond");
5961 id_nsec = rb_intern_const("nsec");
5962 id_usec = rb_intern_const("usec");
5963 id_local_to_utc = rb_intern_const("local_to_utc");
5964 id_utc_to_local = rb_intern_const("utc_to_local");
5965 id_year = rb_intern_const("year");
5966 id_mon = rb_intern_const("mon");
5967 id_mday = rb_intern_const("mday");
5968 id_hour = rb_intern_const("hour");
5969 id_min = rb_intern_const("min");
5970 id_sec = rb_intern_const("sec");
5971 id_isdst = rb_intern_const("isdst");
5972 id_find_timezone = rb_intern_const("find_timezone");
5973
5974 sym_year = ID2SYM(rb_intern_const("year"));
5975 sym_month = ID2SYM(rb_intern_const("month"));
5976 sym_yday = ID2SYM(rb_intern_const("yday"));
5977 sym_wday = ID2SYM(rb_intern_const("wday"));
5978 sym_day = ID2SYM(rb_intern_const("day"));
5979 sym_hour = ID2SYM(rb_intern_const("hour"));
5980 sym_min = ID2SYM(rb_intern_const("min"));
5981 sym_sec = ID2SYM(rb_intern_const("sec"));
5982 sym_subsec = ID2SYM(rb_intern_const("subsec"));
5983 sym_dst = ID2SYM(rb_intern_const("dst"));
5984 sym_zone = ID2SYM(rb_intern_const("zone"));
5985
5986 str_utc = rb_fstring_lit("UTC");
5987 rb_vm_register_global_object(str_utc);
5988 str_empty = rb_fstring_lit("");
5989 rb_vm_register_global_object(str_empty);
5990
5991 rb_cTime = rb_define_class("Time", rb_cObject);
5994
5995 rb_define_alloc_func(rb_cTime, time_s_alloc);
5996 rb_define_singleton_method(rb_cTime, "utc", time_s_mkutc, -1);
5997 rb_define_singleton_method(rb_cTime, "local", time_s_mktime, -1);
5998 rb_define_alias(scTime, "gm", "utc");
5999 rb_define_alias(scTime, "mktime", "local");
6000
6001 rb_define_method(rb_cTime, "to_i", time_to_i, 0);
6002 rb_define_method(rb_cTime, "to_f", time_to_f, 0);
6003 rb_define_method(rb_cTime, "to_r", time_to_r, 0);
6004 rb_define_method(rb_cTime, "<=>", time_cmp, 1);
6005 rb_define_method(rb_cTime, "eql?", time_eql, 1);
6006 rb_define_method(rb_cTime, "hash", time_hash, 0);
6007 rb_define_method(rb_cTime, "initialize_copy", time_init_copy, 1);
6008
6009 rb_define_method(rb_cTime, "localtime", time_localtime_m, -1);
6010 rb_define_method(rb_cTime, "gmtime", time_gmtime, 0);
6011 rb_define_method(rb_cTime, "utc", time_gmtime, 0);
6012 rb_define_method(rb_cTime, "getlocal", time_getlocaltime, -1);
6013 rb_define_method(rb_cTime, "getgm", time_getgmtime, 0);
6014 rb_define_method(rb_cTime, "getutc", time_getgmtime, 0);
6015
6016 rb_define_method(rb_cTime, "ctime", time_asctime, 0);
6017 rb_define_method(rb_cTime, "asctime", time_asctime, 0);
6018 rb_define_method(rb_cTime, "to_s", time_to_s, 0);
6019 rb_define_method(rb_cTime, "inspect", time_inspect, 0);
6020 rb_define_method(rb_cTime, "to_a", time_to_a, 0);
6021 rb_define_method(rb_cTime, "deconstruct_keys", time_deconstruct_keys, 1);
6022
6023 rb_define_method(rb_cTime, "+", time_plus, 1);
6024 rb_define_method(rb_cTime, "-", time_minus, 1);
6025
6026 rb_define_method(rb_cTime, "round", time_round, -1);
6027 rb_define_method(rb_cTime, "floor", time_floor, -1);
6028 rb_define_method(rb_cTime, "ceil", time_ceil, -1);
6029
6030 rb_define_method(rb_cTime, "sec", time_sec, 0);
6031 rb_define_method(rb_cTime, "min", time_min, 0);
6032 rb_define_method(rb_cTime, "hour", time_hour, 0);
6033 rb_define_method(rb_cTime, "mday", time_mday, 0);
6034 rb_define_method(rb_cTime, "day", time_mday, 0);
6035 rb_define_method(rb_cTime, "mon", time_mon, 0);
6036 rb_define_method(rb_cTime, "month", time_mon, 0);
6037 rb_define_method(rb_cTime, "year", time_year, 0);
6038 rb_define_method(rb_cTime, "wday", time_wday, 0);
6039 rb_define_method(rb_cTime, "yday", time_yday, 0);
6040 rb_define_method(rb_cTime, "isdst", time_isdst, 0);
6041 rb_define_method(rb_cTime, "dst?", time_isdst, 0);
6042 rb_define_method(rb_cTime, "zone", time_zone, 0);
6043 rb_define_method(rb_cTime, "gmtoff", rb_time_utc_offset, 0);
6044 rb_define_method(rb_cTime, "gmt_offset", rb_time_utc_offset, 0);
6045 rb_define_method(rb_cTime, "utc_offset", rb_time_utc_offset, 0);
6046
6047 rb_define_method(rb_cTime, "utc?", time_utc_p, 0);
6048 rb_define_method(rb_cTime, "gmt?", time_utc_p, 0);
6049
6050 rb_define_method(rb_cTime, "sunday?", time_sunday, 0);
6051 rb_define_method(rb_cTime, "monday?", time_monday, 0);
6052 rb_define_method(rb_cTime, "tuesday?", time_tuesday, 0);
6053 rb_define_method(rb_cTime, "wednesday?", time_wednesday, 0);
6054 rb_define_method(rb_cTime, "thursday?", time_thursday, 0);
6055 rb_define_method(rb_cTime, "friday?", time_friday, 0);
6056 rb_define_method(rb_cTime, "saturday?", time_saturday, 0);
6057
6058 rb_define_method(rb_cTime, "tv_sec", time_to_i, 0);
6059 rb_define_method(rb_cTime, "tv_usec", time_usec, 0);
6060 rb_define_method(rb_cTime, "usec", time_usec, 0);
6061 rb_define_method(rb_cTime, "tv_nsec", time_nsec, 0);
6062 rb_define_method(rb_cTime, "nsec", time_nsec, 0);
6063 rb_define_method(rb_cTime, "subsec", time_subsec, 0);
6064
6065 rb_define_method(rb_cTime, "strftime", time_strftime, 1);
6066 rb_define_method(rb_cTime, "xmlschema", time_xmlschema, -1);
6067 rb_define_alias(rb_cTime, "iso8601", "xmlschema");
6068
6069 /* methods for marshaling */
6070 rb_define_private_method(rb_cTime, "_dump", time_dump, -1);
6071 rb_define_private_method(scTime, "_load", time_load, 1);
6072
6073 if (debug_find_time_numguess) {
6074 rb_define_hooked_variable("$find_time_numguess", (VALUE *)&find_time_numguess,
6075 find_time_numguess_getter, 0);
6076 }
6077
6078 rb_cTimeTM = Init_tm(rb_cTime, "tm");
6079}
6080
6081#include "timev.rbinc"
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_private_method(klass, mid, func, arity)
Defines klass#mid and makes it private.
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1701
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:1484
VALUE rb_singleton_class(VALUE obj)
Finds or creates the singleton class of the passed object.
Definition class.c:2800
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1520
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2848
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition class.c:3138
#define TYPE(_)
Old name of rb_type.
Definition value_type.h:108
#define RB_INTEGER_TYPE_P
Old name of rb_integer_type_p.
Definition value_type.h:87
#define OBJ_INIT_COPY(obj, orig)
Old name of RB_OBJ_INIT_COPY.
Definition object.h:41
#define ISSPACE
Old name of rb_isspace.
Definition ctype.h:88
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define T_NIL
Old name of RUBY_T_NIL.
Definition value_type.h:72
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition value_type.h:79
#define T_FIXNUM
Old name of RUBY_T_FIXNUM.
Definition value_type.h:63
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define ISDIGIT
Old name of rb_isdigit.
Definition ctype.h:93
#define ASSUME
Old name of RBIMPL_ASSUME.
Definition assume.h:27
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition value_type.h:76
#define rb_ary_new3
Old name of rb_ary_new_from_args.
Definition array.h:658
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
#define STRNCASECMP
Old name of st_locale_insensitive_strncasecmp.
Definition ctype.h:103
#define ISASCII
Old name of rb_isascii.
Definition ctype.h:85
#define ULL2NUM
Old name of RB_ULL2NUM.
Definition long_long.h:31
#define FIXNUM_MIN
Old name of RUBY_FIXNUM_MIN.
Definition fixnum.h:27
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define NUM2SIZET
Old name of RB_NUM2SIZE.
Definition size_t.h:61
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:682
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
Definition error.c:1380
VALUE rb_eRangeError
RangeError exception.
Definition error.c:1434
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1430
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1428
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1481
void rb_warning(const char *fmt,...)
Issues a warning.
Definition error.c:497
VALUE rb_cTime
Time class.
Definition time.c:679
VALUE rb_Float(VALUE val)
This is the logic behind Kernel#Float.
Definition object.c:3655
VALUE rb_check_to_int(VALUE val)
Identical to rb_check_to_integer(), except it uses #to_int for conversion.
Definition object.c:3229
VALUE rb_Integer(VALUE val)
This is the logic behind Kernel#Integer.
Definition object.c:3298
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:243
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
Definition object.c:175
VALUE rb_mComparable
Comparable module.
Definition compar.c:19
VALUE rb_to_int(VALUE val)
Identical to rb_check_to_int(), except it raises in case of conversion mismatch.
Definition object.c:3223
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
Encoding relates APIs.
static bool rb_enc_str_asciicompat_p(VALUE str)
Queries if the passed string is in an ASCII-compatible encoding.
Definition encoding.h:789
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1117
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_check_array_type(VALUE obj)
Try converting an object to its array representation using its to_ary method, if any.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
#define INTEGER_PACK_NATIVE_BYTE_ORDER
Means either INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST, depending on the host processor'...
Definition bignum.h:546
#define INTEGER_PACK_LITTLE_ENDIAN
Little endian combination.
Definition bignum.h:567
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
void rb_num_zerodiv(void)
Just always raises an exception.
Definition numeric.c:206
VALUE rb_int_positive_pow(long x, unsigned long y)
Raises the passed x to the power of y.
Definition numeric.c:4559
VALUE rb_rational_new(VALUE num, VALUE den)
Constructs a Rational, with reduction.
Definition rational.c:1974
#define rb_Rational1(x)
Shorthand of (x/1)r.
Definition rational.h:116
VALUE rb_str_subseq(VALUE str, long beg, long len)
Identical to rb_str_substr(), except the numbers are interpreted as byte offsets instead of character...
Definition string.c:3458
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1498
#define rb_usascii_str_new(str, len)
Identical to rb_str_new, except it generates a string of "US ASCII" encoding.
Definition string.h:1532
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:2302
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3870
#define rb_usascii_str_new_cstr(str)
Identical to rb_str_new_cstr, except it generates a string of "US ASCII" encoding.
Definition string.h:1567
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3692
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
Definition string.c:4351
#define rb_strlen_lit(str)
Length of a string literal.
Definition string.h:1692
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition string.c:3255
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1656
#define rb_utf8_str_new(str, len)
Identical to rb_str_new, except it generates a string of "UTF-8" encoding.
Definition string.h:1549
void rb_str_modify_expand(VALUE str, long capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
Definition string.c:3049
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1514
VALUE rb_obj_as_string(VALUE obj)
Try converting an object to its stringised representation using its to_s method, if any.
Definition string.c:2161
VALUE rb_time_nano_new(time_t sec, long nsec)
Identical to rb_time_new(), except it accepts the time in nanoseconds resolution.
Definition time.c:2814
void rb_timespec_now(struct timespec *ts)
Fills the current time into the given struct.
Definition time.c:2016
VALUE rb_time_timespec_new(const struct timespec *ts, int offset)
Creates an instance of rb_cTime, with given time and offset.
Definition time.c:2822
struct timespec rb_time_timespec(VALUE time)
Identical to rb_time_timeval(), except for return type.
Definition time.c:2985
VALUE rb_time_new(time_t sec, long usec)
Creates an instance of rb_cTime with the given time and the local timezone.
Definition time.c:2805
struct timeval rb_time_timeval(VALUE time)
Converts an instance of rb_cTime to a struct timeval that represents the identical point of time.
Definition time.c:2968
struct timeval rb_time_interval(VALUE num)
Creates a "time interval".
Definition time.c:2962
VALUE rb_time_num_new(VALUE timev, VALUE off)
Identical to rb_time_timespec_new(), except it takes Ruby values instead of C structs.
Definition time.c:2845
VALUE rb_time_utc_offset(VALUE time)
Queries the offset, in seconds between the time zone of the time and the UTC.
Definition time.c:5092
struct timespec rb_time_timespec_interval(VALUE num)
Identical to rb_time_interval(), except for return type.
Definition time.c:2999
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:2079
int rb_respond_to(VALUE obj, ID mid)
Queries if the object responds to the method.
Definition vm_method.c:3094
void rb_alias(VALUE klass, ID dst, ID src)
Resembles alias.
Definition vm_method.c:2423
VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcallv(), except it returns RUBY_Qundef instead of raising rb_eNoMethodError.
Definition vm_eval.c:686
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:284
int off
Offset inside of ptr.
Definition io.h:5
int len
Length of the buffer.
Definition io.h:8
#define DECIMAL_SIZE_OF(expr)
An approximation of decimal representation size.
Definition util.h:48
#define rb_long2int
Just another name of rb_long2int_inline.
Definition long.h:62
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
void rb_define_hooked_variable(const char *q, VALUE *w, type *e, void_type *r)
Define a function-backended global variable.
VALUE rb_rescue(type *q, VALUE w, type *e, VALUE r)
An equivalent of rescue clause.
void rb_copy_generic_ivar(VALUE clone, VALUE obj)
Copies the list of instance variables.
Definition variable.c:2276
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
Definition rstring.h:76
VALUE rb_str_export_locale(VALUE obj)
Identical to rb_str_export(), except it converts into the locale encoding instead.
Definition string.c:1762
static char * RSTRING_END(VALUE str)
Queries the end of the contents pointer of the string.
Definition rstring.h:442
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:79
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:515
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:497
#define RTEST
This is an old name of RB_TEST.
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:203
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:210
Definition timev.h:5
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
Definition value.h:63
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_FLOAT_TYPE_P(VALUE obj)
Queries if the object is an instance of rb_cFloat.
Definition value_type.h:264
static bool rb_integer_type_p(VALUE obj)
Queries if the object is an instance of rb_cInteger.
Definition value_type.h:204
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376