Ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e0ba6b95ab71a441357ed5484e33498)
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);
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 VALUE time_get_tm(VALUE, struct time_object *);
1769#define MAKE_TM(time, tobj) \
1770 do { \
1771 if ((tobj)->tm_got == 0) { \
1772 time_get_tm((time), (tobj)); \
1773 } \
1774 } while (0)
1775#define MAKE_TM_ENSURE(time, tobj, cond) \
1776 do { \
1777 MAKE_TM(time, tobj); \
1778 if (!(cond)) { \
1779 VALUE zone = (tobj)->vtm.zone; \
1780 if (!NIL_P(zone)) zone_localtime(zone, (time)); \
1781 } \
1782 } while (0)
1783
1784static void
1785time_mark(void *ptr)
1786{
1787 struct time_object *tobj = ptr;
1788 if (!FIXWV_P(tobj->timew))
1789 rb_gc_mark(w2v(tobj->timew));
1790 rb_gc_mark(tobj->vtm.year);
1791 rb_gc_mark(tobj->vtm.subsecx);
1792 rb_gc_mark(tobj->vtm.utc_offset);
1793 rb_gc_mark(tobj->vtm.zone);
1794}
1795
1796static size_t
1797time_memsize(const void *tobj)
1798{
1799 return sizeof(struct time_object);
1800}
1801
1802static const rb_data_type_t time_data_type = {
1803 "time",
1804 {time_mark, RUBY_TYPED_DEFAULT_FREE, time_memsize,},
1805 0, 0,
1806 (RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE),
1807};
1808
1809static VALUE
1810time_s_alloc(VALUE klass)
1811{
1812 VALUE obj;
1813 struct time_object *tobj;
1814
1815 obj = TypedData_Make_Struct(klass, struct time_object, &time_data_type, tobj);
1816 tobj->tzmode = TIME_TZMODE_UNINITIALIZED;
1817 tobj->tm_got=0;
1818 tobj->timew = WINT2FIXWV(0);
1819 tobj->vtm.zone = Qnil;
1820
1821 return obj;
1822}
1823
1824static struct time_object *
1825get_timeval(VALUE obj)
1826{
1827 struct time_object *tobj;
1828 TypedData_Get_Struct(obj, struct time_object, &time_data_type, tobj);
1829 if (!TIME_INIT_P(tobj)) {
1830 rb_raise(rb_eTypeError, "uninitialized %"PRIsVALUE, rb_obj_class(obj));
1831 }
1832 return tobj;
1833}
1834
1835static struct time_object *
1836get_new_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, "already initialized %"PRIsVALUE, rb_obj_class(obj));
1842 }
1843 return tobj;
1844}
1845
1846static void
1847time_modify(VALUE time)
1848{
1849 rb_check_frozen(time);
1850}
1851
1852static wideval_t
1853timenano2timew(time_t sec, long nsec)
1854{
1855 wideval_t timew;
1856
1857 timew = rb_time_magnify(TIMET2WV(sec));
1858 if (nsec)
1859 timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000));
1860 return timew;
1861}
1862
1863static struct timespec
1864timew2timespec(wideval_t timew)
1865{
1866 VALUE subsecx;
1867 struct timespec ts;
1868 wideval_t timew2;
1869
1870 if (timew_out_of_timet_range(timew))
1871 rb_raise(rb_eArgError, "time out of system range");
1872 split_second(timew, &timew2, &subsecx);
1873 ts.tv_sec = WV2TIMET(timew2);
1874 ts.tv_nsec = NUM2LONG(mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE)));
1875 return ts;
1876}
1877
1878static struct timespec *
1879timew2timespec_exact(wideval_t timew, struct timespec *ts)
1880{
1881 VALUE subsecx;
1882 wideval_t timew2;
1883 VALUE nsecv;
1884
1885 if (timew_out_of_timet_range(timew))
1886 return NULL;
1887 split_second(timew, &timew2, &subsecx);
1888 ts->tv_sec = WV2TIMET(timew2);
1889 nsecv = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
1890 if (!FIXNUM_P(nsecv))
1891 return NULL;
1892 ts->tv_nsec = NUM2LONG(nsecv);
1893 return ts;
1894}
1895
1896void
1898{
1899#ifdef HAVE_CLOCK_GETTIME
1900 if (clock_gettime(CLOCK_REALTIME, ts) == -1) {
1901 rb_sys_fail("clock_gettime");
1902 }
1903#else
1904 {
1905 struct timeval tv;
1906 if (gettimeofday(&tv, 0) < 0) {
1907 rb_sys_fail("gettimeofday");
1908 }
1909 ts->tv_sec = tv.tv_sec;
1910 ts->tv_nsec = tv.tv_usec * 1000;
1911 }
1912#endif
1913}
1914
1915static VALUE
1916time_init_now(rb_execution_context_t *ec, VALUE time, VALUE zone)
1917{
1918 struct time_object *tobj;
1919 struct timespec ts;
1920
1921 time_modify(time);
1922 GetNewTimeval(time, tobj);
1923 tobj->tzmode = TIME_TZMODE_LOCALTIME;
1924 tobj->tm_got=0;
1925 tobj->timew = WINT2FIXWV(0);
1926 rb_timespec_now(&ts);
1927 tobj->timew = timenano2timew(ts.tv_sec, ts.tv_nsec);
1928
1929 if (!NIL_P(zone)) {
1930 time_zonelocal(time, zone);
1931 }
1932 return time;
1933}
1934
1935static VALUE
1936time_set_utc_offset(VALUE time, VALUE off)
1937{
1938 struct time_object *tobj;
1939 off = num_exact(off);
1940
1941 time_modify(time);
1942 GetTimeval(time, tobj);
1943
1944 tobj->tm_got = 0;
1945 tobj->vtm.zone = Qnil;
1946 TZMODE_SET_FIXOFF(tobj, off);
1947
1948 return time;
1949}
1950
1951static void
1952vtm_add_offset(struct vtm *vtm, VALUE off, int sign)
1953{
1954 VALUE subsec, v;
1955 int sec, min, hour;
1956 int day;
1957
1958 if (lt(off, INT2FIX(0))) {
1959 sign = -sign;
1960 off = neg(off);
1961 }
1962 divmodv(off, INT2FIX(1), &off, &subsec);
1963 divmodv(off, INT2FIX(60), &off, &v);
1964 sec = NUM2INT(v);
1965 divmodv(off, INT2FIX(60), &off, &v);
1966 min = NUM2INT(v);
1967 divmodv(off, INT2FIX(24), &off, &v);
1968 hour = NUM2INT(v);
1969
1970 if (sign < 0) {
1971 subsec = neg(subsec);
1972 sec = -sec;
1973 min = -min;
1974 hour = -hour;
1975 }
1976
1977 day = 0;
1978
1979 if (!rb_equal(subsec, INT2FIX(0))) {
1980 vtm->subsecx = addv(vtm->subsecx, w2v(rb_time_magnify(v2w(subsec))));
1981 if (lt(vtm->subsecx, INT2FIX(0))) {
1982 vtm->subsecx = addv(vtm->subsecx, INT2FIX(TIME_SCALE));
1983 sec -= 1;
1984 }
1985 if (le(INT2FIX(TIME_SCALE), vtm->subsecx)) {
1986 vtm->subsecx = subv(vtm->subsecx, INT2FIX(TIME_SCALE));
1987 sec += 1;
1988 }
1989 }
1990 if (sec) {
1991 /* If sec + subsec == 0, don't change vtm->sec.
1992 * It may be 60 which is a leap second. */
1993 sec += vtm->sec;
1994 if (sec < 0) {
1995 sec += 60;
1996 min -= 1;
1997 }
1998 if (60 <= sec) {
1999 sec -= 60;
2000 min += 1;
2001 }
2002 vtm->sec = sec;
2003 }
2004 if (min) {
2005 min += vtm->min;
2006 if (min < 0) {
2007 min += 60;
2008 hour -= 1;
2009 }
2010 if (60 <= min) {
2011 min -= 60;
2012 hour += 1;
2013 }
2014 vtm->min = min;
2015 }
2016 if (hour) {
2017 hour += vtm->hour;
2018 if (hour < 0) {
2019 hour += 24;
2020 day = -1;
2021 }
2022 if (24 <= hour) {
2023 hour -= 24;
2024 day = 1;
2025 }
2026 vtm->hour = hour;
2027 }
2028
2029 vtm_add_day(vtm, day);
2030}
2031
2032static void
2033vtm_add_day(struct vtm *vtm, int day)
2034{
2035 if (day) {
2036 if (day < 0) {
2037 if (vtm->mon == 1 && vtm->mday == 1) {
2038 vtm->mday = 31;
2039 vtm->mon = 12; /* December */
2040 vtm->year = subv(vtm->year, INT2FIX(1));
2041 vtm->yday = leap_year_v_p(vtm->year) ? 366 : 365;
2042 }
2043 else if (vtm->mday == 1) {
2044 const int8_t *days_in_month = days_in_month_in_v(vtm->year);
2045 vtm->mon--;
2046 vtm->mday = days_in_month[vtm->mon-1];
2047 vtm->yday--;
2048 }
2049 else {
2050 vtm->mday--;
2051 vtm->yday--;
2052 }
2053 vtm->wday = (vtm->wday + 6) % 7;
2054 }
2055 else {
2056 int leap = leap_year_v_p(vtm->year);
2057 if (vtm->mon == 12 && vtm->mday == 31) {
2058 vtm->year = addv(vtm->year, INT2FIX(1));
2059 vtm->mon = 1; /* January */
2060 vtm->mday = 1;
2061 vtm->yday = 1;
2062 }
2063 else if (vtm->mday == days_in_month_of(leap)[vtm->mon-1]) {
2064 vtm->mon++;
2065 vtm->mday = 1;
2066 vtm->yday++;
2067 }
2068 else {
2069 vtm->mday++;
2070 vtm->yday++;
2071 }
2072 vtm->wday = (vtm->wday + 1) % 7;
2073 }
2074 }
2075}
2076
2077static int
2078maybe_tzobj_p(VALUE obj)
2079{
2080 if (NIL_P(obj)) return FALSE;
2081 if (RB_INTEGER_TYPE_P(obj)) return FALSE;
2082 if (RB_TYPE_P(obj, T_STRING)) return FALSE;
2083 return TRUE;
2084}
2085
2086NORETURN(static void invalid_utc_offset(VALUE));
2087static void
2088invalid_utc_offset(VALUE zone)
2089{
2090 rb_raise(rb_eArgError, "\"+HH:MM\", \"-HH:MM\", \"UTC\" or "
2091 "\"A\"..\"I\",\"K\"..\"Z\" expected for utc_offset: %"PRIsVALUE,
2092 zone);
2093}
2094
2095static VALUE
2096utc_offset_arg(VALUE arg)
2097{
2098 VALUE tmp;
2099 if (!NIL_P(tmp = rb_check_string_type(arg))) {
2100 int n = 0;
2101 const char *s = RSTRING_PTR(tmp), *min = NULL, *sec = NULL;
2102 if (!rb_enc_str_asciicompat_p(tmp)) {
2103 goto invalid_utc_offset;
2104 }
2105 switch (RSTRING_LEN(tmp)) {
2106 case 1:
2107 if (s[0] == 'Z') {
2108 return UTC_ZONE;
2109 }
2110 /* Military Time Zone Names */
2111 if (s[0] >= 'A' && s[0] <= 'I') {
2112 n = (int)s[0] - 'A' + 1;
2113 }
2114 else if (s[0] >= 'K' && s[0] <= 'M') {
2115 n = (int)s[0] - 'A';
2116 }
2117 else if (s[0] >= 'N' && s[0] <= 'Y') {
2118 n = 'M' - (int)s[0];
2119 }
2120 else {
2121 goto invalid_utc_offset;
2122 }
2123 n *= 3600;
2124 return INT2FIX(n);
2125 case 3:
2126 if (STRNCASECMP("UTC", s, 3) == 0) {
2127 return UTC_ZONE;
2128 }
2129 break; /* +HH */
2130 case 5: /* +HHMM */
2131 min = s+3;
2132 break;
2133 case 6: /* +HH:MM */
2134 min = s+4;
2135 break;
2136 case 7: /* +HHMMSS */
2137 sec = s+5;
2138 min = s+3;
2139 break;
2140 case 9: /* +HH:MM:SS */
2141 sec = s+7;
2142 min = s+4;
2143 break;
2144 default:
2145 goto invalid_utc_offset;
2146 }
2147 if (sec) {
2148 if (sec == s+7 && *(sec-1) != ':') goto invalid_utc_offset;
2149 if (!ISDIGIT(sec[0]) || !ISDIGIT(sec[1])) goto invalid_utc_offset;
2150 n += (sec[0] * 10 + sec[1] - '0' * 11);
2151 }
2152 if (min) {
2153 if (min == s+4 && *(min-1) != ':') goto invalid_utc_offset;
2154 if (!ISDIGIT(min[0]) || !ISDIGIT(min[1])) goto invalid_utc_offset;
2155 if (min[0] > '5') goto invalid_utc_offset;
2156 n += (min[0] * 10 + min[1] - '0' * 11) * 60;
2157 }
2158 if (s[0] != '+' && s[0] != '-') goto invalid_utc_offset;
2159 if (!ISDIGIT(s[1]) || !ISDIGIT(s[2])) goto invalid_utc_offset;
2160 n += (s[1] * 10 + s[2] - '0' * 11) * 3600;
2161 if (s[0] == '-') {
2162 if (n == 0) return UTC_ZONE;
2163 n = -n;
2164 }
2165 return INT2FIX(n);
2166 }
2167 else {
2168 return num_exact(arg);
2169 }
2170 invalid_utc_offset:
2171 return Qnil;
2172}
2173
2174static void
2175zone_set_offset(VALUE zone, struct time_object *tobj,
2176 wideval_t tlocal, wideval_t tutc)
2177{
2178 /* tlocal and tutc must be unmagnified and in seconds */
2179 wideval_t w = wsub(tlocal, tutc);
2180 VALUE off = w2v(w);
2181 validate_utc_offset(off);
2182 tobj->vtm.utc_offset = off;
2183 tobj->vtm.zone = zone;
2184 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2185}
2186
2187static wideval_t
2188extract_time(VALUE time)
2189{
2190 wideval_t t;
2191 const ID id_to_i = idTo_i;
2192
2193#define EXTRACT_TIME() do { \
2194 t = v2w(rb_Integer(AREF(to_i))); \
2195 } while (0)
2196
2197 if (rb_typeddata_is_kind_of(time, &time_data_type)) {
2198 struct time_object *tobj = DATA_PTR(time);
2199
2200 time_gmtime(time); /* ensure tm got */
2201 t = rb_time_unmagnify(tobj->timew);
2202 }
2203 else if (RB_TYPE_P(time, T_STRUCT)) {
2204#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2205 EXTRACT_TIME();
2206#undef AREF
2207 }
2208 else {
2209#define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2210 EXTRACT_TIME();
2211#undef AREF
2212 }
2213#undef EXTRACT_TIME
2214
2215 return t;
2216}
2217
2218static wideval_t
2219extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx)
2220{
2221 wideval_t t;
2222 const ID id_to_i = idTo_i;
2223
2224#define EXTRACT_VTM() do { \
2225 VALUE subsecx; \
2226 vtm->year = obj2vint(AREF(year)); \
2227 vtm->mon = month_arg(AREF(mon)); \
2228 vtm->mday = obj2ubits(AREF(mday), 5); \
2229 vtm->hour = obj2ubits(AREF(hour), 5); \
2230 vtm->min = obj2ubits(AREF(min), 6); \
2231 vtm->sec = obj2subsecx(AREF(sec), &subsecx); \
2232 vtm->isdst = RTEST(AREF(isdst)); \
2233 vtm->utc_offset = Qnil; \
2234 t = v2w(rb_Integer(AREF(to_i))); \
2235 } while (0)
2236
2237 if (rb_typeddata_is_kind_of(time, &time_data_type)) {
2238 struct time_object *tobj = DATA_PTR(time);
2239
2240 time_get_tm(time, tobj);
2241 *vtm = tobj->vtm;
2242 t = rb_time_unmagnify(tobj->timew);
2243 if (TZMODE_FIXOFF_P(tobj) && vtm->utc_offset != INT2FIX(0))
2244 t = wadd(t, v2w(vtm->utc_offset));
2245 }
2246 else if (RB_TYPE_P(time, T_STRUCT)) {
2247#define AREF(x) rb_struct_aref(time, ID2SYM(id_##x))
2248 EXTRACT_VTM();
2249#undef AREF
2250 }
2251 else if (rb_integer_type_p(time)) {
2252 t = v2w(time);
2253 GMTIMEW(rb_time_magnify(t), vtm);
2254 }
2255 else {
2256#define AREF(x) rb_funcallv(time, id_##x, 0, 0)
2257 EXTRACT_VTM();
2258#undef AREF
2259 }
2260#undef EXTRACT_VTM
2261 vtm->subsecx = subsecx;
2262 validate_vtm(vtm);
2263 return t;
2264}
2265
2266static void
2267zone_set_dst(VALUE zone, struct time_object *tobj, VALUE tm)
2268{
2269 ID id_dst_p;
2270 VALUE dst;
2271 CONST_ID(id_dst_p, "dst?");
2272 dst = rb_check_funcall(zone, id_dst_p, 1, &tm);
2273 tobj->vtm.isdst = (dst != Qundef && RTEST(dst));
2274}
2275
2276static int
2277zone_timelocal(VALUE zone, VALUE time)
2278{
2279 VALUE utc, tm;
2280 struct time_object *tobj = DATA_PTR(time);
2281 wideval_t t, s;
2282
2283 t = rb_time_unmagnify(tobj->timew);
2284 tm = tm_from_time(rb_cTimeTM, time);
2285 utc = rb_check_funcall(zone, id_local_to_utc, 1, &tm);
2286 if (utc == Qundef) return 0;
2287
2288 s = extract_time(utc);
2289 zone_set_offset(zone, tobj, t, s);
2290 s = rb_time_magnify(s);
2291 if (tobj->vtm.subsecx != INT2FIX(0)) {
2292 s = wadd(s, v2w(tobj->vtm.subsecx));
2293 }
2294 tobj->timew = s;
2295 zone_set_dst(zone, tobj, tm);
2296 return 1;
2297}
2298
2299static int
2300zone_localtime(VALUE zone, VALUE time)
2301{
2302 VALUE local, tm, subsecx;
2303 struct time_object *tobj = DATA_PTR(time);
2304 wideval_t t, s;
2305
2306 split_second(tobj->timew, &t, &subsecx);
2307 tm = tm_from_time(rb_cTimeTM, time);
2308
2309 local = rb_check_funcall(zone, id_utc_to_local, 1, &tm);
2310 if (local == Qundef) return 0;
2311
2312 s = extract_vtm(local, &tobj->vtm, subsecx);
2313 tobj->tm_got = 1;
2314 zone_set_offset(zone, tobj, s, t);
2315 zone_set_dst(zone, tobj, tm);
2316 return 1;
2317}
2318
2319static VALUE
2320find_timezone(VALUE time, VALUE zone)
2321{
2322 VALUE klass = CLASS_OF(time);
2323
2324 return rb_check_funcall_default(klass, id_find_timezone, 1, &zone, Qnil);
2325}
2326
2327/* Turn the special case 24:00:00 of already validated vtm into
2328 * 00:00:00 the next day */
2329static void
2330vtm_day_wraparound(struct vtm *vtm)
2331{
2332 if (vtm->hour < 24) return;
2333
2334 /* Assuming UTC and no care of DST, just reset hour and advance
2335 * date, not to discard the validated vtm. */
2336 vtm->hour = 0;
2337 vtm_add_day(vtm, 1);
2338}
2339
2340static VALUE
2341time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday, VALUE hour, VALUE min, VALUE sec, VALUE zone)
2342{
2343 struct vtm vtm;
2344 VALUE utc = Qnil;
2345 struct time_object *tobj;
2346
2347 vtm.wday = VTM_WDAY_INITVAL;
2348 vtm.yday = 0;
2349 vtm.zone = str_empty;
2350
2351 vtm.year = obj2vint(year);
2352
2353 vtm.mon = NIL_P(mon) ? 1 : month_arg(mon);
2354
2355 vtm.mday = NIL_P(mday) ? 1 : obj2ubits(mday, 5);
2356
2357 vtm.hour = NIL_P(hour) ? 0 : obj2ubits(hour, 5);
2358
2359 vtm.min = NIL_P(min) ? 0 : obj2ubits(min, 6);
2360
2361 if (NIL_P(sec)) {
2362 vtm.sec = 0;
2363 vtm.subsecx = INT2FIX(0);
2364 }
2365 else {
2366 VALUE subsecx;
2367 vtm.sec = obj2subsecx(sec, &subsecx);
2368 vtm.subsecx = subsecx;
2369 }
2370
2371 vtm.isdst = VTM_ISDST_INITVAL;
2372 vtm.utc_offset = Qnil;
2373 const VALUE arg = zone;
2374 if (!NIL_P(arg)) {
2375 zone = Qnil;
2376 if (arg == ID2SYM(rb_intern("dst")))
2377 vtm.isdst = 1;
2378 else if (arg == ID2SYM(rb_intern("std")))
2379 vtm.isdst = 0;
2380 else if (maybe_tzobj_p(arg))
2381 zone = arg;
2382 else if (!NIL_P(utc = utc_offset_arg(arg)))
2383 vtm.utc_offset = utc == UTC_ZONE ? INT2FIX(0) : utc;
2384 else if (NIL_P(zone = find_timezone(time, arg)))
2385 invalid_utc_offset(arg);
2386 }
2387
2388 validate_vtm(&vtm);
2389
2390 time_modify(time);
2391 GetNewTimeval(time, tobj);
2392
2393 if (!NIL_P(zone)) {
2394 tobj->timew = timegmw(&vtm);
2395 vtm_day_wraparound(&vtm);
2396 tobj->vtm = vtm;
2397 tobj->tm_got = 1;
2398 TZMODE_SET_LOCALTIME(tobj);
2399 if (zone_timelocal(zone, time)) {
2400 return time;
2401 }
2402 else if (NIL_P(vtm.utc_offset = utc_offset_arg(zone))) {
2403 if (NIL_P(zone = find_timezone(time, zone)) || !zone_timelocal(zone, time))
2404 invalid_utc_offset(arg);
2405 }
2406 }
2407
2408 if (utc == UTC_ZONE) {
2409 tobj->timew = timegmw(&vtm);
2410 vtm_day_wraparound(&vtm);
2411 tobj->vtm = vtm;
2412 tobj->tm_got = 1;
2413 TZMODE_SET_UTC(tobj);
2414 return time;
2415 }
2416
2417 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2418 tobj->tm_got=0;
2419 tobj->timew = WINT2FIXWV(0);
2420
2421 if (!NIL_P(vtm.utc_offset)) {
2422 VALUE off = vtm.utc_offset;
2423 vtm_add_offset(&vtm, off, -1);
2424 vtm.utc_offset = Qnil;
2425 tobj->timew = timegmw(&vtm);
2426 return time_set_utc_offset(time, off);
2427 }
2428 else {
2429 tobj->timew = timelocalw(&vtm);
2430 return time_localtime(time);
2431 }
2432}
2433
2434static void
2435subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec)
2436{
2437 time_t sec = *secp;
2438 long subsec = *subsecp;
2439 long sec2;
2440
2441 if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */
2442 sec2 = subsec / maxsubsec;
2443 if (TIMET_MAX - sec2 < sec) {
2444 rb_raise(rb_eRangeError, "out of Time range");
2445 }
2446 subsec -= sec2 * maxsubsec;
2447 sec += sec2;
2448 }
2449 else if (UNLIKELY(subsec < 0)) { /* subsec negative overflow */
2450 sec2 = NDIV(subsec, maxsubsec); /* negative div */
2451 if (sec < TIMET_MIN - sec2) {
2452 rb_raise(rb_eRangeError, "out of Time range");
2453 }
2454 subsec -= sec2 * maxsubsec;
2455 sec += sec2;
2456 }
2457#ifndef NEGATIVE_TIME_T
2458 if (sec < 0)
2459 rb_raise(rb_eArgError, "time must be positive");
2460#endif
2461 *secp = sec;
2462 *subsecp = subsec;
2463}
2464
2465#define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
2466#define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000)
2467
2468static wideval_t
2469nsec2timew(time_t sec, long nsec)
2470{
2471 time_nsec_normalize(&sec, &nsec);
2472 return timenano2timew(sec, nsec);
2473}
2474
2475static VALUE
2476time_new_timew(VALUE klass, wideval_t timew)
2477{
2478 VALUE time = time_s_alloc(klass);
2479 struct time_object *tobj;
2480
2481 tobj = DATA_PTR(time); /* skip type check */
2482 tobj->tzmode = TIME_TZMODE_LOCALTIME;
2483 tobj->timew = timew;
2484
2485 return time;
2486}
2487
2488VALUE
2489rb_time_new(time_t sec, long usec)
2490{
2491 time_usec_normalize(&sec, &usec);
2492 return time_new_timew(rb_cTime, timenano2timew(sec, usec * 1000));
2493}
2494
2495/* returns localtime time object */
2496VALUE
2497rb_time_nano_new(time_t sec, long nsec)
2498{
2499 return time_new_timew(rb_cTime, nsec2timew(sec, nsec));
2500}
2501
2502VALUE
2503rb_time_timespec_new(const struct timespec *ts, int offset)
2504{
2505 struct time_object *tobj;
2506 VALUE time = time_new_timew(rb_cTime, nsec2timew(ts->tv_sec, ts->tv_nsec));
2507
2508 if (-86400 < offset && offset < 86400) { /* fixoff */
2509 GetTimeval(time, tobj);
2510 TZMODE_SET_FIXOFF(tobj, INT2FIX(offset));
2511 }
2512 else if (offset == INT_MAX) { /* localtime */
2513 }
2514 else if (offset == INT_MAX-1) { /* UTC */
2515 GetTimeval(time, tobj);
2516 TZMODE_SET_UTC(tobj);
2517 }
2518 else {
2519 rb_raise(rb_eArgError, "utc_offset out of range");
2520 }
2521
2522 return time;
2523}
2524
2525VALUE
2526rb_time_num_new(VALUE timev, VALUE off)
2527{
2528 VALUE time = time_new_timew(rb_cTime, rb_time_magnify(v2w(timev)));
2529
2530 if (!NIL_P(off)) {
2531 VALUE zone = off;
2532
2533 if (maybe_tzobj_p(zone)) {
2534 time_gmtime(time);
2535 if (zone_timelocal(zone, time)) return time;
2536 }
2537 if (NIL_P(off = utc_offset_arg(off))) {
2538 off = zone;
2539 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
2540 time_gmtime(time);
2541 if (!zone_timelocal(zone, time)) invalid_utc_offset(off);
2542 return time;
2543 }
2544 else if (off == UTC_ZONE) {
2545 return time_gmtime(time);
2546 }
2547
2548 validate_utc_offset(off);
2549 time_set_utc_offset(time, off);
2550 return time;
2551 }
2552
2553 return time;
2554}
2555
2556static struct timespec
2557time_timespec(VALUE num, int interval)
2558{
2559 struct timespec t;
2560 const char *const tstr = interval ? "time interval" : "time";
2561 VALUE i, f, ary;
2562
2563#ifndef NEGATIVE_TIME_T
2564# define arg_range_check(v) \
2565 (((v) < 0) ? \
2566 rb_raise(rb_eArgError, "%s must not be negative", tstr) : \
2567 (void)0)
2568#else
2569# define arg_range_check(v) \
2570 ((interval && (v) < 0) ? \
2571 rb_raise(rb_eArgError, "time interval must not be negative") : \
2572 (void)0)
2573#endif
2574
2575 if (FIXNUM_P(num)) {
2576 t.tv_sec = NUM2TIMET(num);
2577 arg_range_check(t.tv_sec);
2578 t.tv_nsec = 0;
2579 }
2580 else if (RB_FLOAT_TYPE_P(num)) {
2581 double x = RFLOAT_VALUE(num);
2582 arg_range_check(x);
2583 {
2584 double f, d;
2585
2586 d = modf(x, &f);
2587 if (d >= 0) {
2588 t.tv_nsec = (int)(d*1e9+0.5);
2589 if (t.tv_nsec >= 1000000000) {
2590 t.tv_nsec -= 1000000000;
2591 f += 1;
2592 }
2593 }
2594 else if ((t.tv_nsec = (int)(-d*1e9+0.5)) > 0) {
2595 t.tv_nsec = 1000000000 - t.tv_nsec;
2596 f -= 1;
2597 }
2598 t.tv_sec = (time_t)f;
2599 if (f != t.tv_sec) {
2600 rb_raise(rb_eRangeError, "%f out of Time range", x);
2601 }
2602 }
2603 }
2604 else if (RB_BIGNUM_TYPE_P(num)) {
2605 t.tv_sec = NUM2TIMET(num);
2606 arg_range_check(t.tv_sec);
2607 t.tv_nsec = 0;
2608 }
2609 else {
2610 i = INT2FIX(1);
2611 ary = rb_check_funcall(num, id_divmod, 1, &i);
2612 if (ary != Qundef && !NIL_P(ary = rb_check_array_type(ary))) {
2613 i = rb_ary_entry(ary, 0);
2614 f = rb_ary_entry(ary, 1);
2615 t.tv_sec = NUM2TIMET(i);
2616 arg_range_check(t.tv_sec);
2617 f = rb_funcall(f, '*', 1, INT2FIX(1000000000));
2618 t.tv_nsec = NUM2LONG(f);
2619 }
2620 else {
2621 rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into %s",
2622 rb_obj_class(num), tstr);
2623 }
2624 }
2625 return t;
2626#undef arg_range_check
2627}
2628
2629static struct timeval
2630time_timeval(VALUE num, int interval)
2631{
2632 struct timespec ts;
2633 struct timeval tv;
2634
2635 ts = time_timespec(num, interval);
2636 tv.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
2637 tv.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
2638
2639 return tv;
2640}
2641
2642struct timeval
2644{
2645 return time_timeval(num, TRUE);
2646}
2647
2648struct timeval
2650{
2651 struct time_object *tobj;
2652 struct timeval t;
2653 struct timespec ts;
2654
2655 if (IsTimeval(time)) {
2656 GetTimeval(time, tobj);
2657 ts = timew2timespec(tobj->timew);
2658 t.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
2659 t.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
2660 return t;
2661 }
2662 return time_timeval(time, FALSE);
2663}
2664
2665struct timespec
2667{
2668 struct time_object *tobj;
2669 struct timespec t;
2670
2671 if (IsTimeval(time)) {
2672 GetTimeval(time, tobj);
2673 t = timew2timespec(tobj->timew);
2674 return t;
2675 }
2676 return time_timespec(time, FALSE);
2677}
2678
2679struct timespec
2681{
2682 return time_timespec(num, TRUE);
2683}
2684
2685static int
2686get_scale(VALUE unit)
2687{
2688 if (unit == ID2SYM(id_nanosecond) || unit == ID2SYM(id_nsec)) {
2689 return 1000000000;
2690 }
2691 else if (unit == ID2SYM(id_microsecond) || unit == ID2SYM(id_usec)) {
2692 return 1000000;
2693 }
2694 else if (unit == ID2SYM(id_millisecond)) {
2695 return 1000;
2696 }
2697 else {
2698 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
2699 }
2700}
2701
2702static VALUE
2703time_s_at(rb_execution_context_t *ec, VALUE klass, VALUE time, VALUE subsec, VALUE unit, VALUE zone)
2704{
2705 VALUE t;
2706 wideval_t timew;
2707
2708 if (subsec) {
2709 int scale = get_scale(unit);
2710 time = num_exact(time);
2711 t = num_exact(subsec);
2712 timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, scale));
2713 t = time_new_timew(klass, timew);
2714 }
2715 else if (IsTimeval(time)) {
2716 struct time_object *tobj, *tobj2;
2717 GetTimeval(time, tobj);
2718 t = time_new_timew(klass, tobj->timew);
2719 GetTimeval(t, tobj2);
2720 TZMODE_COPY(tobj2, tobj);
2721 }
2722 else {
2723 timew = rb_time_magnify(v2w(num_exact(time)));
2724 t = time_new_timew(klass, timew);
2725 }
2726 if (!NIL_P(zone)) {
2727 time_zonelocal(t, zone);
2728 }
2729
2730 return t;
2731}
2732
2733static VALUE
2734time_s_at1(rb_execution_context_t *ec, VALUE klass, VALUE time)
2735{
2736 return time_s_at(ec, klass, time, Qfalse, ID2SYM(id_microsecond), Qnil);
2737}
2738
2739static const char months[][4] = {
2740 "jan", "feb", "mar", "apr", "may", "jun",
2741 "jul", "aug", "sep", "oct", "nov", "dec",
2742};
2743
2744static int
2745obj2int(VALUE obj)
2746{
2747 if (RB_TYPE_P(obj, T_STRING)) {
2748 obj = rb_str_to_inum(obj, 10, TRUE);
2749 }
2750
2751 return NUM2INT(obj);
2752}
2753
2754/* bits should be 0 <= x <= 31 */
2755static uint32_t
2756obj2ubits(VALUE obj, unsigned int bits)
2757{
2758 const unsigned int usable_mask = (1U << bits) - 1;
2759 unsigned int rv = (unsigned int)obj2int(obj);
2760
2761 if ((rv & usable_mask) != rv)
2762 rb_raise(rb_eArgError, "argument out of range");
2763 return (uint32_t)rv;
2764}
2765
2766static VALUE
2767obj2vint(VALUE obj)
2768{
2769 if (RB_TYPE_P(obj, T_STRING)) {
2770 obj = rb_str_to_inum(obj, 10, TRUE);
2771 }
2772 else {
2773 obj = rb_to_int(obj);
2774 }
2775
2776 return obj;
2777}
2778
2779static uint32_t
2780obj2subsecx(VALUE obj, VALUE *subsecx)
2781{
2782 VALUE subsec;
2783
2784 if (RB_TYPE_P(obj, T_STRING)) {
2785 obj = rb_str_to_inum(obj, 10, TRUE);
2786 *subsecx = INT2FIX(0);
2787 }
2788 else {
2789 divmodv(num_exact(obj), INT2FIX(1), &obj, &subsec);
2790 *subsecx = w2v(rb_time_magnify(v2w(subsec)));
2791 }
2792 return obj2ubits(obj, 6); /* vtm->sec */
2793}
2794
2795static VALUE
2796usec2subsecx(VALUE obj)
2797{
2798 if (RB_TYPE_P(obj, T_STRING)) {
2799 obj = rb_str_to_inum(obj, 10, TRUE);
2800 }
2801
2802 return mulquov(num_exact(obj), INT2FIX(TIME_SCALE), INT2FIX(1000000));
2803}
2804
2805static uint32_t
2806month_arg(VALUE arg)
2807{
2808 int i, mon;
2809
2810 if (FIXNUM_P(arg)) {
2811 return obj2ubits(arg, 4);
2812 }
2813
2814 mon = 0;
2815 VALUE s = rb_check_string_type(arg);
2816 if (!NIL_P(s) && RSTRING_LEN(s) > 0) {
2817 arg = s;
2818 for (i=0; i<12; i++) {
2819 if (RSTRING_LEN(s) == 3 &&
2820 STRNCASECMP(months[i], RSTRING_PTR(s), 3) == 0) {
2821 mon = i+1;
2822 break;
2823 }
2824 }
2825 }
2826 if (mon == 0) {
2827 mon = obj2ubits(arg, 4);
2828 }
2829 return mon;
2830}
2831
2832static VALUE
2833validate_utc_offset(VALUE utc_offset)
2834{
2835 if (le(utc_offset, INT2FIX(-86400)) || ge(utc_offset, INT2FIX(86400)))
2836 rb_raise(rb_eArgError, "utc_offset out of range");
2837 return utc_offset;
2838}
2839
2840static VALUE
2841validate_zone_name(VALUE zone_name)
2842{
2843 StringValueCStr(zone_name);
2844 return zone_name;
2845}
2846
2847static void
2848validate_vtm(struct vtm *vtm)
2849{
2850#define validate_vtm_range(mem, b, e) \
2851 ((vtm->mem < b || vtm->mem > e) ? \
2852 rb_raise(rb_eArgError, #mem" out of range") : (void)0)
2853 validate_vtm_range(mon, 1, 12);
2854 validate_vtm_range(mday, 1, 31);
2855 validate_vtm_range(hour, 0, 24);
2856 validate_vtm_range(min, 0, (vtm->hour == 24 ? 0 : 59));
2857 validate_vtm_range(sec, 0, (vtm->hour == 24 ? 0 : 60));
2858 if (lt(vtm->subsecx, INT2FIX(0)) || ge(vtm->subsecx, INT2FIX(TIME_SCALE)))
2859 rb_raise(rb_eArgError, "subsecx out of range");
2860 if (!NIL_P(vtm->utc_offset)) validate_utc_offset(vtm->utc_offset);
2861#undef validate_vtm_range
2862}
2863
2864static void
2865time_arg(int argc, const VALUE *argv, struct vtm *vtm)
2866{
2867 VALUE v[8];
2868 VALUE subsecx = INT2FIX(0);
2869
2870 vtm->year = INT2FIX(0);
2871 vtm->mon = 0;
2872 vtm->mday = 0;
2873 vtm->hour = 0;
2874 vtm->min = 0;
2875 vtm->sec = 0;
2876 vtm->subsecx = INT2FIX(0);
2877 vtm->utc_offset = Qnil;
2878 vtm->wday = 0;
2879 vtm->yday = 0;
2880 vtm->isdst = 0;
2881 vtm->zone = str_empty;
2882
2883 if (argc == 10) {
2884 v[0] = argv[5];
2885 v[1] = argv[4];
2886 v[2] = argv[3];
2887 v[3] = argv[2];
2888 v[4] = argv[1];
2889 v[5] = argv[0];
2890 v[6] = Qnil;
2891 vtm->isdst = RTEST(argv[8]) ? 1 : 0;
2892 }
2893 else {
2894 rb_scan_args(argc, argv, "17", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6],&v[7]);
2895 /* v[6] may be usec or zone (parsedate) */
2896 /* v[7] is wday (parsedate; ignored) */
2897 vtm->wday = VTM_WDAY_INITVAL;
2898 vtm->isdst = VTM_ISDST_INITVAL;
2899 }
2900
2901 vtm->year = obj2vint(v[0]);
2902
2903 if (NIL_P(v[1])) {
2904 vtm->mon = 1;
2905 }
2906 else {
2907 vtm->mon = month_arg(v[1]);
2908 }
2909
2910 if (NIL_P(v[2])) {
2911 vtm->mday = 1;
2912 }
2913 else {
2914 vtm->mday = obj2ubits(v[2], 5);
2915 }
2916
2917 /* normalize month-mday */
2918 switch (vtm->mon) {
2919 case 2:
2920 {
2921 /* this drops higher bits but it's not a problem to calc leap year */
2922 unsigned int mday2 = leap_year_v_p(vtm->year) ? 29 : 28;
2923 if (vtm->mday > mday2) {
2924 vtm->mday -= mday2;
2925 vtm->mon++;
2926 }
2927 }
2928 break;
2929 case 4:
2930 case 6:
2931 case 9:
2932 case 11:
2933 if (vtm->mday == 31) {
2934 vtm->mon++;
2935 vtm->mday = 1;
2936 }
2937 break;
2938 }
2939
2940 vtm->hour = NIL_P(v[3])?0:obj2ubits(v[3], 5);
2941
2942 vtm->min = NIL_P(v[4])?0:obj2ubits(v[4], 6);
2943
2944 if (!NIL_P(v[6]) && argc == 7) {
2945 vtm->sec = NIL_P(v[5])?0:obj2ubits(v[5],6);
2946 subsecx = usec2subsecx(v[6]);
2947 }
2948 else {
2949 /* when argc == 8, v[6] is timezone, but ignored */
2950 if (NIL_P(v[5])) {
2951 vtm->sec = 0;
2952 }
2953 else {
2954 vtm->sec = obj2subsecx(v[5], &subsecx);
2955 }
2956 }
2957 vtm->subsecx = subsecx;
2958
2959 validate_vtm(vtm);
2960 RB_GC_GUARD(subsecx);
2961}
2962
2963static int
2964leap_year_p(long y)
2965{
2966 /* TODO:
2967 * ensure about negative years in proleptic Gregorian calendar.
2968 */
2969 unsigned long uy = (unsigned long)(LIKELY(y >= 0) ? y : -y);
2970
2971 if (LIKELY(uy % 4 != 0)) return 0;
2972
2973 unsigned long century = uy / 100;
2974 if (LIKELY(uy != century * 100)) return 1;
2975 return century % 4 == 0;
2976}
2977
2978static time_t
2979timegm_noleapsecond(struct tm *tm)
2980{
2981 long tm_year = tm->tm_year;
2982 int tm_yday = calc_tm_yday(tm->tm_year, tm->tm_mon, tm->tm_mday);
2983
2984 /*
2985 * `Seconds Since the Epoch' in SUSv3:
2986 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
2987 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
2988 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
2989 */
2990 return tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 +
2991 (time_t)(tm_yday +
2992 (tm_year-70)*365 +
2993 DIV(tm_year-69,4) -
2994 DIV(tm_year-1,100) +
2995 DIV(tm_year+299,400))*86400;
2996}
2997
2998#if 0
2999#define DEBUG_FIND_TIME_NUMGUESS
3000#define DEBUG_GUESSRANGE
3001#endif
3002
3003static const bool debug_guessrange =
3004#ifdef DEBUG_GUESSRANGE
3005 true;
3006#else
3007 false;
3008#endif
3009
3010#define DEBUG_REPORT_GUESSRANGE \
3011 (debug_guessrange ? debug_report_guessrange(guess_lo, guess_hi) : (void)0)
3012
3013static inline void
3014debug_report_guessrange(time_t guess_lo, time_t guess_hi)
3015{
3016 unsigned_time_t guess_diff = (unsigned_time_t)(guess_hi-guess_lo);
3017 fprintf(stderr, "find time guess range: %"PRI_TIMET_PREFIX"d - "
3018 "%"PRI_TIMET_PREFIX"d : %"PRI_TIMET_PREFIX"u\n",
3019 guess_lo, guess_hi, guess_diff);
3020}
3021
3022static const bool debug_find_time_numguess =
3023#ifdef DEBUG_FIND_TIME_NUMGUESS
3024 true;
3025#else
3026 false;
3027#endif
3028
3029#define DEBUG_FIND_TIME_NUMGUESS_INC \
3030 (void)(debug_find_time_numguess && find_time_numguess++),
3031static unsigned long long find_time_numguess;
3032
3033static VALUE
3034find_time_numguess_getter(ID name, VALUE *data)
3035{
3036 unsigned long long *numguess = (void *)data;
3037 return ULL2NUM(*numguess);
3038}
3039
3040static const char *
3041find_time_t(struct tm *tptr, int utc_p, time_t *tp)
3042{
3043 time_t guess, guess0, guess_lo, guess_hi;
3044 struct tm *tm, tm0, tm_lo, tm_hi;
3045 int d;
3046 int find_dst;
3047 struct tm result;
3048 int status;
3049 int tptr_tm_yday;
3050
3051#define GUESS(p) (DEBUG_FIND_TIME_NUMGUESS_INC (utc_p ? gmtime_with_leapsecond((p), &result) : LOCALTIME((p), result)))
3052
3053 guess_lo = TIMET_MIN;
3054 guess_hi = TIMET_MAX;
3055
3056 find_dst = 0 < tptr->tm_isdst;
3057
3058 /* /etc/localtime might be changed. reload it. */
3059 update_tz();
3060
3061 tm0 = *tptr;
3062 if (tm0.tm_mon < 0) {
3063 tm0.tm_mon = 0;
3064 tm0.tm_mday = 1;
3065 tm0.tm_hour = 0;
3066 tm0.tm_min = 0;
3067 tm0.tm_sec = 0;
3068 }
3069 else if (11 < tm0.tm_mon) {
3070 tm0.tm_mon = 11;
3071 tm0.tm_mday = 31;
3072 tm0.tm_hour = 23;
3073 tm0.tm_min = 59;
3074 tm0.tm_sec = 60;
3075 }
3076 else if (tm0.tm_mday < 1) {
3077 tm0.tm_mday = 1;
3078 tm0.tm_hour = 0;
3079 tm0.tm_min = 0;
3080 tm0.tm_sec = 0;
3081 }
3082 else if ((d = days_in_month_in(1900 + tm0.tm_year)[tm0.tm_mon]) < tm0.tm_mday) {
3083 tm0.tm_mday = d;
3084 tm0.tm_hour = 23;
3085 tm0.tm_min = 59;
3086 tm0.tm_sec = 60;
3087 }
3088 else if (tm0.tm_hour < 0) {
3089 tm0.tm_hour = 0;
3090 tm0.tm_min = 0;
3091 tm0.tm_sec = 0;
3092 }
3093 else if (23 < tm0.tm_hour) {
3094 tm0.tm_hour = 23;
3095 tm0.tm_min = 59;
3096 tm0.tm_sec = 60;
3097 }
3098 else if (tm0.tm_min < 0) {
3099 tm0.tm_min = 0;
3100 tm0.tm_sec = 0;
3101 }
3102 else if (59 < tm0.tm_min) {
3103 tm0.tm_min = 59;
3104 tm0.tm_sec = 60;
3105 }
3106 else if (tm0.tm_sec < 0) {
3107 tm0.tm_sec = 0;
3108 }
3109 else if (60 < tm0.tm_sec) {
3110 tm0.tm_sec = 60;
3111 }
3112
3113 DEBUG_REPORT_GUESSRANGE;
3114 guess0 = guess = timegm_noleapsecond(&tm0);
3115 tm = GUESS(&guess);
3116 if (tm) {
3117 d = tmcmp(tptr, tm);
3118 if (d == 0) { goto found; }
3119 if (d < 0) {
3120 guess_hi = guess;
3121 guess -= 24 * 60 * 60;
3122 }
3123 else {
3124 guess_lo = guess;
3125 guess += 24 * 60 * 60;
3126 }
3127 DEBUG_REPORT_GUESSRANGE;
3128 if (guess_lo < guess && guess < guess_hi && (tm = GUESS(&guess)) != NULL) {
3129 d = tmcmp(tptr, tm);
3130 if (d == 0) { goto found; }
3131 if (d < 0)
3132 guess_hi = guess;
3133 else
3134 guess_lo = guess;
3135 DEBUG_REPORT_GUESSRANGE;
3136 }
3137 }
3138
3139 tm = GUESS(&guess_lo);
3140 if (!tm) goto error;
3141 d = tmcmp(tptr, tm);
3142 if (d < 0) goto out_of_range;
3143 if (d == 0) { guess = guess_lo; goto found; }
3144 tm_lo = *tm;
3145
3146 tm = GUESS(&guess_hi);
3147 if (!tm) goto error;
3148 d = tmcmp(tptr, tm);
3149 if (d > 0) goto out_of_range;
3150 if (d == 0) { guess = guess_hi; goto found; }
3151 tm_hi = *tm;
3152
3153 DEBUG_REPORT_GUESSRANGE;
3154
3155 status = 1;
3156
3157 while (guess_lo + 1 < guess_hi) {
3158 binsearch:
3159 if (status == 0) {
3160 guess = guess_lo / 2 + guess_hi / 2;
3161 if (guess <= guess_lo)
3162 guess = guess_lo + 1;
3163 else if (guess >= guess_hi)
3164 guess = guess_hi - 1;
3165 status = 1;
3166 }
3167 else {
3168 if (status == 1) {
3169 time_t guess0_hi = timegm_noleapsecond(&tm_hi);
3170 guess = guess_hi - (guess0_hi - guess0);
3171 if (guess == guess_hi) /* hh:mm:60 tends to cause this condition. */
3172 guess--;
3173 status = 2;
3174 }
3175 else if (status == 2) {
3176 time_t guess0_lo = timegm_noleapsecond(&tm_lo);
3177 guess = guess_lo + (guess0 - guess0_lo);
3178 if (guess == guess_lo)
3179 guess++;
3180 status = 0;
3181 }
3182 if (guess <= guess_lo || guess_hi <= guess) {
3183 /* Previous guess is invalid. try binary search. */
3184 if (debug_guessrange) {
3185 if (guess <= guess_lo) {
3186 fprintf(stderr, "too small guess: %"PRI_TIMET_PREFIX"d"\
3187 " <= %"PRI_TIMET_PREFIX"d\n", guess, guess_lo);
3188 }
3189 if (guess_hi <= guess) {
3190 fprintf(stderr, "too big guess: %"PRI_TIMET_PREFIX"d"\
3191 " <= %"PRI_TIMET_PREFIX"d\n", guess_hi, guess);
3192 }
3193 }
3194 status = 0;
3195 goto binsearch;
3196 }
3197 }
3198
3199 tm = GUESS(&guess);
3200 if (!tm) goto error;
3201
3202 d = tmcmp(tptr, tm);
3203
3204 if (d < 0) {
3205 guess_hi = guess;
3206 tm_hi = *tm;
3207 DEBUG_REPORT_GUESSRANGE;
3208 }
3209 else if (d > 0) {
3210 guess_lo = guess;
3211 tm_lo = *tm;
3212 DEBUG_REPORT_GUESSRANGE;
3213 }
3214 else {
3215 goto found;
3216 }
3217 }
3218
3219 /* Given argument has no corresponding time_t. Let's extrapolate. */
3220 /*
3221 * `Seconds Since the Epoch' in SUSv3:
3222 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
3223 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
3224 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
3225 */
3226
3227 tptr_tm_yday = calc_tm_yday(tptr->tm_year, tptr->tm_mon, tptr->tm_mday);
3228
3229 *tp = guess_lo +
3230 ((tptr->tm_year - tm_lo.tm_year) * 365 +
3231 DIV((tptr->tm_year-69), 4) -
3232 DIV((tptr->tm_year-1), 100) +
3233 DIV((tptr->tm_year+299), 400) -
3234 DIV((tm_lo.tm_year-69), 4) +
3235 DIV((tm_lo.tm_year-1), 100) -
3236 DIV((tm_lo.tm_year+299), 400) +
3237 tptr_tm_yday -
3238 tm_lo.tm_yday) * 86400 +
3239 (tptr->tm_hour - tm_lo.tm_hour) * 3600 +
3240 (tptr->tm_min - tm_lo.tm_min) * 60 +
3241 (tptr->tm_sec - (tm_lo.tm_sec == 60 ? 59 : tm_lo.tm_sec));
3242
3243 return NULL;
3244
3245 found:
3246 if (!utc_p) {
3247 /* If localtime is nonmonotonic, another result may exist. */
3248 time_t guess2;
3249 if (find_dst) {
3250 guess2 = guess - 2 * 60 * 60;
3251 tm = LOCALTIME(&guess2, result);
3252 if (tm) {
3253 if (tptr->tm_hour != (tm->tm_hour + 2) % 24 ||
3254 tptr->tm_min != tm->tm_min ||
3255 tptr->tm_sec != tm->tm_sec) {
3256 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
3257 (tm->tm_min - tptr->tm_min) * 60 +
3258 (tm->tm_sec - tptr->tm_sec);
3259 if (tptr->tm_mday != tm->tm_mday)
3260 guess2 += 24 * 60 * 60;
3261 if (guess != guess2) {
3262 tm = LOCALTIME(&guess2, result);
3263 if (tm && tmcmp(tptr, tm) == 0) {
3264 if (guess < guess2)
3265 *tp = guess;
3266 else
3267 *tp = guess2;
3268 return NULL;
3269 }
3270 }
3271 }
3272 }
3273 }
3274 else {
3275 guess2 = guess + 2 * 60 * 60;
3276 tm = LOCALTIME(&guess2, result);
3277 if (tm) {
3278 if ((tptr->tm_hour + 2) % 24 != tm->tm_hour ||
3279 tptr->tm_min != tm->tm_min ||
3280 tptr->tm_sec != tm->tm_sec) {
3281 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
3282 (tm->tm_min - tptr->tm_min) * 60 +
3283 (tm->tm_sec - tptr->tm_sec);
3284 if (tptr->tm_mday != tm->tm_mday)
3285 guess2 -= 24 * 60 * 60;
3286 if (guess != guess2) {
3287 tm = LOCALTIME(&guess2, result);
3288 if (tm && tmcmp(tptr, tm) == 0) {
3289 if (guess < guess2)
3290 *tp = guess2;
3291 else
3292 *tp = guess;
3293 return NULL;
3294 }
3295 }
3296 }
3297 }
3298 }
3299 }
3300 *tp = guess;
3301 return NULL;
3302
3303 out_of_range:
3304 return "time out of range";
3305
3306 error:
3307 return "gmtime/localtime error";
3308}
3309
3310static int
3311vtmcmp(struct vtm *a, struct vtm *b)
3312{
3313 if (ne(a->year, b->year))
3314 return lt(a->year, b->year) ? -1 : 1;
3315 else if (a->mon != b->mon)
3316 return a->mon < b->mon ? -1 : 1;
3317 else if (a->mday != b->mday)
3318 return a->mday < b->mday ? -1 : 1;
3319 else if (a->hour != b->hour)
3320 return a->hour < b->hour ? -1 : 1;
3321 else if (a->min != b->min)
3322 return a->min < b->min ? -1 : 1;
3323 else if (a->sec != b->sec)
3324 return a->sec < b->sec ? -1 : 1;
3325 else if (ne(a->subsecx, b->subsecx))
3326 return lt(a->subsecx, b->subsecx) ? -1 : 1;
3327 else
3328 return 0;
3329}
3330
3331static int
3332tmcmp(struct tm *a, struct tm *b)
3333{
3334 if (a->tm_year != b->tm_year)
3335 return a->tm_year < b->tm_year ? -1 : 1;
3336 else if (a->tm_mon != b->tm_mon)
3337 return a->tm_mon < b->tm_mon ? -1 : 1;
3338 else if (a->tm_mday != b->tm_mday)
3339 return a->tm_mday < b->tm_mday ? -1 : 1;
3340 else if (a->tm_hour != b->tm_hour)
3341 return a->tm_hour < b->tm_hour ? -1 : 1;
3342 else if (a->tm_min != b->tm_min)
3343 return a->tm_min < b->tm_min ? -1 : 1;
3344 else if (a->tm_sec != b->tm_sec)
3345 return a->tm_sec < b->tm_sec ? -1 : 1;
3346 else
3347 return 0;
3348}
3349
3350/*
3351 * call-seq:
3352 * Time.utc(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3353 * Time.utc(sec_i, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3354 *
3355 * Returns a new \Time object based the on given arguments;
3356 * its timezone is UTC.
3357 *
3358 * In the first form (up to seven arguments), argument +year+ is required.
3359 *
3360 * Time.utc(2000) # => 2000-01-01 00:00:00 UTC
3361 * Time.utc(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 UTC
3362 *
3363 * In the second form, all ten arguments are required,
3364 * though the last four are ignored.
3365 * This form is useful for creating a time from a 10-element array
3366 * such as is returned by #to_a.
3367 *
3368 * array = Time.now.to_a
3369 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3370 * array[5] = 2000
3371 * Time.utc(*array) # => 2000-04-24 13:26:57 UTC
3372 *
3373 * Parameters:
3374 * :include: doc/time/year.rdoc
3375 * :include: doc/time/mon-min.rdoc
3376 * :include: doc/time/sec_i.rdoc
3377 * :include: doc/time/usec.rdoc
3378 *
3379 * Alias: Time.gm.
3380
3381 * Related: Time.local.
3382 *
3383 */
3384static VALUE
3385time_s_mkutc(int argc, VALUE *argv, VALUE klass)
3386{
3387 struct vtm vtm;
3388
3389 time_arg(argc, argv, &vtm);
3390 return time_gmtime(time_new_timew(klass, timegmw(&vtm)));
3391}
3392
3393/*
3394 * call-seq:
3395 * Time.local(year, month=1, day=1, hour=0, min=0, sec_i=0, usec=0) -> new_time
3396 * Time.local(sec, min, hour, day, month, year, dummy, dummy, dummy, dummy) -> new_time
3397 *
3398 * Returns a new \Time object based the on given arguments;
3399 * its timezone is the local timezone.
3400 *
3401 * In the first form (up to seven arguments), argument +year+ is required.
3402 *
3403 * Time.local(2000) # => 2000-01-01 00:00:00 -0600
3404 * Time.local(0, 1, 2, 3, 4, 5, 6.5) # => 0000-01-02 03:04:05.0000065 -0600
3405 *
3406 * In the second form, all ten arguments are required,
3407 * though the last four are ignored.
3408 * This form is useful for creating a time from a 10-element array
3409 * such as those returned by #to_a.
3410 *
3411 * array = Time.now.to_a
3412 * p array # => [57, 26, 13, 24, 4, 2021, 6, 114, true, "Central Daylight Time"]
3413 * array[5] = 2000
3414 * Time.local(*array) # => 2000-04-24 13:26:57 -0500
3415 *
3416 * Parameters:
3417 * :include: doc/time/year.rdoc
3418 * :include: doc/time/mon-min.rdoc
3419 * :include: doc/time/sec_i.rdoc
3420 * :include: doc/time/usec.rdoc
3421 *
3422 * Alias: Time.mktime.
3423 *
3424 * Related: Time.utc.
3425 */
3426
3427static VALUE
3428time_s_mktime(int argc, VALUE *argv, VALUE klass)
3429{
3430 struct vtm vtm;
3431
3432 time_arg(argc, argv, &vtm);
3433 return time_localtime(time_new_timew(klass, timelocalw(&vtm)));
3434}
3435
3436/*
3437 * call-seq:
3438 * time.to_i -> int
3439 * time.tv_sec -> int
3440 *
3441 * Returns the value of _time_ as an integer number of seconds
3442 * since the Epoch.
3443 *
3444 * If _time_ contains subsecond, they are truncated.
3445 *
3446 * t = Time.now #=> 2020-07-21 01:41:29.746012609 +0900
3447 * t.to_i #=> 1595263289
3448 */
3449
3450static VALUE
3451time_to_i(VALUE time)
3452{
3453 struct time_object *tobj;
3454
3455 GetTimeval(time, tobj);
3456 return w2v(wdiv(tobj->timew, WINT2FIXWV(TIME_SCALE)));
3457}
3458
3459/*
3460 * call-seq:
3461 * time.to_f -> float
3462 *
3463 * Returns the value of _time_ as a floating point number of
3464 * seconds since the Epoch.
3465 * The return value approximate the exact value in the Time object
3466 * because floating point numbers cannot represent all rational numbers
3467 * exactly.
3468 *
3469 * t = Time.now #=> 2020-07-20 22:00:29.38740268 +0900
3470 * t.to_f #=> 1595250029.3874028
3471 * t.to_i #=> 1595250029
3472 *
3473 * Note that IEEE 754 double is not accurate enough to represent
3474 * the exact number of nanoseconds since the Epoch.
3475 * (IEEE 754 double has 53bit mantissa.
3476 * So it can represent exact number of nanoseconds only in
3477 * <tt>2 ** 53 / 1_000_000_000 / 60 / 60 / 24 = 104.2</tt> days.)
3478 * When Ruby uses a nanosecond-resolution clock function,
3479 * such as +clock_gettime+ of POSIX, to obtain the current time,
3480 * Time#to_f can lose information of a Time object created with +Time.now+.
3481 */
3482
3483static VALUE
3484time_to_f(VALUE time)
3485{
3486 struct time_object *tobj;
3487
3488 GetTimeval(time, tobj);
3489 return rb_Float(rb_time_unmagnify_to_float(tobj->timew));
3490}
3491
3492/*
3493 * call-seq:
3494 * time.to_r -> a_rational
3495 *
3496 * Returns the value of _time_ as a rational number of seconds
3497 * since the Epoch.
3498 *
3499 * t = Time.now #=> 2020-07-20 22:03:45.212167333 +0900
3500 * t.to_r #=> (1595250225212167333/1000000000)
3501 *
3502 * This method is intended to be used to get an accurate value
3503 * representing the seconds (including subsecond) since the Epoch.
3504 */
3505
3506static VALUE
3507time_to_r(VALUE time)
3508{
3509 struct time_object *tobj;
3510 VALUE v;
3511
3512 GetTimeval(time, tobj);
3513 v = rb_time_unmagnify_to_rational(tobj->timew);
3514 if (!RB_TYPE_P(v, T_RATIONAL)) {
3515 v = rb_Rational1(v);
3516 }
3517 return v;
3518}
3519
3520/*
3521 * call-seq:
3522 * time.usec -> int
3523 * time.tv_usec -> int
3524 *
3525 * Returns the number of microseconds for the subsecond part of _time_.
3526 * The result is a non-negative integer less than 10**6.
3527 *
3528 * t = Time.now #=> 2020-07-20 22:05:58.459785953 +0900
3529 * t.usec #=> 459785
3530 *
3531 * If _time_ has fraction of microsecond (such as nanoseconds),
3532 * it is truncated.
3533 *
3534 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3535 * t.usec #=> 666777
3536 *
3537 * Time#subsec can be used to obtain the subsecond part exactly.
3538 */
3539
3540static VALUE
3541time_usec(VALUE time)
3542{
3543 struct time_object *tobj;
3544 wideval_t w, q, r;
3545
3546 GetTimeval(time, tobj);
3547
3548 w = wmod(tobj->timew, WINT2WV(TIME_SCALE));
3549 wmuldivmod(w, WINT2FIXWV(1000000), WINT2FIXWV(TIME_SCALE), &q, &r);
3550 return rb_to_int(w2v(q));
3551}
3552
3553/*
3554 * call-seq:
3555 * time.nsec -> int
3556 * time.tv_nsec -> int
3557 *
3558 * Returns the number of nanoseconds for the subsecond part of _time_.
3559 * The result is a non-negative integer less than 10**9.
3560 *
3561 * t = Time.now #=> 2020-07-20 22:07:10.963933942 +0900
3562 * t.nsec #=> 963933942
3563 *
3564 * If _time_ has fraction of nanosecond (such as picoseconds),
3565 * it is truncated.
3566 *
3567 * t = Time.new(2000,1,1,0,0,0.666_777_888_999r)
3568 * t.nsec #=> 666777888
3569 *
3570 * Time#subsec can be used to obtain the subsecond part exactly.
3571 */
3572
3573static VALUE
3574time_nsec(VALUE time)
3575{
3576 struct time_object *tobj;
3577
3578 GetTimeval(time, tobj);
3579 return rb_to_int(w2v(wmulquoll(wmod(tobj->timew, WINT2WV(TIME_SCALE)), 1000000000, TIME_SCALE)));
3580}
3581
3582/*
3583 * call-seq:
3584 * time.subsec -> number
3585 *
3586 * Returns the subsecond for _time_.
3587 *
3588 * The return value can be a rational number.
3589 *
3590 * t = Time.now #=> 2020-07-20 15:40:26.867462289 +0900
3591 * t.subsec #=> (867462289/1000000000)
3592 *
3593 * t = Time.now #=> 2020-07-20 15:40:50.313828595 +0900
3594 * t.subsec #=> (62765719/200000000)
3595 *
3596 * t = Time.new(2000,1,1,2,3,4) #=> 2000-01-01 02:03:04 +0900
3597 * t.subsec #=> 0
3598 *
3599 * Time.new(2000,1,1,0,0,1/3r,"UTC").subsec #=> (1/3)
3600 *
3601 */
3602
3603static VALUE
3604time_subsec(VALUE time)
3605{
3606 struct time_object *tobj;
3607
3608 GetTimeval(time, tobj);
3609 return quov(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE));
3610}
3611
3612/*
3613 * call-seq:
3614 * time <=> other_time -> -1, 0, +1, or nil
3615 *
3616 * Compares +time+ with +other_time+.
3617 *
3618 * -1, 0, +1 or nil depending on whether +time+ is less than, equal to, or
3619 * greater than +other_time+.
3620 *
3621 * +nil+ is returned if the two values are incomparable.
3622 *
3623 * t = Time.now #=> 2007-11-19 08:12:12 -0600
3624 * t2 = t + 2592000 #=> 2007-12-19 08:12:12 -0600
3625 * t <=> t2 #=> -1
3626 * t2 <=> t #=> 1
3627 *
3628 * t = Time.now #=> 2007-11-19 08:13:38 -0600
3629 * t2 = t + 0.1 #=> 2007-11-19 08:13:38 -0600
3630 * t.nsec #=> 98222999
3631 * t2.nsec #=> 198222999
3632 * t <=> t2 #=> -1
3633 * t2 <=> t #=> 1
3634 * t <=> t #=> 0
3635 */
3636
3637static VALUE
3638time_cmp(VALUE time1, VALUE time2)
3639{
3640 struct time_object *tobj1, *tobj2;
3641 int n;
3642
3643 GetTimeval(time1, tobj1);
3644 if (IsTimeval(time2)) {
3645 GetTimeval(time2, tobj2);
3646 n = wcmp(tobj1->timew, tobj2->timew);
3647 }
3648 else {
3649 return rb_invcmp(time1, time2);
3650 }
3651 if (n == 0) return INT2FIX(0);
3652 if (n > 0) return INT2FIX(1);
3653 return INT2FIX(-1);
3654}
3655
3656/*
3657 * call-seq:
3658 * time.eql?(other_time)
3659 *
3660 * Returns +true+ if _time_ and +other_time+ are
3661 * both Time objects with the same seconds (including subsecond) from the Epoch.
3662 */
3663
3664static VALUE
3665time_eql(VALUE time1, VALUE time2)
3666{
3667 struct time_object *tobj1, *tobj2;
3668
3669 GetTimeval(time1, tobj1);
3670 if (IsTimeval(time2)) {
3671 GetTimeval(time2, tobj2);
3672 return rb_equal(w2v(tobj1->timew), w2v(tobj2->timew));
3673 }
3674 return Qfalse;
3675}
3676
3677/*
3678 * call-seq:
3679 * time.utc? -> true or false
3680 * time.gmt? -> true or false
3681 *
3682 * Returns +true+ if _time_ represents a time in UTC (GMT).
3683 *
3684 * t = Time.now #=> 2007-11-19 08:15:23 -0600
3685 * t.utc? #=> false
3686 * t = Time.gm(2000,"jan",1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3687 * t.utc? #=> true
3688 *
3689 * t = Time.now #=> 2007-11-19 08:16:03 -0600
3690 * t.gmt? #=> false
3691 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3692 * t.gmt? #=> true
3693 */
3694
3695static VALUE
3696time_utc_p(VALUE time)
3697{
3698 struct time_object *tobj;
3699
3700 GetTimeval(time, tobj);
3701 return RBOOL(TZMODE_UTC_P(tobj));
3702}
3703
3704/*
3705 * call-seq:
3706 * time.hash -> integer
3707 *
3708 * Returns a hash code for this Time object.
3709 *
3710 * See also Object#hash.
3711 */
3712
3713static VALUE
3714time_hash(VALUE time)
3715{
3716 struct time_object *tobj;
3717
3718 GetTimeval(time, tobj);
3719 return rb_hash(w2v(tobj->timew));
3720}
3721
3722/* :nodoc: */
3723static VALUE
3724time_init_copy(VALUE copy, VALUE time)
3725{
3726 struct time_object *tobj, *tcopy;
3727
3728 if (!OBJ_INIT_COPY(copy, time)) return copy;
3729 GetTimeval(time, tobj);
3730 GetNewTimeval(copy, tcopy);
3731 MEMCPY(tcopy, tobj, struct time_object, 1);
3732
3733 return copy;
3734}
3735
3736static VALUE
3737time_dup(VALUE time)
3738{
3739 VALUE dup = time_s_alloc(rb_obj_class(time));
3740 time_init_copy(dup, time);
3741 return dup;
3742}
3743
3744static VALUE
3745time_localtime(VALUE time)
3746{
3747 struct time_object *tobj;
3748 struct vtm vtm;
3749 VALUE zone;
3750
3751 GetTimeval(time, tobj);
3752 if (TZMODE_LOCALTIME_P(tobj)) {
3753 if (tobj->tm_got)
3754 return time;
3755 }
3756 else {
3757 time_modify(time);
3758 }
3759
3760 zone = tobj->vtm.zone;
3761 if (maybe_tzobj_p(zone) && zone_localtime(zone, time)) {
3762 return time;
3763 }
3764
3765 if (!localtimew(tobj->timew, &vtm))
3766 rb_raise(rb_eArgError, "localtime error");
3767 tobj->vtm = vtm;
3768
3769 tobj->tm_got = 1;
3770 TZMODE_SET_LOCALTIME(tobj);
3771 return time;
3772}
3773
3774static VALUE
3775time_zonelocal(VALUE time, VALUE off)
3776{
3777 VALUE zone = off;
3778 if (zone_localtime(zone, time)) return time;
3779
3780 if (NIL_P(off = utc_offset_arg(off))) {
3781 off = zone;
3782 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
3783 if (!zone_localtime(zone, time)) invalid_utc_offset(off);
3784 return time;
3785 }
3786 else if (off == UTC_ZONE) {
3787 return time_gmtime(time);
3788 }
3789 validate_utc_offset(off);
3790
3791 time_set_utc_offset(time, off);
3792 return time_fixoff(time);
3793}
3794
3795/*
3796 * call-seq:
3797 * time.localtime -> time
3798 * time.localtime(utc_offset) -> time
3799 *
3800 * Converts _time_ to local time (using the local time zone in
3801 * effect at the creation time of _time_) modifying the receiver.
3802 *
3803 * If +utc_offset+ is given, it is used instead of the local time.
3804 *
3805 * t = Time.utc(2000, "jan", 1, 20, 15, 1) #=> 2000-01-01 20:15:01 UTC
3806 * t.utc? #=> true
3807 *
3808 * t.localtime #=> 2000-01-01 14:15:01 -0600
3809 * t.utc? #=> false
3810 *
3811 * t.localtime("+09:00") #=> 2000-01-02 05:15:01 +0900
3812 * t.utc? #=> false
3813 *
3814 * If +utc_offset+ is not given and _time_ is local time, just returns
3815 * the receiver.
3816 */
3817
3818static VALUE
3819time_localtime_m(int argc, VALUE *argv, VALUE time)
3820{
3821 VALUE off;
3822
3823 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
3824 return time_zonelocal(time, off);
3825 }
3826
3827 return time_localtime(time);
3828}
3829
3830/*
3831 * call-seq:
3832 * time.gmtime -> time
3833 * time.utc -> time
3834 *
3835 * Converts _time_ to UTC (GMT), modifying the receiver.
3836 *
3837 * t = Time.now #=> 2007-11-19 08:18:31 -0600
3838 * t.gmt? #=> false
3839 * t.gmtime #=> 2007-11-19 14:18:31 UTC
3840 * t.gmt? #=> true
3841 *
3842 * t = Time.now #=> 2007-11-19 08:18:51 -0600
3843 * t.utc? #=> false
3844 * t.utc #=> 2007-11-19 14:18:51 UTC
3845 * t.utc? #=> true
3846 */
3847
3848static VALUE
3849time_gmtime(VALUE time)
3850{
3851 struct time_object *tobj;
3852 struct vtm vtm;
3853
3854 GetTimeval(time, tobj);
3855 if (TZMODE_UTC_P(tobj)) {
3856 if (tobj->tm_got)
3857 return time;
3858 }
3859 else {
3860 time_modify(time);
3861 }
3862
3863 vtm.zone = str_utc;
3864 GMTIMEW(tobj->timew, &vtm);
3865 tobj->vtm = vtm;
3866
3867 tobj->tm_got = 1;
3868 TZMODE_SET_UTC(tobj);
3869 return time;
3870}
3871
3872static VALUE
3873time_fixoff(VALUE time)
3874{
3875 struct time_object *tobj;
3876 struct vtm vtm;
3877 VALUE off, zone;
3878
3879 GetTimeval(time, tobj);
3880 if (TZMODE_FIXOFF_P(tobj)) {
3881 if (tobj->tm_got)
3882 return time;
3883 }
3884 else {
3885 time_modify(time);
3886 }
3887
3888 if (TZMODE_FIXOFF_P(tobj))
3889 off = tobj->vtm.utc_offset;
3890 else
3891 off = INT2FIX(0);
3892
3893 GMTIMEW(tobj->timew, &vtm);
3894
3895 zone = tobj->vtm.zone;
3896 tobj->vtm = vtm;
3897 tobj->vtm.zone = zone;
3898 vtm_add_offset(&tobj->vtm, off, +1);
3899
3900 tobj->tm_got = 1;
3901 TZMODE_SET_FIXOFF(tobj, off);
3902 return time;
3903}
3904
3905/*
3906 * call-seq:
3907 * time.getlocal -> new_time
3908 * time.getlocal(utc_offset) -> new_time
3909 * time.getlocal(timezone) -> new_time
3910 *
3911 * Returns a new Time object representing _time_ in
3912 * local time (using the local time zone in effect for this process).
3913 *
3914 * If +utc_offset+ is given, it is used instead of the local time.
3915 * +utc_offset+ can be given as a human-readable string (eg. <code>"+09:00"</code>)
3916 * or as a number of seconds (eg. <code>32400</code>).
3917 *
3918 * t = Time.utc(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
3919 * t.utc? #=> true
3920 *
3921 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
3922 * l.utc? #=> false
3923 * t == l #=> true
3924 *
3925 * j = t.getlocal("+09:00") #=> 2000-01-02 05:15:01 +0900
3926 * j.utc? #=> false
3927 * t == j #=> true
3928 *
3929 * k = t.getlocal(9*60*60) #=> 2000-01-02 05:15:01 +0900
3930 * k.utc? #=> false
3931 * t == k #=> true
3932 */
3933
3934static VALUE
3935time_getlocaltime(int argc, VALUE *argv, VALUE time)
3936{
3937 VALUE off;
3938
3939 if (rb_check_arity(argc, 0, 1) && !NIL_P(off = argv[0])) {
3940 VALUE zone = off;
3941 if (maybe_tzobj_p(zone)) {
3942 VALUE t = time_dup(time);
3943 if (zone_localtime(off, t)) return t;
3944 }
3945
3946 if (NIL_P(off = utc_offset_arg(off))) {
3947 off = zone;
3948 if (NIL_P(zone = find_timezone(time, off))) invalid_utc_offset(off);
3949 time = time_dup(time);
3950 if (!zone_localtime(zone, time)) invalid_utc_offset(off);
3951 return time;
3952 }
3953 else if (off == UTC_ZONE) {
3954 return time_gmtime(time_dup(time));
3955 }
3956 validate_utc_offset(off);
3957
3958 time = time_dup(time);
3959 time_set_utc_offset(time, off);
3960 return time_fixoff(time);
3961 }
3962
3963 return time_localtime(time_dup(time));
3964}
3965
3966/*
3967 * call-seq:
3968 * time.getgm -> new_time
3969 * time.getutc -> new_time
3970 *
3971 * Returns a new Time object representing _time_ in UTC.
3972 *
3973 * t = Time.local(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 -0600
3974 * t.gmt? #=> false
3975 * y = t.getgm #=> 2000-01-02 02:15:01 UTC
3976 * y.gmt? #=> true
3977 * t == y #=> true
3978 */
3979
3980static VALUE
3981time_getgmtime(VALUE time)
3982{
3983 return time_gmtime(time_dup(time));
3984}
3985
3986static VALUE
3987time_get_tm(VALUE time, struct time_object *tobj)
3988{
3989 if (TZMODE_UTC_P(tobj)) return time_gmtime(time);
3990 if (TZMODE_FIXOFF_P(tobj)) return time_fixoff(time);
3991 return time_localtime(time);
3992}
3993
3994static VALUE strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc);
3995#define strftimev(fmt, time, enc) strftime_cstr((fmt), rb_strlen_lit(fmt), (time), (enc))
3996
3997/*
3998 * call-seq:
3999 * time.asctime -> string
4000 * time.ctime -> string
4001 *
4002 * Returns a canonical string representation of _time_.
4003 *
4004 * Time.now.asctime #=> "Wed Apr 9 08:56:03 2003"
4005 * Time.now.ctime #=> "Wed Apr 9 08:56:03 2003"
4006 */
4007
4008static VALUE
4009time_asctime(VALUE time)
4010{
4011 return strftimev("%a %b %e %T %Y", time, rb_usascii_encoding());
4012}
4013
4014/*
4015 * call-seq:
4016 * time.to_s -> string
4017 *
4018 * Returns a string representing _time_. Equivalent to calling
4019 * #strftime with the appropriate format string.
4020 *
4021 * t = Time.now
4022 * t.to_s #=> "2012-11-10 18:16:12 +0100"
4023 * t.strftime "%Y-%m-%d %H:%M:%S %z" #=> "2012-11-10 18:16:12 +0100"
4024 *
4025 * t.utc.to_s #=> "2012-11-10 17:16:12 UTC"
4026 * t.strftime "%Y-%m-%d %H:%M:%S UTC" #=> "2012-11-10 17:16:12 UTC"
4027 */
4028
4029static VALUE
4030time_to_s(VALUE time)
4031{
4032 struct time_object *tobj;
4033
4034 GetTimeval(time, tobj);
4035 if (TZMODE_UTC_P(tobj))
4036 return strftimev("%Y-%m-%d %H:%M:%S UTC", time, rb_usascii_encoding());
4037 else
4038 return strftimev("%Y-%m-%d %H:%M:%S %z", time, rb_usascii_encoding());
4039}
4040
4041/*
4042 * call-seq:
4043 * time.inspect -> string
4044 *
4045 * Returns a detailed string representing _time_. Unlike to_s,
4046 * preserves subsecond in the representation for easier debugging.
4047 *
4048 * t = Time.now
4049 * t.inspect #=> "2012-11-10 18:16:12.261257655 +0100"
4050 * t.strftime "%Y-%m-%d %H:%M:%S.%N %z" #=> "2012-11-10 18:16:12.261257655 +0100"
4051 *
4052 * t.utc.inspect #=> "2012-11-10 17:16:12.261257655 UTC"
4053 * t.strftime "%Y-%m-%d %H:%M:%S.%N UTC" #=> "2012-11-10 17:16:12.261257655 UTC"
4054 */
4055
4056static VALUE
4057time_inspect(VALUE time)
4058{
4059 struct time_object *tobj;
4060 VALUE str, subsec;
4061
4062 GetTimeval(time, tobj);
4063 str = strftimev("%Y-%m-%d %H:%M:%S", time, rb_usascii_encoding());
4064 subsec = w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE)));
4065 if (subsec == INT2FIX(0)) {
4066 }
4067 else if (FIXNUM_P(subsec) && FIX2LONG(subsec) < TIME_SCALE) {
4068 long len;
4069 rb_str_catf(str, ".%09ld", FIX2LONG(subsec));
4070 for (len=RSTRING_LEN(str); RSTRING_PTR(str)[len-1] == '0' && len > 0; len--)
4071 ;
4072 rb_str_resize(str, len);
4073 }
4074 else {
4075 rb_str_cat_cstr(str, " ");
4076 subsec = quov(subsec, INT2FIX(TIME_SCALE));
4077 rb_str_concat(str, rb_obj_as_string(subsec));
4078 }
4079 if (TZMODE_UTC_P(tobj)) {
4080 rb_str_cat_cstr(str, " UTC");
4081 }
4082 else {
4083 /* ?TODO: subsecond offset */
4084 long off = NUM2LONG(rb_funcall(tobj->vtm.utc_offset, rb_intern("round"), 0));
4085 char sign = (off < 0) ? (off = -off, '-') : '+';
4086 int sec = off % 60;
4087 int min = (off /= 60) % 60;
4088 off /= 60;
4089 rb_str_catf(str, " %c%.2d%.2d", sign, (int)off, min);
4090 if (sec) rb_str_catf(str, "%.2d", sec);
4091 }
4092 return str;
4093}
4094
4095static VALUE
4096time_add0(VALUE klass, const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4097{
4098 VALUE result;
4099 struct time_object *result_tobj;
4100
4101 offset = num_exact(offset);
4102 if (sign < 0)
4103 result = time_new_timew(klass, wsub(tobj->timew, rb_time_magnify(v2w(offset))));
4104 else
4105 result = time_new_timew(klass, wadd(tobj->timew, rb_time_magnify(v2w(offset))));
4106 GetTimeval(result, result_tobj);
4107 TZMODE_COPY(result_tobj, tobj);
4108
4109 return result;
4110}
4111
4112static VALUE
4113time_add(const struct time_object *tobj, VALUE torig, VALUE offset, int sign)
4114{
4115 return time_add0(rb_cTime, tobj, torig, offset, sign);
4116}
4117
4118/*
4119 * call-seq:
4120 * time + numeric -> time
4121 *
4122 * Adds some number of seconds (possibly including subsecond) to
4123 * _time_ and returns that value as a new Time object.
4124 *
4125 * t = Time.now #=> 2020-07-20 22:14:43.170490982 +0900
4126 * t + (60 * 60 * 24) #=> 2020-07-21 22:14:43.170490982 +0900
4127 */
4128
4129static VALUE
4130time_plus(VALUE time1, VALUE time2)
4131{
4132 struct time_object *tobj;
4133 GetTimeval(time1, tobj);
4134
4135 if (IsTimeval(time2)) {
4136 rb_raise(rb_eTypeError, "time + time?");
4137 }
4138 return time_add(tobj, time1, time2, 1);
4139}
4140
4141/*
4142 * call-seq:
4143 * time - other_time -> float
4144 * time - numeric -> time
4145 *
4146 * Returns a difference in seconds as a Float
4147 * between _time_ and +other_time+, or subtracts the given number
4148 * of seconds in +numeric+ from _time_.
4149 *
4150 * t = Time.now #=> 2020-07-20 22:15:49.302766336 +0900
4151 * t2 = t + 2592000 #=> 2020-08-19 22:15:49.302766336 +0900
4152 * t2 - t #=> 2592000.0
4153 * t2 - 2592000 #=> 2020-07-20 22:15:49.302766336 +0900
4154 */
4155
4156static VALUE
4157time_minus(VALUE time1, VALUE time2)
4158{
4159 struct time_object *tobj;
4160
4161 GetTimeval(time1, tobj);
4162 if (IsTimeval(time2)) {
4163 struct time_object *tobj2;
4164
4165 GetTimeval(time2, tobj2);
4166 return rb_Float(rb_time_unmagnify_to_float(wsub(tobj->timew, tobj2->timew)));
4167 }
4168 return time_add(tobj, time1, time2, -1);
4169}
4170
4171static VALUE
4172ndigits_denominator(VALUE ndigits)
4173{
4174 long nd = NUM2LONG(ndigits);
4175
4176 if (nd < 0) {
4177 rb_raise(rb_eArgError, "negative ndigits given");
4178 }
4179 if (nd == 0) {
4180 return INT2FIX(1);
4181 }
4182 return rb_rational_new(INT2FIX(1),
4183 rb_int_positive_pow(10, (unsigned long)nd));
4184}
4185
4186/*
4187 * call-seq:
4188 * time.round([ndigits]) -> new_time
4189 *
4190 * Rounds subsecond to a given precision in decimal digits (0 digits by default).
4191 * It returns a new Time object.
4192 * +ndigits+ should be zero or a positive integer.
4193 *
4194 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4195 * t #=> 2010-03-30 05:43:25.123456789 UTC
4196 * t.round #=> 2010-03-30 05:43:25 UTC
4197 * t.round(0) #=> 2010-03-30 05:43:25 UTC
4198 * t.round(1) #=> 2010-03-30 05:43:25.1 UTC
4199 * t.round(2) #=> 2010-03-30 05:43:25.12 UTC
4200 * t.round(3) #=> 2010-03-30 05:43:25.123 UTC
4201 * t.round(4) #=> 2010-03-30 05:43:25.1235 UTC
4202 *
4203 * t = Time.utc(1999,12,31, 23,59,59)
4204 * (t + 0.4).round #=> 1999-12-31 23:59:59 UTC
4205 * (t + 0.49).round #=> 1999-12-31 23:59:59 UTC
4206 * (t + 0.5).round #=> 2000-01-01 00:00:00 UTC
4207 * (t + 1.4).round #=> 2000-01-01 00:00:00 UTC
4208 * (t + 1.49).round #=> 2000-01-01 00:00:00 UTC
4209 * (t + 1.5).round #=> 2000-01-01 00:00:01 UTC
4210 *
4211 * t = Time.utc(1999,12,31, 23,59,59) #=> 1999-12-31 23:59:59 UTC
4212 * (t + 0.123456789).round(4).iso8601(6) #=> 1999-12-31 23:59:59.1235 UTC
4213 */
4214
4215static VALUE
4216time_round(int argc, VALUE *argv, VALUE time)
4217{
4218 VALUE ndigits, v, den;
4219 struct time_object *tobj;
4220
4221 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4222 den = INT2FIX(1);
4223 else
4224 den = ndigits_denominator(ndigits);
4225
4226 GetTimeval(time, tobj);
4227 v = w2v(rb_time_unmagnify(tobj->timew));
4228
4229 v = modv(v, den);
4230 if (lt(v, quov(den, INT2FIX(2))))
4231 return time_add(tobj, time, v, -1);
4232 else
4233 return time_add(tobj, time, subv(den, v), 1);
4234}
4235
4236/*
4237 * call-seq:
4238 * time.floor([ndigits]) -> new_time
4239 *
4240 * Floors subsecond to a given precision in decimal digits (0 digits by default).
4241 * It returns a new Time object.
4242 * +ndigits+ should be zero or a positive integer.
4243 *
4244 * t = Time.utc(2010,3,30, 5,43,25.123456789r)
4245 * t #=> 2010-03-30 05:43:25.123456789 UTC
4246 * t.floor #=> 2010-03-30 05:43:25 UTC
4247 * t.floor(0) #=> 2010-03-30 05:43:25 UTC
4248 * t.floor(1) #=> 2010-03-30 05:43:25.1 UTC
4249 * t.floor(2) #=> 2010-03-30 05:43:25.12 UTC
4250 * t.floor(3) #=> 2010-03-30 05:43:25.123 UTC
4251 * t.floor(4) #=> 2010-03-30 05:43:25.1234 UTC
4252 *
4253 * t = Time.utc(1999,12,31, 23,59,59)
4254 * (t + 0.4).floor #=> 1999-12-31 23:59:59 UTC
4255 * (t + 0.9).floor #=> 1999-12-31 23:59:59 UTC
4256 * (t + 1.4).floor #=> 2000-01-01 00:00:00 UTC
4257 * (t + 1.9).floor #=> 2000-01-01 00:00:00 UTC
4258 *
4259 * t = Time.utc(1999,12,31, 23,59,59)
4260 * (t + 0.123456789).floor(4) #=> 1999-12-31 23:59:59.1234 UTC
4261 */
4262
4263static VALUE
4264time_floor(int argc, VALUE *argv, VALUE time)
4265{
4266 VALUE ndigits, v, den;
4267 struct time_object *tobj;
4268
4269 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4270 den = INT2FIX(1);
4271 else
4272 den = ndigits_denominator(ndigits);
4273
4274 GetTimeval(time, tobj);
4275 v = w2v(rb_time_unmagnify(tobj->timew));
4276
4277 v = modv(v, den);
4278 return time_add(tobj, time, v, -1);
4279}
4280
4281/*
4282 * call-seq:
4283 * time.ceil([ndigits]) -> new_time
4284 *
4285 * Ceils subsecond to a given precision in decimal digits (0 digits by default).
4286 * It returns a new Time object.
4287 * +ndigits+ should be zero or a positive integer.
4288 *
4289 * t = Time.utc(2010,3,30, 5,43,25.0123456789r)
4290 * t #=> 2010-03-30 05:43:25 123456789/10000000000 UTC
4291 * t.ceil #=> 2010-03-30 05:43:26 UTC
4292 * t.ceil(0) #=> 2010-03-30 05:43:26 UTC
4293 * t.ceil(1) #=> 2010-03-30 05:43:25.1 UTC
4294 * t.ceil(2) #=> 2010-03-30 05:43:25.02 UTC
4295 * t.ceil(3) #=> 2010-03-30 05:43:25.013 UTC
4296 * t.ceil(4) #=> 2010-03-30 05:43:25.0124 UTC
4297 *
4298 * t = Time.utc(1999,12,31, 23,59,59)
4299 * (t + 0.4).ceil #=> 2000-01-01 00:00:00 UTC
4300 * (t + 0.9).ceil #=> 2000-01-01 00:00:00 UTC
4301 * (t + 1.4).ceil #=> 2000-01-01 00:00:01 UTC
4302 * (t + 1.9).ceil #=> 2000-01-01 00:00:01 UTC
4303 *
4304 * t = Time.utc(1999,12,31, 23,59,59)
4305 * (t + 0.123456789).ceil(4) #=> 1999-12-31 23:59:59.1235 UTC
4306 */
4307
4308static VALUE
4309time_ceil(int argc, VALUE *argv, VALUE time)
4310{
4311 VALUE ndigits, v, den;
4312 struct time_object *tobj;
4313
4314 if (!rb_check_arity(argc, 0, 1) || NIL_P(ndigits = argv[0]))
4315 den = INT2FIX(1);
4316 else
4317 den = ndigits_denominator(ndigits);
4318
4319 GetTimeval(time, tobj);
4320 v = w2v(rb_time_unmagnify(tobj->timew));
4321
4322 v = modv(v, den);
4323 if (!rb_equal(v, INT2FIX(0))) {
4324 v = subv(den, v);
4325 }
4326 return time_add(tobj, time, v, 1);
4327}
4328
4329/*
4330 * call-seq:
4331 * time.sec -> integer
4332 *
4333 * Returns the second of the minute (0..60) for _time_.
4334 *
4335 * *Note:* Seconds range from zero to 60 to allow the system to inject
4336 * leap seconds. See https://en.wikipedia.org/wiki/Leap_second for further
4337 * details.
4338 *
4339 * t = Time.now #=> 2007-11-19 08:25:02 -0600
4340 * t.sec #=> 2
4341 */
4342
4343static VALUE
4344time_sec(VALUE time)
4345{
4346 struct time_object *tobj;
4347
4348 GetTimeval(time, tobj);
4349 MAKE_TM(time, tobj);
4350 return INT2FIX(tobj->vtm.sec);
4351}
4352
4353/*
4354 * call-seq:
4355 * time.min -> integer
4356 *
4357 * Returns the minute of the hour (0..59) for _time_.
4358 *
4359 * t = Time.now #=> 2007-11-19 08:25:51 -0600
4360 * t.min #=> 25
4361 */
4362
4363static VALUE
4364time_min(VALUE time)
4365{
4366 struct time_object *tobj;
4367
4368 GetTimeval(time, tobj);
4369 MAKE_TM(time, tobj);
4370 return INT2FIX(tobj->vtm.min);
4371}
4372
4373/*
4374 * call-seq:
4375 * time.hour -> integer
4376 *
4377 * Returns the hour of the day (0..23) for _time_.
4378 *
4379 * t = Time.now #=> 2007-11-19 08:26:20 -0600
4380 * t.hour #=> 8
4381 */
4382
4383static VALUE
4384time_hour(VALUE time)
4385{
4386 struct time_object *tobj;
4387
4388 GetTimeval(time, tobj);
4389 MAKE_TM(time, tobj);
4390 return INT2FIX(tobj->vtm.hour);
4391}
4392
4393/*
4394 * call-seq:
4395 * time.day -> integer
4396 * time.mday -> integer
4397 *
4398 * Returns the day of the month (1..31) for _time_.
4399 *
4400 * t = Time.now #=> 2007-11-19 08:27:03 -0600
4401 * t.day #=> 19
4402 * t.mday #=> 19
4403 */
4404
4405static VALUE
4406time_mday(VALUE time)
4407{
4408 struct time_object *tobj;
4409
4410 GetTimeval(time, tobj);
4411 MAKE_TM(time, tobj);
4412 return INT2FIX(tobj->vtm.mday);
4413}
4414
4415/*
4416 * call-seq:
4417 * time.mon -> integer
4418 * time.month -> integer
4419 *
4420 * Returns the month of the year (1..12) for _time_.
4421 *
4422 * t = Time.now #=> 2007-11-19 08:27:30 -0600
4423 * t.mon #=> 11
4424 * t.month #=> 11
4425 */
4426
4427static VALUE
4428time_mon(VALUE time)
4429{
4430 struct time_object *tobj;
4431
4432 GetTimeval(time, tobj);
4433 MAKE_TM(time, tobj);
4434 return INT2FIX(tobj->vtm.mon);
4435}
4436
4437/*
4438 * call-seq:
4439 * time.year -> integer
4440 *
4441 * Returns the year for _time_ (including the century).
4442 *
4443 * t = Time.now #=> 2007-11-19 08:27:51 -0600
4444 * t.year #=> 2007
4445 */
4446
4447static VALUE
4448time_year(VALUE time)
4449{
4450 struct time_object *tobj;
4451
4452 GetTimeval(time, tobj);
4453 MAKE_TM(time, tobj);
4454 return tobj->vtm.year;
4455}
4456
4457/*
4458 * call-seq:
4459 * time.wday -> integer
4460 *
4461 * Returns an integer representing the day of the week, 0..6, with
4462 * Sunday == 0.
4463 *
4464 * t = Time.now #=> 2007-11-20 02:35:35 -0600
4465 * t.wday #=> 2
4466 * t.sunday? #=> false
4467 * t.monday? #=> false
4468 * t.tuesday? #=> true
4469 * t.wednesday? #=> false
4470 * t.thursday? #=> false
4471 * t.friday? #=> false
4472 * t.saturday? #=> false
4473 */
4474
4475static VALUE
4476time_wday(VALUE time)
4477{
4478 struct time_object *tobj;
4479
4480 GetTimeval(time, tobj);
4481 MAKE_TM_ENSURE(time, tobj, tobj->vtm.wday != VTM_WDAY_INITVAL);
4482 return INT2FIX((int)tobj->vtm.wday);
4483}
4484
4485#define wday_p(n) {\
4486 return RBOOL(time_wday(time) == INT2FIX(n)); \
4487}
4488
4489/*
4490 * call-seq:
4491 * time.sunday? -> true or false
4492 *
4493 * Returns +true+ if _time_ represents Sunday.
4494 *
4495 * t = Time.local(1990, 4, 1) #=> 1990-04-01 00:00:00 -0600
4496 * t.sunday? #=> true
4497 */
4498
4499static VALUE
4500time_sunday(VALUE time)
4501{
4502 wday_p(0);
4503}
4504
4505/*
4506 * call-seq:
4507 * time.monday? -> true or false
4508 *
4509 * Returns +true+ if _time_ represents Monday.
4510 *
4511 * t = Time.local(2003, 8, 4) #=> 2003-08-04 00:00:00 -0500
4512 * t.monday? #=> true
4513 */
4514
4515static VALUE
4516time_monday(VALUE time)
4517{
4518 wday_p(1);
4519}
4520
4521/*
4522 * call-seq:
4523 * time.tuesday? -> true or false
4524 *
4525 * Returns +true+ if _time_ represents Tuesday.
4526 *
4527 * t = Time.local(1991, 2, 19) #=> 1991-02-19 00:00:00 -0600
4528 * t.tuesday? #=> true
4529 */
4530
4531static VALUE
4532time_tuesday(VALUE time)
4533{
4534 wday_p(2);
4535}
4536
4537/*
4538 * call-seq:
4539 * time.wednesday? -> true or false
4540 *
4541 * Returns +true+ if _time_ represents Wednesday.
4542 *
4543 * t = Time.local(1993, 2, 24) #=> 1993-02-24 00:00:00 -0600
4544 * t.wednesday? #=> true
4545 */
4546
4547static VALUE
4548time_wednesday(VALUE time)
4549{
4550 wday_p(3);
4551}
4552
4553/*
4554 * call-seq:
4555 * time.thursday? -> true or false
4556 *
4557 * Returns +true+ if _time_ represents Thursday.
4558 *
4559 * t = Time.local(1995, 12, 21) #=> 1995-12-21 00:00:00 -0600
4560 * t.thursday? #=> true
4561 */
4562
4563static VALUE
4564time_thursday(VALUE time)
4565{
4566 wday_p(4);
4567}
4568
4569/*
4570 * call-seq:
4571 * time.friday? -> true or false
4572 *
4573 * Returns +true+ if _time_ represents Friday.
4574 *
4575 * t = Time.local(1987, 12, 18) #=> 1987-12-18 00:00:00 -0600
4576 * t.friday? #=> true
4577 */
4578
4579static VALUE
4580time_friday(VALUE time)
4581{
4582 wday_p(5);
4583}
4584
4585/*
4586 * call-seq:
4587 * time.saturday? -> true or false
4588 *
4589 * Returns +true+ if _time_ represents Saturday.
4590 *
4591 * t = Time.local(2006, 6, 10) #=> 2006-06-10 00:00:00 -0500
4592 * t.saturday? #=> true
4593 */
4594
4595static VALUE
4596time_saturday(VALUE time)
4597{
4598 wday_p(6);
4599}
4600
4601/*
4602 * call-seq:
4603 * time.yday -> integer
4604 *
4605 * Returns an integer representing the day of the year, 1..366.
4606 *
4607 * t = Time.now #=> 2007-11-19 08:32:31 -0600
4608 * t.yday #=> 323
4609 */
4610
4611static VALUE
4612time_yday(VALUE time)
4613{
4614 struct time_object *tobj;
4615
4616 GetTimeval(time, tobj);
4617 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
4618 return INT2FIX(tobj->vtm.yday);
4619}
4620
4621/*
4622 * call-seq:
4623 * time.isdst -> true or false
4624 * time.dst? -> true or false
4625 *
4626 * Returns +true+ if _time_ occurs during Daylight
4627 * Saving Time in its time zone.
4628 *
4629 * # CST6CDT:
4630 * Time.local(2000, 1, 1).zone #=> "CST"
4631 * Time.local(2000, 1, 1).isdst #=> false
4632 * Time.local(2000, 1, 1).dst? #=> false
4633 * Time.local(2000, 7, 1).zone #=> "CDT"
4634 * Time.local(2000, 7, 1).isdst #=> true
4635 * Time.local(2000, 7, 1).dst? #=> true
4636 *
4637 * # Asia/Tokyo:
4638 * Time.local(2000, 1, 1).zone #=> "JST"
4639 * Time.local(2000, 1, 1).isdst #=> false
4640 * Time.local(2000, 1, 1).dst? #=> false
4641 * Time.local(2000, 7, 1).zone #=> "JST"
4642 * Time.local(2000, 7, 1).isdst #=> false
4643 * Time.local(2000, 7, 1).dst? #=> false
4644 */
4645
4646static VALUE
4647time_isdst(VALUE time)
4648{
4649 struct time_object *tobj;
4650
4651 GetTimeval(time, tobj);
4652 MAKE_TM(time, tobj);
4653 if (tobj->vtm.isdst == VTM_ISDST_INITVAL) {
4654 rb_raise(rb_eRuntimeError, "isdst is not set yet");
4655 }
4656 return RBOOL(tobj->vtm.isdst);
4657}
4658
4659/*
4660 * call-seq:
4661 * time.zone -> string or timezone
4662 *
4663 * Returns the name of the time zone used for _time_. As of Ruby
4664 * 1.8, returns ``UTC'' rather than ``GMT'' for UTC times.
4665 *
4666 * t = Time.gm(2000, "jan", 1, 20, 15, 1)
4667 * t.zone #=> "UTC"
4668 * t = Time.local(2000, "jan", 1, 20, 15, 1)
4669 * t.zone #=> "CST"
4670 */
4671
4672static VALUE
4673time_zone(VALUE time)
4674{
4675 struct time_object *tobj;
4676 VALUE zone;
4677
4678 GetTimeval(time, tobj);
4679 MAKE_TM(time, tobj);
4680
4681 if (TZMODE_UTC_P(tobj)) {
4682 return rb_usascii_str_new_cstr("UTC");
4683 }
4684 zone = tobj->vtm.zone;
4685 if (NIL_P(zone))
4686 return Qnil;
4687
4688 if (RB_TYPE_P(zone, T_STRING))
4689 zone = rb_str_dup(zone);
4690 return zone;
4691}
4692
4693/*
4694 * call-seq:
4695 * time.gmt_offset -> integer
4696 * time.gmtoff -> integer
4697 * time.utc_offset -> integer
4698 *
4699 * Returns the offset in seconds between the timezone of _time_
4700 * and UTC.
4701 *
4702 * t = Time.gm(2000,1,1,20,15,1) #=> 2000-01-01 20:15:01 UTC
4703 * t.gmt_offset #=> 0
4704 * l = t.getlocal #=> 2000-01-01 14:15:01 -0600
4705 * l.gmt_offset #=> -21600
4706 */
4707
4708VALUE
4710{
4711 struct time_object *tobj;
4712
4713 GetTimeval(time, tobj);
4714
4715 if (TZMODE_UTC_P(tobj)) {
4716 return INT2FIX(0);
4717 }
4718 else {
4719 MAKE_TM(time, tobj);
4720 return tobj->vtm.utc_offset;
4721 }
4722}
4723
4724/*
4725 * call-seq:
4726 * time.to_a -> array
4727 *
4728 * Returns a ten-element _array_ of values for _time_:
4729 *
4730 * [sec, min, hour, day, month, year, wday, yday, isdst, zone]
4731 *
4732 * See the individual methods for an explanation of the
4733 * valid ranges of each value. The ten elements can be passed directly
4734 * to Time.utc or Time.local to create a
4735 * new Time object.
4736 *
4737 * t = Time.now #=> 2007-11-19 08:36:01 -0600
4738 * now = t.to_a #=> [1, 36, 8, 19, 11, 2007, 1, 323, false, "CST"]
4739 */
4740
4741static VALUE
4742time_to_a(VALUE time)
4743{
4744 struct time_object *tobj;
4745
4746 GetTimeval(time, tobj);
4747 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
4748 return rb_ary_new3(10,
4749 INT2FIX(tobj->vtm.sec),
4750 INT2FIX(tobj->vtm.min),
4751 INT2FIX(tobj->vtm.hour),
4752 INT2FIX(tobj->vtm.mday),
4753 INT2FIX(tobj->vtm.mon),
4754 tobj->vtm.year,
4755 INT2FIX(tobj->vtm.wday),
4756 INT2FIX(tobj->vtm.yday),
4757 RBOOL(tobj->vtm.isdst),
4758 time_zone(time));
4759}
4760
4761static VALUE
4762rb_strftime_alloc(const char *format, size_t format_len, rb_encoding *enc,
4763 VALUE time, struct vtm *vtm, wideval_t timew, int gmt)
4764{
4765 VALUE timev = Qnil;
4766 struct timespec ts;
4767
4768 if (!timew2timespec_exact(timew, &ts))
4769 timev = w2v(rb_time_unmagnify(timew));
4770
4771 if (NIL_P(timev)) {
4772 return rb_strftime_timespec(format, format_len, enc, time, vtm, &ts, gmt);
4773 }
4774 else {
4775 return rb_strftime(format, format_len, enc, time, vtm, timev, gmt);
4776 }
4777}
4778
4779static VALUE
4780strftime_cstr(const char *fmt, size_t len, VALUE time, rb_encoding *enc)
4781{
4782 struct time_object *tobj;
4783 VALUE str;
4784
4785 GetTimeval(time, tobj);
4786 MAKE_TM(time, tobj);
4787 str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew, TZMODE_UTC_P(tobj));
4788 if (!str) rb_raise(rb_eArgError, "invalid format: %s", fmt);
4789 return str;
4790}
4791
4792/*
4793 * call-seq:
4794 * time.strftime( string ) -> string
4795 *
4796 * Formats _time_ according to the directives in the given format string.
4797 *
4798 * The directives begin with a percent (%) character.
4799 * Any text not listed as a directive will be passed through to the
4800 * output string.
4801 *
4802 * The directive consists of a percent (%) character,
4803 * zero or more flags, optional minimum field width,
4804 * optional modifier and a conversion specifier
4805 * as follows:
4806 *
4807 * %<flags><width><modifier><conversion>
4808 *
4809 * Flags:
4810 * - don't pad a numerical output
4811 * _ use spaces for padding
4812 * 0 use zeros for padding
4813 * ^ upcase the result string
4814 * # change case
4815 * : use colons for %z
4816 *
4817 * The minimum field width specifies the minimum width.
4818 *
4819 * The modifiers are "E" and "O".
4820 * They are ignored.
4821 *
4822 * Format directives:
4823 *
4824 * Date (Year, Month, Day):
4825 * %Y - Year with century if provided, will pad result at least 4 digits.
4826 * -0001, 0000, 1995, 2009, 14292, etc.
4827 * %C - year / 100 (rounded down such as 20 in 2009)
4828 * %y - year % 100 (00..99)
4829 *
4830 * %m - Month of the year, zero-padded (01..12)
4831 * %_m blank-padded ( 1..12)
4832 * %-m no-padded (1..12)
4833 * %B - The full month name (``January'')
4834 * %^B uppercased (``JANUARY'')
4835 * %b - The abbreviated month name (``Jan'')
4836 * %^b uppercased (``JAN'')
4837 * %h - Equivalent to %b
4838 *
4839 * %d - Day of the month, zero-padded (01..31)
4840 * %-d no-padded (1..31)
4841 * %e - Day of the month, blank-padded ( 1..31)
4842 *
4843 * %j - Day of the year (001..366)
4844 *
4845 * Time (Hour, Minute, Second, Subsecond):
4846 * %H - Hour of the day, 24-hour clock, zero-padded (00..23)
4847 * %k - Hour of the day, 24-hour clock, blank-padded ( 0..23)
4848 * %I - Hour of the day, 12-hour clock, zero-padded (01..12)
4849 * %l - Hour of the day, 12-hour clock, blank-padded ( 1..12)
4850 * %P - Meridian indicator, lowercase (``am'' or ``pm'')
4851 * %p - Meridian indicator, uppercase (``AM'' or ``PM'')
4852 *
4853 * %M - Minute of the hour (00..59)
4854 *
4855 * %S - Second of the minute (00..60)
4856 *
4857 * %L - Millisecond of the second (000..999)
4858 * The digits under millisecond are truncated to not produce 1000.
4859 * %N - Fractional seconds digits, default is 9 digits (nanosecond)
4860 * %3N millisecond (3 digits)
4861 * %6N microsecond (6 digits)
4862 * %9N nanosecond (9 digits)
4863 * %12N picosecond (12 digits)
4864 * %15N femtosecond (15 digits)
4865 * %18N attosecond (18 digits)
4866 * %21N zeptosecond (21 digits)
4867 * %24N yoctosecond (24 digits)
4868 * The digits under the specified length are truncated to avoid
4869 * carry up.
4870 *
4871 * Time zone:
4872 * %z - Time zone as hour and minute offset from UTC (e.g. +0900)
4873 * %:z - hour and minute offset from UTC with a colon (e.g. +09:00)
4874 * %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
4875 * %Z - Abbreviated time zone name or similar information. (OS dependent)
4876 *
4877 * Weekday:
4878 * %A - The full weekday name (``Sunday'')
4879 * %^A uppercased (``SUNDAY'')
4880 * %a - The abbreviated name (``Sun'')
4881 * %^a uppercased (``SUN'')
4882 * %u - Day of the week (Monday is 1, 1..7)
4883 * %w - Day of the week (Sunday is 0, 0..6)
4884 *
4885 * ISO 8601 week-based year and week number:
4886 * The first week of YYYY starts with a Monday and includes YYYY-01-04.
4887 * The days in the year before the first week are in the last week of
4888 * the previous year.
4889 * %G - The week-based year
4890 * %g - The last 2 digits of the week-based year (00..99)
4891 * %V - Week number of the week-based year (01..53)
4892 *
4893 * Week number:
4894 * The first week of YYYY that starts with a Sunday or Monday (according to %U
4895 * or %W). The days in the year before the first week are in week 0.
4896 * %U - Week number of the year. The week starts with Sunday. (00..53)
4897 * %W - Week number of the year. The week starts with Monday. (00..53)
4898 *
4899 * Seconds since the Epoch:
4900 * %s - Number of seconds since 1970-01-01 00:00:00 UTC.
4901 *
4902 * Literal string:
4903 * %n - Newline character (\n)
4904 * %t - Tab character (\t)
4905 * %% - Literal ``%'' character
4906 *
4907 * Combination:
4908 * %c - date and time (%a %b %e %T %Y)
4909 * %D - Date (%m/%d/%y)
4910 * %F - The ISO 8601 date format (%Y-%m-%d)
4911 * %v - VMS date (%e-%^b-%4Y)
4912 * %x - Same as %D
4913 * %X - Same as %T
4914 * %r - 12-hour time (%I:%M:%S %p)
4915 * %R - 24-hour time (%H:%M)
4916 * %T - 24-hour time (%H:%M:%S)
4917 *
4918 * This method is similar to strftime() function defined in ISO C and POSIX.
4919 *
4920 * While all directives are locale independent since Ruby 1.9, %Z is platform
4921 * dependent.
4922 * So, the result may differ even if the same format string is used in other
4923 * systems such as C.
4924 *
4925 * %z is recommended over %Z.
4926 * %Z doesn't identify the timezone.
4927 * For example, "CST" is used at America/Chicago (-06:00),
4928 * America/Havana (-05:00), Asia/Harbin (+08:00), Australia/Darwin (+09:30)
4929 * and Australia/Adelaide (+10:30).
4930 * Also, %Z is highly dependent on the operating system.
4931 * For example, it may generate a non ASCII string on Japanese Windows,
4932 * i.e. the result can be different to "JST".
4933 * So the numeric time zone offset, %z, is recommended.
4934 *
4935 * Examples:
4936 *
4937 * t = Time.new(2007,11,19,8,37,48,"-06:00") #=> 2007-11-19 08:37:48 -0600
4938 * t.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007"
4939 * t.strftime("at %I:%M %p") #=> "at 08:37 AM"
4940 *
4941 * Various ISO 8601 formats:
4942 * %Y%m%d => 20071119 Calendar date (basic)
4943 * %F => 2007-11-19 Calendar date (extended)
4944 * %Y-%m => 2007-11 Calendar date, reduced accuracy, specific month
4945 * %Y => 2007 Calendar date, reduced accuracy, specific year
4946 * %C => 20 Calendar date, reduced accuracy, specific century
4947 * %Y%j => 2007323 Ordinal date (basic)
4948 * %Y-%j => 2007-323 Ordinal date (extended)
4949 * %GW%V%u => 2007W471 Week date (basic)
4950 * %G-W%V-%u => 2007-W47-1 Week date (extended)
4951 * %GW%V => 2007W47 Week date, reduced accuracy, specific week (basic)
4952 * %G-W%V => 2007-W47 Week date, reduced accuracy, specific week (extended)
4953 * %H%M%S => 083748 Local time (basic)
4954 * %T => 08:37:48 Local time (extended)
4955 * %H%M => 0837 Local time, reduced accuracy, specific minute (basic)
4956 * %H:%M => 08:37 Local time, reduced accuracy, specific minute (extended)
4957 * %H => 08 Local time, reduced accuracy, specific hour
4958 * %H%M%S,%L => 083748,000 Local time with decimal fraction, comma as decimal sign (basic)
4959 * %T,%L => 08:37:48,000 Local time with decimal fraction, comma as decimal sign (extended)
4960 * %H%M%S.%L => 083748.000 Local time with decimal fraction, full stop as decimal sign (basic)
4961 * %T.%L => 08:37:48.000 Local time with decimal fraction, full stop as decimal sign (extended)
4962 * %H%M%S%z => 083748-0600 Local time and the difference from UTC (basic)
4963 * %T%:z => 08:37:48-06:00 Local time and the difference from UTC (extended)
4964 * %Y%m%dT%H%M%S%z => 20071119T083748-0600 Date and time of day for calendar date (basic)
4965 * %FT%T%:z => 2007-11-19T08:37:48-06:00 Date and time of day for calendar date (extended)
4966 * %Y%jT%H%M%S%z => 2007323T083748-0600 Date and time of day for ordinal date (basic)
4967 * %Y-%jT%T%:z => 2007-323T08:37:48-06:00 Date and time of day for ordinal date (extended)
4968 * %GW%V%uT%H%M%S%z => 2007W471T083748-0600 Date and time of day for week date (basic)
4969 * %G-W%V-%uT%T%:z => 2007-W47-1T08:37:48-06:00 Date and time of day for week date (extended)
4970 * %Y%m%dT%H%M => 20071119T0837 Calendar date and local time (basic)
4971 * %FT%R => 2007-11-19T08:37 Calendar date and local time (extended)
4972 * %Y%jT%H%MZ => 2007323T0837Z Ordinal date and UTC of day (basic)
4973 * %Y-%jT%RZ => 2007-323T08:37Z Ordinal date and UTC of day (extended)
4974 * %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic)
4975 * %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended)
4976 *
4977 */
4978
4979static VALUE
4980time_strftime(VALUE time, VALUE format)
4981{
4982 struct time_object *tobj;
4983 const char *fmt;
4984 long len;
4985 rb_encoding *enc;
4986 VALUE tmp;
4987
4988 GetTimeval(time, tobj);
4989 MAKE_TM_ENSURE(time, tobj, tobj->vtm.yday != 0);
4990 StringValue(format);
4991 if (!rb_enc_str_asciicompat_p(format)) {
4992 rb_raise(rb_eArgError, "format should have ASCII compatible encoding");
4993 }
4994 tmp = rb_str_tmp_frozen_acquire(format);
4995 fmt = RSTRING_PTR(tmp);
4996 len = RSTRING_LEN(tmp);
4997 enc = rb_enc_get(format);
4998 if (len == 0) {
4999 rb_warning("strftime called with empty format string");
5000 return rb_enc_str_new(0, 0, enc);
5001 }
5002 else {
5003 VALUE str = rb_strftime_alloc(fmt, len, enc, time, &tobj->vtm, tobj->timew,
5004 TZMODE_UTC_P(tobj));
5005 rb_str_tmp_frozen_release(format, tmp);
5006 if (!str) rb_raise(rb_eArgError, "invalid format: %"PRIsVALUE, format);
5007 return str;
5008 }
5009}
5010
5011int ruby_marshal_write_long(long x, char *buf);
5012
5013enum {base_dump_size = 8};
5014
5015/* :nodoc: */
5016static VALUE
5017time_mdump(VALUE time)
5018{
5019 struct time_object *tobj;
5020 unsigned long p, s;
5021 char buf[base_dump_size + sizeof(long) + 1];
5022 int i;
5023 VALUE str;
5024
5025 struct vtm vtm;
5026 long year;
5027 long usec, nsec;
5028 VALUE subsecx, nano, subnano, v, zone;
5029
5030 VALUE year_extend = Qnil;
5031 const int max_year = 1900+0xffff;
5032
5033 GetTimeval(time, tobj);
5034
5035 gmtimew(tobj->timew, &vtm);
5036
5037 if (FIXNUM_P(vtm.year)) {
5038 year = FIX2LONG(vtm.year);
5039 if (year > max_year) {
5040 year_extend = INT2FIX(year - max_year);
5041 year = max_year;
5042 }
5043 else if (year < 1900) {
5044 year_extend = LONG2NUM(1900 - year);
5045 year = 1900;
5046 }
5047 }
5048 else {
5049 if (rb_int_positive_p(vtm.year)) {
5050 year_extend = rb_int_minus(vtm.year, INT2FIX(max_year));
5051 year = max_year;
5052 }
5053 else {
5054 year_extend = rb_int_minus(INT2FIX(1900), vtm.year);
5055 year = 1900;
5056 }
5057 }
5058
5059 subsecx = vtm.subsecx;
5060
5061 nano = mulquov(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
5062 divmodv(nano, INT2FIX(1), &v, &subnano);
5063 nsec = FIX2LONG(v);
5064 usec = nsec / 1000;
5065 nsec = nsec % 1000;
5066
5067 nano = addv(LONG2FIX(nsec), subnano);
5068
5069 p = 0x1UL << 31 | /* 1 */
5070 TZMODE_UTC_P(tobj) << 30 | /* 1 */
5071 (year-1900) << 14 | /* 16 */
5072 (vtm.mon-1) << 10 | /* 4 */
5073 vtm.mday << 5 | /* 5 */
5074 vtm.hour; /* 5 */
5075 s = (unsigned long)vtm.min << 26 | /* 6 */
5076 vtm.sec << 20 | /* 6 */
5077 usec; /* 20 */
5078
5079 for (i=0; i<4; i++) {
5080 buf[i] = (unsigned char)p;
5081 p = RSHIFT(p, 8);
5082 }
5083 for (i=4; i<8; i++) {
5084 buf[i] = (unsigned char)s;
5085 s = RSHIFT(s, 8);
5086 }
5087
5088 if (!NIL_P(year_extend)) {
5089 /*
5090 * Append extended year distance from 1900..(1900+0xffff). In
5091 * each cases, there is no sign as the value is positive. The
5092 * format is length (marshaled long) + little endian packed
5093 * binary (like as Integer).
5094 */
5095 size_t ysize = rb_absint_size(year_extend, NULL);
5096 char *p, *const buf_year_extend = buf + base_dump_size;
5097 if (ysize > LONG_MAX ||
5098 (i = ruby_marshal_write_long((long)ysize, buf_year_extend)) < 0) {
5099 rb_raise(rb_eArgError, "year too %s to marshal: %"PRIsVALUE" UTC",
5100 (year == 1900 ? "small" : "big"), vtm.year);
5101 }
5102 i += base_dump_size;
5103 str = rb_str_new(NULL, i + ysize);
5104 p = RSTRING_PTR(str);
5105 memcpy(p, buf, i);
5106 p += i;
5107 rb_integer_pack(year_extend, p, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
5108 }
5109 else {
5110 str = rb_str_new(buf, base_dump_size);
5111 }
5112 rb_copy_generic_ivar(str, time);
5113 if (!rb_equal(nano, INT2FIX(0))) {
5114 if (RB_TYPE_P(nano, T_RATIONAL)) {
5115 rb_ivar_set(str, id_nano_num, RRATIONAL(nano)->num);
5116 rb_ivar_set(str, id_nano_den, RRATIONAL(nano)->den);
5117 }
5118 else {
5119 rb_ivar_set(str, id_nano_num, nano);
5120 rb_ivar_set(str, id_nano_den, INT2FIX(1));
5121 }
5122 }
5123 if (nsec) { /* submicro is only for Ruby 1.9.1 compatibility */
5124 /*
5125 * submicro is formatted in fixed-point packed BCD (without sign).
5126 * It represent digits under microsecond.
5127 * For nanosecond resolution, 3 digits (2 bytes) are used.
5128 * However it can be longer.
5129 * Extra digits are ignored for loading.
5130 */
5131 char buf[2];
5132 int len = (int)sizeof(buf);
5133 buf[1] = (char)((nsec % 10) << 4);
5134 nsec /= 10;
5135 buf[0] = (char)(nsec % 10);
5136 nsec /= 10;
5137 buf[0] |= (char)((nsec % 10) << 4);
5138 if (buf[1] == 0)
5139 len = 1;
5140 rb_ivar_set(str, id_submicro, rb_str_new(buf, len));
5141 }
5142 if (!TZMODE_UTC_P(tobj)) {
5143 VALUE off = rb_time_utc_offset(time), div, mod;
5144 divmodv(off, INT2FIX(1), &div, &mod);
5145 if (rb_equal(mod, INT2FIX(0)))
5146 off = rb_Integer(div);
5147 rb_ivar_set(str, id_offset, off);
5148 }
5149 zone = tobj->vtm.zone;
5150 if (maybe_tzobj_p(zone)) {
5151 zone = rb_funcallv(zone, id_name, 0, 0);
5152 }
5153 rb_ivar_set(str, id_zone, zone);
5154 return str;
5155}
5156
5157/* :nodoc: */
5158static VALUE
5159time_dump(int argc, VALUE *argv, VALUE time)
5160{
5161 VALUE str;
5162
5163 rb_check_arity(argc, 0, 1);
5164 str = time_mdump(time);
5165
5166 return str;
5167}
5168
5169static VALUE
5170mload_findzone(VALUE arg)
5171{
5172 VALUE *argp = (VALUE *)arg;
5173 VALUE time = argp[0], zone = argp[1];
5174 return find_timezone(time, zone);
5175}
5176
5177static VALUE
5178mload_zone(VALUE time, VALUE zone)
5179{
5180 VALUE z, args[2];
5181 args[0] = time;
5182 args[1] = zone;
5183 z = rb_rescue(mload_findzone, (VALUE)args, 0, Qnil);
5184 if (NIL_P(z)) return rb_fstring(zone);
5185 if (RB_TYPE_P(z, T_STRING)) return rb_fstring(z);
5186 return z;
5187}
5188
5189long ruby_marshal_read_long(const char **buf, long len);
5190
5191/* :nodoc: */
5192static VALUE
5193time_mload(VALUE time, VALUE str)
5194{
5195 struct time_object *tobj;
5196 unsigned long p, s;
5197 time_t sec;
5198 long usec;
5199 unsigned char *buf;
5200 struct vtm vtm;
5201 int i, gmt;
5202 long nsec;
5203 VALUE submicro, nano_num, nano_den, offset, zone, year;
5204 wideval_t timew;
5205
5206 time_modify(time);
5207
5208#define get_attr(attr, iffound) \
5209 attr = rb_attr_delete(str, id_##attr); \
5210 if (!NIL_P(attr)) { \
5211 iffound; \
5212 }
5213
5214 get_attr(nano_num, {});
5215 get_attr(nano_den, {});
5216 get_attr(submicro, {});
5217 get_attr(offset, (offset = rb_rescue(validate_utc_offset, offset, 0, Qnil)));
5218 get_attr(zone, (zone = rb_rescue(validate_zone_name, zone, 0, Qnil)));
5219 get_attr(year, {});
5220
5221#undef get_attr
5222
5223 rb_copy_generic_ivar(time, str);
5224
5225 StringValue(str);
5226 buf = (unsigned char *)RSTRING_PTR(str);
5227 if (RSTRING_LEN(str) < base_dump_size) {
5228 goto invalid_format;
5229 }
5230
5231 p = s = 0;
5232 for (i=0; i<4; i++) {
5233 p |= (unsigned long)buf[i]<<(8*i);
5234 }
5235 for (i=4; i<8; i++) {
5236 s |= (unsigned long)buf[i]<<(8*(i-4));
5237 }
5238
5239 if ((p & (1UL<<31)) == 0) {
5240 gmt = 0;
5241 offset = Qnil;
5242 sec = p;
5243 usec = s;
5244 nsec = usec * 1000;
5245 timew = wadd(rb_time_magnify(TIMET2WV(sec)), wmulquoll(WINT2FIXWV(usec), TIME_SCALE, 1000000));
5246 }
5247 else {
5248 p &= ~(1UL<<31);
5249 gmt = (int)((p >> 30) & 0x1);
5250
5251 if (NIL_P(year)) {
5252 year = INT2FIX(((int)(p >> 14) & 0xffff) + 1900);
5253 }
5254 if (RSTRING_LEN(str) > base_dump_size) {
5255 long len = RSTRING_LEN(str) - base_dump_size;
5256 long ysize = 0;
5257 VALUE year_extend;
5258 const char *ybuf = (const char *)(buf += base_dump_size);
5259 ysize = ruby_marshal_read_long(&ybuf, len);
5260 len -= ybuf - (const char *)buf;
5261 if (ysize < 0 || ysize > len) goto invalid_format;
5262 year_extend = rb_integer_unpack(ybuf, ysize, 1, 0, INTEGER_PACK_LITTLE_ENDIAN);
5263 if (year == INT2FIX(1900)) {
5264 year = rb_int_minus(year, year_extend);
5265 }
5266 else {
5267 year = rb_int_plus(year, year_extend);
5268 }
5269 }
5270 unsigned int mon = ((int)(p >> 10) & 0xf); /* 0...12 */
5271 if (mon >= 12) {
5272 mon -= 12;
5273 year = addv(year, LONG2FIX(1));
5274 }
5275 vtm.year = year;
5276 vtm.mon = mon + 1;
5277 vtm.mday = (int)(p >> 5) & 0x1f;
5278 vtm.hour = (int) p & 0x1f;
5279 vtm.min = (int)(s >> 26) & 0x3f;
5280 vtm.sec = (int)(s >> 20) & 0x3f;
5281 vtm.utc_offset = INT2FIX(0);
5282 vtm.yday = vtm.wday = 0;
5283 vtm.isdst = 0;
5284 vtm.zone = str_empty;
5285
5286 usec = (long)(s & 0xfffff);
5287 nsec = usec * 1000;
5288
5289
5290 vtm.subsecx = mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000));
5291 if (nano_num != Qnil) {
5292 VALUE nano = quov(num_exact(nano_num), num_exact(nano_den));
5293 vtm.subsecx = addv(vtm.subsecx, mulquov(nano, INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
5294 }
5295 else if (submicro != Qnil) { /* for Ruby 1.9.1 compatibility */
5296 unsigned char *ptr;
5297 long len;
5298 int digit;
5299 ptr = (unsigned char*)StringValuePtr(submicro);
5300 len = RSTRING_LEN(submicro);
5301 nsec = 0;
5302 if (0 < len) {
5303 if (10 <= (digit = ptr[0] >> 4)) goto end_submicro;
5304 nsec += digit * 100;
5305 if (10 <= (digit = ptr[0] & 0xf)) goto end_submicro;
5306 nsec += digit * 10;
5307 }
5308 if (1 < len) {
5309 if (10 <= (digit = ptr[1] >> 4)) goto end_submicro;
5310 nsec += digit;
5311 }
5312 vtm.subsecx = addv(vtm.subsecx, mulquov(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
5313end_submicro: ;
5314 }
5315 timew = timegmw(&vtm);
5316 }
5317
5318 GetNewTimeval(time, tobj);
5319 tobj->tzmode = TIME_TZMODE_LOCALTIME;
5320 tobj->tm_got = 0;
5321 tobj->timew = timew;
5322 if (gmt) {
5323 TZMODE_SET_UTC(tobj);
5324 }
5325 else if (!NIL_P(offset)) {
5326 time_set_utc_offset(time, offset);
5327 time_fixoff(time);
5328 }
5329 if (!NIL_P(zone)) {
5330 zone = mload_zone(time, zone);
5331 tobj->vtm.zone = zone;
5332 zone_localtime(zone, time);
5333 }
5334
5335 return time;
5336
5337 invalid_format:
5338 rb_raise(rb_eTypeError, "marshaled time format differ");
5340}
5341
5342/* :nodoc: */
5343static VALUE
5344time_load(VALUE klass, VALUE str)
5345{
5346 VALUE time = time_s_alloc(klass);
5347
5348 time_mload(time, str);
5349 return time;
5350}
5351
5352/* :nodoc:*/
5353/* Document-class: Time::tm
5354 *
5355 * A container class for timezone conversion.
5356 */
5357
5358/*
5359 * call-seq:
5360 *
5361 * Time::tm.from_time(t) -> tm
5362 *
5363 * Creates new Time::tm object from a Time object.
5364 */
5365
5366static VALUE
5367tm_from_time(VALUE klass, VALUE time)
5368{
5369 struct time_object *tobj;
5370 struct vtm vtm, *v;
5371#if TM_IS_TIME
5372 VALUE tm;
5373 struct time_object *ttm;
5374
5375 GetTimeval(time, tobj);
5376 tm = time_s_alloc(klass);
5377 ttm = DATA_PTR(tm);
5378 v = &vtm;
5379 GMTIMEW(ttm->timew = tobj->timew, v);
5380 ttm->timew = wsub(ttm->timew, v->subsecx);
5381 v->subsecx = INT2FIX(0);
5382 v->zone = Qnil;
5383 ttm->vtm = *v;
5384 ttm->tm_got = 1;
5385 TZMODE_SET_UTC(ttm);
5386 return tm;
5387#else
5388 VALUE args[8];
5389 int i = 0;
5390
5391 GetTimeval(time, tobj);
5392 if (tobj->tm_got && TZMODE_UTC_P(tobj))
5393 v = &tobj->vtm;
5394 else
5395 GMTIMEW(tobj->timew, v = &vtm);
5396 args[i++] = v->year;
5397 args[i++] = INT2FIX(v->mon);
5398 args[i++] = INT2FIX(v->mday);
5399 args[i++] = INT2FIX(v->hour);
5400 args[i++] = INT2FIX(v->min);
5401 args[i++] = INT2FIX(v->sec);
5402 switch (v->isdst) {
5403 case 0: args[i++] = Qfalse; break;
5404 case 1: args[i++] = Qtrue; break;
5405 default: args[i++] = Qnil; break;
5406 }
5407 args[i++] = w2v(rb_time_unmagnify(tobj->timew));
5408 return rb_class_new_instance(i, args, klass);
5409#endif
5410}
5411
5412/*
5413 * call-seq:
5414 *
5415 * Time::tm.new(year, month=nil, day=nil, hour=nil, min=nil, sec=nil, zone=nil) -> tm
5416 *
5417 * Creates new Time::tm object.
5418 */
5419
5420static VALUE
5421tm_initialize(int argc, VALUE *argv, VALUE tm)
5422{
5423 struct vtm vtm;
5424 wideval_t t;
5425
5426 if (rb_check_arity(argc, 1, 7) > 6) argc = 6;
5427 time_arg(argc, argv, &vtm);
5428 t = timegmw(&vtm);
5429 {
5430#if TM_IS_TIME
5431 struct time_object *tobj = DATA_PTR(tm);
5432 tobj->tzmode = TIME_TZMODE_UTC;
5433 tobj->timew = t;
5434 tobj->vtm = vtm;
5435#else
5436 int i = 0;
5437 RSTRUCT_SET(tm, i++, INT2FIX(vtm.sec));
5438 RSTRUCT_SET(tm, i++, INT2FIX(vtm.min));
5439 RSTRUCT_SET(tm, i++, INT2FIX(vtm.hour));
5440 RSTRUCT_SET(tm, i++, INT2FIX(vtm.mday));
5441 RSTRUCT_SET(tm, i++, INT2FIX(vtm.mon));
5442 RSTRUCT_SET(tm, i++, vtm.year);
5443 RSTRUCT_SET(tm, i++, w2v(rb_time_unmagnify(t)));
5444#endif
5445 }
5446 return tm;
5447}
5448
5449/* call-seq:
5450 *
5451 * tm.to_time -> time
5452 *
5453 * Returns a new Time object.
5454 */
5455
5456static VALUE
5457tm_to_time(VALUE tm)
5458{
5459#if TM_IS_TIME
5460 struct time_object *torig = get_timeval(tm);
5461 VALUE dup = time_s_alloc(rb_cTime);
5462 struct time_object *tobj = DATA_PTR(dup);
5463 *tobj = *torig;
5464 return dup;
5465#else
5466 VALUE t[6];
5467 const VALUE *p = RSTRUCT_CONST_PTR(tm);
5468 int i;
5469
5470 for (i = 0; i < numberof(t); ++i) {
5471 t[i] = p[numberof(t) - 1 - i];
5472 }
5473 return time_s_mkutc(numberof(t), t, rb_cTime);
5474#endif
5475}
5476
5477#if !TM_IS_TIME
5478static VALUE
5479tm_zero(VALUE tm)
5480{
5481 return INT2FIX(0);
5482}
5483
5484#define tm_subsec tm_zero
5485#define tm_utc_offset tm_zero
5486
5487static VALUE
5488tm_isdst(VALUE tm)
5489{
5490 return Qfalse;
5491}
5492
5493static VALUE
5494tm_to_s(VALUE tm)
5495{
5496 const VALUE *p = RSTRUCT_CONST_PTR(tm);
5497
5498 return rb_sprintf("%.4"PRIsVALUE"-%.2"PRIsVALUE"-%.2"PRIsVALUE" "
5499 "%.2"PRIsVALUE":%.2"PRIsVALUE":%.2"PRIsVALUE" "
5500 "UTC",
5501 p[5], p[4], p[3], p[2], p[1], p[0]);
5502}
5503#else
5504static VALUE
5505tm_plus(VALUE tm, VALUE offset)
5506{
5507 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, +1);
5508}
5509
5510static VALUE
5511tm_minus(VALUE tm, VALUE offset)
5512{
5513 return time_add0(rb_obj_class(tm), get_timeval(tm), tm, offset, -1);
5514}
5515#endif
5516
5517static VALUE
5518Init_tm(VALUE outer, const char *name)
5519{
5520 /* :stopdoc:*/
5521 VALUE tm;
5522#if TM_IS_TIME
5523 tm = rb_define_class_under(outer, name, rb_cObject);
5524 rb_define_alloc_func(tm, time_s_alloc);
5525 rb_define_method(tm, "sec", time_sec, 0);
5526 rb_define_method(tm, "min", time_min, 0);
5527 rb_define_method(tm, "hour", time_hour, 0);
5528 rb_define_method(tm, "mday", time_mday, 0);
5529 rb_define_method(tm, "day", time_mday, 0);
5530 rb_define_method(tm, "mon", time_mon, 0);
5531 rb_define_method(tm, "month", time_mon, 0);
5532 rb_define_method(tm, "year", time_year, 0);
5533 rb_define_method(tm, "isdst", time_isdst, 0);
5534 rb_define_method(tm, "dst?", time_isdst, 0);
5535 rb_define_method(tm, "zone", time_zone, 0);
5536 rb_define_method(tm, "gmtoff", rb_time_utc_offset, 0);
5537 rb_define_method(tm, "gmt_offset", rb_time_utc_offset, 0);
5538 rb_define_method(tm, "utc_offset", rb_time_utc_offset, 0);
5539 rb_define_method(tm, "utc?", time_utc_p, 0);
5540 rb_define_method(tm, "gmt?", time_utc_p, 0);
5541 rb_define_method(tm, "to_s", time_to_s, 0);
5542 rb_define_method(tm, "inspect", time_inspect, 0);
5543 rb_define_method(tm, "to_a", time_to_a, 0);
5544 rb_define_method(tm, "tv_sec", time_to_i, 0);
5545 rb_define_method(tm, "tv_usec", time_usec, 0);
5546 rb_define_method(tm, "usec", time_usec, 0);
5547 rb_define_method(tm, "tv_nsec", time_nsec, 0);
5548 rb_define_method(tm, "nsec", time_nsec, 0);
5549 rb_define_method(tm, "subsec", time_subsec, 0);
5550 rb_define_method(tm, "to_i", time_to_i, 0);
5551 rb_define_method(tm, "to_f", time_to_f, 0);
5552 rb_define_method(tm, "to_r", time_to_r, 0);
5553 rb_define_method(tm, "+", tm_plus, 1);
5554 rb_define_method(tm, "-", tm_minus, 1);
5555#else
5556 tm = rb_struct_define_under(outer, "tm",
5557 "sec", "min", "hour",
5558 "mday", "mon", "year",
5559 "to_i", NULL);
5560 rb_define_method(tm, "subsec", tm_subsec, 0);
5561 rb_define_method(tm, "utc_offset", tm_utc_offset, 0);
5562 rb_define_method(tm, "to_s", tm_to_s, 0);
5563 rb_define_method(tm, "inspect", tm_to_s, 0);
5564 rb_define_method(tm, "isdst", tm_isdst, 0);
5565 rb_define_method(tm, "dst?", tm_isdst, 0);
5566#endif
5567 rb_define_method(tm, "initialize", tm_initialize, -1);
5568 rb_define_method(tm, "utc", tm_to_time, 0);
5569 rb_alias(tm, rb_intern_const("to_time"), rb_intern_const("utc"));
5570 rb_define_singleton_method(tm, "from_time", tm_from_time, 1);
5571 /* :startdoc:*/
5572
5573 return tm;
5574}
5575
5576VALUE
5577rb_time_zone_abbreviation(VALUE zone, VALUE time)
5578{
5579 VALUE tm, abbr, strftime_args[2];
5580
5581 abbr = rb_check_string_type(zone);
5582 if (!NIL_P(abbr)) return abbr;
5583
5584 tm = tm_from_time(rb_cTimeTM, time);
5585 abbr = rb_check_funcall(zone, rb_intern("abbr"), 1, &tm);
5586 if (abbr != Qundef) {
5587 goto found;
5588 }
5589#ifdef SUPPORT_TZINFO_ZONE_ABBREVIATION
5590 abbr = rb_check_funcall(zone, rb_intern("period_for_utc"), 1, &tm);
5591 if (abbr != Qundef) {
5592 abbr = rb_funcallv(abbr, rb_intern("abbreviation"), 0, 0);
5593 goto found;
5594 }
5595#endif
5596 strftime_args[0] = rb_fstring_lit("%Z");
5597 strftime_args[1] = tm;
5598 abbr = rb_check_funcall(zone, rb_intern("strftime"), 2, strftime_args);
5599 if (abbr != Qundef) {
5600 goto found;
5601 }
5602 abbr = rb_check_funcall_default(zone, idName, 0, 0, Qnil);
5603 found:
5604 return rb_obj_as_string(abbr);
5605}
5606
5607/* Internal Details:
5608 *
5609 * Since Ruby 1.9.2, Time implementation uses a signed 63 bit integer or
5610 * Integer(T_BIGNUM), Rational.
5611 * The integer is a number of nanoseconds since the _Epoch_ which can
5612 * represent 1823-11-12 to 2116-02-20.
5613 * When Integer(T_BIGNUM) or Rational is used (before 1823, after 2116, under
5614 * nanosecond), Time works slower than when integer is used.
5615 */
5616
5617//
5618void
5619Init_Time(void)
5620{
5621 id_submicro = rb_intern_const("submicro");
5622 id_nano_num = rb_intern_const("nano_num");
5623 id_nano_den = rb_intern_const("nano_den");
5624 id_offset = rb_intern_const("offset");
5625 id_zone = rb_intern_const("zone");
5626 id_nanosecond = rb_intern_const("nanosecond");
5627 id_microsecond = rb_intern_const("microsecond");
5628 id_millisecond = rb_intern_const("millisecond");
5629 id_nsec = rb_intern_const("nsec");
5630 id_usec = rb_intern_const("usec");
5631 id_local_to_utc = rb_intern_const("local_to_utc");
5632 id_utc_to_local = rb_intern_const("utc_to_local");
5633 id_year = rb_intern_const("year");
5634 id_mon = rb_intern_const("mon");
5635 id_mday = rb_intern_const("mday");
5636 id_hour = rb_intern_const("hour");
5637 id_min = rb_intern_const("min");
5638 id_sec = rb_intern_const("sec");
5639 id_isdst = rb_intern_const("isdst");
5640 id_find_timezone = rb_intern_const("find_timezone");
5641
5642 str_utc = rb_fstring_lit("UTC");
5644 str_empty = rb_fstring_lit("");
5645 rb_gc_register_mark_object(str_empty);
5646
5647 rb_cTime = rb_define_class("Time", rb_cObject);
5649
5650 rb_define_alloc_func(rb_cTime, time_s_alloc);
5651 rb_define_singleton_method(rb_cTime, "utc", time_s_mkutc, -1);
5652 rb_define_singleton_method(rb_cTime, "local", time_s_mktime, -1);
5653 rb_define_alias(rb_singleton_class(rb_cTime), "gm", "utc");
5654 rb_define_alias(rb_singleton_class(rb_cTime), "mktime", "local");
5655
5656 rb_define_method(rb_cTime, "to_i", time_to_i, 0);
5657 rb_define_method(rb_cTime, "to_f", time_to_f, 0);
5658 rb_define_method(rb_cTime, "to_r", time_to_r, 0);
5659 rb_define_method(rb_cTime, "<=>", time_cmp, 1);
5660 rb_define_method(rb_cTime, "eql?", time_eql, 1);
5661 rb_define_method(rb_cTime, "hash", time_hash, 0);
5662 rb_define_method(rb_cTime, "initialize_copy", time_init_copy, 1);
5663
5664 rb_define_method(rb_cTime, "localtime", time_localtime_m, -1);
5665 rb_define_method(rb_cTime, "gmtime", time_gmtime, 0);
5666 rb_define_method(rb_cTime, "utc", time_gmtime, 0);
5667 rb_define_method(rb_cTime, "getlocal", time_getlocaltime, -1);
5668 rb_define_method(rb_cTime, "getgm", time_getgmtime, 0);
5669 rb_define_method(rb_cTime, "getutc", time_getgmtime, 0);
5670
5671 rb_define_method(rb_cTime, "ctime", time_asctime, 0);
5672 rb_define_method(rb_cTime, "asctime", time_asctime, 0);
5673 rb_define_method(rb_cTime, "to_s", time_to_s, 0);
5674 rb_define_method(rb_cTime, "inspect", time_inspect, 0);
5675 rb_define_method(rb_cTime, "to_a", time_to_a, 0);
5676
5677 rb_define_method(rb_cTime, "+", time_plus, 1);
5678 rb_define_method(rb_cTime, "-", time_minus, 1);
5679
5680 rb_define_method(rb_cTime, "round", time_round, -1);
5681 rb_define_method(rb_cTime, "floor", time_floor, -1);
5682 rb_define_method(rb_cTime, "ceil", time_ceil, -1);
5683
5684 rb_define_method(rb_cTime, "sec", time_sec, 0);
5685 rb_define_method(rb_cTime, "min", time_min, 0);
5686 rb_define_method(rb_cTime, "hour", time_hour, 0);
5687 rb_define_method(rb_cTime, "mday", time_mday, 0);
5688 rb_define_method(rb_cTime, "day", time_mday, 0);
5689 rb_define_method(rb_cTime, "mon", time_mon, 0);
5690 rb_define_method(rb_cTime, "month", time_mon, 0);
5691 rb_define_method(rb_cTime, "year", time_year, 0);
5692 rb_define_method(rb_cTime, "wday", time_wday, 0);
5693 rb_define_method(rb_cTime, "yday", time_yday, 0);
5694 rb_define_method(rb_cTime, "isdst", time_isdst, 0);
5695 rb_define_method(rb_cTime, "dst?", time_isdst, 0);
5696 rb_define_method(rb_cTime, "zone", time_zone, 0);
5698 rb_define_method(rb_cTime, "gmt_offset", rb_time_utc_offset, 0);
5699 rb_define_method(rb_cTime, "utc_offset", rb_time_utc_offset, 0);
5700
5701 rb_define_method(rb_cTime, "utc?", time_utc_p, 0);
5702 rb_define_method(rb_cTime, "gmt?", time_utc_p, 0);
5703
5704 rb_define_method(rb_cTime, "sunday?", time_sunday, 0);
5705 rb_define_method(rb_cTime, "monday?", time_monday, 0);
5706 rb_define_method(rb_cTime, "tuesday?", time_tuesday, 0);
5707 rb_define_method(rb_cTime, "wednesday?", time_wednesday, 0);
5708 rb_define_method(rb_cTime, "thursday?", time_thursday, 0);
5709 rb_define_method(rb_cTime, "friday?", time_friday, 0);
5710 rb_define_method(rb_cTime, "saturday?", time_saturday, 0);
5711
5712 rb_define_method(rb_cTime, "tv_sec", time_to_i, 0);
5713 rb_define_method(rb_cTime, "tv_usec", time_usec, 0);
5714 rb_define_method(rb_cTime, "usec", time_usec, 0);
5715 rb_define_method(rb_cTime, "tv_nsec", time_nsec, 0);
5716 rb_define_method(rb_cTime, "nsec", time_nsec, 0);
5717 rb_define_method(rb_cTime, "subsec", time_subsec, 0);
5718
5719 rb_define_method(rb_cTime, "strftime", time_strftime, 1);
5720
5721 /* methods for marshaling */
5722 rb_define_private_method(rb_cTime, "_dump", time_dump, -1);
5723 rb_define_private_method(rb_singleton_class(rb_cTime), "_load", time_load, 1);
5724#if 0
5725 /* Time will support marshal_dump and marshal_load in the future (1.9 maybe) */
5726 rb_define_private_method(rb_cTime, "marshal_dump", time_mdump, 0);
5727 rb_define_private_method(rb_cTime, "marshal_load", time_mload, 1);
5728#endif
5729
5730 if (debug_find_time_numguess) {
5731 rb_define_hooked_variable("$find_time_numguess", (VALUE *)&find_time_numguess,
5732 find_time_numguess_getter, NULL);
5733 }
5734
5735 rb_cTimeTM = Init_tm(rb_cTime, "tm");
5736}
5737
5738#include "timev.rbinc"
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:685
#define rb_define_private_method(klass, mid, func, arity)
Defines klass#mid and makes it private.
Definition: cxxanyargs.hpp:677
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_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
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
Definition: class.c:1914
#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:3021
void rb_sys_fail(const char *mesg)
Converts a C errno into a Ruby exception, then raises it.
Definition: error.c:3145
VALUE rb_rescue(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*r_proc)(VALUE, VALUE), VALUE data2)
Identical to rb_rescue2(), except it does not take a list of exception classes.
Definition: eval.c:950
void rb_warning(const char *fmt,...)
Issues a warning.
Definition: error.c:449
VALUE rb_cTime
Time class.
Definition: time.c:647
VALUE rb_mComparable
Comparable module.
Definition: compar.c:19
Encoding relates APIs.
rb_encoding * rb_enc_get(VALUE obj)
Identical to rb_enc_get_index(), except the return type.
Definition: encoding.c:1072
rb_encoding * rb_locale_encoding(void)
Queries the encoding that represents the current locale.
Definition: encoding.c:1573
rb_encoding * rb_usascii_encoding(void)
Queries the encoding that represents US-ASCII.
Definition: encoding.c:1539
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
VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcall(), except it takes the method arguments as a C array.
Definition: vm_eval.c:1061
void rb_gc_register_mark_object(VALUE object)
Inform the garbage collector that object is a live Ruby object that should not be moved.
Definition: gc.c:8686
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_check_array_type(VALUE obj)
Try converting an object to its array representation using its to_ary method, if any.
Definition: array.c:989
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
Definition: array.c:1679
int rb_integer_pack(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags)
Exports an integer into a buffer.
Definition: bignum.c:3559
VALUE rb_big_minus(VALUE x, VALUE y)
Performs subtraction of the passed two objects.
Definition: bignum.c:5850
VALUE rb_big_modulo(VALUE x, VALUE y)
Performs modulo of the passed two objects.
Definition: bignum.c:6100
#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
VALUE rb_big_plus(VALUE x, VALUE y)
Performs addition of the passed two objects.
Definition: bignum.c:5821
VALUE rb_str_to_inum(VALUE str, int base, int badcheck)
Identical to rb_cstr2inum(), except it takes Ruby's strings instead of C's.
Definition: bignum.c:4280
size_t rb_absint_size(VALUE val, int *nlz_bits_ret)
Calculates the number of bytes needed to represent the absolute value of the passed integer.
Definition: bignum.c:3258
VALUE rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags)
Import an integer from a buffer.
Definition: bignum.c:3645
#define INTEGER_PACK_LITTLE_ENDIAN
Little endian combination.
Definition: bignum.h:567
VALUE rb_big_cmp(VALUE lhs, VALUE rhs)
Compares the passed two bignums.
Definition: bignum.c:5418
VALUE rb_big_mul(VALUE x, VALUE y)
Performs multiplication of the passed two objects.
Definition: bignum.c:5930
VALUE rb_big_div(VALUE x, VALUE y)
Performs division of the passed two objects.
Definition: bignum.c:6088
int rb_cmpint(VALUE val, VALUE a, VALUE b)
Canonicalises the passed val, which is the return value of a <=> b, into C's {-1, 0,...
Definition: bignum.c:2935
#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_gc_mark(VALUE obj)
Marks an object.
Definition: gc.c:6774
VALUE rb_hash(VALUE obj)
Calculates a message authentication code of the passed object.
Definition: hash.c:227
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
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition: string.c:1808
VALUE rb_usascii_str_new(const char *ptr, long len)
Identical to rb_str_new(), except it generates a string of "US ASCII" encoding.
Definition: string.c:924
VALUE rb_usascii_str_new_cstr(const char *ptr)
Identical to rb_str_new_cstr(), except it generates a string of "US ASCII" encoding.
Definition: string.c:964
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_str_new(const char *ptr, long len)
Allocates an instance of rb_cString.
Definition: string.c:918
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition: string.c:2659
VALUE rb_str_resize(VALUE str, long len)
Overwrites the length of the string.
Definition: string.c:3056
VALUE rb_str_cat_cstr(VALUE dst, const char *src)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
Definition: string.c:3171
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:2497
void rb_timespec_now(struct timespec *ts)
Fills the current time into the given struct.
Definition: time.c:1897
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:2503
struct timespec rb_time_timespec(VALUE time)
Identical to rb_time_timeval(), except for return type.
Definition: time.c:2666
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:2489
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:2649
struct timeval rb_time_interval(VALUE num)
Creates a "time interval".
Definition: time.c:2643
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:2526
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:4709
struct timespec rb_time_timespec_interval(VALUE num)
Identical to rb_time_interval(), except for return type.
Definition: time.c:2680
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
void rb_define_hooked_variable(const char *name, VALUE *var, rb_gvar_getter_t *getter, rb_gvar_setter_t *setter)
Identical to rb_define_virtual_variable(), but can also specify a storage.
Definition: variable.c:563
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_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
static VALUE RSTRUCT_SET(VALUE st, int k, VALUE v)
Resembles Struct#[]=.
Definition: rstruct.h:104
#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: timev.h:21
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
Definition: value.h:63
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