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