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