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