00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "ruby/ruby.h"
00013 #include <sys/types.h>
00014 #include <time.h>
00015 #include <errno.h>
00016 #include "ruby/encoding.h"
00017
00018 #ifdef HAVE_UNISTD_H
00019 #include <unistd.h>
00020 #endif
00021
00022 #include <float.h>
00023 #include <math.h>
00024
00025 #include "timev.h"
00026
00027 static ID id_divmod, id_mul, id_submicro, id_nano_num, id_nano_den, id_offset;
00028 static ID id_eq, id_ne, id_quo, id_div, id_cmp, id_lshift;
00029
00030 #define NDIV(x,y) (-(-((x)+1)/(y))-1)
00031 #define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
00032 #define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
00033 #define MOD(n,d) ((n)<0 ? NMOD((n),(d)) : (n)%(d))
00034
00035 static int
00036 eq(VALUE x, VALUE y)
00037 {
00038 if (FIXNUM_P(x) && FIXNUM_P(y)) {
00039 return x == y;
00040 }
00041 return RTEST(rb_funcall(x, id_eq, 1, y));
00042 }
00043
00044 static int
00045 cmp(VALUE x, VALUE y)
00046 {
00047 if (FIXNUM_P(x) && FIXNUM_P(y)) {
00048 if ((long)x < (long)y)
00049 return -1;
00050 if ((long)x > (long)y)
00051 return 1;
00052 return 0;
00053 }
00054 return rb_cmpint(rb_funcall(x, id_cmp, 1, y), x, y);
00055 }
00056
00057 #define ne(x,y) (!eq((x),(y)))
00058 #define lt(x,y) (cmp((x),(y)) < 0)
00059 #define gt(x,y) (cmp((x),(y)) > 0)
00060 #define le(x,y) (cmp((x),(y)) <= 0)
00061 #define ge(x,y) (cmp((x),(y)) >= 0)
00062
00063 static VALUE
00064 add(VALUE x, VALUE y)
00065 {
00066 if (FIXNUM_P(x) && FIXNUM_P(y)) {
00067 long l = FIX2LONG(x) + FIX2LONG(y);
00068 if (FIXABLE(l)) return LONG2FIX(l);
00069 return LONG2NUM(l);
00070 }
00071 if (TYPE(x) == T_BIGNUM) return rb_big_plus(x, y);
00072 return rb_funcall(x, '+', 1, y);
00073 }
00074
00075 static VALUE
00076 sub(VALUE x, VALUE y)
00077 {
00078 if (FIXNUM_P(x) && FIXNUM_P(y)) {
00079 long l = FIX2LONG(x) - FIX2LONG(y);
00080 if (FIXABLE(l)) return LONG2FIX(l);
00081 return LONG2NUM(l);
00082 }
00083 if (TYPE(x) == T_BIGNUM) return rb_big_minus(x, y);
00084 return rb_funcall(x, '-', 1, y);
00085 }
00086
00087 #if !(HAVE_LONG_LONG && SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG)
00088 static int
00089 long_mul(long x, long y, long *z)
00090 {
00091 unsigned long a, b, c;
00092 int s;
00093 if (x == 0 || y == 0) {
00094 *z = 0;
00095 return 1;
00096 }
00097 if (x < 0) {
00098 s = -1;
00099 a = (unsigned long)-x;
00100 }
00101 else {
00102 s = 1;
00103 a = (unsigned long)x;
00104 }
00105 if (y < 0) {
00106 s = -s;
00107 b = (unsigned long)-y;
00108 }
00109 else {
00110 b = (unsigned long)y;
00111 }
00112 if (a <= ULONG_MAX / b) {
00113 c = a * b;
00114 if (s < 0) {
00115 if (c <= (unsigned long)LONG_MAX + 1) {
00116 *z = -(long)c;
00117 return 1;
00118 }
00119 }
00120 else {
00121 if (c <= (unsigned long)LONG_MAX) {
00122 *z = (long)c;
00123 return 1;
00124 }
00125 }
00126 }
00127 return 0;
00128 }
00129 #endif
00130
00131 static VALUE
00132 mul(VALUE x, VALUE y)
00133 {
00134 if (FIXNUM_P(x) && FIXNUM_P(y)) {
00135 #if HAVE_LONG_LONG && SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG
00136 LONG_LONG ll = (LONG_LONG)FIX2LONG(x) * FIX2LONG(y);
00137 if (FIXABLE(ll))
00138 return LONG2FIX(ll);
00139 return LL2NUM(ll);
00140 #else
00141 long z;
00142 if (long_mul(FIX2LONG(x), FIX2LONG(y), &z))
00143 return LONG2NUM(z);
00144 #endif
00145 }
00146 if (TYPE(x) == T_BIGNUM)
00147 return rb_big_mul(x, y);
00148 return rb_funcall(x, '*', 1, y);
00149 }
00150
00151 #define div(x,y) (rb_funcall((x), id_div, 1, (y)))
00152
00153 static VALUE
00154 mod(VALUE x, VALUE y)
00155 {
00156 switch (TYPE(x)) {
00157 case T_BIGNUM: return rb_big_modulo(x, y);
00158 default: return rb_funcall(x, '%', 1, y);
00159 }
00160 }
00161
00162 #define neg(x) (sub(INT2FIX(0), (x)))
00163 #define lshift(x,y) (rb_funcall((x), id_lshift, 1, (y)))
00164
00165 static VALUE
00166 quo(VALUE x, VALUE y)
00167 {
00168 VALUE ret;
00169 if (FIXNUM_P(x) && FIXNUM_P(y)) {
00170 long a, b, c;
00171 a = FIX2LONG(x);
00172 b = FIX2LONG(y);
00173 if (b == 0) rb_num_zerodiv();
00174 c = a / b;
00175 if (c * b == a) {
00176 return LONG2NUM(c);
00177 }
00178 }
00179 ret = rb_funcall(x, id_quo, 1, y);
00180 if (TYPE(ret) == T_RATIONAL &&
00181 RRATIONAL(ret)->den == INT2FIX(1)) {
00182 ret = RRATIONAL(ret)->num;
00183 }
00184 return ret;
00185 }
00186
00187 #define mulquo(x,y,z) (((y) == (z)) ? (x) : quo(mul((x),(y)),(z)))
00188
00189 static void
00190 divmodv(VALUE n, VALUE d, VALUE *q, VALUE *r)
00191 {
00192 VALUE tmp, ary;
00193 tmp = rb_funcall(n, id_divmod, 1, d);
00194 ary = rb_check_array_type(tmp);
00195 if (NIL_P(ary)) {
00196 rb_raise(rb_eTypeError, "unexpected divmod result: into %s",
00197 rb_obj_classname(tmp));
00198 }
00199 *q = rb_ary_entry(ary, 0);
00200 *r = rb_ary_entry(ary, 1);
00201 }
00202
00203 #if SIZEOF_LONG == 8
00204 # define INT64toNUM(x) LONG2NUM(x)
00205 # define UINT64toNUM(x) ULONG2NUM(x)
00206 #elif defined(HAVE_LONG_LONG) && SIZEOF_LONG_LONG == 8
00207 # define INT64toNUM(x) LL2NUM(x)
00208 # define UINT64toNUM(x) ULL2NUM(x)
00209 #endif
00210
00211 #if defined(HAVE_UINT64_T) && SIZEOF_LONG*2 <= SIZEOF_UINT64_T
00212 typedef uint64_t uwideint_t;
00213 typedef int64_t wideint_t;
00214 typedef uint64_t WIDEVALUE;
00215 typedef int64_t SIGNED_WIDEVALUE;
00216 # define WIDEVALUE_IS_WIDER 1
00217 # define UWIDEINT_MAX UINT64_MAX
00218 # define WIDEINT_MAX INT64_MAX
00219 # define WIDEINT_MIN INT64_MIN
00220 # define FIXWINT_P(tv) ((tv) & 1)
00221 # define FIXWVtoINT64(tv) RSHIFT((SIGNED_WIDEVALUE)(tv), 1)
00222 # define INT64toFIXWV(wi) ((WIDEVALUE)((SIGNED_WIDEVALUE)(wi) << 1 | FIXNUM_FLAG))
00223 # define FIXWV_MAX (((int64_t)1 << 62) - 1)
00224 # define FIXWV_MIN (-((int64_t)1 << 62))
00225 # define FIXWVABLE(wi) (POSFIXWVABLE(wi) && NEGFIXWVABLE(wi))
00226 # define WINT2FIXWV(i) WIDEVAL_WRAP(INT64toFIXWV(i))
00227 # define FIXWV2WINT(w) FIXWVtoINT64(WIDEVAL_GET(w))
00228 #else
00229 typedef unsigned long uwideint_t;
00230 typedef long wideint_t;
00231 typedef VALUE WIDEVALUE;
00232 typedef SIGNED_VALUE SIGNED_WIDEVALUE;
00233 # define WIDEVALUE_IS_WIDER 0
00234 # define UWIDEINT_MAX ULONG_MAX
00235 # define WIDEINT_MAX LONG_MAX
00236 # define WIDEINT_MIN LONG_MIN
00237 # define FIXWINT_P(v) FIXNUM_P(v)
00238 # define FIXWV_MAX FIXNUM_MAX
00239 # define FIXWV_MIN FIXNUM_MIN
00240 # define FIXWVABLE(i) FIXABLE(i)
00241 # define WINT2FIXWV(i) WIDEVAL_WRAP(LONG2FIX(i))
00242 # define FIXWV2WINT(w) FIX2LONG(WIDEVAL_GET(w))
00243 #endif
00244
00245 #define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1)
00246 #define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN)
00247 #define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w))
00248
00249
00250 #ifdef STRUCT_WIDEVAL
00251
00252 typedef struct {
00253 WIDEVALUE value;
00254 } wideval_t;
00255 static inline wideval_t WIDEVAL_WRAP(WIDEVALUE v) { wideval_t w = { v }; return w; }
00256 # define WIDEVAL_GET(w) ((w).value)
00257 #else
00258 typedef WIDEVALUE wideval_t;
00259 # define WIDEVAL_WRAP(v) (v)
00260 # define WIDEVAL_GET(w) (w)
00261 #endif
00262
00263 #if WIDEVALUE_IS_WIDER
00264 static inline wideval_t
00265 wint2wv(wideint_t wi)
00266 {
00267 if (FIXWVABLE(wi))
00268 return WINT2FIXWV(wi);
00269 else
00270 return WIDEVAL_WRAP(INT64toNUM(wi));
00271 }
00272 # define WINT2WV(wi) wint2wv(wi)
00273 #else
00274 # define WINT2WV(wi) WIDEVAL_WRAP(LONG2NUM(wi))
00275 #endif
00276
00277 static inline VALUE
00278 w2v(wideval_t w)
00279 {
00280 #if WIDEVALUE_IS_WIDER
00281 if (FIXWV_P(w))
00282 return INT64toNUM(FIXWV2WINT(w));
00283 return (VALUE)WIDEVAL_GET(w);
00284 #else
00285 return WIDEVAL_GET(w);
00286 #endif
00287 }
00288
00289 #if WIDEVALUE_IS_WIDER
00290 static int
00291 bdigit_find_maxbit(BDIGIT d)
00292 {
00293 int res = 0;
00294 if (d & ~(BDIGIT)0xffff) {
00295 d >>= 16;
00296 res += 16;
00297 }
00298 if (d & ~(BDIGIT)0xff) {
00299 d >>= 8;
00300 res += 8;
00301 }
00302 if (d & ~(BDIGIT)0xf) {
00303 d >>= 4;
00304 res += 4;
00305 }
00306 if (d & ~(BDIGIT)0x3) {
00307 d >>= 2;
00308 res += 2;
00309 }
00310 if (d & ~(BDIGIT)0x1) {
00311 d >>= 1;
00312 res += 1;
00313 }
00314 return res;
00315 }
00316
00317 static VALUE
00318 rb_big_abs_find_maxbit(VALUE big)
00319 {
00320 BDIGIT *ds = RBIGNUM_DIGITS(big);
00321 BDIGIT d;
00322 long len = RBIGNUM_LEN(big);
00323 VALUE res;
00324 while (0 < len && ds[len-1] == 0)
00325 len--;
00326 if (len == 0)
00327 return Qnil;
00328 res = mul(LONG2NUM(len-1), INT2FIX(SIZEOF_BDIGITS * CHAR_BIT));
00329 d = ds[len-1];
00330 res = add(res, LONG2FIX(bdigit_find_maxbit(d)));
00331 return res;
00332 }
00333
00334 static VALUE
00335 rb_big_abs_find_minbit(VALUE big)
00336 {
00337 BDIGIT *ds = RBIGNUM_DIGITS(big);
00338 BDIGIT d;
00339 long len = RBIGNUM_LEN(big);
00340 long i;
00341 VALUE res;
00342 for (i = 0; i < len; i++)
00343 if (ds[i])
00344 break;
00345 if (i == len)
00346 return Qnil;
00347 res = mul(LONG2NUM(i), INT2FIX(SIZEOF_BDIGITS * CHAR_BIT));
00348 d = ds[i];
00349 res = add(res, LONG2FIX(bdigit_find_maxbit(d & (~d-1))));
00350 return res;
00351 }
00352
00353 static wideval_t
00354 v2w_bignum(VALUE v)
00355 {
00356 long len = RBIGNUM_LEN(v);
00357 BDIGIT *ds;
00358 wideval_t w;
00359 VALUE maxbit;
00360 ds = RBIGNUM_DIGITS(v);
00361 w = WIDEVAL_WRAP(v);
00362 maxbit = rb_big_abs_find_maxbit(v);
00363 if (NIL_P(maxbit))
00364 return WINT2FIXWV(0);
00365 if (lt(maxbit, INT2FIX(sizeof(wideint_t) * CHAR_BIT - 2)) ||
00366 (eq(maxbit, INT2FIX(sizeof(wideint_t) * CHAR_BIT - 2)) &&
00367 RBIGNUM_NEGATIVE_P(v) &&
00368 eq(rb_big_abs_find_minbit(v), INT2FIX(sizeof(wideint_t) * CHAR_BIT - 2)))) {
00369 wideint_t i;
00370 i = 0;
00371 while (len)
00372 i = (i << sizeof(BDIGIT)*CHAR_BIT) | ds[--len];
00373 if (RBIGNUM_NEGATIVE_P(v)) {
00374 i = -i;
00375 }
00376 w = WINT2FIXWV(i);
00377 }
00378 return w;
00379 }
00380 #endif
00381
00382 static inline wideval_t
00383 v2w(VALUE v)
00384 {
00385 #if WIDEVALUE_IS_WIDER
00386 if (FIXNUM_P(v)) {
00387 return WIDEVAL_WRAP((WIDEVALUE)(SIGNED_WIDEVALUE)(long)v);
00388 }
00389 else if (TYPE(v) == T_BIGNUM &&
00390 RBIGNUM_LEN(v) * sizeof(BDIGIT) <= sizeof(WIDEVALUE)) {
00391 return v2w_bignum(v);
00392 }
00393 #endif
00394 return WIDEVAL_WRAP(v);
00395 }
00396
00397 static int
00398 weq(wideval_t wx, wideval_t wy)
00399 {
00400 #if WIDEVALUE_IS_WIDER
00401 if (FIXWV_P(wx) && FIXWV_P(wy)) {
00402 return WIDEVAL_GET(wx) == WIDEVAL_GET(wy);
00403 }
00404 return RTEST(rb_funcall(w2v(wx), id_eq, 1, w2v(wy)));
00405 #else
00406 return eq(WIDEVAL_GET(wx), WIDEVAL_GET(wy));
00407 #endif
00408 }
00409
00410 static int
00411 wcmp(wideval_t wx, wideval_t wy)
00412 {
00413 VALUE x, y;
00414 #if WIDEVALUE_IS_WIDER
00415 if (FIXWV_P(wx) && FIXWV_P(wy)) {
00416 wideint_t a, b;
00417 a = FIXWV2WINT(wx);
00418 b = FIXWV2WINT(wy);
00419 if (a < b)
00420 return -1;
00421 if (a > b)
00422 return 1;
00423 return 0;
00424 }
00425 #endif
00426 x = w2v(wx);
00427 y = w2v(wy);
00428 return rb_cmpint(rb_funcall(x, id_cmp, 1, y), x, y);
00429 }
00430
00431 #define wne(x,y) (!weq((x),(y)))
00432 #define wlt(x,y) (wcmp((x),(y)) < 0)
00433 #define wgt(x,y) (wcmp((x),(y)) > 0)
00434 #define wle(x,y) (wcmp((x),(y)) <= 0)
00435 #define wge(x,y) (wcmp((x),(y)) >= 0)
00436
00437 static wideval_t
00438 wadd(wideval_t wx, wideval_t wy)
00439 {
00440 VALUE x;
00441 #if WIDEVALUE_IS_WIDER
00442 if (FIXWV_P(wx) && FIXWV_P(wy)) {
00443 wideint_t r = FIXWV2WINT(wx) + FIXWV2WINT(wy);
00444 return WINT2WV(r);
00445 }
00446 else
00447 #endif
00448 x = w2v(wx);
00449 if (TYPE(x) == T_BIGNUM) return v2w(rb_big_plus(x, w2v(wy)));
00450 return v2w(rb_funcall(x, '+', 1, w2v(wy)));
00451 }
00452
00453 static wideval_t
00454 wsub(wideval_t wx, wideval_t wy)
00455 {
00456 VALUE x;
00457 #if WIDEVALUE_IS_WIDER
00458 if (FIXWV_P(wx) && FIXWV_P(wy)) {
00459 wideint_t r = FIXWV2WINT(wx) - FIXWV2WINT(wy);
00460 return WINT2WV(r);
00461 }
00462 else
00463 #endif
00464 x = w2v(wx);
00465 if (TYPE(x) == T_BIGNUM) return v2w(rb_big_minus(x, w2v(wy)));
00466 return v2w(rb_funcall(x, '-', 1, w2v(wy)));
00467 }
00468
00469 static int
00470 wi_mul(wideint_t x, wideint_t y, wideint_t *z)
00471 {
00472 uwideint_t a, b, c;
00473 int s;
00474 if (x == 0 || y == 0) {
00475 *z = 0;
00476 return 1;
00477 }
00478 if (x < 0) {
00479 s = -1;
00480 a = (uwideint_t)-x;
00481 }
00482 else {
00483 s = 1;
00484 a = (uwideint_t)x;
00485 }
00486 if (y < 0) {
00487 s = -s;
00488 b = (uwideint_t)-y;
00489 }
00490 else {
00491 b = (uwideint_t)y;
00492 }
00493 if (a <= UWIDEINT_MAX / b) {
00494 c = a * b;
00495 if (s < 0) {
00496 if (c <= (uwideint_t)WIDEINT_MAX + 1) {
00497 *z = -(wideint_t)c;
00498 return 1;
00499 }
00500 }
00501 else {
00502 if (c <= (uwideint_t)WIDEINT_MAX) {
00503 *z = (wideint_t)c;
00504 return 1;
00505 }
00506 }
00507 }
00508 return 0;
00509 }
00510
00511 static wideval_t
00512 wmul(wideval_t wx, wideval_t wy)
00513 {
00514 VALUE x, z;
00515 #if WIDEVALUE_IS_WIDER
00516 if (FIXWV_P(wx) && FIXWV_P(wy)) {
00517 wideint_t z;
00518 if (wi_mul(FIXWV2WINT(wx), FIXWV2WINT(wy), &z))
00519 return WINT2WV(z);
00520 }
00521 #endif
00522 x = w2v(wx);
00523 if (TYPE(x) == T_BIGNUM) return v2w(rb_big_mul(x, w2v(wy)));
00524 z = rb_funcall(x, '*', 1, w2v(wy));
00525 if (TYPE(z) == T_RATIONAL && RRATIONAL(z)->den == INT2FIX(1)) {
00526 z = RRATIONAL(z)->num;
00527 }
00528 return v2w(z);
00529 }
00530
00531 static wideval_t
00532 wquo(wideval_t wx, wideval_t wy)
00533 {
00534 VALUE x, y, ret;
00535 #if WIDEVALUE_IS_WIDER
00536 if (FIXWV_P(wx) && FIXWV_P(wy)) {
00537 wideint_t a, b, c;
00538 a = FIXWV2WINT(wx);
00539 b = FIXWV2WINT(wy);
00540 if (b == 0) rb_num_zerodiv();
00541 c = a / b;
00542 if (c * b == a) {
00543 return WINT2WV(c);
00544 }
00545 }
00546 #endif
00547 x = w2v(wx);
00548 y = w2v(wy);
00549 ret = rb_funcall(x, id_quo, 1, y);
00550 if (TYPE(ret) == T_RATIONAL &&
00551 RRATIONAL(ret)->den == INT2FIX(1)) {
00552 ret = RRATIONAL(ret)->num;
00553 }
00554 return v2w(ret);
00555 }
00556
00557 #define wmulquo(x,y,z) ((WIDEVAL_GET(y) == WIDEVAL_GET(z)) ? (x) : wquo(wmul((x),(y)),(z)))
00558 #define wmulquoll(x,y,z) (((y) == (z)) ? (x) : wquo(wmul((x),WINT2WV(y)),WINT2WV(z)))
00559
00560 static void
00561 wdivmod(wideval_t wn, wideval_t wd, wideval_t *wq, wideval_t *wr)
00562 {
00563 VALUE tmp, ary;
00564 #if WIDEVALUE_IS_WIDER
00565 if (FIXWV_P(wn) && FIXWV_P(wd)) {
00566 wideint_t n, d, q, r;
00567 d = FIXWV2WINT(wd);
00568 if (d == 0) rb_num_zerodiv();
00569 if (d == 1) {
00570 *wq = wn;
00571 *wr = WINT2FIXWV(0);
00572 return;
00573 }
00574 if (d == -1) {
00575 wideint_t xneg = -FIXWV2WINT(wn);
00576 *wq = WINT2WV(xneg);
00577 *wr = WINT2FIXWV(0);
00578 return;
00579 }
00580 n = FIXWV2WINT(wn);
00581 if (n == 0) {
00582 *wq = WINT2FIXWV(0);
00583 *wr = WINT2FIXWV(0);
00584 return;
00585 }
00586 if (d < 0) {
00587 if (n < 0) {
00588 q = ((-n) / (-d));
00589 r = ((-n) % (-d));
00590 if (r != 0) {
00591 q -= 1;
00592 r += d;
00593 }
00594 }
00595 else {
00596 q = -(n / (-d));
00597 r = -(n % (-d));
00598 }
00599 }
00600 else {
00601 if (n < 0) {
00602 q = -((-n) / d);
00603 r = -((-n) % d);
00604 if (r != 0) {
00605 q -= 1;
00606 r += d;
00607 }
00608 }
00609 else {
00610 q = n / d;
00611 r = n % d;
00612 }
00613 }
00614 *wq = WINT2FIXWV(q);
00615 *wr = WINT2FIXWV(r);
00616 return;
00617 }
00618 #endif
00619 tmp = rb_funcall(w2v(wn), id_divmod, 1, w2v(wd));
00620 ary = rb_check_array_type(tmp);
00621 if (NIL_P(ary)) {
00622 rb_raise(rb_eTypeError, "unexpected divmod result: into %s",
00623 rb_obj_classname(tmp));
00624 }
00625 *wq = v2w(rb_ary_entry(ary, 0));
00626 *wr = v2w(rb_ary_entry(ary, 1));
00627 }
00628
00629 static void
00630 wmuldivmod(wideval_t wx, wideval_t wy, wideval_t wz, wideval_t *wq, wideval_t *wr)
00631 {
00632 if (WIDEVAL_GET(wy) == WIDEVAL_GET(wz)) {
00633 *wq = wx;
00634 *wr = WINT2FIXWV(0);
00635 return;
00636 }
00637 wdivmod(wmul(wx,wy), wz, wq, wr);
00638 }
00639
00640 static wideval_t
00641 wdiv(wideval_t wx, wideval_t wy)
00642 {
00643 wideval_t q, r;
00644 wdivmod(wx, wy, &q, &r);
00645 return q;
00646 }
00647
00648 static wideval_t
00649 wmod(wideval_t wx, wideval_t wy)
00650 {
00651 wideval_t q, r;
00652 wdivmod(wx, wy, &q, &r);
00653 return r;
00654 }
00655
00656 static VALUE
00657 num_exact(VALUE v)
00658 {
00659 VALUE tmp;
00660 int t;
00661
00662 t = TYPE(v);
00663 switch (t) {
00664 case T_FIXNUM:
00665 case T_BIGNUM:
00666 return v;
00667
00668 case T_RATIONAL:
00669 break;
00670
00671 case T_STRING:
00672 case T_NIL:
00673 goto typeerror;
00674
00675 default:
00676 if ((tmp = rb_check_funcall(v, rb_intern("to_r"), 0, NULL)) != Qundef) {
00677 if (rb_respond_to(v, rb_intern("to_str"))) goto typeerror;
00678 v = tmp;
00679 break;
00680 }
00681 if (!NIL_P(tmp = rb_check_to_integer(v, "to_int"))) {
00682 v = tmp;
00683 break;
00684 }
00685 goto typeerror;
00686 }
00687
00688 t = TYPE(v);
00689 switch (t) {
00690 case T_FIXNUM:
00691 case T_BIGNUM:
00692 return v;
00693
00694 case T_RATIONAL:
00695 if (RRATIONAL(v)->den == INT2FIX(1))
00696 v = RRATIONAL(v)->num;
00697 break;
00698
00699 default:
00700 typeerror:
00701 rb_raise(rb_eTypeError, "can't convert %s into an exact number",
00702 NIL_P(v) ? "nil" : rb_obj_classname(v));
00703 }
00704 return v;
00705 }
00706
00707
00708
00709 #ifndef TYPEOF_TIMEVAL_TV_SEC
00710 # define TYPEOF_TIMEVAL_TV_SEC time_t
00711 #endif
00712 #ifndef TYPEOF_TIMEVAL_TV_USEC
00713 # if INT_MAX >= 1000000
00714 # define TYPEOF_TIMEVAL_TV_USEC int
00715 # else
00716 # define TYPEOF_TIMEVAL_TV_USEC long
00717 # endif
00718 #endif
00719
00720 #if SIZEOF_TIME_T == SIZEOF_LONG
00721 typedef unsigned long unsigned_time_t;
00722 #elif SIZEOF_TIME_T == SIZEOF_INT
00723 typedef unsigned int unsigned_time_t;
00724 #elif SIZEOF_TIME_T == SIZEOF_LONG_LONG
00725 typedef unsigned LONG_LONG unsigned_time_t;
00726 #else
00727 # error cannot find integer type which size is same as time_t.
00728 #endif
00729
00730 #define TIMET_MAX (~(time_t)0 <= 0 ? (time_t)((~(unsigned_time_t)0) >> 1) : (time_t)(~(unsigned_time_t)0))
00731 #define TIMET_MIN (~(time_t)0 <= 0 ? (time_t)(((unsigned_time_t)1) << (sizeof(time_t) * CHAR_BIT - 1)) : (time_t)0)
00732
00733 static wideval_t
00734 rb_time_magnify(wideval_t w)
00735 {
00736 if (FIXWV_P(w)) {
00737 wideint_t z;
00738 if (wi_mul(FIXWV2WINT(w), TIME_SCALE, &z))
00739 return WINT2WV(z);
00740 }
00741 return wmul(w, WINT2FIXWV(TIME_SCALE));
00742 }
00743
00744 static wideval_t
00745 rb_time_unmagnify(wideval_t w)
00746 {
00747 #if WIDEVALUE_IS_WIDER
00748 if (FIXWV_P(w)) {
00749 wideint_t a, b, c;
00750 a = FIXWV2WINT(w);
00751 b = TIME_SCALE;
00752 c = a / b;
00753 if (c * b == a) {
00754 return WINT2FIXWV(c);
00755 }
00756 }
00757 #endif
00758 return wquo(w, WINT2FIXWV(TIME_SCALE));
00759 }
00760
00761 static VALUE
00762 rb_time_unmagnify_to_float(wideval_t w)
00763 {
00764 VALUE v;
00765 #if WIDEVALUE_IS_WIDER
00766 if (FIXWV_P(w)) {
00767 wideint_t a, b, c;
00768 a = FIXWV2WINT(w);
00769 b = TIME_SCALE;
00770 c = a / b;
00771 if (c * b == a) {
00772 return DBL2NUM((double)c);
00773 }
00774 v = DBL2NUM((double)FIXWV2WINT(w));
00775 return quo(v, DBL2NUM(TIME_SCALE));
00776 }
00777 #endif
00778 v = w2v(w);
00779 return quo(v, DBL2NUM(TIME_SCALE));
00780 }
00781
00782 static void
00783 split_second(wideval_t timew, wideval_t *timew_p, VALUE *subsecx_p)
00784 {
00785 wideval_t q, r;
00786 wdivmod(timew, WINT2FIXWV(TIME_SCALE), &q, &r);
00787 *timew_p = q;
00788 *subsecx_p = w2v(r);
00789 }
00790
00791 static wideval_t
00792 timet2wv(time_t t)
00793 {
00794 #if WIDEVALUE_IS_WIDER
00795 if (TIMET_MIN == 0) {
00796 uwideint_t wi = (uwideint_t)t;
00797 if (wi <= FIXWV_MAX) {
00798 return WINT2FIXWV(wi);
00799 }
00800 }
00801 else {
00802 wideint_t wi = (wideint_t)t;
00803 if (FIXWV_MIN <= wi && wi <= FIXWV_MAX) {
00804 return WINT2FIXWV(wi);
00805 }
00806 }
00807 #endif
00808 return v2w(TIMET2NUM(t));
00809 }
00810 #define TIMET2WV(t) timet2wv(t)
00811
00812 static time_t
00813 wv2timet(wideval_t w)
00814 {
00815 #if WIDEVALUE_IS_WIDER
00816 if (FIXWV_P(w)) {
00817 wideint_t wi = FIXWV2WINT(w);
00818 if (TIMET_MIN == 0) {
00819 if (wi < 0)
00820 rb_raise(rb_eRangeError, "negative value to convert into `time_t'");
00821 if (TIMET_MAX < (uwideint_t)wi)
00822 rb_raise(rb_eRangeError, "too big to convert into `time_t'");
00823 }
00824 else {
00825 if (wi < TIMET_MIN || TIMET_MAX < wi)
00826 rb_raise(rb_eRangeError, "too big to convert into `time_t'");
00827 }
00828 return (time_t)wi;
00829 }
00830 #endif
00831 return NUM2TIMET(w2v(w));
00832 }
00833 #define WV2TIMET(t) wv2timet(t)
00834
00835 VALUE rb_cTime;
00836 static VALUE time_utc_offset _((VALUE));
00837
00838 static int obj2int(VALUE obj);
00839 static VALUE obj2vint(VALUE obj);
00840 static int month_arg(VALUE arg);
00841 static void validate_utc_offset(VALUE utc_offset);
00842 static void validate_vtm(struct vtm *vtm);
00843
00844 static VALUE time_gmtime(VALUE);
00845 static VALUE time_localtime(VALUE);
00846 static VALUE time_fixoff(VALUE);
00847
00848 static time_t timegm_noleapsecond(struct tm *tm);
00849 static int tmcmp(struct tm *a, struct tm *b);
00850 static int vtmcmp(struct vtm *a, struct vtm *b);
00851 static const char *find_time_t(struct tm *tptr, int utc_p, time_t *tp);
00852
00853 static struct vtm *localtimew(wideval_t timew, struct vtm *result);
00854
00855 static int leap_year_p(long y);
00856 #define leap_year_v_p(y) leap_year_p(NUM2LONG(mod((y), INT2FIX(400))))
00857
00858 #ifdef HAVE_GMTIME_R
00859 #define rb_gmtime_r(t, tm) gmtime_r((t), (tm))
00860 #define rb_localtime_r(t, tm) localtime_r((t), (tm))
00861 #else
00862 static inline struct tm *
00863 rb_gmtime_r(const time_t *tp, struct tm *result)
00864 {
00865 struct tm *t = gmtime(tp);
00866 if (t) *result = *t;
00867 return t;
00868 }
00869
00870 static inline struct tm *
00871 rb_localtime_r(const time_t *tp, struct tm *result)
00872 {
00873 struct tm *t = localtime(tp);
00874 if (t) *result = *t;
00875 return t;
00876 }
00877 #endif
00878
00879 static struct tm *
00880 rb_localtime_r2(const time_t *t, struct tm *result)
00881 {
00882 #if defined __APPLE__ && defined __LP64__
00883 if (*t != (time_t)(int)*t) return NULL;
00884 #endif
00885 result = rb_localtime_r(t, result);
00886 #if defined(HAVE_MKTIME) && defined(LOCALTIME_OVERFLOW_PROBLEM)
00887 if (result) {
00888 int gmtoff1 = 0;
00889 int gmtoff2 = 0;
00890 struct tm tmp = *result;
00891 time_t t2;
00892 # if defined(HAVE_STRUCT_TM_TM_GMTOFF)
00893 gmtoff1 = result->tm_gmtoff;
00894 # endif
00895 t2 = mktime(&tmp);
00896 # if defined(HAVE_STRUCT_TM_TM_GMTOFF)
00897 gmtoff2 = tmp.tm_gmtoff;
00898 # endif
00899 if (*t + gmtoff1 != t2 + gmtoff2)
00900 result = NULL;
00901 }
00902 #endif
00903 return result;
00904 }
00905 #define LOCALTIME(tm, result) (tzset(),rb_localtime_r2((tm), &(result)))
00906
00907 #if !defined(HAVE_STRUCT_TM_TM_GMTOFF)
00908 static struct tm *
00909 rb_gmtime_r2(const time_t *t, struct tm *result)
00910 {
00911 result = rb_gmtime_r(t, result);
00912 #if defined(HAVE_TIMEGM) && defined(LOCALTIME_OVERFLOW_PROBLEM)
00913 if (result) {
00914 struct tm tmp = *result;
00915 time_t t2 = timegm(&tmp);
00916 if (*t != t2)
00917 result = NULL;
00918 }
00919 #endif
00920 return result;
00921 }
00922 # define GMTIME(tm, result) rb_gmtime_r2((tm), &(result))
00923 #endif
00924
00925 static const int common_year_yday_offset[] = {
00926 -1,
00927 -1 + 31,
00928 -1 + 31 + 28,
00929 -1 + 31 + 28 + 31,
00930 -1 + 31 + 28 + 31 + 30,
00931 -1 + 31 + 28 + 31 + 30 + 31,
00932 -1 + 31 + 28 + 31 + 30 + 31 + 30,
00933 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31,
00934 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
00935 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
00936 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
00937 -1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
00938
00939 };
00940 static const int leap_year_yday_offset[] = {
00941 -1,
00942 -1 + 31,
00943 -1 + 31 + 29,
00944 -1 + 31 + 29 + 31,
00945 -1 + 31 + 29 + 31 + 30,
00946 -1 + 31 + 29 + 31 + 30 + 31,
00947 -1 + 31 + 29 + 31 + 30 + 31 + 30,
00948 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31,
00949 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
00950 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
00951 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
00952 -1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
00953
00954 };
00955
00956 static const int common_year_days_in_month[] = {
00957 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
00958 };
00959 static const int leap_year_days_in_month[] = {
00960 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
00961 };
00962
00963 static int
00964 calc_tm_yday(long tm_year, int tm_mon, int tm_mday)
00965 {
00966 int tm_year_mod400;
00967 int tm_yday = tm_mday;
00968
00969 tm_year_mod400 = MOD(tm_year, 400);
00970
00971 if (leap_year_p(tm_year_mod400 + 1900))
00972 tm_yday += leap_year_yday_offset[tm_mon];
00973 else
00974 tm_yday += common_year_yday_offset[tm_mon];
00975
00976 return tm_yday;
00977 }
00978
00979 static wideval_t
00980 timegmw_noleapsecond(struct vtm *vtm)
00981 {
00982 VALUE year1900;
00983 VALUE q400, r400;
00984 int year_mod400;
00985 int yday;
00986 long days_in400;
00987 VALUE vdays, ret;
00988 wideval_t wret;
00989
00990 year1900 = sub(vtm->year, INT2FIX(1900));
00991
00992 divmodv(year1900, INT2FIX(400), &q400, &r400);
00993 year_mod400 = NUM2INT(r400);
00994
00995 yday = calc_tm_yday(year_mod400, vtm->mon-1, vtm->mday);
00996
00997
00998
00999
01000
01001
01002
01003 ret = LONG2NUM(vtm->sec
01004 + vtm->min*60
01005 + vtm->hour*3600);
01006 days_in400 = yday
01007 - 70*365
01008 + DIV(year_mod400 - 69, 4)
01009 - DIV(year_mod400 - 1, 100)
01010 + (year_mod400 + 299) / 400;
01011 vdays = LONG2NUM(days_in400);
01012 vdays = add(vdays, mul(q400, INT2FIX(97)));
01013 vdays = add(vdays, mul(year1900, INT2FIX(365)));
01014 wret = wadd(rb_time_magnify(v2w(ret)), wmul(rb_time_magnify(v2w(vdays)), WINT2FIXWV(86400)));
01015 wret = wadd(wret, v2w(vtm->subsecx));
01016
01017 return wret;
01018 }
01019
01020 static st_table *zone_table;
01021
01022 static const char *
01023 zone_str(const char *s)
01024 {
01025 st_data_t k, v;
01026
01027 if (!zone_table)
01028 zone_table = st_init_strtable();
01029
01030 k = (st_data_t)s;
01031 if (st_lookup(zone_table, k, &v)) {
01032 return (const char *)v;
01033 }
01034 s = strdup(s);
01035 k = (st_data_t)s;
01036 st_add_direct(zone_table, k, k);
01037
01038 return s;
01039 }
01040
01041 static void
01042 gmtimew_noleapsecond(wideval_t timew, struct vtm *vtm)
01043 {
01044 VALUE v;
01045 int i, n, x, y;
01046 const int *yday_offset;
01047 int wday;
01048 VALUE timev;
01049 wideval_t timew2, w, w2;
01050
01051 vtm->isdst = 0;
01052
01053 split_second(timew, &timew2, &vtm->subsecx);
01054
01055 wdivmod(timew2, WINT2FIXWV(86400), &w2, &w);
01056 timev = w2v(w2);
01057 v = w2v(w);
01058
01059 wday = NUM2INT(mod(timev, INT2FIX(7)));
01060 vtm->wday = (wday + 4) % 7;
01061
01062 n = NUM2INT(v);
01063 vtm->sec = n % 60; n = n / 60;
01064 vtm->min = n % 60; n = n / 60;
01065 vtm->hour = n;
01066
01067
01068 divmodv(timev, INT2FIX(400*365 + 97), &timev, &v);
01069 vtm->year = mul(timev, INT2FIX(400));
01070
01071
01072
01073
01074 n = NUM2INT(v);
01075 y = 1970;
01076
01077
01078
01079
01080
01081 if (30*365+7+31+29-1 <= n) {
01082
01083 if (n < 31*365+8) {
01084
01085 y += 30;
01086 n -= 30*365+7;
01087 goto found;
01088 }
01089 else {
01090
01091 n -= 1;
01092 }
01093 }
01094
01095 x = n / (365*100 + 24);
01096 n = n % (365*100 + 24);
01097 y += x * 100;
01098 if (30*365+7+31+29-1 <= n) {
01099 if (n < 31*365+7) {
01100 y += 30;
01101 n -= 30*365+7;
01102 goto found;
01103 }
01104 else
01105 n += 1;
01106 }
01107
01108 x = n / (365*4 + 1);
01109 n = n % (365*4 + 1);
01110 y += x * 4;
01111 if (365*2+31+29-1 <= n) {
01112 if (n < 365*2+366) {
01113 y += 2;
01114 n -= 365*2;
01115 goto found;
01116 }
01117 else
01118 n -= 1;
01119 }
01120
01121 x = n / 365;
01122 n = n % 365;
01123 y += x;
01124
01125 found:
01126 vtm->yday = n+1;
01127 vtm->year = add(vtm->year, INT2NUM(y));
01128
01129 if (leap_year_p(y))
01130 yday_offset = leap_year_yday_offset;
01131 else
01132 yday_offset = common_year_yday_offset;
01133
01134 for (i = 0; i < 12; i++) {
01135 if (yday_offset[i] < n) {
01136 vtm->mon = i+1;
01137 vtm->mday = n - yday_offset[i];
01138 }
01139 else
01140 break;
01141 }
01142
01143 vtm->utc_offset = INT2FIX(0);
01144 vtm->zone = "UTC";
01145 }
01146
01147 static struct tm *
01148 gmtime_with_leapsecond(const time_t *timep, struct tm *result)
01149 {
01150 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
01151
01152 struct tm *t;
01153 int sign;
01154 int gmtoff_sec, gmtoff_min, gmtoff_hour, gmtoff_day;
01155 long gmtoff;
01156 t = LOCALTIME(timep, *result);
01157 if (t == NULL)
01158 return NULL;
01159
01160
01161 if (t->tm_gmtoff < 0) {
01162 sign = 1;
01163 gmtoff = -t->tm_gmtoff;
01164 }
01165 else {
01166 sign = -1;
01167 gmtoff = t->tm_gmtoff;
01168 }
01169 gmtoff_sec = (int)(gmtoff % 60);
01170 gmtoff = gmtoff / 60;
01171 gmtoff_min = (int)(gmtoff % 60);
01172 gmtoff = gmtoff / 60;
01173 gmtoff_hour = (int)gmtoff;
01174
01175 gmtoff_sec *= sign;
01176 gmtoff_min *= sign;
01177 gmtoff_hour *= sign;
01178
01179 gmtoff_day = 0;
01180
01181 if (gmtoff_sec) {
01182
01183
01184 result->tm_sec += gmtoff_sec;
01185 if (result->tm_sec < 0) {
01186 result->tm_sec += 60;
01187 gmtoff_min -= 1;
01188 }
01189 if (60 <= result->tm_sec) {
01190 result->tm_sec -= 60;
01191 gmtoff_min += 1;
01192 }
01193 }
01194 if (gmtoff_min) {
01195 result->tm_min += gmtoff_min;
01196 if (result->tm_min < 0) {
01197 result->tm_min += 60;
01198 gmtoff_hour -= 1;
01199 }
01200 if (60 <= result->tm_min) {
01201 result->tm_min -= 60;
01202 gmtoff_hour += 1;
01203 }
01204 }
01205 if (gmtoff_hour) {
01206 result->tm_hour += gmtoff_hour;
01207 if (result->tm_hour < 0) {
01208 result->tm_hour += 24;
01209 gmtoff_day = -1;
01210 }
01211 if (24 <= result->tm_hour) {
01212 result->tm_hour -= 24;
01213 gmtoff_day = 1;
01214 }
01215 }
01216
01217 if (gmtoff_day) {
01218 if (gmtoff_day < 0) {
01219 if (result->tm_yday == 0) {
01220 result->tm_mday = 31;
01221 result->tm_mon = 11;
01222 result->tm_year--;
01223 result->tm_yday = leap_year_p(result->tm_year + 1900) ? 365 : 364;
01224 }
01225 else if (result->tm_mday == 1) {
01226 const int *days_in_month = leap_year_p(result->tm_year + 1900) ?
01227 leap_year_days_in_month :
01228 common_year_days_in_month;
01229 result->tm_mon--;
01230 result->tm_mday = days_in_month[result->tm_mon];
01231 result->tm_yday--;
01232 }
01233 else {
01234 result->tm_mday--;
01235 result->tm_yday--;
01236 }
01237 result->tm_wday = (result->tm_wday + 6) % 7;
01238 }
01239 else {
01240 int leap = leap_year_p(result->tm_year + 1900);
01241 if (result->tm_yday == (leap ? 365 : 364)) {
01242 result->tm_year++;
01243 result->tm_mon = 0;
01244 result->tm_mday = 1;
01245 result->tm_yday = 0;
01246 }
01247 else if (result->tm_mday == (leap ? leap_year_days_in_month :
01248 common_year_days_in_month)[result->tm_mon]) {
01249 result->tm_mon++;
01250 result->tm_mday = 1;
01251 result->tm_yday++;
01252 }
01253 else {
01254 result->tm_mday++;
01255 result->tm_yday++;
01256 }
01257 result->tm_wday = (result->tm_wday + 1) % 7;
01258 }
01259 }
01260 result->tm_isdst = 0;
01261 result->tm_gmtoff = 0;
01262 #if defined(HAVE_TM_ZONE)
01263 result->tm_zone = (char *)"UTC";
01264 #endif
01265 return result;
01266 #else
01267 return GMTIME(timep, *result);
01268 #endif
01269 }
01270
01271 static long this_year = 0;
01272 static time_t known_leap_seconds_limit;
01273 static int number_of_leap_seconds_known;
01274
01275 static void
01276 init_leap_second_info()
01277 {
01278
01279
01280
01281
01282
01283 if (this_year == 0) {
01284 time_t now;
01285 struct tm *tm, result;
01286 struct vtm vtm;
01287 wideval_t timew;
01288 now = time(NULL);
01289 gmtime(&now);
01290 tm = gmtime_with_leapsecond(&now, &result);
01291 if (!tm) return;
01292 this_year = tm->tm_year;
01293
01294 if (TIMET_MAX - now < (time_t)(366*86400))
01295 known_leap_seconds_limit = TIMET_MAX;
01296 else
01297 known_leap_seconds_limit = now + (time_t)(366*86400);
01298
01299 if (!gmtime_with_leapsecond(&known_leap_seconds_limit, &result))
01300 return;
01301
01302 vtm.year = LONG2NUM(result.tm_year + 1900);
01303 vtm.mon = result.tm_mon + 1;
01304 vtm.mday = result.tm_mday;
01305 vtm.hour = result.tm_hour;
01306 vtm.min = result.tm_min;
01307 vtm.sec = result.tm_sec;
01308 vtm.subsecx = INT2FIX(0);
01309 vtm.utc_offset = INT2FIX(0);
01310
01311 timew = timegmw_noleapsecond(&vtm);
01312
01313 number_of_leap_seconds_known = NUM2INT(w2v(wsub(TIMET2WV(known_leap_seconds_limit), rb_time_unmagnify(timew))));
01314 }
01315 }
01316
01317 static wideval_t
01318 timegmw(struct vtm *vtm)
01319 {
01320 wideval_t timew;
01321 struct tm tm;
01322 time_t t;
01323 const char *errmsg;
01324
01325
01326
01327 if (gt(INT2FIX(1972), vtm->year))
01328 return timegmw_noleapsecond(vtm);
01329
01330 init_leap_second_info();
01331
01332 timew = timegmw_noleapsecond(vtm);
01333
01334 if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
01335 return wadd(timew, rb_time_magnify(WINT2WV(number_of_leap_seconds_known)));
01336 }
01337
01338 tm.tm_year = rb_long2int(NUM2LONG(vtm->year) - 1900);
01339 tm.tm_mon = vtm->mon - 1;
01340 tm.tm_mday = vtm->mday;
01341 tm.tm_hour = vtm->hour;
01342 tm.tm_min = vtm->min;
01343 tm.tm_sec = vtm->sec;
01344 tm.tm_isdst = 0;
01345
01346 errmsg = find_time_t(&tm, 1, &t);
01347 if (errmsg)
01348 rb_raise(rb_eArgError, "%s", errmsg);
01349 return wadd(rb_time_magnify(TIMET2WV(t)), v2w(vtm->subsecx));
01350 }
01351
01352 static struct vtm *
01353 gmtimew(wideval_t timew, struct vtm *result)
01354 {
01355 time_t t;
01356 struct tm tm;
01357 VALUE subsecx;
01358 wideval_t timew2;
01359
01360 if (wlt(timew, WINT2FIXWV(0))) {
01361 gmtimew_noleapsecond(timew, result);
01362 return result;
01363 }
01364
01365 init_leap_second_info();
01366
01367 if (wlt(rb_time_magnify(TIMET2WV(known_leap_seconds_limit)), timew)) {
01368 timew = wsub(timew, rb_time_magnify(WINT2WV(number_of_leap_seconds_known)));
01369 gmtimew_noleapsecond(timew, result);
01370 return result;
01371 }
01372
01373 split_second(timew, &timew2, &subsecx);
01374
01375 t = WV2TIMET(timew2);
01376 if (!gmtime_with_leapsecond(&t, &tm))
01377 return NULL;
01378
01379 result->year = LONG2NUM((long)tm.tm_year + 1900);
01380 result->mon = tm.tm_mon + 1;
01381 result->mday = tm.tm_mday;
01382 result->hour = tm.tm_hour;
01383 result->min = tm.tm_min;
01384 result->sec = tm.tm_sec;
01385 result->subsecx = subsecx;
01386 result->utc_offset = INT2FIX(0);
01387 result->wday = tm.tm_wday;
01388 result->yday = tm.tm_yday+1;
01389 result->isdst = tm.tm_isdst;
01390 result->zone = "UTC";
01391
01392 return result;
01393 }
01394
01395 static struct tm *localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, const char **zone);
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428
01429
01430 static int compat_common_month_table[12][7] = {
01431
01432 { 2034, 2035, 2036, 2031, 2032, 2027, 2033 },
01433 { 2026, 2027, 2033, 2034, 2035, 2030, 2031 },
01434 { 2026, 2032, 2033, 2034, 2035, 2030, 2036 },
01435 { 2035, 2030, 2036, 2026, 2032, 2033, 2034 },
01436 { 2033, 2034, 2035, 2030, 2036, 2026, 2032 },
01437 { 2036, 2026, 2032, 2033, 2034, 2035, 2030 },
01438 { 2035, 2030, 2036, 2026, 2032, 2033, 2034 },
01439 { 2032, 2033, 2034, 2035, 2030, 2036, 2026 },
01440 { 2030, 2036, 2026, 2032, 2033, 2034, 2035 },
01441 { 2034, 2035, 2030, 2036, 2026, 2032, 2033 },
01442 { 2026, 2032, 2033, 2034, 2035, 2030, 2036 },
01443 { 2030, 2036, 2026, 2032, 2033, 2034, 2035 },
01444 };
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471 static int compat_leap_month_table[7] = {
01472
01473 2032, 2016, 2028, 2012, 2024, 2036, 2020,
01474 };
01475
01476 static int
01477 calc_wday(int year, int month, int day)
01478 {
01479 int a, y, m;
01480 int wday;
01481
01482 a = (14 - month) / 12;
01483 y = year + 4800 - a;
01484 m = month + 12 * a - 3;
01485 wday = day + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 + 2;
01486 wday = wday % 7;
01487 return wday;
01488 }
01489
01490 static VALUE
01491 guess_local_offset(struct vtm *vtm_utc, int *isdst_ret, const char **zone_ret)
01492 {
01493 struct tm tm;
01494 long gmtoff;
01495 const char *zone;
01496 time_t t;
01497 struct vtm vtm2;
01498 VALUE timev;
01499 int y, wday;
01500
01501
01502
01503 if (lt(vtm_utc->year, INT2FIX(1916))) {
01504 VALUE off = INT2FIX(0);
01505 int isdst = 0;
01506 zone = "UTC";
01507
01508 # if defined(NEGATIVE_TIME_T)
01509
01510 if (localtime_with_gmtoff_zone((t = (time_t)0x80000000, &t), &tm, &gmtoff, &zone)) {
01511 off = LONG2FIX(gmtoff);
01512 isdst = tm.tm_isdst;
01513 }
01514 else
01515 # endif
01516
01517 if (localtime_with_gmtoff_zone((t = 0, &t), &tm, &gmtoff, &zone)) {
01518 off = LONG2FIX(gmtoff);
01519 isdst = tm.tm_isdst;
01520 }
01521
01522 if (isdst_ret)
01523 *isdst_ret = isdst;
01524 if (zone_ret)
01525 *zone_ret = zone;
01526 return off;
01527 }
01528
01529
01530
01531 vtm2 = *vtm_utc;
01532
01533
01534 y = NUM2INT(mod(vtm_utc->year, INT2FIX(400)));
01535 wday = calc_wday(y, vtm_utc->mon, 1);
01536 if (vtm_utc->mon == 2 && leap_year_p(y))
01537 vtm2.year = INT2FIX(compat_leap_month_table[wday]);
01538 else
01539 vtm2.year = INT2FIX(compat_common_month_table[vtm_utc->mon-1][wday]);
01540
01541 timev = w2v(rb_time_unmagnify(timegmw(&vtm2)));
01542 t = NUM2TIMET(timev);
01543 zone = "UTC";
01544 if (localtime_with_gmtoff_zone(&t, &tm, &gmtoff, &zone)) {
01545 if (isdst_ret)
01546 *isdst_ret = tm.tm_isdst;
01547 if (zone_ret)
01548 *zone_ret = zone;
01549 return LONG2FIX(gmtoff);
01550 }
01551
01552 {
01553
01554 static time_t now = 0;
01555 static long now_gmtoff = 0;
01556 static const char *now_zone = "UTC";
01557 if (now == 0) {
01558 now = time(NULL);
01559 localtime_with_gmtoff_zone(&now, &tm, &now_gmtoff, &now_zone);
01560 }
01561 if (isdst_ret)
01562 *isdst_ret = tm.tm_isdst;
01563 if (zone_ret)
01564 *zone_ret = now_zone;
01565 return LONG2FIX(now_gmtoff);
01566 }
01567 }
01568
01569 static VALUE
01570 small_vtm_sub(struct vtm *vtm1, struct vtm *vtm2)
01571 {
01572 int off;
01573
01574 off = vtm1->sec - vtm2->sec;
01575 off += (vtm1->min - vtm2->min) * 60;
01576 off += (vtm1->hour - vtm2->hour) * 3600;
01577 if (ne(vtm1->year, vtm2->year))
01578 off += lt(vtm1->year, vtm2->year) ? -24*3600 : 24*3600;
01579 else if (vtm1->mon != vtm2->mon)
01580 off += vtm1->mon < vtm2->mon ? -24*3600 : 24*3600;
01581 else if (vtm1->mday != vtm2->mday)
01582 off += vtm1->mday < vtm2->mday ? -24*3600 : 24*3600;
01583
01584 return INT2FIX(off);
01585 }
01586
01587 static wideval_t
01588 timelocalw(struct vtm *vtm)
01589 {
01590 time_t t;
01591 struct tm tm;
01592 VALUE v;
01593 wideval_t timew1, timew2;
01594 struct vtm vtm1, vtm2;
01595 int n;
01596
01597 if (FIXNUM_P(vtm->year)) {
01598 long l = FIX2LONG(vtm->year) - 1900;
01599 if (l < INT_MIN || INT_MAX < l)
01600 goto no_localtime;
01601 tm.tm_year = (int)l;
01602 }
01603 else {
01604 v = sub(vtm->year, INT2FIX(1900));
01605 if (lt(v, INT2NUM(INT_MIN)) || lt(INT2NUM(INT_MAX), v))
01606 goto no_localtime;
01607 tm.tm_year = NUM2INT(v);
01608 }
01609
01610 tm.tm_mon = vtm->mon-1;
01611 tm.tm_mday = vtm->mday;
01612 tm.tm_hour = vtm->hour;
01613 tm.tm_min = vtm->min;
01614 tm.tm_sec = vtm->sec;
01615 tm.tm_isdst = vtm->isdst;
01616
01617 if (find_time_t(&tm, 0, &t))
01618 goto no_localtime;
01619 return wadd(rb_time_magnify(TIMET2WV(t)), v2w(vtm->subsecx));
01620
01621 no_localtime:
01622 timew1 = timegmw(vtm);
01623
01624 if (!localtimew(timew1, &vtm1))
01625 rb_raise(rb_eArgError, "localtimew error");
01626
01627 n = vtmcmp(vtm, &vtm1);
01628 if (n == 0) {
01629 timew1 = wsub(timew1, rb_time_magnify(WINT2FIXWV(12*3600)));
01630 if (!localtimew(timew1, &vtm1))
01631 rb_raise(rb_eArgError, "localtimew error");
01632 n = 1;
01633 }
01634
01635 if (n < 0) {
01636 timew2 = timew1;
01637 vtm2 = vtm1;
01638 timew1 = wsub(timew1, rb_time_magnify(WINT2FIXWV(24*3600)));
01639 if (!localtimew(timew1, &vtm1))
01640 rb_raise(rb_eArgError, "localtimew error");
01641 }
01642 else {
01643 timew2 = wadd(timew1, rb_time_magnify(WINT2FIXWV(24*3600)));
01644 if (!localtimew(timew2, &vtm2))
01645 rb_raise(rb_eArgError, "localtimew error");
01646 }
01647 timew1 = wadd(timew1, rb_time_magnify(v2w(small_vtm_sub(vtm, &vtm1))));
01648 timew2 = wadd(timew2, rb_time_magnify(v2w(small_vtm_sub(vtm, &vtm2))));
01649
01650 if (weq(timew1, timew2))
01651 return timew1;
01652
01653 if (!localtimew(timew1, &vtm1))
01654 rb_raise(rb_eArgError, "localtimew error");
01655 if (vtm->hour != vtm1.hour || vtm->min != vtm1.min || vtm->sec != vtm1.sec)
01656 return timew2;
01657
01658 if (!localtimew(timew2, &vtm2))
01659 rb_raise(rb_eArgError, "localtimew error");
01660 if (vtm->hour != vtm2.hour || vtm->min != vtm2.min || vtm->sec != vtm2.sec)
01661 return timew1;
01662
01663 if (vtm->isdst)
01664 return lt(vtm1.utc_offset, vtm2.utc_offset) ? timew2 : timew1;
01665 else
01666 return lt(vtm1.utc_offset, vtm2.utc_offset) ? timew1 : timew2;
01667 }
01668
01669 static struct tm *
01670 localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, const char **zone)
01671 {
01672 struct tm tm;
01673
01674 if (LOCALTIME(t, tm)) {
01675 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
01676 *gmtoff = tm.tm_gmtoff;
01677 #else
01678 struct tm *u, *l;
01679 long off;
01680 struct tm tmbuf;
01681 l = &tm;
01682 u = GMTIME(t, tmbuf);
01683 if (!u)
01684 return NULL;
01685 if (l->tm_year != u->tm_year)
01686 off = l->tm_year < u->tm_year ? -1 : 1;
01687 else if (l->tm_mon != u->tm_mon)
01688 off = l->tm_mon < u->tm_mon ? -1 : 1;
01689 else if (l->tm_mday != u->tm_mday)
01690 off = l->tm_mday < u->tm_mday ? -1 : 1;
01691 else
01692 off = 0;
01693 off = off * 24 + l->tm_hour - u->tm_hour;
01694 off = off * 60 + l->tm_min - u->tm_min;
01695 off = off * 60 + l->tm_sec - u->tm_sec;
01696 *gmtoff = off;
01697 #endif
01698
01699 if (zone) {
01700 #if defined(HAVE_TM_ZONE)
01701 *zone = zone_str(tm.tm_zone);
01702 #elif defined(HAVE_TZNAME) && defined(HAVE_DAYLIGHT)
01703
01704 *zone = zone_str(tzname[daylight && tm.tm_isdst]);
01705 #else
01706 {
01707 char buf[64];
01708 strftime(buf, sizeof(buf), "%Z", &tm);
01709 *zone = zone_str(buf);
01710 }
01711 #endif
01712 }
01713
01714 *result = tm;
01715 return result;
01716 }
01717 return NULL;
01718 }
01719
01720 static int
01721 timew_out_of_timet_range(wideval_t timew)
01722 {
01723 VALUE timexv;
01724 #if WIDEVALUE_IS_WIDER && SIZEOF_TIME_T < SIZEOF_INT64_T
01725 if (FIXWV_P(timew)) {
01726 wideint_t t = FIXWV2WINT(timew);
01727 if (t < TIME_SCALE * (wideint_t)TIMET_MIN ||
01728 TIME_SCALE * (1 + (wideint_t)TIMET_MAX) <= t)
01729 return 1;
01730 return 0;
01731 }
01732 #endif
01733 timexv = w2v(timew);
01734 if (lt(timexv, mul(INT2FIX(TIME_SCALE), TIMET2NUM(TIMET_MIN))) ||
01735 le(mul(INT2FIX(TIME_SCALE), add(TIMET2NUM(TIMET_MAX), INT2FIX(1))), timexv))
01736 return 1;
01737 return 0;
01738 }
01739
01740 static struct vtm *
01741 localtimew(wideval_t timew, struct vtm *result)
01742 {
01743 VALUE subsecx, offset;
01744 const char *zone;
01745 int isdst;
01746
01747 if (!timew_out_of_timet_range(timew)) {
01748 time_t t;
01749 struct tm tm;
01750 long gmtoff;
01751 wideval_t timew2;
01752
01753 split_second(timew, &timew2, &subsecx);
01754
01755 t = WV2TIMET(timew2);
01756
01757 if (localtime_with_gmtoff_zone(&t, &tm, &gmtoff, &zone)) {
01758 result->year = LONG2NUM((long)tm.tm_year + 1900);
01759 result->mon = tm.tm_mon + 1;
01760 result->mday = tm.tm_mday;
01761 result->hour = tm.tm_hour;
01762 result->min = tm.tm_min;
01763 result->sec = tm.tm_sec;
01764 result->subsecx = subsecx;
01765 result->wday = tm.tm_wday;
01766 result->yday = tm.tm_yday+1;
01767 result->isdst = tm.tm_isdst;
01768 result->utc_offset = LONG2NUM(gmtoff);
01769 result->zone = zone;
01770 return result;
01771 }
01772 }
01773
01774 if (!gmtimew(timew, result))
01775 return NULL;
01776
01777 offset = guess_local_offset(result, &isdst, &zone);
01778
01779 if (!gmtimew(wadd(timew, rb_time_magnify(v2w(offset))), result))
01780 return NULL;
01781
01782 result->utc_offset = offset;
01783 result->isdst = isdst;
01784 result->zone = zone;
01785
01786 return result;
01787 }
01788
01789 struct time_object {
01790 wideval_t timew;
01791 struct vtm vtm;
01792 int gmt;
01793 int tm_got;
01794 };
01795
01796 #define GetTimeval(obj, tobj) \
01797 TypedData_Get_Struct((obj), struct time_object, &time_data_type, (tobj))
01798
01799 #define IsTimeval(obj) rb_typeddata_is_kind_of((obj), &time_data_type)
01800
01801 #define TIME_UTC_P(tobj) ((tobj)->gmt == 1)
01802 #define TIME_SET_UTC(tobj) ((tobj)->gmt = 1)
01803
01804 #define TIME_LOCALTIME_P(tobj) ((tobj)->gmt == 0)
01805 #define TIME_SET_LOCALTIME(tobj) ((tobj)->gmt = 0)
01806
01807 #define TIME_FIXOFF_P(tobj) ((tobj)->gmt == 2)
01808 #define TIME_SET_FIXOFF(tobj, off) \
01809 ((tobj)->gmt = 2, \
01810 (tobj)->vtm.utc_offset = (off), \
01811 (tobj)->vtm.zone = NULL)
01812
01813 #define TIME_COPY_GMT(tobj1, tobj2) ((tobj1)->gmt = (tobj2)->gmt)
01814
01815 static VALUE time_get_tm(VALUE, struct time_object *);
01816 #define MAKE_TM(time, tobj) \
01817 do { \
01818 if ((tobj)->tm_got == 0) { \
01819 time_get_tm((time), (tobj)); \
01820 } \
01821 } while (0)
01822
01823 static void
01824 time_mark(void *ptr)
01825 {
01826 struct time_object *tobj = ptr;
01827 if (!tobj) return;
01828 if (!FIXWV_P(tobj->timew))
01829 rb_gc_mark(w2v(tobj->timew));
01830 rb_gc_mark(tobj->vtm.year);
01831 rb_gc_mark(tobj->vtm.subsecx);
01832 rb_gc_mark(tobj->vtm.utc_offset);
01833 }
01834
01835 static void
01836 time_free(void *tobj)
01837 {
01838 if (tobj) xfree(tobj);
01839 }
01840
01841 static size_t
01842 time_memsize(const void *tobj)
01843 {
01844 return tobj ? sizeof(struct time_object) : 0;
01845 }
01846
01847 static const rb_data_type_t time_data_type = {
01848 "time",
01849 time_mark, time_free, time_memsize,
01850 };
01851
01852 static VALUE
01853 time_s_alloc(VALUE klass)
01854 {
01855 VALUE obj;
01856 struct time_object *tobj;
01857
01858 obj = TypedData_Make_Struct(klass, struct time_object, &time_data_type, tobj);
01859 tobj->tm_got=0;
01860 tobj->timew = WINT2FIXWV(0);
01861
01862 return obj;
01863 }
01864
01865 static void
01866 time_modify(VALUE time)
01867 {
01868 rb_check_frozen(time);
01869 if (!OBJ_UNTRUSTED(time) && rb_safe_level() >= 4)
01870 rb_raise(rb_eSecurityError, "Insecure: can't modify Time");
01871 }
01872
01873 static wideval_t
01874 timespec2timew(struct timespec *ts)
01875 {
01876 wideval_t timew;
01877
01878 timew = rb_time_magnify(TIMET2WV(ts->tv_sec));
01879 if (ts->tv_nsec)
01880 timew = wadd(timew, wmulquoll(WINT2WV(ts->tv_nsec), TIME_SCALE, 1000000000));
01881 return timew;
01882 }
01883
01884 static struct timespec
01885 timew2timespec(wideval_t timew)
01886 {
01887 VALUE subsecx;
01888 struct timespec ts;
01889 wideval_t timew2;
01890
01891 if (timew_out_of_timet_range(timew))
01892 rb_raise(rb_eArgError, "time out of system range");
01893 split_second(timew, &timew2, &subsecx);
01894 ts.tv_sec = WV2TIMET(timew2);
01895 ts.tv_nsec = NUM2LONG(mulquo(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE)));
01896 return ts;
01897 }
01898
01899 static struct timespec *
01900 timew2timespec_exact(wideval_t timew, struct timespec *ts)
01901 {
01902 VALUE subsecx;
01903 wideval_t timew2;
01904 VALUE nsecv;
01905
01906 if (timew_out_of_timet_range(timew))
01907 return NULL;
01908 split_second(timew, &timew2, &subsecx);
01909 ts->tv_sec = WV2TIMET(timew2);
01910 nsecv = mulquo(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
01911 if (!FIXNUM_P(nsecv))
01912 return NULL;
01913 ts->tv_nsec = NUM2LONG(nsecv);
01914 return ts;
01915 }
01916
01917
01918
01919
01920
01921
01922
01923
01924 static VALUE
01925 time_init_0(VALUE time)
01926 {
01927 struct time_object *tobj;
01928 struct timespec ts;
01929
01930 time_modify(time);
01931 GetTimeval(time, tobj);
01932 tobj->tm_got=0;
01933 tobj->timew = WINT2FIXWV(0);
01934 #ifdef HAVE_CLOCK_GETTIME
01935 if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
01936 rb_sys_fail("clock_gettime");
01937 }
01938 #else
01939 {
01940 struct timeval tv;
01941 if (gettimeofday(&tv, 0) < 0) {
01942 rb_sys_fail("gettimeofday");
01943 }
01944 ts.tv_sec = tv.tv_sec;
01945 ts.tv_nsec = tv.tv_usec * 1000;
01946 }
01947 #endif
01948 tobj->timew = timespec2timew(&ts);
01949
01950 return time;
01951 }
01952
01953 static VALUE
01954 time_set_utc_offset(VALUE time, VALUE off)
01955 {
01956 struct time_object *tobj;
01957 off = num_exact(off);
01958
01959 time_modify(time);
01960 GetTimeval(time, tobj);
01961
01962 tobj->tm_got = 0;
01963 TIME_SET_FIXOFF(tobj, off);
01964
01965 return time;
01966 }
01967
01968 static void
01969 vtm_add_offset(struct vtm *vtm, VALUE off)
01970 {
01971 int sign;
01972 VALUE subsec, v;
01973 int sec, min, hour;
01974 int day;
01975
01976 vtm->utc_offset = sub(vtm->utc_offset, off);
01977
01978 if (lt(off, INT2FIX(0))) {
01979 sign = -1;
01980 off = neg(off);
01981 }
01982 else {
01983 sign = 1;
01984 }
01985 divmodv(off, INT2FIX(1), &off, &subsec);
01986 divmodv(off, INT2FIX(60), &off, &v);
01987 sec = NUM2INT(v);
01988 divmodv(off, INT2FIX(60), &off, &v);
01989 min = NUM2INT(v);
01990 divmodv(off, INT2FIX(24), &off, &v);
01991 hour = NUM2INT(v);
01992
01993 if (sign < 0) {
01994 subsec = neg(subsec);
01995 sec = -sec;
01996 min = -min;
01997 hour = -hour;
01998 }
01999
02000 day = 0;
02001
02002 if (!rb_equal(subsec, INT2FIX(0))) {
02003 vtm->subsecx = add(vtm->subsecx, w2v(rb_time_magnify(v2w(subsec))));
02004 if (lt(vtm->subsecx, INT2FIX(0))) {
02005 vtm->subsecx = add(vtm->subsecx, INT2FIX(TIME_SCALE));
02006 sec -= 1;
02007 }
02008 if (le(INT2FIX(TIME_SCALE), vtm->subsecx)) {
02009 vtm->subsecx = sub(vtm->subsecx, INT2FIX(TIME_SCALE));
02010 sec += 1;
02011 }
02012 goto not_zero_sec;
02013 }
02014 if (sec) {
02015 not_zero_sec:
02016
02017
02018 vtm->sec += sec;
02019 if (vtm->sec < 0) {
02020 vtm->sec += 60;
02021 min -= 1;
02022 }
02023 if (60 <= vtm->sec) {
02024 vtm->sec -= 60;
02025 min += 1;
02026 }
02027 }
02028 if (min) {
02029 vtm->min += min;
02030 if (vtm->min < 0) {
02031 vtm->min += 60;
02032 hour -= 1;
02033 }
02034 if (60 <= vtm->min) {
02035 vtm->min -= 60;
02036 hour += 1;
02037 }
02038 }
02039 if (hour) {
02040 vtm->hour += hour;
02041 if (vtm->hour < 0) {
02042 vtm->hour += 24;
02043 day = -1;
02044 }
02045 if (24 <= vtm->hour) {
02046 vtm->hour -= 24;
02047 day = 1;
02048 }
02049 }
02050
02051 if (day) {
02052 if (day < 0) {
02053 if (vtm->mon == 1 && vtm->mday == 1) {
02054 vtm->mday = 31;
02055 vtm->mon = 12;
02056 vtm->year = sub(vtm->year, INT2FIX(1));
02057 vtm->yday = leap_year_v_p(vtm->year) ? 365 : 364;
02058 }
02059 else if (vtm->mday == 1) {
02060 const int *days_in_month = leap_year_v_p(vtm->year) ?
02061 leap_year_days_in_month :
02062 common_year_days_in_month;
02063 vtm->mon--;
02064 vtm->mday = days_in_month[vtm->mon-1];
02065 vtm->yday--;
02066 }
02067 else {
02068 vtm->mday--;
02069 vtm->yday--;
02070 }
02071 vtm->wday = (vtm->wday + 6) % 7;
02072 }
02073 else {
02074 int leap = leap_year_v_p(vtm->year);
02075 if (vtm->mon == 12 && vtm->mday == 31) {
02076 vtm->year = add(vtm->year, INT2FIX(1));
02077 vtm->mon = 1;
02078 vtm->mday = 1;
02079 vtm->yday = 1;
02080 }
02081 else if (vtm->mday == (leap ? leap_year_days_in_month :
02082 common_year_days_in_month)[vtm->mon-1]) {
02083 vtm->mon++;
02084 vtm->mday = 1;
02085 vtm->yday++;
02086 }
02087 else {
02088 vtm->mday++;
02089 vtm->yday++;
02090 }
02091 vtm->wday = (vtm->wday + 1) % 7;
02092 }
02093 }
02094 }
02095
02096 static VALUE
02097 utc_offset_arg(VALUE arg)
02098 {
02099 VALUE tmp;
02100 if (!NIL_P(tmp = rb_check_string_type(arg))) {
02101 int n;
02102 char *s = RSTRING_PTR(tmp);
02103 if (!rb_enc_str_asciicompat_p(tmp) ||
02104 RSTRING_LEN(tmp) != 6 ||
02105 (s[0] != '+' && s[0] != '-') ||
02106 !ISDIGIT(s[1]) ||
02107 !ISDIGIT(s[2]) ||
02108 s[3] != ':' ||
02109 !ISDIGIT(s[4]) ||
02110 !ISDIGIT(s[5]))
02111 rb_raise(rb_eArgError, "\"+HH:MM\" or \"-HH:MM\" expected for utc_offset");
02112 n = (s[1] * 10 + s[2] - '0' * 11) * 3600;
02113 n += (s[4] * 10 + s[5] - '0' * 11) * 60;
02114 if (s[0] == '-')
02115 n = -n;
02116 return INT2FIX(n);
02117 }
02118 else {
02119 return num_exact(arg);
02120 }
02121 }
02122
02123 static VALUE
02124 time_init_1(int argc, VALUE *argv, VALUE time)
02125 {
02126 struct vtm vtm;
02127 VALUE v[7];
02128 struct time_object *tobj;
02129
02130 vtm.wday = -1;
02131 vtm.yday = 0;
02132 vtm.zone = "";
02133
02134
02135 rb_scan_args(argc, argv, "16", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6]);
02136
02137 vtm.year = obj2vint(v[0]);
02138
02139 vtm.mon = NIL_P(v[1]) ? 1 : month_arg(v[1]);
02140
02141 vtm.mday = NIL_P(v[2]) ? 1 : obj2int(v[2]);
02142
02143 vtm.hour = NIL_P(v[3]) ? 0 : obj2int(v[3]);
02144
02145 vtm.min = NIL_P(v[4]) ? 0 : obj2int(v[4]);
02146
02147 vtm.sec = 0;
02148 vtm.subsecx = INT2FIX(0);
02149 if (!NIL_P(v[5])) {
02150 VALUE sec = num_exact(v[5]);
02151 VALUE subsec;
02152 divmodv(sec, INT2FIX(1), &sec, &subsec);
02153 vtm.sec = NUM2INT(sec);
02154 vtm.subsecx = w2v(rb_time_magnify(v2w(subsec)));
02155 }
02156
02157 vtm.isdst = -1;
02158 vtm.utc_offset = Qnil;
02159 if (!NIL_P(v[6])) {
02160 VALUE arg = v[6];
02161 if (arg == ID2SYM(rb_intern("dst")))
02162 vtm.isdst = 1;
02163 else if (arg == ID2SYM(rb_intern("std")))
02164 vtm.isdst = 0;
02165 else
02166 vtm.utc_offset = utc_offset_arg(arg);
02167 }
02168
02169 validate_vtm(&vtm);
02170
02171 time_modify(time);
02172 GetTimeval(time, tobj);
02173 tobj->tm_got=0;
02174 tobj->timew = WINT2FIXWV(0);
02175
02176 if (!NIL_P(vtm.utc_offset)) {
02177 VALUE off = vtm.utc_offset;
02178 vtm_add_offset(&vtm, neg(off));
02179 vtm.utc_offset = Qnil;
02180 tobj->timew = timegmw(&vtm);
02181 return time_set_utc_offset(time, off);
02182 }
02183 else {
02184 tobj->timew = timelocalw(&vtm);
02185 return time_localtime(time);
02186 }
02187 }
02188
02189
02190
02191
02192
02193
02194
02195
02196
02197
02198
02199
02200
02201
02202
02203
02204
02205
02206
02207
02208
02209
02210
02211
02212
02213
02214
02215
02216
02217
02218
02219
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232
02233 static VALUE
02234 time_init(int argc, VALUE *argv, VALUE time)
02235 {
02236 if (argc == 0)
02237 return time_init_0(time);
02238 else
02239 return time_init_1(argc, argv, time);
02240 }
02241
02242 static void
02243 time_overflow_p(time_t *secp, long *nsecp)
02244 {
02245 time_t tmp, sec = *secp;
02246 long nsec = *nsecp;
02247
02248 if (nsec >= 1000000000) {
02249 tmp = sec + nsec / 1000000000;
02250 nsec %= 1000000000;
02251 if (sec > 0 && tmp < 0) {
02252 rb_raise(rb_eRangeError, "out of Time range");
02253 }
02254 sec = tmp;
02255 }
02256 if (nsec < 0) {
02257 tmp = sec + NDIV(nsec,1000000000);
02258 nsec = NMOD(nsec,1000000000);
02259 if (sec < 0 && tmp > 0) {
02260 rb_raise(rb_eRangeError, "out of Time range");
02261 }
02262 sec = tmp;
02263 }
02264 #ifndef NEGATIVE_TIME_T
02265 if (sec < 0)
02266 rb_raise(rb_eArgError, "time must be positive");
02267 #endif
02268 *secp = sec;
02269 *nsecp = nsec;
02270 }
02271
02272 static wideval_t
02273 nsec2timew(time_t sec, long nsec)
02274 {
02275 struct timespec ts;
02276 time_overflow_p(&sec, &nsec);
02277 ts.tv_sec = sec;
02278 ts.tv_nsec = nsec;
02279 return timespec2timew(&ts);
02280 }
02281
02282 static VALUE
02283 time_new_timew(VALUE klass, wideval_t timew)
02284 {
02285 VALUE time = time_s_alloc(klass);
02286 struct time_object *tobj;
02287
02288 GetTimeval(time, tobj);
02289 tobj->timew = timew;
02290
02291 return time;
02292 }
02293
02294 VALUE
02295 rb_time_new(time_t sec, long usec)
02296 {
02297 return time_new_timew(rb_cTime, nsec2timew(sec, usec * 1000));
02298 }
02299
02300 VALUE
02301 rb_time_nano_new(time_t sec, long nsec)
02302 {
02303 return time_new_timew(rb_cTime, nsec2timew(sec, nsec));
02304 }
02305
02306 VALUE
02307 rb_time_num_new(VALUE timev, VALUE off)
02308 {
02309 VALUE time = time_new_timew(rb_cTime, rb_time_magnify(v2w(timev)));
02310
02311 if (!NIL_P(off)) {
02312 off = utc_offset_arg(off);
02313 validate_utc_offset(off);
02314 time_set_utc_offset(time, off);
02315 return time;
02316 }
02317
02318 return time;
02319 }
02320
02321 static struct timespec
02322 time_timespec(VALUE num, int interval)
02323 {
02324 struct timespec t;
02325 const char *tstr = interval ? "time interval" : "time";
02326 VALUE i, f, ary;
02327
02328 #ifndef NEGATIVE_TIME_T
02329 interval = 1;
02330 #endif
02331
02332 switch (TYPE(num)) {
02333 case T_FIXNUM:
02334 t.tv_sec = NUM2TIMET(num);
02335 if (interval && t.tv_sec < 0)
02336 rb_raise(rb_eArgError, "%s must be positive", tstr);
02337 t.tv_nsec = 0;
02338 break;
02339
02340 case T_FLOAT:
02341 if (interval && RFLOAT_VALUE(num) < 0.0)
02342 rb_raise(rb_eArgError, "%s must be positive", tstr);
02343 else {
02344 double f, d;
02345
02346 d = modf(RFLOAT_VALUE(num), &f);
02347 if (d >= 0) {
02348 t.tv_nsec = (int)(d*1e9+0.5);
02349 }
02350 else if ((t.tv_nsec = (int)(-d*1e9+0.5)) > 0) {
02351 t.tv_nsec = 1000000000 - t.tv_nsec;
02352 f -= 1;
02353 }
02354 t.tv_sec = (time_t)f;
02355 if (f != t.tv_sec) {
02356 rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT_VALUE(num));
02357 }
02358 }
02359 break;
02360
02361 case T_BIGNUM:
02362 t.tv_sec = NUM2TIMET(num);
02363 if (interval && t.tv_sec < 0)
02364 rb_raise(rb_eArgError, "%s must be positive", tstr);
02365 t.tv_nsec = 0;
02366 break;
02367
02368 default:
02369 i = INT2FIX(1);
02370 ary = rb_check_funcall(num, id_divmod, 1, &i);
02371 if (ary != Qundef && !NIL_P(ary = rb_check_array_type(ary))) {
02372 i = rb_ary_entry(ary, 0);
02373 f = rb_ary_entry(ary, 1);
02374 t.tv_sec = NUM2TIMET(i);
02375 if (interval && t.tv_sec < 0)
02376 rb_raise(rb_eArgError, "%s must be positive", tstr);
02377 f = rb_funcall(f, id_mul, 1, INT2FIX(1000000000));
02378 t.tv_nsec = NUM2LONG(f);
02379 }
02380 else {
02381 rb_raise(rb_eTypeError, "can't convert %s into %s",
02382 rb_obj_classname(num), tstr);
02383 }
02384 break;
02385 }
02386 return t;
02387 }
02388
02389 static struct timeval
02390 time_timeval(VALUE num, int interval)
02391 {
02392 struct timespec ts;
02393 struct timeval tv;
02394
02395 ts = time_timespec(num, interval);
02396 tv.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
02397 tv.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
02398
02399 return tv;
02400 }
02401
02402 struct timeval
02403 rb_time_interval(VALUE num)
02404 {
02405 return time_timeval(num, TRUE);
02406 }
02407
02408 struct timeval
02409 rb_time_timeval(VALUE time)
02410 {
02411 struct time_object *tobj;
02412 struct timeval t;
02413 struct timespec ts;
02414
02415 if (IsTimeval(time)) {
02416 GetTimeval(time, tobj);
02417 ts = timew2timespec(tobj->timew);
02418 t.tv_sec = (TYPEOF_TIMEVAL_TV_SEC)ts.tv_sec;
02419 t.tv_usec = (TYPEOF_TIMEVAL_TV_USEC)(ts.tv_nsec / 1000);
02420 return t;
02421 }
02422 return time_timeval(time, FALSE);
02423 }
02424
02425 struct timespec
02426 rb_time_timespec(VALUE time)
02427 {
02428 struct time_object *tobj;
02429 struct timespec t;
02430
02431 if (IsTimeval(time)) {
02432 GetTimeval(time, tobj);
02433 t = timew2timespec(tobj->timew);
02434 return t;
02435 }
02436 return time_timespec(time, FALSE);
02437 }
02438
02439
02440
02441
02442
02443
02444
02445
02446
02447
02448 static VALUE
02449 time_s_now(VALUE klass)
02450 {
02451 return rb_class_new_instance(0, NULL, klass);
02452 }
02453
02454
02455
02456
02457
02458
02459
02460
02461
02462
02463
02464
02465
02466
02467
02468
02469
02470
02471
02472
02473
02474
02475 static VALUE
02476 time_s_at(int argc, VALUE *argv, VALUE klass)
02477 {
02478 VALUE time, t;
02479 wideval_t timew;
02480
02481 if (rb_scan_args(argc, argv, "11", &time, &t) == 2) {
02482 time = num_exact(time);
02483 t = num_exact(t);
02484 timew = wadd(rb_time_magnify(v2w(time)), wmulquoll(v2w(t), TIME_SCALE, 1000000));
02485 t = time_new_timew(klass, timew);
02486 }
02487 else if (IsTimeval(time)) {
02488 struct time_object *tobj, *tobj2;
02489 GetTimeval(time, tobj);
02490 t = time_new_timew(klass, tobj->timew);
02491 GetTimeval(t, tobj2);
02492 TIME_COPY_GMT(tobj2, tobj);
02493 }
02494 else {
02495 timew = rb_time_magnify(v2w(num_exact(time)));
02496 t = time_new_timew(klass, timew);
02497 }
02498
02499 return t;
02500 }
02501
02502 static const char months[][4] = {
02503 "jan", "feb", "mar", "apr", "may", "jun",
02504 "jul", "aug", "sep", "oct", "nov", "dec",
02505 };
02506
02507 static int
02508 obj2int(VALUE obj)
02509 {
02510 if (TYPE(obj) == T_STRING) {
02511 obj = rb_str_to_inum(obj, 10, FALSE);
02512 }
02513
02514 return NUM2INT(obj);
02515 }
02516
02517 static VALUE
02518 obj2vint(VALUE obj)
02519 {
02520 if (TYPE(obj) == T_STRING) {
02521 obj = rb_str_to_inum(obj, 10, FALSE);
02522 }
02523 else {
02524 obj = rb_to_int(obj);
02525 }
02526
02527 return obj;
02528 }
02529
02530 static int
02531 obj2subsecx(VALUE obj, VALUE *subsecx)
02532 {
02533 VALUE subsec;
02534
02535 if (TYPE(obj) == T_STRING) {
02536 obj = rb_str_to_inum(obj, 10, FALSE);
02537 *subsecx = INT2FIX(0);
02538 return NUM2INT(obj);
02539 }
02540
02541 divmodv(num_exact(obj), INT2FIX(1), &obj, &subsec);
02542 *subsecx = w2v(rb_time_magnify(v2w(subsec)));
02543 return NUM2INT(obj);
02544 }
02545
02546 static long
02547 usec2subsecx(VALUE obj)
02548 {
02549 if (TYPE(obj) == T_STRING) {
02550 obj = rb_str_to_inum(obj, 10, FALSE);
02551 }
02552
02553 return mulquo(num_exact(obj), INT2FIX(TIME_SCALE), INT2FIX(1000000));
02554 }
02555
02556 static int
02557 month_arg(VALUE arg)
02558 {
02559 int i, mon;
02560
02561 VALUE s = rb_check_string_type(arg);
02562 if (!NIL_P(s)) {
02563 mon = 0;
02564 for (i=0; i<12; i++) {
02565 if (RSTRING_LEN(s) == 3 &&
02566 STRCASECMP(months[i], RSTRING_PTR(s)) == 0) {
02567 mon = i+1;
02568 break;
02569 }
02570 }
02571 if (mon == 0) {
02572 char c = RSTRING_PTR(s)[0];
02573
02574 if ('0' <= c && c <= '9') {
02575 mon = obj2int(s);
02576 }
02577 }
02578 }
02579 else {
02580 mon = obj2int(arg);
02581 }
02582 return mon;
02583 }
02584
02585 static void
02586 validate_utc_offset(VALUE utc_offset)
02587 {
02588 if (le(utc_offset, INT2FIX(-86400)) || ge(utc_offset, INT2FIX(86400)))
02589 rb_raise(rb_eArgError, "utc_offset out of range");
02590 }
02591
02592 static void
02593 validate_vtm(struct vtm *vtm)
02594 {
02595 if ( vtm->mon < 1 || vtm->mon > 12
02596 || vtm->mday < 1 || vtm->mday > 31
02597 || vtm->hour < 0 || vtm->hour > 24
02598 || (vtm->hour == 24 && (vtm->min > 0 || vtm->sec > 0))
02599 || vtm->min < 0 || vtm->min > 59
02600 || vtm->sec < 0 || vtm->sec > 60
02601 || lt(vtm->subsecx, INT2FIX(0)) || ge(vtm->subsecx, INT2FIX(TIME_SCALE))
02602 || (!NIL_P(vtm->utc_offset) && (validate_utc_offset(vtm->utc_offset), 0)))
02603 rb_raise(rb_eArgError, "argument out of range");
02604 }
02605
02606 static void
02607 time_arg(int argc, VALUE *argv, struct vtm *vtm)
02608 {
02609 VALUE v[8];
02610
02611 vtm->year = INT2FIX(0);
02612 vtm->mon = 0;
02613 vtm->mday = 0;
02614 vtm->hour = 0;
02615 vtm->min = 0;
02616 vtm->sec = 0;
02617 vtm->subsecx = INT2FIX(0);
02618 vtm->utc_offset = Qnil;
02619 vtm->wday = 0;
02620 vtm->yday = 0;
02621 vtm->isdst = 0;
02622 vtm->zone = "";
02623
02624 if (argc == 10) {
02625 v[0] = argv[5];
02626 v[1] = argv[4];
02627 v[2] = argv[3];
02628 v[3] = argv[2];
02629 v[4] = argv[1];
02630 v[5] = argv[0];
02631 v[6] = Qnil;
02632 vtm->isdst = RTEST(argv[8]) ? 1 : 0;
02633 }
02634 else {
02635 rb_scan_args(argc, argv, "17", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6],&v[7]);
02636
02637
02638 vtm->wday = -1;
02639 vtm->isdst = -1;
02640 }
02641
02642 vtm->year = obj2vint(v[0]);
02643
02644 if (NIL_P(v[1])) {
02645 vtm->mon = 1;
02646 }
02647 else {
02648 vtm->mon = month_arg(v[1]);
02649 }
02650
02651 if (NIL_P(v[2])) {
02652 vtm->mday = 1;
02653 }
02654 else {
02655 vtm->mday = obj2int(v[2]);
02656 }
02657
02658 vtm->hour = NIL_P(v[3])?0:obj2int(v[3]);
02659
02660 vtm->min = NIL_P(v[4])?0:obj2int(v[4]);
02661
02662 if (!NIL_P(v[6]) && argc == 7) {
02663 vtm->sec = NIL_P(v[5])?0:obj2int(v[5]);
02664 vtm->subsecx = usec2subsecx(v[6]);
02665 }
02666 else {
02667
02668 vtm->sec = NIL_P(v[5])?0:obj2subsecx(v[5], &vtm->subsecx);
02669 }
02670
02671 validate_vtm(vtm);
02672 }
02673
02674 static int
02675 leap_year_p(long y)
02676 {
02677 return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
02678 }
02679
02680 static time_t
02681 timegm_noleapsecond(struct tm *tm)
02682 {
02683 long tm_year = tm->tm_year;
02684 int tm_yday = tm->tm_mday;
02685 if (leap_year_p(tm_year + 1900))
02686 tm_yday += leap_year_yday_offset[tm->tm_mon];
02687 else
02688 tm_yday += common_year_yday_offset[tm->tm_mon];
02689
02690
02691
02692
02693
02694
02695
02696 return tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 +
02697 (time_t)(tm_yday +
02698 (tm_year-70)*365 +
02699 DIV(tm_year-69,4) -
02700 DIV(tm_year-1,100) +
02701 DIV(tm_year+299,400))*86400;
02702 }
02703
02704 #if 0
02705 #define DEBUG_FIND_TIME_NUMGUESS
02706 #define DEBUG_GUESSRANGE
02707 #endif
02708
02709 #ifdef DEBUG_GUESSRANGE
02710 #define DEBUG_REPORT_GUESSRANGE fprintf(stderr, "find time guess range: %ld - %ld : %lu\n", guess_lo, guess_hi, (unsigned_time_t)(guess_hi-guess_lo))
02711 #else
02712 #define DEBUG_REPORT_GUESSRANGE
02713 #endif
02714
02715 #ifdef DEBUG_FIND_TIME_NUMGUESS
02716 #define DEBUG_FIND_TIME_NUMGUESS_INC find_time_numguess++,
02717 static unsigned long long find_time_numguess;
02718
02719 static VALUE find_time_numguess_getter(void)
02720 {
02721 return ULL2NUM(find_time_numguess);
02722 }
02723 #else
02724 #define DEBUG_FIND_TIME_NUMGUESS_INC
02725 #endif
02726
02727 static const char *
02728 find_time_t(struct tm *tptr, int utc_p, time_t *tp)
02729 {
02730 time_t guess, guess0, guess_lo, guess_hi;
02731 struct tm *tm, tm0, tm_lo, tm_hi;
02732 int d;
02733 int find_dst;
02734 struct tm result;
02735 int status;
02736 int tptr_tm_yday;
02737
02738 #define GUESS(p) (DEBUG_FIND_TIME_NUMGUESS_INC (utc_p ? gmtime_with_leapsecond((p), &result) : LOCALTIME((p), result)))
02739
02740 guess_lo = TIMET_MIN;
02741 guess_hi = TIMET_MAX;
02742
02743 find_dst = 0 < tptr->tm_isdst;
02744
02745 #if defined(HAVE_MKTIME)
02746 tm0 = *tptr;
02747 if (!utc_p && (guess = mktime(&tm0)) != -1) {
02748 tm = GUESS(&guess);
02749 if (tm && tmcmp(tptr, tm) == 0) {
02750 goto found;
02751 }
02752 }
02753 #endif
02754
02755 tm0 = *tptr;
02756 if (tm0.tm_mon < 0) {
02757 tm0.tm_mon = 0;
02758 tm0.tm_mday = 1;
02759 tm0.tm_hour = 0;
02760 tm0.tm_min = 0;
02761 tm0.tm_sec = 0;
02762 }
02763 else if (11 < tm0.tm_mon) {
02764 tm0.tm_mon = 11;
02765 tm0.tm_mday = 31;
02766 tm0.tm_hour = 23;
02767 tm0.tm_min = 59;
02768 tm0.tm_sec = 60;
02769 }
02770 else if (tm0.tm_mday < 1) {
02771 tm0.tm_mday = 1;
02772 tm0.tm_hour = 0;
02773 tm0.tm_min = 0;
02774 tm0.tm_sec = 0;
02775 }
02776 else if ((d = (leap_year_p(1900 + tm0.tm_year) ?
02777 leap_year_days_in_month :
02778 common_year_days_in_month)[tm0.tm_mon]) < tm0.tm_mday) {
02779 tm0.tm_mday = d;
02780 tm0.tm_hour = 23;
02781 tm0.tm_min = 59;
02782 tm0.tm_sec = 60;
02783 }
02784 else if (tm0.tm_hour < 0) {
02785 tm0.tm_hour = 0;
02786 tm0.tm_min = 0;
02787 tm0.tm_sec = 0;
02788 }
02789 else if (23 < tm0.tm_hour) {
02790 tm0.tm_hour = 23;
02791 tm0.tm_min = 59;
02792 tm0.tm_sec = 60;
02793 }
02794 else if (tm0.tm_min < 0) {
02795 tm0.tm_min = 0;
02796 tm0.tm_sec = 0;
02797 }
02798 else if (59 < tm0.tm_min) {
02799 tm0.tm_min = 59;
02800 tm0.tm_sec = 60;
02801 }
02802 else if (tm0.tm_sec < 0) {
02803 tm0.tm_sec = 0;
02804 }
02805 else if (60 < tm0.tm_sec) {
02806 tm0.tm_sec = 60;
02807 }
02808
02809 DEBUG_REPORT_GUESSRANGE;
02810 guess0 = guess = timegm_noleapsecond(&tm0);
02811 tm = GUESS(&guess);
02812 if (tm) {
02813 d = tmcmp(tptr, tm);
02814 if (d == 0) { goto found; }
02815 if (d < 0) {
02816 guess_hi = guess;
02817 guess -= 24 * 60 * 60;
02818 }
02819 else {
02820 guess_lo = guess;
02821 guess += 24 * 60 * 60;
02822 }
02823 DEBUG_REPORT_GUESSRANGE;
02824 if (guess_lo < guess && guess < guess_hi && (tm = GUESS(&guess)) != NULL) {
02825 d = tmcmp(tptr, tm);
02826 if (d == 0) { goto found; }
02827 if (d < 0)
02828 guess_hi = guess;
02829 else
02830 guess_lo = guess;
02831 DEBUG_REPORT_GUESSRANGE;
02832 }
02833 }
02834
02835 tm = GUESS(&guess_lo);
02836 if (!tm) goto error;
02837 d = tmcmp(tptr, tm);
02838 if (d < 0) goto out_of_range;
02839 if (d == 0) { guess = guess_lo; goto found; }
02840 tm_lo = *tm;
02841
02842 tm = GUESS(&guess_hi);
02843 if (!tm) goto error;
02844 d = tmcmp(tptr, tm);
02845 if (d > 0) goto out_of_range;
02846 if (d == 0) { guess = guess_hi; goto found; }
02847 tm_hi = *tm;
02848
02849 DEBUG_REPORT_GUESSRANGE;
02850
02851 status = 1;
02852
02853 while (guess_lo + 1 < guess_hi) {
02854 if (status == 0) {
02855 binsearch:
02856 guess = guess_lo / 2 + guess_hi / 2;
02857 if (guess <= guess_lo)
02858 guess = guess_lo + 1;
02859 else if (guess >= guess_hi)
02860 guess = guess_hi - 1;
02861 status = 1;
02862 }
02863 else {
02864 if (status == 1) {
02865 time_t guess0_hi = timegm_noleapsecond(&tm_hi);
02866 guess = guess_hi - (guess0_hi - guess0);
02867 if (guess == guess_hi)
02868 guess--;
02869 status = 2;
02870 }
02871 else if (status == 2) {
02872 time_t guess0_lo = timegm_noleapsecond(&tm_lo);
02873 guess = guess_lo + (guess0 - guess0_lo);
02874 if (guess == guess_lo)
02875 guess++;
02876 status = 0;
02877 }
02878 if (guess <= guess_lo || guess_hi <= guess) {
02879
02880 #ifdef DEBUG_GUESSRANGE
02881 if (guess <= guess_lo) fprintf(stderr, "too small guess: %ld <= %ld\n", guess, guess_lo);
02882 if (guess_hi <= guess) fprintf(stderr, "too big guess: %ld <= %ld\n", guess_hi, guess);
02883 #endif
02884 goto binsearch;
02885 }
02886 }
02887
02888 tm = GUESS(&guess);
02889 if (!tm) goto error;
02890
02891 d = tmcmp(tptr, tm);
02892
02893 if (d < 0) {
02894 guess_hi = guess;
02895 tm_hi = *tm;
02896 DEBUG_REPORT_GUESSRANGE;
02897 }
02898 else if (d > 0) {
02899 guess_lo = guess;
02900 tm_lo = *tm;
02901 DEBUG_REPORT_GUESSRANGE;
02902 }
02903 else {
02904 found:
02905 if (!utc_p) {
02906
02907 time_t guess2;
02908 if (find_dst) {
02909 guess2 = guess - 2 * 60 * 60;
02910 tm = LOCALTIME(&guess2, result);
02911 if (tm) {
02912 if (tptr->tm_hour != (tm->tm_hour + 2) % 24 ||
02913 tptr->tm_min != tm->tm_min ||
02914 tptr->tm_sec != tm->tm_sec) {
02915 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
02916 (tm->tm_min - tptr->tm_min) * 60 +
02917 (tm->tm_sec - tptr->tm_sec);
02918 if (tptr->tm_mday != tm->tm_mday)
02919 guess2 += 24 * 60 * 60;
02920 if (guess != guess2) {
02921 tm = LOCALTIME(&guess2, result);
02922 if (tm && tmcmp(tptr, tm) == 0) {
02923 if (guess < guess2)
02924 *tp = guess;
02925 else
02926 *tp = guess2;
02927 return NULL;
02928 }
02929 }
02930 }
02931 }
02932 }
02933 else {
02934 guess2 = guess + 2 * 60 * 60;
02935 tm = LOCALTIME(&guess2, result);
02936 if (tm) {
02937 if ((tptr->tm_hour + 2) % 24 != tm->tm_hour ||
02938 tptr->tm_min != tm->tm_min ||
02939 tptr->tm_sec != tm->tm_sec) {
02940 guess2 -= (tm->tm_hour - tptr->tm_hour) * 60 * 60 +
02941 (tm->tm_min - tptr->tm_min) * 60 +
02942 (tm->tm_sec - tptr->tm_sec);
02943 if (tptr->tm_mday != tm->tm_mday)
02944 guess2 -= 24 * 60 * 60;
02945 if (guess != guess2) {
02946 tm = LOCALTIME(&guess2, result);
02947 if (tm && tmcmp(tptr, tm) == 0) {
02948 if (guess < guess2)
02949 *tp = guess2;
02950 else
02951 *tp = guess;
02952 return NULL;
02953 }
02954 }
02955 }
02956 }
02957 }
02958 }
02959 *tp = guess;
02960 return NULL;
02961 }
02962 }
02963
02964
02965
02966
02967
02968
02969
02970
02971 tptr_tm_yday = calc_tm_yday(tptr->tm_year, tptr->tm_mon, tptr->tm_mday);
02972
02973 *tp = guess_lo +
02974 ((tptr->tm_year - tm_lo.tm_year) * 365 +
02975 ((tptr->tm_year-69)/4) -
02976 ((tptr->tm_year-1)/100) +
02977 ((tptr->tm_year+299)/400) -
02978 ((tm_lo.tm_year-69)/4) +
02979 ((tm_lo.tm_year-1)/100) -
02980 ((tm_lo.tm_year+299)/400) +
02981 tptr_tm_yday -
02982 tm_lo.tm_yday) * 86400 +
02983 (tptr->tm_hour - tm_lo.tm_hour) * 3600 +
02984 (tptr->tm_min - tm_lo.tm_min) * 60 +
02985 (tptr->tm_sec - tm_lo.tm_sec);
02986
02987 return NULL;
02988
02989 out_of_range:
02990 return "time out of range";
02991
02992 error:
02993 return "gmtime/localtime error";
02994 }
02995
02996 static int
02997 vtmcmp(struct vtm *a, struct vtm *b)
02998 {
02999 if (ne(a->year, b->year))
03000 return lt(a->year, b->year) ? -1 : 1;
03001 else if (a->mon != b->mon)
03002 return a->mon < b->mon ? -1 : 1;
03003 else if (a->mday != b->mday)
03004 return a->mday < b->mday ? -1 : 1;
03005 else if (a->hour != b->hour)
03006 return a->hour < b->hour ? -1 : 1;
03007 else if (a->min != b->min)
03008 return a->min < b->min ? -1 : 1;
03009 else if (a->sec != b->sec)
03010 return a->sec < b->sec ? -1 : 1;
03011 else if (ne(a->subsecx, b->subsecx))
03012 return lt(a->subsecx, b->subsecx) ? -1 : 1;
03013 else
03014 return 0;
03015 }
03016
03017 static int
03018 tmcmp(struct tm *a, struct tm *b)
03019 {
03020 if (a->tm_year != b->tm_year)
03021 return a->tm_year < b->tm_year ? -1 : 1;
03022 else if (a->tm_mon != b->tm_mon)
03023 return a->tm_mon < b->tm_mon ? -1 : 1;
03024 else if (a->tm_mday != b->tm_mday)
03025 return a->tm_mday < b->tm_mday ? -1 : 1;
03026 else if (a->tm_hour != b->tm_hour)
03027 return a->tm_hour < b->tm_hour ? -1 : 1;
03028 else if (a->tm_min != b->tm_min)
03029 return a->tm_min < b->tm_min ? -1 : 1;
03030 else if (a->tm_sec != b->tm_sec)
03031 return a->tm_sec < b->tm_sec ? -1 : 1;
03032 else
03033 return 0;
03034 }
03035
03036 static VALUE
03037 time_utc_or_local(int argc, VALUE *argv, int utc_p, VALUE klass)
03038 {
03039 struct vtm vtm;
03040 VALUE time;
03041
03042 time_arg(argc, argv, &vtm);
03043 if (utc_p)
03044 time = time_new_timew(klass, timegmw(&vtm));
03045 else
03046 time = time_new_timew(klass, timelocalw(&vtm));
03047 if (utc_p) return time_gmtime(time);
03048 return time_localtime(time);
03049 }
03050
03051
03052
03053
03054
03055
03056
03057
03058
03059
03060
03061
03062
03063
03064
03065
03066
03067
03068
03069
03070
03071
03072
03073
03074
03075
03076
03077
03078
03079
03080
03081
03082
03083 static VALUE
03084 time_s_mkutc(int argc, VALUE *argv, VALUE klass)
03085 {
03086 return time_utc_or_local(argc, argv, TRUE, klass);
03087 }
03088
03089
03090
03091
03092
03093
03094
03095
03096
03097
03098
03099
03100
03101
03102
03103
03104
03105
03106
03107
03108
03109
03110
03111
03112
03113
03114 static VALUE
03115 time_s_mktime(int argc, VALUE *argv, VALUE klass)
03116 {
03117 return time_utc_or_local(argc, argv, FALSE, klass);
03118 }
03119
03120
03121
03122
03123
03124
03125
03126
03127
03128
03129
03130
03131
03132
03133 static VALUE
03134 time_to_i(VALUE time)
03135 {
03136 struct time_object *tobj;
03137
03138 GetTimeval(time, tobj);
03139 return w2v(wdiv(tobj->timew, WINT2FIXWV(TIME_SCALE)));
03140 }
03141
03142
03143
03144
03145
03146
03147
03148
03149
03150
03151
03152
03153
03154
03155
03156
03157 static VALUE
03158 time_to_f(VALUE time)
03159 {
03160 struct time_object *tobj;
03161
03162 GetTimeval(time, tobj);
03163 return rb_Float(rb_time_unmagnify_to_float(tobj->timew));
03164 }
03165
03166
03167
03168
03169
03170
03171
03172
03173
03174
03175
03176
03177
03178
03179
03180
03181 static VALUE
03182 time_to_r(VALUE time)
03183 {
03184 struct time_object *tobj;
03185 VALUE v;
03186
03187 GetTimeval(time, tobj);
03188 v = w2v(rb_time_unmagnify(tobj->timew));
03189 if (TYPE(v) != T_RATIONAL) {
03190 v = rb_Rational1(v);
03191 }
03192 return v;
03193 }
03194
03195
03196
03197
03198
03199
03200
03201
03202
03203
03204
03205
03206
03207 static VALUE
03208 time_usec(VALUE time)
03209 {
03210 struct time_object *tobj;
03211 wideval_t w, q, r;
03212
03213 GetTimeval(time, tobj);
03214
03215 w = wmod(tobj->timew, WINT2WV(TIME_SCALE));
03216 wmuldivmod(w, WINT2FIXWV(1000000), WINT2FIXWV(TIME_SCALE), &q, &r);
03217 return rb_to_int(w2v(q));
03218 }
03219
03220
03221
03222
03223
03224
03225
03226
03227
03228
03229
03230
03231
03232
03233
03234
03235
03236
03237 static VALUE
03238 time_nsec(VALUE time)
03239 {
03240 struct time_object *tobj;
03241
03242 GetTimeval(time, tobj);
03243 return rb_to_int(w2v(wmulquoll(wmod(tobj->timew, WINT2WV(TIME_SCALE)), 1000000000, TIME_SCALE)));
03244 }
03245
03246
03247
03248
03249
03250
03251
03252
03253
03254
03255
03256
03257
03258
03259
03260
03261
03262
03263
03264 static VALUE
03265 time_subsec(VALUE time)
03266 {
03267 struct time_object *tobj;
03268
03269 GetTimeval(time, tobj);
03270 return quo(w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))), INT2FIX(TIME_SCALE));
03271 }
03272
03273
03274
03275
03276
03277
03278
03279
03280
03281
03282
03283
03284
03285
03286
03287
03288
03289
03290
03291
03292
03293 static VALUE
03294 time_cmp(VALUE time1, VALUE time2)
03295 {
03296 struct time_object *tobj1, *tobj2;
03297 int n;
03298
03299 GetTimeval(time1, tobj1);
03300 if (IsTimeval(time2)) {
03301 GetTimeval(time2, tobj2);
03302 n = wcmp(tobj1->timew, tobj2->timew);
03303 }
03304 else {
03305 VALUE tmp;
03306
03307 tmp = rb_funcall(time2, rb_intern("<=>"), 1, time1);
03308 if (NIL_P(tmp)) return Qnil;
03309
03310 n = -rb_cmpint(tmp, time1, time2);
03311 }
03312 if (n == 0) return INT2FIX(0);
03313 if (n > 0) return INT2FIX(1);
03314 return INT2FIX(-1);
03315 }
03316
03317
03318
03319
03320
03321
03322
03323
03324
03325
03326 static VALUE
03327 time_eql(VALUE time1, VALUE time2)
03328 {
03329 struct time_object *tobj1, *tobj2;
03330
03331 GetTimeval(time1, tobj1);
03332 if (IsTimeval(time2)) {
03333 GetTimeval(time2, tobj2);
03334 return rb_equal(w2v(tobj1->timew), w2v(tobj2->timew));
03335 }
03336 return Qfalse;
03337 }
03338
03339
03340
03341
03342
03343
03344
03345
03346
03347
03348
03349
03350
03351
03352
03353
03354
03355
03356
03357
03358 static VALUE
03359 time_utc_p(VALUE time)
03360 {
03361 struct time_object *tobj;
03362
03363 GetTimeval(time, tobj);
03364 if (TIME_UTC_P(tobj)) return Qtrue;
03365 return Qfalse;
03366 }
03367
03368
03369
03370
03371
03372
03373
03374
03375 static VALUE
03376 time_hash(VALUE time)
03377 {
03378 struct time_object *tobj;
03379
03380 GetTimeval(time, tobj);
03381 return rb_hash(w2v(tobj->timew));
03382 }
03383
03384
03385 static VALUE
03386 time_init_copy(VALUE copy, VALUE time)
03387 {
03388 struct time_object *tobj, *tcopy;
03389
03390 if (copy == time) return copy;
03391 time_modify(copy);
03392 GetTimeval(time, tobj);
03393 GetTimeval(copy, tcopy);
03394 MEMCPY(tcopy, tobj, struct time_object, 1);
03395
03396 return copy;
03397 }
03398
03399 static VALUE
03400 time_dup(VALUE time)
03401 {
03402 VALUE dup = time_s_alloc(CLASS_OF(time));
03403 time_init_copy(dup, time);
03404 return dup;
03405 }
03406
03407 static VALUE
03408 time_localtime(VALUE time)
03409 {
03410 struct time_object *tobj;
03411 struct vtm vtm;
03412
03413 GetTimeval(time, tobj);
03414 if (TIME_LOCALTIME_P(tobj)) {
03415 if (tobj->tm_got)
03416 return time;
03417 }
03418 else {
03419 time_modify(time);
03420 }
03421
03422 if (!localtimew(tobj->timew, &vtm))
03423 rb_raise(rb_eArgError, "localtime error");
03424 tobj->vtm = vtm;
03425
03426 tobj->tm_got = 1;
03427 TIME_SET_LOCALTIME(tobj);
03428 return time;
03429 }
03430
03431
03432
03433
03434
03435
03436
03437
03438
03439
03440
03441
03442
03443
03444
03445
03446
03447
03448
03449
03450
03451 static VALUE
03452 time_localtime_m(int argc, VALUE *argv, VALUE time)
03453 {
03454 VALUE off;
03455 rb_scan_args(argc, argv, "01", &off);
03456
03457 if (!NIL_P(off)) {
03458 off = utc_offset_arg(off);
03459 validate_utc_offset(off);
03460
03461 time_set_utc_offset(time, off);
03462 return time_fixoff(time);
03463 }
03464
03465 return time_localtime(time);
03466 }
03467
03468
03469
03470
03471
03472
03473
03474
03475
03476
03477
03478
03479
03480
03481
03482
03483
03484
03485
03486 static VALUE
03487 time_gmtime(VALUE time)
03488 {
03489 struct time_object *tobj;
03490 struct vtm vtm;
03491
03492 GetTimeval(time, tobj);
03493 if (TIME_UTC_P(tobj)) {
03494 if (tobj->tm_got)
03495 return time;
03496 }
03497 else {
03498 time_modify(time);
03499 }
03500
03501 if (!gmtimew(tobj->timew, &vtm))
03502 rb_raise(rb_eArgError, "gmtime error");
03503 tobj->vtm = vtm;
03504
03505 tobj->tm_got = 1;
03506 TIME_SET_UTC(tobj);
03507 return time;
03508 }
03509
03510 static VALUE
03511 time_fixoff(VALUE time)
03512 {
03513 struct time_object *tobj;
03514 struct vtm vtm;
03515 VALUE off;
03516
03517 GetTimeval(time, tobj);
03518 if (TIME_FIXOFF_P(tobj)) {
03519 if (tobj->tm_got)
03520 return time;
03521 }
03522 else {
03523 time_modify(time);
03524 }
03525
03526 if (TIME_FIXOFF_P(tobj))
03527 off = tobj->vtm.utc_offset;
03528 else
03529 off = INT2FIX(0);
03530
03531 if (!gmtimew(tobj->timew, &vtm))
03532 rb_raise(rb_eArgError, "gmtime error");
03533
03534 tobj->vtm = vtm;
03535 vtm_add_offset(&tobj->vtm, off);
03536
03537 tobj->tm_got = 1;
03538 TIME_SET_FIXOFF(tobj, off);
03539 return time;
03540 }
03541
03542
03543
03544
03545
03546
03547
03548
03549
03550
03551
03552
03553
03554
03555
03556
03557
03558
03559
03560
03561
03562
03563
03564 static VALUE
03565 time_getlocaltime(int argc, VALUE *argv, VALUE time)
03566 {
03567 VALUE off;
03568 rb_scan_args(argc, argv, "01", &off);
03569
03570 if (!NIL_P(off)) {
03571 off = utc_offset_arg(off);
03572 validate_utc_offset(off);
03573
03574 time = time_dup(time);
03575 time_set_utc_offset(time, off);
03576 return time_fixoff(time);
03577 }
03578
03579 return time_localtime(time_dup(time));
03580 }
03581
03582
03583
03584
03585
03586
03587
03588
03589
03590
03591
03592
03593
03594
03595
03596
03597 static VALUE
03598 time_getgmtime(VALUE time)
03599 {
03600 return time_gmtime(time_dup(time));
03601 }
03602
03603 static VALUE
03604 time_get_tm(VALUE time, struct time_object *tobj)
03605 {
03606 if (TIME_UTC_P(tobj)) return time_gmtime(time);
03607 if (TIME_FIXOFF_P(tobj)) return time_fixoff(time);
03608 return time_localtime(time);
03609 }
03610
03611 static VALUE strftimev(const char *fmt, VALUE time);
03612
03613
03614
03615
03616
03617
03618
03619
03620
03621
03622
03623 static VALUE
03624 time_asctime(VALUE time)
03625 {
03626 struct time_object *tobj;
03627
03628 GetTimeval(time, tobj);
03629 return strftimev("%a %b %e %T %Y", time);
03630 }
03631
03632
03633
03634
03635
03636
03637
03638
03639
03640
03641
03642
03643
03644
03645
03646
03647
03648 static VALUE
03649 time_to_s(VALUE time)
03650 {
03651 struct time_object *tobj;
03652
03653 GetTimeval(time, tobj);
03654 if (TIME_UTC_P(tobj))
03655 return strftimev("%Y-%m-%d %H:%M:%S UTC", time);
03656 else
03657 return strftimev("%Y-%m-%d %H:%M:%S %z", time);
03658 }
03659
03660 static VALUE
03661 time_add(struct time_object *tobj, VALUE offset, int sign)
03662 {
03663 VALUE result;
03664 offset = num_exact(offset);
03665 if (sign < 0)
03666 result = time_new_timew(rb_cTime, wsub(tobj->timew, rb_time_magnify(v2w(offset))));
03667 else
03668 result = time_new_timew(rb_cTime, wadd(tobj->timew, rb_time_magnify(v2w(offset))));
03669 if (TIME_UTC_P(tobj)) {
03670 GetTimeval(result, tobj);
03671 TIME_SET_UTC(tobj);
03672 }
03673 else if (TIME_FIXOFF_P(tobj)) {
03674 VALUE off = tobj->vtm.utc_offset;
03675 GetTimeval(result, tobj);
03676 TIME_SET_FIXOFF(tobj, off);
03677 }
03678 return result;
03679 }
03680
03681
03682
03683
03684
03685
03686
03687
03688
03689
03690
03691
03692 static VALUE
03693 time_plus(VALUE time1, VALUE time2)
03694 {
03695 struct time_object *tobj;
03696 GetTimeval(time1, tobj);
03697
03698 if (IsTimeval(time2)) {
03699 rb_raise(rb_eTypeError, "time + time?");
03700 }
03701 return time_add(tobj, time2, 1);
03702 }
03703
03704
03705
03706
03707
03708
03709
03710
03711
03712
03713
03714
03715
03716
03717
03718
03719 static VALUE
03720 time_minus(VALUE time1, VALUE time2)
03721 {
03722 struct time_object *tobj;
03723
03724 GetTimeval(time1, tobj);
03725 if (IsTimeval(time2)) {
03726 struct time_object *tobj2;
03727
03728 GetTimeval(time2, tobj2);
03729 return rb_Float(rb_time_unmagnify_to_float(wsub(tobj->timew, tobj2->timew)));
03730 }
03731 return time_add(tobj, time2, -1);
03732 }
03733
03734
03735
03736
03737
03738
03739
03740
03741
03742
03743
03744
03745 VALUE
03746 rb_time_succ(VALUE time)
03747 {
03748 struct time_object *tobj;
03749 struct time_object *tobj2;
03750
03751 rb_warn("Time#succ is obsolete; use time + 1");
03752 GetTimeval(time, tobj);
03753 time = time_new_timew(rb_cTime, wadd(tobj->timew, WINT2FIXWV(TIME_SCALE)));
03754 GetTimeval(time, tobj2);
03755 TIME_COPY_GMT(tobj2, tobj);
03756 return time;
03757 }
03758
03759 #define time_succ rb_time_succ
03760
03761
03762
03763
03764
03765
03766
03767
03768
03769
03770
03771
03772
03773
03774
03775
03776
03777
03778
03779
03780
03781
03782
03783
03784
03785
03786
03787
03788
03789
03790
03791
03792
03793
03794
03795
03796
03797
03798 static VALUE
03799 time_round(int argc, VALUE *argv, VALUE time)
03800 {
03801 VALUE ndigits, v, a, b, den;
03802 long nd;
03803 struct time_object *tobj;
03804
03805 rb_scan_args(argc, argv, "01", &ndigits);
03806
03807 if (NIL_P(ndigits))
03808 ndigits = INT2FIX(0);
03809 else
03810 ndigits = rb_to_int(ndigits);
03811
03812 nd = NUM2LONG(ndigits);
03813 if (nd < 0)
03814 rb_raise(rb_eArgError, "negative ndigits given");
03815
03816 GetTimeval(time, tobj);
03817 v = w2v(rb_time_unmagnify(tobj->timew));
03818
03819 a = INT2FIX(1);
03820 b = INT2FIX(10);
03821 while (0 < nd) {
03822 if (nd & 1)
03823 a = mul(a, b);
03824 b = mul(b, b);
03825 nd = nd >> 1;
03826 }
03827 den = quo(INT2FIX(1), a);
03828 v = mod(v, den);
03829 if (lt(v, quo(den, INT2FIX(2))))
03830 return time_add(tobj, v, -1);
03831 else
03832 return time_add(tobj, sub(den, v), 1);
03833 }
03834
03835
03836
03837
03838
03839
03840
03841
03842
03843
03844
03845
03846
03847
03848 static VALUE
03849 time_sec(VALUE time)
03850 {
03851 struct time_object *tobj;
03852
03853 GetTimeval(time, tobj);
03854 MAKE_TM(time, tobj);
03855 return INT2FIX(tobj->vtm.sec);
03856 }
03857
03858
03859
03860
03861
03862
03863
03864
03865
03866
03867
03868 static VALUE
03869 time_min(VALUE time)
03870 {
03871 struct time_object *tobj;
03872
03873 GetTimeval(time, tobj);
03874 MAKE_TM(time, tobj);
03875 return INT2FIX(tobj->vtm.min);
03876 }
03877
03878
03879
03880
03881
03882
03883
03884
03885
03886
03887
03888 static VALUE
03889 time_hour(VALUE time)
03890 {
03891 struct time_object *tobj;
03892
03893 GetTimeval(time, tobj);
03894 MAKE_TM(time, tobj);
03895 return INT2FIX(tobj->vtm.hour);
03896 }
03897
03898
03899
03900
03901
03902
03903
03904
03905
03906
03907
03908
03909
03910 static VALUE
03911 time_mday(VALUE time)
03912 {
03913 struct time_object *tobj;
03914
03915 GetTimeval(time, tobj);
03916 MAKE_TM(time, tobj);
03917 return INT2FIX(tobj->vtm.mday);
03918 }
03919
03920
03921
03922
03923
03924
03925
03926
03927
03928
03929
03930
03931
03932 static VALUE
03933 time_mon(VALUE time)
03934 {
03935 struct time_object *tobj;
03936
03937 GetTimeval(time, tobj);
03938 MAKE_TM(time, tobj);
03939 return INT2FIX(tobj->vtm.mon);
03940 }
03941
03942
03943
03944
03945
03946
03947
03948
03949
03950
03951
03952 static VALUE
03953 time_year(VALUE time)
03954 {
03955 struct time_object *tobj;
03956
03957 GetTimeval(time, tobj);
03958 MAKE_TM(time, tobj);
03959 return tobj->vtm.year;
03960 }
03961
03962
03963
03964
03965
03966
03967
03968
03969
03970
03971
03972
03973
03974
03975
03976
03977
03978
03979
03980 static VALUE
03981 time_wday(VALUE time)
03982 {
03983 struct time_object *tobj;
03984
03985 GetTimeval(time, tobj);
03986 MAKE_TM(time, tobj);
03987 return INT2FIX(tobj->vtm.wday);
03988 }
03989
03990 #define wday_p(n) {\
03991 struct time_object *tobj;\
03992 GetTimeval(time, tobj);\
03993 MAKE_TM(time, tobj);\
03994 return (tobj->vtm.wday == (n)) ? Qtrue : Qfalse;\
03995 }
03996
03997
03998
03999
04000
04001
04002
04003
04004
04005
04006
04007 static VALUE
04008 time_sunday(VALUE time)
04009 {
04010 wday_p(0);
04011 }
04012
04013
04014
04015
04016
04017
04018
04019
04020
04021
04022
04023 static VALUE
04024 time_monday(VALUE time)
04025 {
04026 wday_p(1);
04027 }
04028
04029
04030
04031
04032
04033
04034
04035
04036
04037
04038
04039 static VALUE
04040 time_tuesday(VALUE time)
04041 {
04042 wday_p(2);
04043 }
04044
04045
04046
04047
04048
04049
04050
04051
04052
04053
04054
04055 static VALUE
04056 time_wednesday(VALUE time)
04057 {
04058 wday_p(3);
04059 }
04060
04061
04062
04063
04064
04065
04066
04067
04068
04069
04070
04071 static VALUE
04072 time_thursday(VALUE time)
04073 {
04074 wday_p(4);
04075 }
04076
04077
04078
04079
04080
04081
04082
04083
04084
04085
04086
04087 static VALUE
04088 time_friday(VALUE time)
04089 {
04090 wday_p(5);
04091 }
04092
04093
04094
04095
04096
04097
04098
04099
04100
04101
04102
04103 static VALUE
04104 time_saturday(VALUE time)
04105 {
04106 wday_p(6);
04107 }
04108
04109
04110
04111
04112
04113
04114
04115
04116
04117
04118
04119 static VALUE
04120 time_yday(VALUE time)
04121 {
04122 struct time_object *tobj;
04123
04124 GetTimeval(time, tobj);
04125 MAKE_TM(time, tobj);
04126 return INT2FIX(tobj->vtm.yday);
04127 }
04128
04129
04130
04131
04132
04133
04134
04135
04136
04137
04138
04139
04140
04141
04142
04143
04144
04145
04146
04147
04148
04149
04150
04151
04152
04153
04154 static VALUE
04155 time_isdst(VALUE time)
04156 {
04157 struct time_object *tobj;
04158
04159 GetTimeval(time, tobj);
04160 MAKE_TM(time, tobj);
04161 return tobj->vtm.isdst ? Qtrue : Qfalse;
04162 }
04163
04164
04165
04166
04167
04168
04169
04170
04171
04172
04173
04174
04175
04176
04177 static VALUE
04178 time_zone(VALUE time)
04179 {
04180 struct time_object *tobj;
04181
04182 GetTimeval(time, tobj);
04183 MAKE_TM(time, tobj);
04184
04185 if (TIME_UTC_P(tobj)) {
04186 return rb_obj_untaint(rb_locale_str_new_cstr("UTC"));
04187 }
04188 if (tobj->vtm.zone == NULL)
04189 return Qnil;
04190 return rb_obj_untaint(rb_locale_str_new_cstr(tobj->vtm.zone));
04191 }
04192
04193
04194
04195
04196
04197
04198
04199
04200
04201
04202
04203
04204
04205
04206
04207
04208 static VALUE
04209 time_utc_offset(VALUE time)
04210 {
04211 struct time_object *tobj;
04212
04213 GetTimeval(time, tobj);
04214 MAKE_TM(time, tobj);
04215
04216 if (TIME_UTC_P(tobj)) {
04217 return INT2FIX(0);
04218 }
04219 else {
04220 return tobj->vtm.utc_offset;
04221 }
04222 }
04223
04224
04225
04226
04227
04228
04229
04230
04231
04232
04233
04234
04235
04236
04237
04238
04239 static VALUE
04240 time_to_a(VALUE time)
04241 {
04242 struct time_object *tobj;
04243
04244 GetTimeval(time, tobj);
04245 MAKE_TM(time, tobj);
04246 return rb_ary_new3(10,
04247 INT2FIX(tobj->vtm.sec),
04248 INT2FIX(tobj->vtm.min),
04249 INT2FIX(tobj->vtm.hour),
04250 INT2FIX(tobj->vtm.mday),
04251 INT2FIX(tobj->vtm.mon),
04252 tobj->vtm.year,
04253 INT2FIX(tobj->vtm.wday),
04254 INT2FIX(tobj->vtm.yday),
04255 tobj->vtm.isdst?Qtrue:Qfalse,
04256 time_zone(time));
04257 }
04258
04259 size_t
04260 rb_strftime(char *s, size_t maxsize, const char *format,
04261 const struct vtm *vtm, VALUE timev,
04262 int gmt);
04263
04264 size_t
04265 rb_strftime_timespec(char *s, size_t maxsize, const char *format, const struct vtm *vtm, struct timespec *ts, int gmt);
04266
04267 #define SMALLBUF 100
04268 static size_t
04269 rb_strftime_alloc(char **buf, const char *format,
04270 struct vtm *vtm, wideval_t timew, int gmt)
04271 {
04272 size_t size, len, flen;
04273 VALUE timev = Qnil;
04274 struct timespec ts;
04275
04276 if (!timew2timespec_exact(timew, &ts))
04277 timev = w2v(rb_time_unmagnify(timew));
04278
04279 (*buf)[0] = '\0';
04280 flen = strlen(format);
04281 if (flen == 0) {
04282 return 0;
04283 }
04284 errno = 0;
04285 if (timev == Qnil)
04286 len = rb_strftime_timespec(*buf, SMALLBUF, format, vtm, &ts, gmt);
04287 else
04288 len = rb_strftime(*buf, SMALLBUF, format, vtm, timev, gmt);
04289 if (len != 0 || (**buf == '\0' && errno != ERANGE)) return len;
04290 for (size=1024; ; size*=2) {
04291 *buf = xmalloc(size);
04292 (*buf)[0] = '\0';
04293 if (timev == Qnil)
04294 len = rb_strftime_timespec(*buf, size, format, vtm, &ts, gmt);
04295 else
04296 len = rb_strftime(*buf, size, format, vtm, timev, gmt);
04297
04298
04299
04300
04301
04302
04303
04304 if (len > 0 || size >= 1024 * flen) break;
04305 xfree(*buf);
04306 }
04307 return len;
04308 }
04309
04310 static VALUE
04311 strftimev(const char *fmt, VALUE time)
04312 {
04313 struct time_object *tobj;
04314 char buffer[SMALLBUF], *buf = buffer;
04315 long len;
04316 VALUE str;
04317
04318 GetTimeval(time, tobj);
04319 MAKE_TM(time, tobj);
04320 len = rb_strftime_alloc(&buf, fmt, &tobj->vtm, tobj->timew, TIME_UTC_P(tobj));
04321 str = rb_str_new(buf, len);
04322 if (buf != buffer) xfree(buf);
04323 return str;
04324 }
04325
04326
04327
04328
04329
04330
04331
04332
04333
04334
04335
04336
04337
04338
04339
04340
04341
04342
04343
04344
04345
04346
04347
04348
04349
04350
04351
04352
04353
04354
04355
04356
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366
04367
04368
04369
04370
04371
04372
04373
04374
04375
04376
04377
04378
04379
04380
04381
04382
04383
04384
04385
04386
04387
04388
04389
04390 static VALUE
04391 time_strftime(VALUE time, VALUE format)
04392 {
04393 void rb_enc_copy(VALUE, VALUE);
04394 struct time_object *tobj;
04395 char buffer[SMALLBUF], *buf = buffer;
04396 const char *fmt;
04397 long len;
04398 VALUE str;
04399
04400 GetTimeval(time, tobj);
04401 MAKE_TM(time, tobj);
04402 StringValue(format);
04403 if (!rb_enc_str_asciicompat_p(format)) {
04404 rb_raise(rb_eArgError, "format should have ASCII compatible encoding");
04405 }
04406 format = rb_str_new4(format);
04407 fmt = RSTRING_PTR(format);
04408 len = RSTRING_LEN(format);
04409 if (len == 0) {
04410 rb_warning("strftime called with empty format string");
04411 }
04412 else if (memchr(fmt, '\0', len)) {
04413
04414 const char *p = fmt, *pe = fmt + len;
04415
04416 str = rb_str_new(0, 0);
04417 while (p < pe) {
04418 len = rb_strftime_alloc(&buf, p, &tobj->vtm, tobj->timew, TIME_UTC_P(tobj));
04419 rb_str_cat(str, buf, len);
04420 p += strlen(p);
04421 if (buf != buffer) {
04422 xfree(buf);
04423 buf = buffer;
04424 }
04425 for (fmt = p; p < pe && !*p; ++p);
04426 if (p > fmt) rb_str_cat(str, fmt, p - fmt);
04427 }
04428 return str;
04429 }
04430 else {
04431 len = rb_strftime_alloc(&buf, RSTRING_PTR(format),
04432 &tobj->vtm, tobj->timew, TIME_UTC_P(tobj));
04433 }
04434 str = rb_str_new(buf, len);
04435 if (buf != buffer) xfree(buf);
04436 rb_enc_copy(str, format);
04437 return str;
04438 }
04439
04440
04441
04442
04443
04444 static VALUE
04445 time_mdump(VALUE time)
04446 {
04447 struct time_object *tobj;
04448 unsigned long p, s;
04449 char buf[8];
04450 int i;
04451 VALUE str;
04452
04453 struct vtm vtm;
04454 long year;
04455 long usec, nsec;
04456 VALUE subsecx, nano, subnano, v;
04457
04458 GetTimeval(time, tobj);
04459
04460 gmtimew(tobj->timew, &vtm);
04461
04462 if (FIXNUM_P(vtm.year)) {
04463 year = FIX2LONG(vtm.year);
04464 if (year < 1900 || 1900+0xffff < year)
04465 rb_raise(rb_eArgError, "year too big to marshal: %ld UTC", year);
04466 }
04467 else {
04468 rb_raise(rb_eArgError, "year too big to marshal");
04469 }
04470
04471 subsecx = vtm.subsecx;
04472
04473 nano = mulquo(subsecx, INT2FIX(1000000000), INT2FIX(TIME_SCALE));
04474 divmodv(nano, INT2FIX(1), &v, &subnano);
04475 nsec = FIX2LONG(v);
04476 usec = nsec / 1000;
04477 nsec = nsec % 1000;
04478
04479 nano = add(LONG2FIX(nsec), subnano);
04480
04481 p = 0x1UL << 31 |
04482 TIME_UTC_P(tobj) << 30 |
04483 (year-1900) << 14 |
04484 (vtm.mon-1) << 10 |
04485 vtm.mday << 5 |
04486 vtm.hour;
04487 s = vtm.min << 26 |
04488 vtm.sec << 20 |
04489 usec;
04490
04491 for (i=0; i<4; i++) {
04492 buf[i] = (unsigned char)p;
04493 p = RSHIFT(p, 8);
04494 }
04495 for (i=4; i<8; i++) {
04496 buf[i] = (unsigned char)s;
04497 s = RSHIFT(s, 8);
04498 }
04499
04500 str = rb_str_new(buf, 8);
04501 rb_copy_generic_ivar(str, time);
04502 if (!rb_equal(nano, INT2FIX(0))) {
04503 if (TYPE(nano) == T_RATIONAL) {
04504 rb_ivar_set(str, id_nano_num, RRATIONAL(nano)->num);
04505 rb_ivar_set(str, id_nano_den, RRATIONAL(nano)->den);
04506 }
04507 else {
04508 rb_ivar_set(str, id_nano_num, nano);
04509 rb_ivar_set(str, id_nano_den, INT2FIX(1));
04510 }
04511 }
04512 if (nsec) {
04513
04514
04515
04516
04517
04518
04519
04520 char buf[2];
04521 int len = (int)sizeof(buf);
04522 buf[1] = (char)((nsec % 10) << 4);
04523 nsec /= 10;
04524 buf[0] = (char)(nsec % 10);
04525 nsec /= 10;
04526 buf[0] |= (char)((nsec % 10) << 4);
04527 if (buf[1] == 0)
04528 len = 1;
04529 rb_ivar_set(str, id_submicro, rb_str_new(buf, len));
04530 }
04531 if (!TIME_UTC_P(tobj)) {
04532 VALUE off = time_utc_offset(time), div, mod;
04533 divmodv(off, INT2FIX(1), &div, &mod);
04534 if (rb_equal(mod, INT2FIX(0)))
04535 off = rb_Integer(div);
04536 rb_ivar_set(str, id_offset, off);
04537 }
04538 return str;
04539 }
04540
04541
04542
04543
04544
04545
04546
04547
04548 static VALUE
04549 time_dump(int argc, VALUE *argv, VALUE time)
04550 {
04551 VALUE str;
04552
04553 rb_scan_args(argc, argv, "01", 0);
04554 str = time_mdump(time);
04555
04556 return str;
04557 }
04558
04559
04560
04561
04562
04563 static VALUE
04564 time_mload(VALUE time, VALUE str)
04565 {
04566 struct time_object *tobj;
04567 unsigned long p, s;
04568 time_t sec;
04569 long usec;
04570 unsigned char *buf;
04571 struct vtm vtm;
04572 int i, gmt;
04573 long nsec;
04574 VALUE submicro, nano_num, nano_den, offset;
04575 wideval_t timew;
04576
04577 time_modify(time);
04578
04579 nano_num = rb_attr_get(str, id_nano_num);
04580 if (nano_num != Qnil) {
04581 st_delete(rb_generic_ivar_table(str), (st_data_t*)&id_nano_num, 0);
04582 }
04583 nano_den = rb_attr_get(str, id_nano_den);
04584 if (nano_den != Qnil) {
04585 st_delete(rb_generic_ivar_table(str), (st_data_t*)&id_nano_den, 0);
04586 }
04587 submicro = rb_attr_get(str, id_submicro);
04588 if (submicro != Qnil) {
04589 st_delete(rb_generic_ivar_table(str), (st_data_t*)&id_submicro, 0);
04590 }
04591 offset = rb_attr_get(str, id_offset);
04592 if (offset != Qnil) {
04593 validate_utc_offset(offset);
04594 st_delete(rb_generic_ivar_table(str), (st_data_t*)&id_offset, 0);
04595 }
04596 rb_copy_generic_ivar(time, str);
04597
04598 StringValue(str);
04599 buf = (unsigned char *)RSTRING_PTR(str);
04600 if (RSTRING_LEN(str) != 8) {
04601 rb_raise(rb_eTypeError, "marshaled time format differ");
04602 }
04603
04604 p = s = 0;
04605 for (i=0; i<4; i++) {
04606 p |= buf[i]<<(8*i);
04607 }
04608 for (i=4; i<8; i++) {
04609 s |= buf[i]<<(8*(i-4));
04610 }
04611
04612 if ((p & (1UL<<31)) == 0) {
04613 gmt = 0;
04614 offset = Qnil;
04615 sec = p;
04616 usec = s;
04617 nsec = usec * 1000;
04618 timew = wadd(rb_time_magnify(TIMET2WV(sec)), wmulquoll(WINT2FIXWV(usec), TIME_SCALE, 1000000));
04619 }
04620 else {
04621 p &= ~(1UL<<31);
04622 gmt = (int)((p >> 30) & 0x1);
04623
04624 vtm.year = INT2FIX(((int)(p >> 14) & 0xffff) + 1900);
04625 vtm.mon = ((int)(p >> 10) & 0xf) + 1;
04626 vtm.mday = (int)(p >> 5) & 0x1f;
04627 vtm.hour = (int) p & 0x1f;
04628 vtm.min = (int)(s >> 26) & 0x3f;
04629 vtm.sec = (int)(s >> 20) & 0x3f;
04630 vtm.utc_offset = INT2FIX(0);
04631 vtm.yday = vtm.wday = 0;
04632 vtm.isdst = 0;
04633 vtm.zone = "";
04634
04635 usec = (long)(s & 0xfffff);
04636 nsec = usec * 1000;
04637
04638
04639 vtm.subsecx = mulquo(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000));
04640 if (nano_num != Qnil) {
04641 VALUE nano = quo(num_exact(nano_num), num_exact(nano_den));
04642 vtm.subsecx = add(vtm.subsecx, mulquo(nano, INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
04643 }
04644 else if (submicro != Qnil) {
04645 unsigned char *ptr;
04646 long len;
04647 int digit;
04648 ptr = (unsigned char*)StringValuePtr(submicro);
04649 len = RSTRING_LEN(submicro);
04650 nsec = 0;
04651 if (0 < len) {
04652 if (10 <= (digit = ptr[0] >> 4)) goto end_submicro;
04653 nsec += digit * 100;
04654 if (10 <= (digit = ptr[0] & 0xf)) goto end_submicro;
04655 nsec += digit * 10;
04656 }
04657 if (1 < len) {
04658 if (10 <= (digit = ptr[1] >> 4)) goto end_submicro;
04659 nsec += digit;
04660 }
04661 vtm.subsecx = add(vtm.subsecx, mulquo(LONG2FIX(nsec), INT2FIX(TIME_SCALE), LONG2FIX(1000000000)));
04662 end_submicro: ;
04663 }
04664 timew = timegmw(&vtm);
04665 }
04666
04667 GetTimeval(time, tobj);
04668 tobj->tm_got = 0;
04669 tobj->timew = timew;
04670 if (gmt) {
04671 TIME_SET_UTC(tobj);
04672 }
04673 else if (!NIL_P(offset)) {
04674 time_set_utc_offset(time, offset);
04675 time_fixoff(time);
04676 }
04677
04678 return time;
04679 }
04680
04681
04682
04683
04684
04685
04686
04687
04688 static VALUE
04689 time_load(VALUE klass, VALUE str)
04690 {
04691 VALUE time = time_s_alloc(klass);
04692
04693 time_mload(time, str);
04694 return time;
04695 }
04696
04697
04698
04699
04700
04701
04702
04703
04704
04705
04706
04707
04708
04709
04710
04711
04712
04713
04714 void
04715 Init_Time(void)
04716 {
04717 #undef rb_intern
04718 #define rb_intern(str) rb_intern_const(str)
04719
04720 id_eq = rb_intern("==");
04721 id_ne = rb_intern("!=");
04722 id_quo = rb_intern("quo");
04723 id_div = rb_intern("div");
04724 id_cmp = rb_intern("<=>");
04725 id_lshift = rb_intern("<<");
04726 id_divmod = rb_intern("divmod");
04727 id_mul = rb_intern("*");
04728 id_submicro = rb_intern("submicro");
04729 id_nano_num = rb_intern("nano_num");
04730 id_nano_den = rb_intern("nano_den");
04731 id_offset = rb_intern("offset");
04732
04733 rb_cTime = rb_define_class("Time", rb_cObject);
04734 rb_include_module(rb_cTime, rb_mComparable);
04735
04736 rb_define_alloc_func(rb_cTime, time_s_alloc);
04737 rb_define_singleton_method(rb_cTime, "now", time_s_now, 0);
04738 rb_define_singleton_method(rb_cTime, "at", time_s_at, -1);
04739 rb_define_singleton_method(rb_cTime, "utc", time_s_mkutc, -1);
04740 rb_define_singleton_method(rb_cTime, "gm", time_s_mkutc, -1);
04741 rb_define_singleton_method(rb_cTime, "local", time_s_mktime, -1);
04742 rb_define_singleton_method(rb_cTime, "mktime", time_s_mktime, -1);
04743
04744 rb_define_method(rb_cTime, "to_i", time_to_i, 0);
04745 rb_define_method(rb_cTime, "to_f", time_to_f, 0);
04746 rb_define_method(rb_cTime, "to_r", time_to_r, 0);
04747 rb_define_method(rb_cTime, "<=>", time_cmp, 1);
04748 rb_define_method(rb_cTime, "eql?", time_eql, 1);
04749 rb_define_method(rb_cTime, "hash", time_hash, 0);
04750 rb_define_method(rb_cTime, "initialize", time_init, -1);
04751 rb_define_method(rb_cTime, "initialize_copy", time_init_copy, 1);
04752
04753 rb_define_method(rb_cTime, "localtime", time_localtime_m, -1);
04754 rb_define_method(rb_cTime, "gmtime", time_gmtime, 0);
04755 rb_define_method(rb_cTime, "utc", time_gmtime, 0);
04756 rb_define_method(rb_cTime, "getlocal", time_getlocaltime, -1);
04757 rb_define_method(rb_cTime, "getgm", time_getgmtime, 0);
04758 rb_define_method(rb_cTime, "getutc", time_getgmtime, 0);
04759
04760 rb_define_method(rb_cTime, "ctime", time_asctime, 0);
04761 rb_define_method(rb_cTime, "asctime", time_asctime, 0);
04762 rb_define_method(rb_cTime, "to_s", time_to_s, 0);
04763 rb_define_method(rb_cTime, "inspect", time_to_s, 0);
04764 rb_define_method(rb_cTime, "to_a", time_to_a, 0);
04765
04766 rb_define_method(rb_cTime, "+", time_plus, 1);
04767 rb_define_method(rb_cTime, "-", time_minus, 1);
04768
04769 rb_define_method(rb_cTime, "succ", time_succ, 0);
04770 rb_define_method(rb_cTime, "round", time_round, -1);
04771
04772 rb_define_method(rb_cTime, "sec", time_sec, 0);
04773 rb_define_method(rb_cTime, "min", time_min, 0);
04774 rb_define_method(rb_cTime, "hour", time_hour, 0);
04775 rb_define_method(rb_cTime, "mday", time_mday, 0);
04776 rb_define_method(rb_cTime, "day", time_mday, 0);
04777 rb_define_method(rb_cTime, "mon", time_mon, 0);
04778 rb_define_method(rb_cTime, "month", time_mon, 0);
04779 rb_define_method(rb_cTime, "year", time_year, 0);
04780 rb_define_method(rb_cTime, "wday", time_wday, 0);
04781 rb_define_method(rb_cTime, "yday", time_yday, 0);
04782 rb_define_method(rb_cTime, "isdst", time_isdst, 0);
04783 rb_define_method(rb_cTime, "dst?", time_isdst, 0);
04784 rb_define_method(rb_cTime, "zone", time_zone, 0);
04785 rb_define_method(rb_cTime, "gmtoff", time_utc_offset, 0);
04786 rb_define_method(rb_cTime, "gmt_offset", time_utc_offset, 0);
04787 rb_define_method(rb_cTime, "utc_offset", time_utc_offset, 0);
04788
04789 rb_define_method(rb_cTime, "utc?", time_utc_p, 0);
04790 rb_define_method(rb_cTime, "gmt?", time_utc_p, 0);
04791
04792 rb_define_method(rb_cTime, "sunday?", time_sunday, 0);
04793 rb_define_method(rb_cTime, "monday?", time_monday, 0);
04794 rb_define_method(rb_cTime, "tuesday?", time_tuesday, 0);
04795 rb_define_method(rb_cTime, "wednesday?", time_wednesday, 0);
04796 rb_define_method(rb_cTime, "thursday?", time_thursday, 0);
04797 rb_define_method(rb_cTime, "friday?", time_friday, 0);
04798 rb_define_method(rb_cTime, "saturday?", time_saturday, 0);
04799
04800 rb_define_method(rb_cTime, "tv_sec", time_to_i, 0);
04801 rb_define_method(rb_cTime, "tv_usec", time_usec, 0);
04802 rb_define_method(rb_cTime, "usec", time_usec, 0);
04803 rb_define_method(rb_cTime, "tv_nsec", time_nsec, 0);
04804 rb_define_method(rb_cTime, "nsec", time_nsec, 0);
04805 rb_define_method(rb_cTime, "subsec", time_subsec, 0);
04806
04807 rb_define_method(rb_cTime, "strftime", time_strftime, 1);
04808
04809
04810 rb_define_method(rb_cTime, "_dump", time_dump, -1);
04811 rb_define_singleton_method(rb_cTime, "_load", time_load, 1);
04812 #if 0
04813
04814 rb_define_method(rb_cTime, "marshal_dump", time_mdump, 0);
04815 rb_define_method(rb_cTime, "marshal_load", time_mload, 1);
04816 #endif
04817
04818 #ifdef DEBUG_FIND_TIME_NUMGUESS
04819 rb_define_virtual_variable("$find_time_numguess", find_time_numguess_getter, NULL);
04820 #endif
04821 }
04822