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