diff options
author | tadf <tadf@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-05-21 12:25:03 +0000 |
---|---|---|
committer | tadf <tadf@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-05-21 12:25:03 +0000 |
commit | 559c025a22e7a1df0182c505017eec7cb3d2228e (patch) | |
tree | 94a871cf9f9b59e144a2d31b44cd0d4c884c98f4 /ext/date | |
parent | 42cb637942628ab87119482e4bcf372cdc19f6d6 (diff) |
* ext/date/date_{core,parse}.c: moved nearly all core code from ext/date/lib.
* ext/date/lib/{date,date/format}.rb: removed nearly all code.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@31668 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/date')
-rw-r--r-- | ext/date/date_core.c | 6575 | ||||
-rw-r--r-- | ext/date/date_parse.c | 1191 | ||||
-rw-r--r-- | ext/date/date_strftime.c | 4 | ||||
-rw-r--r-- | ext/date/date_strptime.c | 4 | ||||
-rw-r--r-- | ext/date/lib/date.rb | 1590 | ||||
-rw-r--r-- | ext/date/lib/date/format.rb | 583 |
6 files changed, 7232 insertions, 2715 deletions
diff --git a/ext/date/date_core.c b/ext/date/date_core.c index b6e2688299..12c393515c 100644 --- a/ext/date/date_core.c +++ b/ext/date/date_core.c @@ -10,21 +10,26 @@ #define NDEBUG #include <assert.h> +/* #define FORCE_RIGHT */ + #ifdef RUBY_EXTCONF_H #include RUBY_EXTCONF_H #endif -#define LIGHT_MODE (1 << 0) -#define HAVE_JD (1 << 1) -#define HAVE_DF (1 << 2) -#define HAVE_CIVIL (1 << 3) -#define HAVE_TIME (1 << 4) +#define LIGHT_MODE (1 << 0) +#define HAVE_JD (1 << 1) +#define HAVE_DF (1 << 2) +#define HAVE_CIVIL (1 << 3) +#define HAVE_TIME (1 << 4) +#define DATETIME_OBJ (1 << 7) #define light_mode_p(x) ((x)->flags & LIGHT_MODE) #define have_jd_p(x) ((x)->flags & HAVE_JD) #define have_df_p(x) ((x)->flags & HAVE_DF) #define have_civil_p(x) ((x)->flags & HAVE_CIVIL) #define have_time_p(x) ((x)->flags & HAVE_TIME) +#define datetime_obj_p(x) ((x)->flags & DATETIME_OBJ) +#define date_obj_p(x) (!datetime_obj_p(x)) #define MIN_YEAR -4713 #define MAX_YEAR 1000000 @@ -40,15 +45,10 @@ #define JULIAN (NUM2DBL(rb_const_get(rb_cFloat, rb_intern("INFINITY")))) #define GREGORIAN (-NUM2DBL(rb_const_get(rb_cFloat, rb_intern("INFINITY")))) +#define MINUTE_IN_SECONDS 60 +#define HOUR_IN_SECONDS 3600 #define DAY_IN_SECONDS 86400 #define SECOND_IN_NANOSECONDS 1000000000 -#if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS -#define DAY_IN_NANOSECONDS LONG2NUM((long)DAY_IN_SECONDS * SECOND_IN_NANOSECONDS) -#elif defined HAVE_LONG_LONG -#define DAY_IN_NANOSECONDS LL2NUM((LONG_LONG)DAY_IN_SECONDS * SECOND_IN_NANOSECONDS) -#else -#define DAY_IN_NANOSECONDS f_mul(INT2FIX(DAY_IN_SECONDS), INT2FIX(SECOND_IN_NANOSECONDS)) -#endif /* copied from time.c */ #define NDIV(x,y) (-(-((x)+1)/(y))-1) @@ -106,33 +106,43 @@ union DateTimeData #define get_d1(x)\ union DateData *dat;\ - Data_Get_Struct(x, union DateData, dat) + Data_Get_Struct(x, union DateData, dat);\ + assert(date_obj_p(dat)) #define get_d2(x,y)\ union DateData *adat, *bdat;\ Data_Get_Struct(x, union DateData, adat);\ - Data_Get_Struct(y, union DateData, bdat) + Data_Get_Struct(y, union DateData, bdat);\ + assert(date_obj_p(adat));\ + assert(date_obj_p(bdat)) #define get_d1_dt1(x,y)\ union DateData *adat;\ union DateTimeData *bdat;\ Data_Get_Struct(x, union DateData, adat);\ - Data_Get_Struct(y, union DateTimeData, bdat) + Data_Get_Struct(y, union DateTimeData, bdat)\ + assert(date_obj_p(adat));\ + assert(datetime_obj_p(bdat)) #define get_dt1(x)\ union DateTimeData *dat;\ - Data_Get_Struct(x, union DateTimeData, dat) + Data_Get_Struct(x, union DateTimeData, dat);\ + assert(datetime_obj_p(dat)) #define get_dt2(x,y)\ union DateTimeData *adat, *bdat;\ Data_Get_Struct(x, union DateTimeData, adat);\ - Data_Get_Struct(y, union DateTimeData, bdat) + Data_Get_Struct(y, union DateTimeData, bdat);\ + assert(datetime_obj_p(adat));\ + assert(datetime_obj_p(bdat)) #define get_dt1_d1(x,y)\ union DateTimeData *adat;\ union DateData *bdat;\ Data_Get_Struct(x, union DateTimeData, adat);\ - Data_Get_Struct(y, union DateData, bdat) + Data_Get_Struct(y, union DateData, bdat);\ + assert(datetime_obj_p(adat));\ + assert(date_obj_p(bdat)) #define get_dt2_cast(x,y)\ union DateData *atmp, *btmp;\ @@ -152,7 +162,7 @@ union DateTimeData abuf.l.hour = 0;\ abuf.l.min = 0;\ abuf.l.sec = 0;\ - abuf.flags = HAVE_DF | HAVE_TIME | atmp->l.flags;\ + abuf.flags = HAVE_DF | HAVE_TIME | DATETIME_OBJ | atmp->l.flags;\ adat = &abuf;\ }\ if (k_datetime_p(y))\ @@ -170,29 +180,614 @@ union DateTimeData bbuf.l.hour = 0;\ bbuf.l.min = 0;\ bbuf.l.sec = 0;\ - bbuf.flags = HAVE_DF | HAVE_TIME | btmp->l.flags;\ + bbuf.flags = HAVE_DF | HAVE_TIME | DATETIME_OBJ | btmp->l.flags;\ bdat = &bbuf;\ } +#define f_abs(x) rb_funcall(x, rb_intern("abs"), 0) +#define f_negate(x) rb_funcall(x, rb_intern("-@"), 0) #define f_add(x,y) rb_funcall(x, '+', 1, y) #define f_sub(x,y) rb_funcall(x, '-', 1, y) #define f_mul(x,y) rb_funcall(x, '*', 1, y) #define f_div(x,y) rb_funcall(x, '/', 1, y) - -#define forward0(k,m) rb_funcall(k, rb_intern(m), 0) -#define cforward0(m) forward0(klass, m) -#define iforward0(m) forward0(self, m) -#define forward1(k,m,a) rb_funcall(k, rb_intern(m), 1, a) -#define iforward1(m,a) forward1(self, m, a) -#define forwardv(k,m) rb_funcall2(k, rb_intern(m), argc, argv) -#define cforwardv(m) forwardv(klass, m) -#define iforwardv(m) forwardv(self, m) -#define forwardop(k,m) rb_funcall(k, rb_intern(m), 1, other) -#define iforwardop(m) forwardop(self, m) +#define f_idiv(x,y) rb_funcall(x, rb_intern("div"), 1, y) +#define f_mod(x,y) rb_funcall(x, '%', 1, y) +#define f_remainder(x,y) rb_funcall(x, rb_intern("remainder"), 1, y) +#define f_expt(x,y) rb_funcall(x, rb_intern("**"), 1, y) +#define f_floor(x) rb_funcall(x, rb_intern("floor"), 0) +#define f_ceil(x) rb_funcall(x, rb_intern("ceil"), 0) +#define f_truncate(x) rb_funcall(x, rb_intern("truncate"), 0) +#define f_round(x) rb_funcall(x, rb_intern("round"), 0) + +#define f_add3(x,y,z) f_add(f_add(x, y), z) +#define f_sub3(x,y,z) f_sub(f_sub(x, y), z) + +#define f_cmp(x,y) rb_funcall(x, rb_intern("<=>"), 1, y) +#define f_lt_p(x,y) rb_funcall(x, '<', 1, y) +#define f_gt_p(x,y) rb_funcall(x, '>', 1, y) +#define f_le_p(x,y) rb_funcall(x, rb_intern("<="), 1, y) +#define f_ge_p(x,y) rb_funcall(x, rb_intern(">="), 1, y) + +#define f_eqeq_p(x,y) rb_funcall(x, rb_intern("=="), 1, y) +#define f_equal_p(x,y) rb_funcall(x, rb_intern("==="), 1, y) +#define f_zero_p(x) rb_funcall(x, rb_intern("zero?"), 0) +#define f_negative_p(x) f_lt_p(x, INT2FIX(0)) +#define f_positive_p(x) (!f_negative_p(x)) + +#define f_compact(x) rb_funcall(x, rb_intern("compact"), 0) + +#define f_ajd(x) rb_funcall(x, rb_intern("ajd"), 0) +#define f_jd(x) rb_funcall(x, rb_intern("jd"), 0) +#define f_year(x) rb_funcall(x, rb_intern("year"), 0) +#define f_mon(x) rb_funcall(x, rb_intern("mon"), 0) +#define f_mday(x) rb_funcall(x, rb_intern("mday"), 0) +#define f_wday(x) rb_funcall(x, rb_intern("wday"), 0) +#define f_hour(x) rb_funcall(x, rb_intern("hour"), 0) +#define f_min(x) rb_funcall(x, rb_intern("min"), 0) +#define f_sec(x) rb_funcall(x, rb_intern("sec"), 0) +#define f_start(x) rb_funcall(x, rb_intern("start"), 0) static VALUE cDate, cDateTime; static VALUE rzero, rhalf, day_in_nanoseconds; +/* right base */ + +#define HALF_DAYS_IN_DAY rb_rational_new2(INT2FIX(1), INT2FIX(2)) +#define HOURS_IN_DAY rb_rational_new2(INT2FIX(1), INT2FIX(24)) +#define MINUTES_IN_DAY rb_rational_new2(INT2FIX(1), INT2FIX(1440)) +#define SECONDS_IN_DAY rb_rational_new2(INT2FIX(1), INT2FIX(86400)) +#define MILLISECONDS_IN_DAY\ + rb_rational_new2(INT2FIX(1), f_mul(INT2FIX(86400), INT2FIX(1000))) +#define NANOSECONDS_IN_DAY\ + rb_rational_new2(INT2FIX(1), f_mul(INT2FIX(86400), INT2FIX(1000000000))) +#define MILLISECONDS_IN_SECOND rb_rational_new2(INT2FIX(1), INT2FIX(1000)) +#define NANOSECONDS_IN_SECOND rb_rational_new2(INT2FIX(1), INT2FIX(1000000000)) + +#define MJD_EPOCH_IN_AJD\ + rb_rational_new2(INT2FIX(4800001), INT2FIX(2)) /* 1858-11-17 */ +#define UNIX_EPOCH_IN_AJD\ + rb_rational_new2(INT2FIX(4881175), INT2FIX(2)) /* 1970-01-01 */ +#define MJD_EPOCH_IN_CJD INT2FIX(2400001) +#define UNIX_EPOCH_IN_CJD INT2FIX(2440588) +#define LD_EPOCH_IN_CJD INT2FIX(2299160) + +static VALUE rt__valid_civil_p(VALUE, VALUE, VALUE, VALUE); + +static VALUE +rt_find_fdoy(VALUE y, VALUE sg) +{ + int d; + + for (d = 1; d < 31; d++) { + VALUE j = rt__valid_civil_p(y, INT2FIX(1), INT2FIX(d), sg); + if (!NIL_P(j)) + return j; + } + return Qnil; +} + +static VALUE +rt_find_ldoy(VALUE y, VALUE sg) +{ + int i; + + for (i = 0; i < 30; i++) { + VALUE j = rt__valid_civil_p(y, INT2FIX(12), INT2FIX(31 - i), sg); + if (!NIL_P(j)) + return j; + } + return Qnil; +} + +#ifndef NDEBUG +static VALUE +rt_find_fdom(VALUE y, VALUE m, VALUE sg) +{ + int d; + + for (d = 1; d < 31; d++) { + VALUE j = rt__valid_civil_p(y, m, INT2FIX(d), sg); + if (!NIL_P(j)) + return j; + } + return Qnil; +} +#endif + +static VALUE +rt_find_ldom(VALUE y, VALUE m, VALUE sg) +{ + int i; + + for (i = 0; i < 30; i++) { + VALUE j = rt__valid_civil_p(y, m, INT2FIX(31 - i), sg); + if (!NIL_P(j)) + return j; + } + return Qnil; +} + +static VALUE +rt_ordinal_to_jd(VALUE y, VALUE d, VALUE sg) +{ + return f_sub(f_add(rt_find_fdoy(y, sg), d), INT2FIX(1)); +} + +static VALUE rt_jd_to_civil(VALUE jd, VALUE sg); + +static VALUE +rt_jd_to_ordinal(VALUE jd, VALUE sg) +{ + VALUE a, y, j, doy; + + a = rt_jd_to_civil(jd, sg); + y = RARRAY_PTR(a)[0]; + j = rt_find_fdoy(y, sg); + doy = f_add(f_sub(jd, j), INT2FIX(1)); + return rb_assoc_new(y, doy); +} + +static VALUE +rt_civil_to_jd(VALUE y, VALUE m, VALUE d, VALUE sg) +{ + VALUE a, b, jd; + + if (f_le_p(m, INT2FIX(2))) { + y = f_sub(y, INT2FIX(1)); + m = f_add(m, INT2FIX(12)); + } + a = f_floor(f_div(y, DBL2NUM(100.0))); + b = f_add(f_sub(INT2FIX(2), a), f_floor(f_div(a, DBL2NUM(4.0)))); + jd = f_add3(f_floor(f_mul(DBL2NUM(365.25), f_add(y, INT2FIX(4716)))), + f_floor(f_mul(DBL2NUM(30.6001), f_add(m, INT2FIX(1)))), + f_sub(f_add(d, b), INT2FIX(1524))); + if (f_lt_p(jd, sg)) + jd = f_sub(jd, b); + return jd; +} + +static VALUE +rt_jd_to_civil(VALUE jd, VALUE sg) +{ + VALUE a, x, b, c, d, e, dom, m, y; + + if (f_lt_p(jd, sg)) + a = jd; + else { + x = f_floor(f_div(f_sub(jd, DBL2NUM(1867216.25)), DBL2NUM(36524.25))); + a = f_sub(f_add3(jd, INT2FIX(1), x), f_floor(f_div(x, DBL2NUM(4.0)))); + } + b = f_add(a, INT2FIX(1524)); + c = f_floor(f_div(f_sub(b, DBL2NUM(122.1)), DBL2NUM(365.25))); + d = f_floor(f_mul(DBL2NUM(365.25), c)); + e = f_floor(f_div(f_sub(b, d), DBL2NUM(30.6001))); + dom = f_sub3(b, d, f_floor(f_mul(DBL2NUM(30.6001), e))); + if (f_le_p(e, INT2FIX(13))) { + m = f_sub(e, INT2FIX(1)); + y = f_sub(c, INT2FIX(4716)); + } + else { + m = f_sub(e, INT2FIX(13)); + y = f_sub(c, INT2FIX(4715)); + } + return rb_ary_new3(3, y, m, dom); +} + +static VALUE +rt_commercial_to_jd(VALUE y, VALUE w, VALUE d, VALUE sg) +{ + VALUE j; + + j = f_add(rt_find_fdoy(y, sg), INT2FIX(3)); + return f_add3(f_sub(j, f_mod(j, INT2FIX(7))), + f_mul(INT2FIX(7), f_sub(w, INT2FIX(1))), + f_sub(d, INT2FIX(1))); +} + +static VALUE +rt_jd_to_commercial(VALUE jd, VALUE sg) +{ + VALUE t, a, j, y, w, d; + + t = rt_jd_to_civil(f_sub(jd, INT2FIX(3)), sg); + a = RARRAY_PTR(t)[0]; + j = rt_commercial_to_jd(f_add(a, INT2FIX(1)), INT2FIX(1), INT2FIX(1), sg); + if (f_ge_p(jd, j)) + y = f_add(a, INT2FIX(1)); + else { + j = rt_commercial_to_jd(a, INT2FIX(1), INT2FIX(1), sg); + y = a; + } + w = f_add(INT2FIX(1), f_idiv(f_sub(jd, j), INT2FIX(7))); + d = f_mod(f_add(jd, INT2FIX(1)), INT2FIX(7)); + if (f_zero_p(d)) + d = INT2FIX(7); + return rb_ary_new3(3, y, w, d); +} + +static VALUE +rt_weeknum_to_jd(VALUE y, VALUE w, VALUE d, VALUE f, VALUE sg) +{ + VALUE a; + + a = f_add(rt_find_fdoy(y, sg), INT2FIX(6)); + return f_add3(f_sub3(a, + f_mod(f_add(f_sub(a, f), INT2FIX(1)), INT2FIX(7)), + INT2FIX(7)), + f_mul(INT2FIX(7), w), + d); +} + +static VALUE +rt_jd_to_weeknum(VALUE jd, VALUE f, VALUE sg) +{ + VALUE t, y, d, a, w; + + t = rt_jd_to_civil(jd, sg); + y = RARRAY_PTR(t)[0]; + d = RARRAY_PTR(t)[2]; + a = f_add(rt_find_fdoy(y, sg), INT2FIX(6)); + t = f_add(f_sub(jd, + f_sub(a, f_mod(f_add(f_sub(a, f), INT2FIX(1)), + INT2FIX(7)))), + INT2FIX(7)); + w = f_idiv(t, INT2FIX(7)); + d = f_mod(t, INT2FIX(7)); + return rb_ary_new3(3, y, w, d); +} + +#ifndef NDEBUG +static VALUE +rt_nth_kday_to_jd(VALUE y, VALUE m, VALUE n, VALUE k, VALUE sg) +{ + VALUE j; + + if (f_gt_p(n, INT2FIX(0))) + j = f_sub(rt_find_fdom(y, m, sg), INT2FIX(1)); + else + j = f_add(rt_find_ldom(y, m, sg), INT2FIX(7)); + return f_add(f_sub(j, + f_mod(f_add(f_sub(j, k), INT2FIX(1)), INT2FIX(7))), + f_mul(INT2FIX(7), n)); +} + +static VALUE rt_jd_to_wday(VALUE); + +static VALUE +rt_jd_to_nth_kday(VALUE jd, VALUE sg) +{ + VALUE t, y, m, n, k, j; + + t = rt_jd_to_civil(jd, sg); + y = RARRAY_PTR(t)[0]; + m = RARRAY_PTR(t)[1]; + j = rt_find_fdom(y, m, sg); + n = f_add(f_idiv(f_sub(jd, j), INT2FIX(7)), INT2FIX(1)); + k = rt_jd_to_wday(jd); + return rb_ary_new3(4, y, m, n, k); +} +#endif + +static VALUE +rt_ajd_to_jd(VALUE ajd, VALUE of) +{ + VALUE t, jd, fr; + + t = f_add3(ajd, of, HALF_DAYS_IN_DAY); + jd = f_idiv(t, INT2FIX(1)); + fr = f_mod(t, INT2FIX(1)); + return rb_assoc_new(jd, fr); +} + +static VALUE +rt_jd_to_ajd(VALUE jd, VALUE fr, VALUE of) +{ + return f_sub3(f_add(jd, fr), of, HALF_DAYS_IN_DAY); +} + +#ifndef NDEBUG +static VALUE +rt_day_fraction_to_time(VALUE fr) +{ + VALUE h, min, s, ss; + + ss = f_idiv(fr, SECONDS_IN_DAY); + fr = f_mod(fr, SECONDS_IN_DAY); + + h = f_idiv(ss, INT2FIX(HOUR_IN_SECONDS)); + ss = f_mod(ss, INT2FIX(HOUR_IN_SECONDS)); + + min = f_idiv(ss, INT2FIX(MINUTE_IN_SECONDS)); + s = f_mod(ss, INT2FIX(MINUTE_IN_SECONDS)); + + return rb_ary_new3(4, h, min, s, f_mul(fr, INT2FIX(DAY_IN_SECONDS))); +} +#endif + +static VALUE +rt_day_fraction_to_time_wo_sf(VALUE fr) +{ + VALUE h, min, s, ss; + + ss = f_idiv(fr, SECONDS_IN_DAY); + + h = f_idiv(ss, INT2FIX(HOUR_IN_SECONDS)); + ss = f_mod(ss, INT2FIX(HOUR_IN_SECONDS)); + + min = f_idiv(ss, INT2FIX(MINUTE_IN_SECONDS)); + s = f_mod(ss, INT2FIX(MINUTE_IN_SECONDS)); + + return rb_ary_new3(3, h, min, s); +} + +static VALUE +rt_time_to_day_fraction(VALUE h, VALUE min, VALUE s) +{ + return rb_Rational(f_add3(f_mul(h, INT2FIX(HOUR_IN_SECONDS)), + f_mul(min, INT2FIX(MINUTE_IN_SECONDS)), + s), + INT2FIX(DAY_IN_SECONDS)); +} + +#ifndef NDEBUG +static VALUE +rt_amjd_to_ajd(VALUE amjd) +{ + return f_add(amjd, MJD_EPOCH_IN_AJD); +} +#endif + +static VALUE +rt_ajd_to_amjd(VALUE ajd) +{ + return f_sub(ajd, MJD_EPOCH_IN_AJD); +} + +#ifndef NDEBUG +static VALUE +rt_mjd_to_jd(VALUE mjd) +{ + return f_add(mjd, MJD_EPOCH_IN_CJD); +} +#endif + +static VALUE +rt_jd_to_mjd(VALUE jd) +{ + return f_sub(jd, MJD_EPOCH_IN_CJD); +} + +#ifndef NDEBUG +static VALUE +rt_ld_to_jd(VALUE ld) +{ + return f_add(ld, LD_EPOCH_IN_CJD); +} +#endif + +static VALUE +rt_jd_to_ld(VALUE jd) +{ + return f_sub(jd, LD_EPOCH_IN_CJD); +} + +static VALUE +rt_jd_to_wday(VALUE jd) +{ + return f_mod(f_add(jd, INT2FIX(1)), INT2FIX(7)); +} + +static VALUE +rt__valid_jd_p(VALUE jd, VALUE sg) +{ + return jd; +} + +static VALUE +rt__valid_ordinal_p(VALUE y, VALUE d, VALUE sg) +{ + VALUE jd, t, ny, nd; + + if (f_negative_p(d)) { + VALUE j; + + j = rt_find_ldoy(y, sg); + if (NIL_P(j)) + return Qnil; + t = rt_jd_to_ordinal(f_add3(j, d, INT2FIX(1)), sg); + ny = RARRAY_PTR(t)[0]; + nd = RARRAY_PTR(t)[1]; + if (!f_eqeq_p(ny, y)) + return Qnil; + d = nd; + } + jd = rt_ordinal_to_jd(y, d, sg); + t = rt_jd_to_ordinal(jd, sg); + ny = RARRAY_PTR(t)[0]; + nd = RARRAY_PTR(t)[1]; + if (!f_eqeq_p(y, ny)) + return Qnil; + if (!f_eqeq_p(d, nd)) + return Qnil; + return jd; +} + +static VALUE +rt__valid_civil_p(VALUE y, VALUE m, VALUE d, VALUE sg) +{ + VALUE t, ny, nm, nd, jd; + + if (f_negative_p(m)) + m = f_add(m, INT2FIX(13)); + if (f_negative_p(d)) { + VALUE j; + + j = rt_find_ldom(y, m, sg); + if (NIL_P(j)) + return Qnil; + t = rt_jd_to_civil(f_add3(j, d, INT2FIX(1)), sg); + ny = RARRAY_PTR(t)[0]; + nm = RARRAY_PTR(t)[1]; + nd = RARRAY_PTR(t)[2]; + if (!f_eqeq_p(ny, y)) + return Qnil; + if (!f_eqeq_p(nm, m)) + return Qnil; + d = nd; + } + jd = rt_civil_to_jd(y, m, d, sg); + t = rt_jd_to_civil(jd, sg); + ny = RARRAY_PTR(t)[0]; + nm = RARRAY_PTR(t)[1]; + nd = RARRAY_PTR(t)[2]; + if (!f_eqeq_p(y, ny)) + return Qnil; + if (!f_eqeq_p(m, nm)) + return Qnil; + if (!f_eqeq_p(d, nd)) + return Qnil; + return jd; +} + +static VALUE +rt__valid_commercial_p(VALUE y, VALUE w, VALUE d, VALUE sg) +{ + VALUE t, ny, nw, nd, jd; + + if (f_negative_p(d)) + d = f_add(d, INT2FIX(8)); + if (f_negative_p(w)) { + VALUE j; + + j = rt_commercial_to_jd(f_add(y, INT2FIX(1)), + INT2FIX(1), INT2FIX(1), sg); + t = rt_jd_to_commercial(f_add(j, f_mul(w, INT2FIX(7))), sg); + ny = RARRAY_PTR(t)[0]; + nw = RARRAY_PTR(t)[1]; + if (!f_eqeq_p(ny, y)) + return Qnil; + w = nw; + } + jd = rt_commercial_to_jd(y, w, d, sg); + t = rt_jd_to_commercial(jd, sg); + ny = RARRAY_PTR(t)[0]; + nw = RARRAY_PTR(t)[1]; + nd = RARRAY_PTR(t)[2]; + if (!f_eqeq_p(ny, y)) + return Qnil; + if (!f_eqeq_p(nw, w)) + return Qnil; + if (!f_eqeq_p(nd, d)) + return Qnil; + return jd; +} + +static VALUE +rt__valid_weeknum_p(VALUE y, VALUE w, VALUE d, VALUE f, VALUE sg) +{ + VALUE t, ny, nw, nd, jd; + + if (f_negative_p(d)) + d = f_add(d, INT2FIX(7)); + if (f_negative_p(w)) { + VALUE j; + + j = rt_weeknum_to_jd(f_add(y, INT2FIX(1)), + INT2FIX(1), f, f, sg); + t = rt_jd_to_weeknum(f_add(j, f_mul(w, INT2FIX(7))), f, sg); + ny = RARRAY_PTR(t)[0]; + nw = RARRAY_PTR(t)[1]; + if (!f_eqeq_p(ny, y)) + return Qnil; + w = nw; + } + jd = rt_weeknum_to_jd(y, w, d, f, sg); + t = rt_jd_to_weeknum(jd, f, sg); + ny = RARRAY_PTR(t)[0]; + nw = RARRAY_PTR(t)[1]; + nd = RARRAY_PTR(t)[2]; + if (!f_eqeq_p(ny, y)) + return Qnil; + if (!f_eqeq_p(nw, w)) + return Qnil; + if (!f_eqeq_p(nd, d)) + return Qnil; + return jd; +} + +#ifndef NDEBUG +static VALUE +rt__valid_nth_kday_p(VALUE y, VALUE m, VALUE n, VALUE k, VALUE sg) +{ + VALUE t, ny, nm, nn, nk, jd; + + if (f_negative_p(k)) + k = f_add(k, INT2FIX(7)); + if (f_negative_p(n)) { + VALUE j; + + t = f_add(f_mul(y, INT2FIX(12)), m); + ny = f_idiv(t, INT2FIX(12)); + nm = f_mod(t, INT2FIX(12)); + nm = f_floor(f_add(nm, INT2FIX(1))); + + j = rt_nth_kday_to_jd(ny, nm, INT2FIX(1), k, sg); + t = rt_jd_to_nth_kday(f_add(j, f_mul(n, INT2FIX(7))), sg); + ny = RARRAY_PTR(t)[0]; + nm = RARRAY_PTR(t)[1]; + nn = RARRAY_PTR(t)[2]; + if (!f_eqeq_p(ny, y)) + return Qnil; + if (!f_eqeq_p(nm, m)) + return Qnil; + n = nn; + } + jd = rt_nth_kday_to_jd(y, m, n, k, sg); + t = rt_jd_to_nth_kday(jd, sg); + ny = RARRAY_PTR(t)[0]; + nm = RARRAY_PTR(t)[1]; + nn = RARRAY_PTR(t)[2]; + nk = RARRAY_PTR(t)[3]; + if (!f_eqeq_p(ny, y)) + return Qnil; + if (!f_eqeq_p(nm, m)) + return Qnil; + if (!f_eqeq_p(nn, n)) + return Qnil; + if (!f_eqeq_p(nk, k)) + return Qnil; + return jd; +} +#endif + +static VALUE +rt__valid_time_p(VALUE h, VALUE min, VALUE s) +{ + if (f_negative_p(h)) + h = f_add(h, INT2FIX(24)); + if (f_negative_p(min)) + min = f_add(min, INT2FIX(MINUTE_IN_SECONDS)); + if (f_negative_p(s)) + s = f_add(s, INT2FIX(MINUTE_IN_SECONDS)); + if (f_eqeq_p(h, INT2FIX(24))) { + if (!f_eqeq_p(min, INT2FIX(0))) + return Qnil; + if (!f_eqeq_p(s, INT2FIX(0))) + return Qnil; + } + else { + if (f_lt_p(h, INT2FIX(0)) || f_ge_p(h, INT2FIX(24))) + return Qnil; + if (f_lt_p(min, INT2FIX(0)) || f_ge_p(min, INT2FIX(60))) + return Qnil; + if (f_lt_p(s, INT2FIX(0)) || f_ge_p(s, INT2FIX(60))) + return Qnil; + } + return rt_time_to_day_fraction(h, min, s); +} + +/* light base */ + static int valid_civil_p(int y, int m, int d, double sg, int *rm, int *rd, long *rjd, int *ns); @@ -523,6 +1118,61 @@ valid_commercial_p(int y, int w, int d, double sg, return 1; } +#ifndef NDEBUG +static int +valid_weeknum_p(int y, int w, int d, int f, double sg, + int *rw, int *rd, long *rjd, int *ns) +{ + int ns2, ry2, rw2, rd2; + + if (d < 0) + d += 7; + if (w < 0) { + long rjd2; + + weeknum_to_jd(y + 1, 1, f, f, sg, &rjd2, &ns2); + jd_to_weeknum(rjd2 + w * 7, f, sg, &ry2, &rw2, &rd2); + if (ry2 != y) + return 0; + w = rw2; + } + weeknum_to_jd(y, w, d, f, sg, rjd, ns); + jd_to_weeknum(*rjd, f, sg, &ry2, rw, rd); + if (y != ry2 || w != *rw || d != *rd) + return 0; + return 1; +} + +static int +valid_nth_kday_p(int y, int m, int n, int k, double sg, + int *rm, int *rn, int *rk, long *rjd, int *ns) +{ + int ns2, ry2, rm2, rn2, rk2; + + if (k < 0) + k += 7; + if (n < 0) { + long rjd2; + int t, ny, nm; + + t = y * 12 + m; + ny = DIV(t, 12); + nm = MOD(t, 12) + 1; + + nth_kday_to_jd(ny, nm, 1, k, sg, &rjd2, &ns2); + jd_to_nth_kday(rjd2 + n * 7, sg, &ry2, &rm2, &rn2, &rk2); + if (ry2 != y || rm2 != m) + return 0; + n = rn2; + } + nth_kday_to_jd(y, m, n, k, sg, rjd, ns); + jd_to_nth_kday(*rjd, sg, &ry2, rm, rn, rk); + if (y != ry2 || m != *rm || n != *rn || k != *rk) + return 0; + return 1; +} +#endif + static int valid_time_p(int h, int min, int s, int *rh, int *rmin, int *rs) { @@ -588,7 +1238,7 @@ jd_utc_to_local(long jd, int df, int of) inline static int time_to_df(int h, int min, int s) { - return h * 3600 + min * 60 + s; + return h * HOUR_IN_SECONDS + min * MINUTE_IN_SECONDS + s; } inline static int @@ -597,11 +1247,7 @@ jd_to_wday(long jd) return (int)MOD(jd + 1, 7); } -VALUE -date_zone_to_diff(VALUE s) -{ - return rb_funcall(cDate, rb_intern("zone_to_diff"), 1, s); -} +VALUE date_zone_to_diff(VALUE); static int daydiff_to_sec(VALUE vof, int *rof) @@ -714,10 +1360,10 @@ get_dt_time(union DateTimeData *x) assert(have_df_p(x)); r = df_utc_to_local(x->l.df, x->l.of); - x->l.hour = r / 3600; - r %= 3600; - x->l.min = r / 60; - x->l.sec = r % 60; + x->l.hour = r / HOUR_IN_SECONDS; + r %= HOUR_IN_SECONDS; + x->l.min = r / MINUTE_IN_SECONDS; + x->l.sec = r % MINUTE_IN_SECONDS; x->l.flags |= HAVE_TIME; } } @@ -799,10 +1445,19 @@ k_numeric_p(VALUE x) return f_kind_of_p(x, rb_cNumeric); } +/* date light */ + +static VALUE +rtv__valid_jd_p(int argc, VALUE *argv) +{ + assert(argc == 2); + return rt__valid_jd_p(argv[0], argv[1]); +} + static VALUE valid_jd_sub(int argc, VALUE *argv, VALUE klass) { - return argv[0]; + return rtv__valid_jd_p(argc, argv); } #ifndef NDEBUG @@ -824,6 +1479,13 @@ date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass) } #endif +/* + * call-seq: + * Date.valid_jd?(jd[, start=Date::ITALY]) + * + * Is +jd+ a valid Julian Day Number? + * Returns true or false. + */ static VALUE date_s_valid_jd_p(int argc, VALUE *argv, VALUE klass) { @@ -844,19 +1506,30 @@ date_s_valid_jd_p(int argc, VALUE *argv, VALUE klass) } static VALUE +rtv__valid_civil_p(int argc, VALUE *argv) +{ + assert(argc == 4); + return rt__valid_civil_p(argv[0], argv[1], argv[2], argv[3]); +} + +static VALUE valid_civil_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { int y, m, d, rm, rd; double sg; +#ifdef FORCE_RIGHT + return rtv__valid_civil_p(argc, argv); +#endif + if (!(FIXNUM_P(argv[0]) && FIXNUM_P(argv[1]) && FIXNUM_P(argv[2]))) - return cforwardv("_valid_civil_r?"); + return rtv__valid_civil_p(argc, argv); y = NUM2INT(argv[0]); if (!LIGHTABLE_YEAR(y)) - return cforwardv("_valid_civil_r?"); + return rtv__valid_civil_p(argc, argv); m = NUM2INT(argv[1]); d = NUM2INT(argv[2]); @@ -898,6 +1571,22 @@ date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass) } #endif +/* + * call-seq: + * Date.valid_civil?(year, month, mday[, start=Date::ITALY]) + * + * Do year +y+, month +m+, and day-of-month +d+ make a + * valid Civil Date? Returns true or false. + * + * +m+ and +d+ can be negative, in which case they count + * backwards from the end of the year and the end of the + * month respectively. No wraparound is performed, however, + * and invalid values cause an ArgumentError to be raised. + * A date falling in the period skipped in the Day of Calendar + * Reform adjustment is not valid. + * + * +sg+ specifies the Day of Calendar Reform. + */ static VALUE date_s_valid_civil_p(int argc, VALUE *argv, VALUE klass) { @@ -920,18 +1609,29 @@ date_s_valid_civil_p(int argc, VALUE *argv, VALUE klass) } static VALUE +rtv__valid_ordinal_p(int argc, VALUE *argv) +{ + assert(argc == 3); + return rt__valid_ordinal_p(argv[0], argv[1], argv[2]); +} + +static VALUE valid_ordinal_sub(int argc, VALUE *argv, VALUE klass) { int y, d, rd; double sg; +#ifdef FORCE_RIGHT + return rtv__valid_ordinal_p(argc, argv); +#endif + if (!(FIXNUM_P(argv[0]) && FIXNUM_P(argv[1]))) - return cforwardv("_valid_ordinal_r?"); + return rtv__valid_ordinal_p(argc, argv); y = NUM2INT(argv[0]); if (!LIGHTABLE_YEAR(y)) - return cforwardv("_valid_ordinal_r?"); + return rtv__valid_ordinal_p(argc, argv); d = NUM2INT(argv[1]); sg = NUM2DBL(argv[2]); @@ -966,6 +1666,23 @@ date_s__valid_ordinal_p(int argc, VALUE *argv, VALUE klass) } #endif +/* + * call-seq: + * Date.valid_ordinal?(year, yday[, start=Date::ITALY]) + * + * Do the year +y+ and day-of-year +d+ make a valid Ordinal Date? + * Returns true or false. + * + * +d+ can be a negative number, in which case it counts backwards + * from the end of the year (-1 being the last day of the year). + * No year wraparound is performed, however, so valid values of + * +d+ are -365 .. -1, 1 .. 365 on a non-leap-year, + * -366 .. -1, 1 .. 366 on a leap year. + * A date falling in the period skipped in the Day of Calendar Reform + * adjustment is not valid. + * + * +sg+ specifies the Day of Calendar Reform. + */ static VALUE date_s_valid_ordinal_p(int argc, VALUE *argv, VALUE klass) { @@ -987,19 +1704,30 @@ date_s_valid_ordinal_p(int argc, VALUE *argv, VALUE klass) } static VALUE +rtv__valid_commercial_p(int argc, VALUE *argv) +{ + assert(argc == 4); + return rt__valid_commercial_p(argv[0], argv[1], argv[2], argv[3]); +} + +static VALUE valid_commercial_sub(int argc, VALUE *argv, VALUE klass) { int y, w, d, rw, rd; double sg; +#ifdef FORCE_RIGHT + return rtv__valid_commercial_p(argc, argv); +#endif + if (!(FIXNUM_P(argv[0]) && FIXNUM_P(argv[1]) && FIXNUM_P(argv[2]))) - return cforwardv("_valid_commercial_r?"); + return rtv__valid_commercial_p(argc, argv); y = NUM2INT(argv[0]); if (!LIGHTABLE_CWYEAR(y)) - return cforwardv("_valid_commercial_r?"); + return rtv__valid_commercial_p(argc, argv); w = NUM2INT(argv[1]); d = NUM2INT(argv[2]); @@ -1036,6 +1764,24 @@ date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass) } #endif +/* + * call-seq: + * Date.valid_commercial?(year, week, day[, start=Date::ITALY]) + * + * Do year +y+, week-of-year +w+, and day-of-week +d+ make a + * valid Commercial Date? Returns true or false. + * + * Monday is day-of-week 1; Sunday is day-of-week 7. + * + * +w+ and +d+ can be negative, in which case they count + * backwards from the end of the year and the end of the + * week respectively. No wraparound is performed, however, + * and invalid values cause an ArgumentError to be raised. + * A date falling in the period skipped in the Day of Calendar + * Reform adjustment is not valid. + * + * +sg+ specifies the Day of Calendar Reform. + */ static VALUE date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass) { @@ -1057,25 +1803,233 @@ date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass) return Qtrue; } +#ifndef NDEBUG +static VALUE +rtv__valid_weeknum_p(int argc, VALUE *argv) +{ + assert(argc == 5); + return rt__valid_weeknum_p(argv[0], argv[1], argv[2], argv[3], argv[4]); +} + +static VALUE +valid_weeknum_sub(int argc, VALUE *argv, VALUE klass) +{ + int y, w, d, f, rw, rd; + double sg; + +#ifdef FORCE_RIGHT + return rtv__valid_weeknum_p(argc, argv); +#endif + + if (!(FIXNUM_P(argv[0]) && + FIXNUM_P(argv[1]) && + FIXNUM_P(argv[2]) && + FIXNUM_P(argv[3]))) + return rtv__valid_weeknum_p(argc, argv); + + y = NUM2INT(argv[0]); + if (!LIGHTABLE_YEAR(y)) + return rtv__valid_weeknum_p(argc, argv); + + w = NUM2INT(argv[1]); + d = NUM2INT(argv[2]); + f = NUM2INT(argv[3]); + sg = NUM2DBL(argv[4]); + + { + long jd; + int ns; + + if (!valid_weeknum_p(y, w, d, f, sg, &rw, &rd, &jd, &ns)) + return Qnil; + return LONG2NUM(jd); + } +} + +static VALUE +date_s__valid_weeknum_p(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vw, vd, vf, vsg; + VALUE argv2[5]; + + rb_scan_args(argc, argv, "41", &vy, &vw, &vd, &vf, &vsg); + + argv2[0] = vy; + argv2[1] = vw; + argv2[2] = vd; + argv2[3] = vf; + if (argc < 5) + argv2[4] = DBL2NUM(GREGORIAN); + else + argv2[4] = vsg; + + return valid_weeknum_sub(5, argv2, klass); +} + +static VALUE +date_s_valid_weeknum_p(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vw, vd, vf, vsg; + VALUE argv2[5]; + + rb_scan_args(argc, argv, "41", &vy, &vw, &vd, &vf, &vsg); + + argv2[0] = vy; + argv2[1] = vw; + argv2[2] = vd; + argv2[3] = vf; + if (argc < 5) + argv2[4] = INT2FIX(ITALY); + else + argv2[4] = vsg; + + if (NIL_P(valid_weeknum_sub(5, argv2, klass))) + return Qfalse; + return Qtrue; +} + +static VALUE +rtv__valid_nth_kday_p(int argc, VALUE *argv) +{ + assert(argc == 5); + return rt__valid_nth_kday_p(argv[0], argv[1], argv[2], argv[3], argv[4]); +} +static VALUE +valid_nth_kday_sub(int argc, VALUE *argv, VALUE klass) +{ + int y, m, n, k, rm, rn, rk; + double sg; + +#ifdef FORCE_RIGHT + return rtv__valid_nth_kday_p(argc, argv); +#endif + + if (!(FIXNUM_P(argv[0]) && + FIXNUM_P(argv[1]) && + FIXNUM_P(argv[2]) && + FIXNUM_P(argv[3]))) + return rtv__valid_nth_kday_p(argc, argv); + + y = NUM2INT(argv[0]); + if (!LIGHTABLE_YEAR(y)) + return rtv__valid_nth_kday_p(argc, argv); + + m = NUM2INT(argv[1]); + n = NUM2INT(argv[2]); + k = NUM2INT(argv[3]); + sg = NUM2DBL(argv[4]); + + { + long jd; + int ns; + + if (!valid_nth_kday_p(y, m, n, k, sg, &rm, &rn, &rk, &jd, &ns)) + return Qnil; + return LONG2NUM(jd); + } +} + +static VALUE +date_s__valid_nth_kday_p(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vm, vn, vk, vsg; + VALUE argv2[5]; + + rb_scan_args(argc, argv, "41", &vy, &vm, &vn, &vk, &vsg); + + argv2[0] = vy; + argv2[1] = vm; + argv2[2] = vn; + argv2[3] = vk; + if (argc < 5) + argv2[4] = DBL2NUM(GREGORIAN); + else + argv2[4] = vsg; + + return valid_nth_kday_sub(5, argv2, klass); +} + +static VALUE +date_s_valid_nth_kday_p(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vm, vn, vk, vsg; + VALUE argv2[5]; + + rb_scan_args(argc, argv, "41", &vy, &vm, &vn, &vk, &vsg); + + argv2[0] = vy; + argv2[1] = vm; + argv2[2] = vn; + argv2[3] = vk; + if (argc < 5) + argv2[4] = INT2FIX(ITALY); + else + argv2[4] = vsg; + + if (NIL_P(valid_nth_kday_sub(5, argv2, klass))) + return Qfalse; + return Qtrue; +} + +static VALUE +date_s_zone_to_diff(VALUE klass, VALUE str) +{ + return date_zone_to_diff(str); +} +#endif + +/* + * call-seq: + * Date.julian_leap?(year) + * + * Return true if the given year is a leap year on Julian calendar. + */ +static VALUE +date_s_julian_leap_p(VALUE klass, VALUE y) +{ + if (f_zero_p(f_mod(y, INT2FIX(4)))) + return Qtrue; + return Qfalse; +} + +/* + * call-seq: + * Date.gregorian_leap?(year) + * Date.leap?(year) + * + * Return true if the given year is a leap year on Gregorian calendar. + */ +static VALUE +date_s_gregorian_leap_p(VALUE klass, VALUE y) +{ + if (f_zero_p(f_mod(y, INT2FIX(4))) && + !f_zero_p(f_mod(y, INT2FIX(100))) || + f_zero_p(f_mod(y, INT2FIX(400)))) + return Qtrue; + return Qfalse; +} + static void d_right_gc_mark(union DateData *dat) { - if (!light_mode_p(dat)) { - rb_gc_mark(dat->r.ajd); - rb_gc_mark(dat->r.of); - rb_gc_mark(dat->r.sg); - rb_gc_mark(dat->r.cache); - } + assert(!light_mode_p(dat)); + rb_gc_mark(dat->r.ajd); + rb_gc_mark(dat->r.of); + rb_gc_mark(dat->r.sg); + rb_gc_mark(dat->r.cache); } +#define d_lite_gc_mark 0 + inline static VALUE -d_right_s_new_internal(VALUE klass, VALUE ajd, VALUE of, VALUE sg, - unsigned flags) +d_right_new_internal(VALUE klass, VALUE ajd, VALUE of, VALUE sg, + unsigned flags) { union DateData *dat; VALUE obj; - obj = Data_Make_Struct(klass, union DateData, d_right_gc_mark, -1, dat); + obj = Data_Make_Struct(klass, union DateData, + d_right_gc_mark, -1, dat); dat->r.ajd = ajd; dat->r.of = of; @@ -1087,93 +2041,199 @@ d_right_s_new_internal(VALUE klass, VALUE ajd, VALUE of, VALUE sg, } inline static VALUE -d_lite_s_new_internal(VALUE klass, long jd, double sg, - int y, int m, int d, unsigned flags) +d_lite_new_internal(VALUE klass, long jd, double sg, + int y, int m, int d, unsigned flags) { union DateData *dat; VALUE obj; - obj = Data_Make_Struct(klass, union DateData, d_right_gc_mark, -1, dat); + obj = Data_Make_Struct(klass, union DateData, + d_lite_gc_mark, -1, dat); dat->l.jd = jd; dat->l.sg = sg; dat->l.year = y; dat->l.mon = m; dat->l.mday = d; - dat->l.flags = flags; + dat->l.flags = flags | LIGHT_MODE; return obj; } static VALUE -d_lite_s_new_internal_wo_civil(VALUE klass, long jd, double sg, +d_lite_new_internal_wo_civil(VALUE klass, long jd, double sg, unsigned flags) { - return d_lite_s_new_internal(klass, jd, sg, 0, 0, 0, flags); + return d_lite_new_internal(klass, jd, sg, 0, 0, 0, flags); } static VALUE d_lite_s_alloc(VALUE klass) { - return d_lite_s_new_internal_wo_civil(klass, 0, 0, LIGHT_MODE); + return d_lite_new_internal_wo_civil(klass, 0, 0, 0); +} + +static VALUE +d_right_new(VALUE klass, VALUE ajd, VALUE of, VALUE sg) +{ + return d_right_new_internal(klass, ajd, of, sg, 0); } static VALUE -d_right_s_new_r_bang(int argc, VALUE *argv, VALUE klass) +d_lite_new(VALUE klass, VALUE jd, VALUE sg) { - VALUE vajd, vof, vsg; + return d_lite_new_internal_wo_civil(klass, + NUM2LONG(jd), + NUM2DBL(sg), + HAVE_JD); +} - rb_scan_args(argc, argv, "03", &vajd, &vof, &vsg); +static VALUE +d_switch_new(VALUE klass, VALUE ajd, VALUE of, VALUE sg) +{ + VALUE t, jd, df; - if (argc < 1) - vajd = INT2FIX(0); - if (argc < 2) - vof = INT2FIX(0); - if (argc < 3) - vsg = DBL2NUM(ITALY); + t = rt_ajd_to_jd(ajd, INT2FIX(0)); /* as utc */ + jd = RARRAY_PTR(t)[0]; + df = RARRAY_PTR(t)[1]; + +#ifdef FORCE_RIGHT + if (1) +#else + if (!FIXNUM_P(jd) || + f_lt_p(jd, sg) || !f_zero_p(df) || !f_zero_p(of) || + !LIGHTABLE_JD(NUM2LONG(jd))) +#endif + return d_right_new(klass, ajd, of, sg); + else + return d_lite_new(klass, jd, sg); +} + +#ifndef NDEBUG +static VALUE +d_right_new_m(int argc, VALUE *argv, VALUE klass) +{ + VALUE ajd, of, sg; + + rb_scan_args(argc, argv, "03", &ajd, &of, &sg); + + switch (argc) { + case 0: + ajd = INT2FIX(0); + case 1: + of = INT2FIX(0); + case 2: + sg = INT2FIX(ITALY); + } - return d_right_s_new_internal(klass, vajd, vof, vsg, 0); + return d_right_new(klass, ajd, of, sg); } static VALUE -d_lite_s_new_l_bang(int argc, VALUE *argv, VALUE klass) +d_lite_new_m(int argc, VALUE *argv, VALUE klass) { - VALUE vjd, vsg; - long jd; - double sg; + VALUE jd, sg; - rb_scan_args(argc, argv, "02", &vjd, &vsg); + rb_scan_args(argc, argv, "02", &jd, &sg); - if (argc < 1) - jd = 0; - else { - if (!FIXNUM_P(vjd)) - rb_raise(rb_eArgError, "cannot create"); - jd = NUM2LONG(vjd); - if (!LIGHTABLE_JD(jd)) - rb_raise(rb_eArgError, "cannot create"); + switch (argc) { + case 0: + jd = INT2FIX(0); + case 1: + sg = INT2FIX(ITALY); + } + + return d_lite_new(klass, jd, sg); +} + +static VALUE +d_switch_new_m(int argc, VALUE *argv, VALUE klass) +{ + VALUE ajd, of, sg; + + rb_scan_args(argc, argv, "03", &ajd, &of, &sg); + + switch (argc) { + case 0: + ajd = INT2FIX(0); + case 1: + of = INT2FIX(0); + case 2: + sg = INT2FIX(ITALY); } - if (argc < 2) - sg = 0; - else - sg = NUM2DBL(vsg); - return d_lite_s_new_internal_wo_civil(klass, - jd, - sg, - LIGHT_MODE | HAVE_JD); + return d_switch_new(klass, ajd, of, sg); } +#endif static VALUE +d_right_new_jd(VALUE klass, VALUE jd, VALUE sg) +{ + return d_right_new(klass, + rt_jd_to_ajd(jd, INT2FIX(0), INT2FIX(0)), + INT2FIX(0), + sg); +} + +#ifndef NDEBUG +static VALUE +d_lite_new_jd(VALUE klass, VALUE jd, VALUE sg) +{ + return d_lite_new(klass, jd, sg); +} +#endif + +static VALUE +d_switch_new_jd(VALUE klass, VALUE jd, VALUE sg) +{ + return d_switch_new(klass, + rt_jd_to_ajd(jd, INT2FIX(0), INT2FIX(0)), + INT2FIX(0), + sg); +} + +#ifndef NDEBUG +static VALUE date_s_new_r_bang(int argc, VALUE *argv, VALUE klass) { - return d_right_s_new_r_bang(argc, argv, klass); + return d_right_new_m(argc, argv, klass); } static VALUE date_s_new_l_bang(int argc, VALUE *argv, VALUE klass) { - return d_lite_s_new_l_bang(argc, argv, klass); + return d_lite_new_m(argc, argv, klass); +} + +static VALUE +date_s_new_bang(int argc, VALUE *argv, VALUE klass) +{ + return d_switch_new_m(argc, argv, klass); +} +#endif + +#define c_cforwardv(m) rt_date_s_##m(argc, argv, klass) + +static VALUE +rt_date_s_jd(int argc, VALUE *argv, VALUE klass) +{ + VALUE jd, sg; + + rb_scan_args(argc, argv, "02", &jd, &sg); + + switch (argc) { + case 0: + jd = INT2FIX(0); + case 1: + sg = INT2FIX(ITALY); + } + + jd = rt__valid_jd_p(jd, sg); + + if (NIL_P(jd)) + rb_raise(rb_eArgError, "invalid date"); + + return d_right_new_jd(klass, jd, sg); } /* @@ -1192,28 +2252,55 @@ date_s_jd(int argc, VALUE *argv, VALUE klass) long jd; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(jd); +#endif + rb_scan_args(argc, argv, "02", &vjd, &vsg); if (!FIXNUM_P(vjd)) - return cforwardv("jd_r"); + return c_cforwardv(jd); - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + jd = 0; + sg = ITALY; - if (argc >= 1) { + switch (argc) { + case 2: + sg = NUM2DBL(vsg); + case 1: jd = NUM2LONG(vjd); if (!LIGHTABLE_JD(jd)) - return cforwardv("jd_r"); + return c_cforwardv(jd); } - else - jd = 0; if (jd < sg) - return cforwardv("jd_r"); + return c_cforwardv(jd); + + return d_lite_new_internal_wo_civil(klass, jd, sg, HAVE_JD); +} + +static VALUE +rt_date_s_ordinal(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, d, sg, jd; + + rb_scan_args(argc, argv, "03", &y, &d, &sg); - return d_lite_s_new_internal_wo_civil(klass, jd, sg, LIGHT_MODE | HAVE_JD); + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + d = INT2FIX(1); + case 2: + sg = INT2FIX(ITALY); + } + + jd = rt__valid_ordinal_p(y, d, sg); + + if (NIL_P(jd)) + rb_raise(rb_eArgError, "invalid date"); + + return d_right_new_jd(klass, jd, sg); } /* @@ -1238,28 +2325,29 @@ date_s_ordinal(int argc, VALUE *argv, VALUE klass) int y, d, rd; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(ordinal); +#endif + rb_scan_args(argc, argv, "03", &vy, &vd, &vsg); if (!((NIL_P(vy) || FIXNUM_P(vy)) && (NIL_P(vd) || FIXNUM_P(vd)))) - return cforwardv("ordinal_r"); - - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + return c_cforwardv(ordinal); y = -4712; d = 1; + sg = ITALY; switch (argc) { case 3: + sg = NUM2DBL(vsg); case 2: d = NUM2INT(vd); case 1: y = NUM2INT(vy); if (!LIGHTABLE_YEAR(y)) - return cforwardv("ordinal_r"); + return c_cforwardv(ordinal); } { @@ -1270,11 +2358,37 @@ date_s_ordinal(int argc, VALUE *argv, VALUE klass) rb_raise(rb_eArgError, "invalid date"); if (!LIGHTABLE_JD(jd) || !ns) - return cforwardv("ordinal_r"); + return c_cforwardv(ordinal); + + return d_lite_new_internal_wo_civil(klass, jd, sg, + HAVE_JD); + } +} + +static VALUE +rt_date_s_civil(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, m, d, sg, jd; + + rb_scan_args(argc, argv, "04", &y, &m, &d, &sg); - return d_lite_s_new_internal_wo_civil(klass, jd, sg, - LIGHT_MODE | HAVE_JD); + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + m = INT2FIX(1); + case 2: + d = INT2FIX(1); + case 3: + sg = INT2FIX(ITALY); } + + jd = rt__valid_civil_p(y, m, d, sg); + + if (NIL_P(jd)) + rb_raise(rb_eArgError, "invalid date"); + + return d_right_new_jd(klass, jd, sg); } /* @@ -1303,24 +2417,25 @@ date_s_civil(int argc, VALUE *argv, VALUE klass) int y, m, d, rm, rd; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(civil); +#endif + rb_scan_args(argc, argv, "04", &vy, &vm, &vd, &vsg); if (!((NIL_P(vy) || FIXNUM_P(vy)) && (NIL_P(vm) || FIXNUM_P(vm)) && (NIL_P(vd) || FIXNUM_P(vd)))) - return cforwardv("civil_r"); - - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + return c_cforwardv(civil); y = -4712; m = 1; d = 1; + sg = ITALY; switch (argc) { case 4: + sg = NUM2DBL(vsg); case 3: d = NUM2INT(vd); case 2: @@ -1328,15 +2443,15 @@ date_s_civil(int argc, VALUE *argv, VALUE klass) case 1: y = NUM2INT(vy); if (!LIGHTABLE_YEAR(y)) - return cforwardv("civil_r"); + return c_cforwardv(civil); } if (isinf(sg) && sg < 0) { if (!valid_gregorian_p(y, m, d, &rm, &rd)) rb_raise(rb_eArgError, "invalid date"); - return d_lite_s_new_internal(klass, 0, sg, y, rm, rd, - LIGHT_MODE | HAVE_CIVIL); + return d_lite_new_internal(klass, 0, sg, y, rm, rd, + HAVE_CIVIL); } else { long jd; @@ -1346,11 +2461,37 @@ date_s_civil(int argc, VALUE *argv, VALUE klass) rb_raise(rb_eArgError, "invalid date"); if (!LIGHTABLE_JD(jd) || !ns) - return cforwardv("civil_r"); + return c_cforwardv(civil); + + return d_lite_new_internal(klass, jd, sg, y, rm, rd, + HAVE_JD | HAVE_CIVIL); + } +} + +static VALUE +rt_date_s_commercial(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, w, d, sg, jd; + + rb_scan_args(argc, argv, "04", &y, &w, &d, &sg); - return d_lite_s_new_internal(klass, jd, sg, y, rm, rd, - LIGHT_MODE | HAVE_JD | HAVE_CIVIL); + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + w = INT2FIX(1); + case 2: + d = INT2FIX(1); + case 3: + sg = INT2FIX(ITALY); } + + jd = rt__valid_commercial_p(y, w, d, sg); + + if (NIL_P(jd)) + rb_raise(rb_eArgError, "invalid date"); + + return d_right_new_jd(klass, jd, sg); } /* @@ -1379,24 +2520,25 @@ date_s_commercial(int argc, VALUE *argv, VALUE klass) int y, w, d, rw, rd; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(commercial); +#endif + rb_scan_args(argc, argv, "04", &vy, &vw, &vd, &vsg); if (!((NIL_P(vy) || FIXNUM_P(vy)) && (NIL_P(vw) || FIXNUM_P(vw)) && (NIL_P(vd) || FIXNUM_P(vd)))) - return cforwardv("commercial_r"); - - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + return c_cforwardv(commercial); y = -4712; w = 1; d = 1; + sg = ITALY; switch (argc) { case 4: + sg = NUM2DBL(vsg); case 3: d = NUM2INT(vd); case 2: @@ -1404,7 +2546,7 @@ date_s_commercial(int argc, VALUE *argv, VALUE klass) case 1: y = NUM2INT(vy); if (!LIGHTABLE_CWYEAR(y)) - return cforwardv("commercial_r"); + return c_cforwardv(commercial); } { @@ -1415,12 +2557,180 @@ date_s_commercial(int argc, VALUE *argv, VALUE klass) rb_raise(rb_eArgError, "invalid date"); if (!LIGHTABLE_JD(jd) || !ns) - return cforwardv("commercial_r"); + return c_cforwardv(commercial); + + return d_lite_new_internal_wo_civil(klass, jd, sg, + HAVE_JD); + } +} + +#ifndef NDEBUG +static VALUE +rt_date_s_weeknum(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, w, d, f, sg, jd; + + rb_scan_args(argc, argv, "05", &y, &w, &d, &f, &sg); + + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + w = INT2FIX(0); + case 2: + d = INT2FIX(1); + case 3: + f = INT2FIX(0); + case 4: + sg = INT2FIX(ITALY); + } + + jd = rt__valid_weeknum_p(y, w, d, f, sg); + + if (NIL_P(jd)) + rb_raise(rb_eArgError, "invalid date"); + + return d_right_new_jd(klass, jd, sg); +} + +static VALUE +date_s_weeknum(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vw, vd, vf, vsg; + int y, w, d, f, rw, rd; + double sg; + +#ifdef FORCE_RIGHT + return c_cforwardv(weeknum); +#endif + + rb_scan_args(argc, argv, "05", &vy, &vw, &vd, &vf, &vsg); + + if (!((NIL_P(vy) || FIXNUM_P(vy)) && + (NIL_P(vw) || FIXNUM_P(vw)) && + (NIL_P(vd) || FIXNUM_P(vd)) && + (NIL_P(vf) || FIXNUM_P(vf)))) + return c_cforwardv(weeknum); + + y = -4712; + w = 0; + d = 1; + f = 0; + sg = ITALY; + + switch (argc) { + case 5: + sg = NUM2DBL(vsg); + case 4: + f = NUM2INT(vf); + case 3: + d = NUM2INT(vd); + case 2: + w = NUM2INT(vw); + case 1: + y = NUM2INT(vy); + if (!LIGHTABLE_YEAR(y)) + return c_cforwardv(weeknum); + } + + { + long jd; + int ns; + + if (!valid_weeknum_p(y, w, d, f, sg, &rw, &rd, &jd, &ns)) + rb_raise(rb_eArgError, "invalid date"); + + if (!LIGHTABLE_JD(jd) || !ns) + return c_cforwardv(weeknum); + + return d_lite_new_internal_wo_civil(klass, jd, sg, + HAVE_JD); + } +} + +static VALUE +rt_date_s_nth_kday(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, m, n, k, sg, jd; + + rb_scan_args(argc, argv, "05", &y, &m, &n, &k, &sg); + + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + m = INT2FIX(1); + case 2: + n = INT2FIX(1); + case 3: + k = INT2FIX(1); + case 4: + sg = INT2FIX(ITALY); + } + + jd = rt__valid_nth_kday_p(y, m, n, k, sg); + + if (NIL_P(jd)) + rb_raise(rb_eArgError, "invalid date"); + + return d_right_new_jd(klass, jd, sg); +} + +static VALUE +date_s_nth_kday(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vm, vn, vk, vsg; + int y, m, n, k, rm, rn, rk; + double sg; + +#ifdef FORCE_RIGHT + return c_cforwardv(nth_kday); +#endif + + rb_scan_args(argc, argv, "05", &vy, &vm, &vn, &vk, &vsg); + + if (!((NIL_P(vy) || FIXNUM_P(vy)) && + (NIL_P(vm) || FIXNUM_P(vm)) && + (NIL_P(vn) || FIXNUM_P(vn)) && + (NIL_P(vk) || FIXNUM_P(vk)))) + return c_cforwardv(nth_kday); + + y = -4712; + m = 1; + n = 1; + k = 1; + sg = ITALY; + + switch (argc) { + case 5: + sg = NUM2DBL(vsg); + case 4: + k = NUM2INT(vk); + case 3: + n = NUM2INT(vn); + case 2: + m = NUM2INT(vm); + case 1: + y = NUM2INT(vy); + if (!LIGHTABLE_YEAR(y)) + return c_cforwardv(nth_kday); + } + + { + long jd; + int ns; + + if (!valid_nth_kday_p(y, m, n, k, sg, &rm, &rn, &rk, &jd, &ns)) + rb_raise(rb_eArgError, "invalid date"); + + if (!LIGHTABLE_JD(jd) || !ns) + return c_cforwardv(nth_kday); - return d_lite_s_new_internal_wo_civil(klass, jd, sg, - LIGHT_MODE | HAVE_JD); + return d_lite_new_internal_wo_civil(klass, jd, sg, + HAVE_JD); } } +#endif #if !defined(HAVE_GMTIME_R) static struct tm* @@ -1453,10 +2763,10 @@ date_s_today(int argc, VALUE *argv, VALUE klass) rb_scan_args(argc, argv, "01", &vsg); - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else + if (argc < 1) sg = ITALY; + else + sg = NUM2DBL(vsg); if (time(&t) == -1) rb_sys_fail("time"); @@ -1466,23 +2776,441 @@ date_s_today(int argc, VALUE *argv, VALUE klass) m = tm.tm_mon + 1; d = tm.tm_mday; +#ifdef FORCE_RIGHT + goto right; +#endif + if (!LIGHTABLE_YEAR(y)) - rb_raise(rb_eArgError, "cannot create"); + goto right; if (isinf(sg) && sg < 0) - return d_lite_s_new_internal(klass, 0, sg, y, m, d, - LIGHT_MODE | HAVE_CIVIL); + return d_lite_new_internal(klass, 0, sg, y, m, d, + HAVE_CIVIL); else { long jd; int ns; civil_to_jd(y, m, d, sg, &jd, &ns); - return d_lite_s_new_internal(klass, jd, sg, y, m, d, - LIGHT_MODE | HAVE_JD | HAVE_CIVIL); + return d_lite_new_internal(klass, jd, sg, y, m, d, + HAVE_JD | HAVE_CIVIL); + } + right: + { + VALUE jd, ajd; + + jd = rt_civil_to_jd(INT2FIX(y), INT2FIX(m), INT2FIX(d), DBL2NUM(sg)); + ajd = rt_jd_to_ajd(jd, INT2FIX(0), INT2FIX(0)); + return d_right_new(klass, ajd, INT2FIX(0), DBL2NUM(sg)); } } +#define set_hash0(k,v) rb_hash_aset(hash, k, v) +#define ref_hash0(k) rb_hash_aref(hash, k) +#define del_hash0(k) rb_hash_delete(hash, k) + +#define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k)), v) +#define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k))) +#define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k))) + +static VALUE +rt_rewrite_frags(VALUE hash) +{ + VALUE seconds; + + if (NIL_P(hash)) + hash = rb_hash_new(); + + seconds = ref_hash("seconds"); + if (!NIL_P(seconds)) { + VALUE d, h, min, s, fr; + + d = f_idiv(seconds, INT2FIX(DAY_IN_SECONDS)); + fr = f_mod(seconds, INT2FIX(DAY_IN_SECONDS)); + + h = f_idiv(fr, INT2FIX(HOUR_IN_SECONDS)); + fr = f_mod(fr, INT2FIX(HOUR_IN_SECONDS)); + + min = f_idiv(fr, INT2FIX(MINUTE_IN_SECONDS)); + fr = f_mod(fr, INT2FIX(MINUTE_IN_SECONDS)); + + s = f_idiv(fr, INT2FIX(1)); + fr = f_mod(fr, INT2FIX(1)); + + set_hash("jd", f_add(UNIX_EPOCH_IN_CJD, d)); + set_hash("hour", h); + set_hash("min", min); + set_hash("sec", s); + set_hash("sec_fraction", fr); + del_hash("seconds"); + del_hash("offset"); + } + return hash; +} + +#define sym(x) ID2SYM(rb_intern(x)) + +static VALUE +fv_values_at(VALUE h, VALUE a) +{ + return rb_funcall2(h, rb_intern("values_at"), + RARRAY_LENINT(a), RARRAY_PTR(a)); +} + +static VALUE +rt_complete_frags(VALUE klass, VALUE hash) +{ + static VALUE tab = Qnil; + VALUE t, l, g, d; + + if (NIL_P(tab)) { + tab = rb_ary_new3(11, + rb_ary_new3(2, + sym("time"), + rb_ary_new3(3, + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + Qnil, + rb_ary_new3(1, + sym("jd"))), + rb_ary_new3(2, + sym("ordinal"), + rb_ary_new3(5, + sym("year"), + sym("yday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + sym("civil"), + rb_ary_new3(6, + sym("year"), + sym("mon"), + sym("mday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + sym("commercial"), + rb_ary_new3(6, + sym("cwyear"), + sym("cweek"), + sym("cwday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + sym("wday"), + rb_ary_new3(4, + sym("wday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + sym("wnum0"), + rb_ary_new3(6, + sym("year"), + sym("wnum0"), + sym("wday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + sym("wnum1"), + rb_ary_new3(6, + sym("year"), + sym("wnum1"), + sym("wday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + Qnil, + rb_ary_new3(6, + sym("cwyear"), + sym("cweek"), + sym("wday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + Qnil, + rb_ary_new3(6, + sym("year"), + sym("wnum0"), + sym("cwday"), + sym("hour"), + sym("min"), + sym("sec"))), + rb_ary_new3(2, + Qnil, + rb_ary_new3(6, + sym("year"), + sym("wnum1"), + sym("cwday"), + sym("hour"), + sym("min"), + sym("sec")))); + rb_gc_register_mark_object(tab); + } + + { + int i; + + t = rb_ary_new2(RARRAY_LEN(tab)); + + for (i = 0; i < RARRAY_LENINT(tab); i++) { + VALUE x, k, a, e; + + x = RARRAY_PTR(tab)[i]; + k = RARRAY_PTR(x)[0]; + a = RARRAY_PTR(x)[1]; + e = f_compact(fv_values_at(hash, a)); + + if (RARRAY_LEN(e) > 0) + rb_ary_push(t, rb_ary_new3(5, + INT2FIX(RARRAY_LENINT(e)), + INT2FIX(-i), + k, a, e)); + } + + if (RARRAY_LEN(t) == 0) + g = Qnil; + else { + rb_ary_sort_bang(t); + l = RARRAY_PTR(t)[RARRAY_LENINT(t) - 1]; + g = rb_ary_new3(3, + RARRAY_PTR(l)[2], + RARRAY_PTR(l)[3], + RARRAY_PTR(l)[4]); + } + } + + d = Qnil; + + if (!NIL_P(g) && !NIL_P(RARRAY_PTR(g)[0]) && + (RARRAY_LEN(RARRAY_PTR(g)[1]) - + RARRAY_LEN(RARRAY_PTR(g)[2]))) { + VALUE k, a; + + if (NIL_P(d)) + d = date_s_today(0, (VALUE *)0, cDate); + + k = RARRAY_PTR(g)[0]; + a = RARRAY_PTR(g)[1]; + + if (k == sym("ordinal")) { + if (NIL_P(ref_hash("year"))) + set_hash("year", f_year(d)); + if (NIL_P(ref_hash("yday"))) + set_hash("yday", INT2FIX(1)); + } + else if (k == sym("civil")) { + int i; + + for (i = 0; i < RARRAY_LENINT(a); i++) { + VALUE e = RARRAY_PTR(a)[i]; + + if (!NIL_P(ref_hash0(e))) + break; + set_hash0(e, rb_funcall(d, SYM2ID(e), 0)); + } + if (NIL_P(ref_hash("mon"))) + set_hash("mon", INT2FIX(1)); + if (NIL_P(ref_hash("mday"))) + set_hash("mday", INT2FIX(1)); + } + else if (k == sym("commercial")) { + int i; + + for (i = 0; i < RARRAY_LENINT(a); i++) { + VALUE e = RARRAY_PTR(a)[i]; + + if (!NIL_P(ref_hash0(e))) + break; + set_hash0(e, rb_funcall(d, SYM2ID(e), 0)); + } + if (NIL_P(ref_hash("cweek"))) + set_hash("cweek", INT2FIX(1)); + if (NIL_P(ref_hash("cwday"))) + set_hash("cwday", INT2FIX(1)); + } + else if (k == sym("wday")) { + set_hash("jd", f_jd(f_add(f_sub(d, f_wday(d)), ref_hash("wday")))); + } + else if (k == sym("wnum0")) { + int i; + + for (i = 0; i < RARRAY_LENINT(a); i++) { + VALUE e = RARRAY_PTR(a)[i]; + + if (!NIL_P(ref_hash0(e))) + break; + set_hash0(e, rb_funcall(d, SYM2ID(e), 0)); + } + if (NIL_P(ref_hash("wnum0"))) + set_hash("wnum0", INT2FIX(0)); + if (NIL_P(ref_hash("wday"))) + set_hash("wday", INT2FIX(0)); + } + else if (k == sym("wnum1")) { + int i; + + for (i = 0; i < RARRAY_LENINT(a); i++) { + VALUE e = RARRAY_PTR(a)[i]; + + if (!NIL_P(ref_hash0(e))) + break; + set_hash0(e, rb_funcall(d, SYM2ID(e), 0)); + } + if (NIL_P(ref_hash("wnum1"))) + set_hash("wnum1", INT2FIX(0)); + if (NIL_P(ref_hash("wday"))) + set_hash("wday", INT2FIX(1)); + } + } + + if (!NIL_P(g) && RARRAY_PTR(g)[0] == sym("time")) { + if (f_le_p(klass, cDateTime)) { + if (NIL_P(d)) + d = date_s_today(0, (VALUE *)0, cDate); + if (NIL_P(ref_hash("jd"))) + set_hash("jd", f_jd(d)); + } + } + + if (NIL_P(ref_hash("hour"))) + set_hash("hour", INT2FIX(0)); + if (NIL_P(ref_hash("min"))) + set_hash("min", INT2FIX(0)); + if (NIL_P(ref_hash("sec"))) + set_hash("sec", INT2FIX(0)); + else if (f_gt_p(ref_hash("sec"), INT2FIX(59))) + set_hash("sec", INT2FIX(59)); + + return hash; +} + +#define f_values_at1(o,k1) rb_funcall(o, rb_intern("values_at"), 1, k1) +#define f_values_at2(o,k1,k2) rb_funcall(o, rb_intern("values_at"), 2, k1, k2) +#define f_values_at3(o,k1,k2,k3) rb_funcall(o, rb_intern("values_at"), 3, k1, k2, k3) + +static VALUE +f_all_p(VALUE a) +{ + int i; + + for (i = 0; i < RARRAY_LENINT(a); i++) + if (NIL_P(RARRAY_PTR(a)[i])) + return Qfalse; + return Qtrue; +} + +static VALUE +rt__valid_date_frags_p(VALUE hash, VALUE sg) +{ + VALUE a; + + a = f_values_at1(hash, sym("jd")); + if (f_all_p(a)) { + VALUE jd = rt__valid_jd_p(RARRAY_PTR(a)[0], + sg); + if (!NIL_P(jd)) + return jd; + } + + a = f_values_at2(hash, sym("year"), sym("yday")); + if (f_all_p(a)) { + VALUE jd = rt__valid_ordinal_p(RARRAY_PTR(a)[0], + RARRAY_PTR(a)[1], + sg); + if (!NIL_P(jd)) + return jd; + } + + a = f_values_at3(hash, sym("year"), sym("mon"), sym("mday")); + if (f_all_p(a)) { + VALUE jd = rt__valid_civil_p(RARRAY_PTR(a)[0], + RARRAY_PTR(a)[1], + RARRAY_PTR(a)[2], + sg); + if (!NIL_P(jd)) + return jd; + } + + a = f_values_at3(hash, sym("cwyear"), sym("cweek"), sym("cwday")); + if (NIL_P(RARRAY_PTR(a)[2]) && !NIL_P(ref_hash("wday"))) + if (f_zero_p(ref_hash("wday"))) + RARRAY_PTR(a)[2] = INT2FIX(7); + else + RARRAY_PTR(a)[2] = ref_hash("wday"); + if (f_all_p(a)) { + VALUE jd = rt__valid_commercial_p(RARRAY_PTR(a)[0], + RARRAY_PTR(a)[1], + RARRAY_PTR(a)[2], + sg); + if (!NIL_P(jd)) + return jd; + } + + a = f_values_at3(hash, sym("year"), sym("wnum0"), sym("wday")); + if (NIL_P(RARRAY_PTR(a)[2]) && !NIL_P(ref_hash("cwday"))) + RARRAY_PTR(a)[2] = f_mod(ref_hash("cwday"), INT2FIX(7)); + if (f_all_p(a)) { + VALUE jd = rt__valid_weeknum_p(RARRAY_PTR(a)[0], + RARRAY_PTR(a)[1], + RARRAY_PTR(a)[2], + INT2FIX(0), + sg); + if (!NIL_P(jd)) + return jd; + } + + a = f_values_at3(hash, sym("year"), sym("wnum1"), sym("wday")); + if (!NIL_P(RARRAY_PTR(a)[2])) + RARRAY_PTR(a)[2] = f_mod(f_sub(RARRAY_PTR(a)[2], INT2FIX(1)), + INT2FIX(7)); + if (NIL_P(RARRAY_PTR(a)[2]) && !NIL_P(ref_hash("cwday"))) + RARRAY_PTR(a)[2] = f_mod(f_sub(ref_hash("cwday"), INT2FIX(1)), + INT2FIX(7)); + if (f_all_p(a)) { + VALUE jd = rt__valid_weeknum_p(RARRAY_PTR(a)[0], + RARRAY_PTR(a)[1], + RARRAY_PTR(a)[2], + INT2FIX(1), + sg); + if (!NIL_P(jd)) + return jd; + } + return Qnil; +} + +static VALUE +rt__valid_time_frags_p(VALUE hash) +{ + VALUE a; + + a = f_values_at3(hash, sym("hour"), sym("min"), sym("sec")); + return rt__valid_time_p(RARRAY_PTR(a)[0], + RARRAY_PTR(a)[1], + RARRAY_PTR(a)[2]); +} + +static VALUE +d_switch_new_by_frags(VALUE klass, VALUE hash, VALUE sg) +{ + VALUE jd; + + hash = rt_rewrite_frags(hash); + hash = rt_complete_frags(klass, hash); + jd = rt__valid_date_frags_p(hash, sg); + if (NIL_P(jd)) + rb_raise(rb_eArgError, "invalid date"); + return d_switch_new_jd(klass, jd, sg); +} + VALUE date__strptime(const char *str, size_t slen, const char *fmt, size_t flen, VALUE hash); @@ -1525,10 +3253,12 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, if (!NIL_P(zone)) { rb_enc_copy(zone, vstr); + OBJ_INFECT(zone, vstr); rb_hash_aset(hash, ID2SYM(rb_intern("zone")), zone); } if (!NIL_P(left)) { rb_enc_copy(left, vstr); + OBJ_INFECT(left, vstr); rb_hash_aset(hash, ID2SYM(rb_intern("leftover")), left); } } @@ -1548,6 +3278,52 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass) return date_s__strptime_internal(argc, argv, klass, "%F"); } +/* + * call-seq: + * Date.strptime([string="-4712-01-01"[, format="%F"[,start=ITALY]]]) + * + * Create a new Date object by parsing from a String + * according to a specified format. + * + * +str+ is a String holding a date representation. + * +fmt+ is the format that the date is in. See + * date/format.rb for details on supported formats. + * + * The default +str+ is '-4712-01-01', and the default + * +fmt+ is '%F', which means Year-Month-Day_of_Month. + * This gives Julian Day Number day 0. + * + * +sg+ specifies the Day of Calendar Reform. + * + * An ArgumentError will be raised if +str+ cannot be + * parsed. + */ +static VALUE +date_s_strptime(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, fmt, sg; + + rb_scan_args(argc, argv, "03", &str, &fmt, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01"); + case 1: + fmt = rb_str_new2("%F"); + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE argv2[2], hash; + + argv2[0] = str; + argv2[1] = fmt; + hash = date_s__strptime(2, argv2, klass); + return d_switch_new_by_frags(klass, hash, sg); + } +} + VALUE date__parse(VALUE str, VALUE comp); @@ -1571,6 +3347,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass) if (!NIL_P(zone)) { rb_enc_copy(zone, vstr); + OBJ_INFECT(zone, vstr); rb_hash_aset(hash, ID2SYM(rb_intern("zone")), zone); } } @@ -1592,6 +3369,295 @@ date_s__parse(int argc, VALUE *argv, VALUE klass) /* * call-seq: + * Date.parse(string="-4712-01-01"[, comp=true[,start=ITALY]]) + * + * Create a new Date object by parsing from a String, + * without specifying the format. + * + * +str+ is a String holding a date representation. + * +comp+ specifies whether to interpret 2-digit years + * as 19XX (>= 69) or 20XX (< 69); the default is to. + * The method will attempt to parse a date from the String + * using various heuristics; see #_parse in date/format.rb + * for more details. If parsing fails, an ArgumentError + * will be raised. + * + * The default +str+ is '-4712-01-01'; this is Julian + * Day Number day 0. + * + * +sg+ specifies the Day of Calendar Reform. + */ +static VALUE +date_s_parse(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE argv2[2], hash; + + argv2[0] = str; + argv2[1] = comp; + hash = date_s__parse(2, argv2, klass); + return d_switch_new_by_frags(klass, hash, sg); + } +} + +VALUE date__iso8601(VALUE); +VALUE date__rfc3339(VALUE); +VALUE date__xmlschema(VALUE); +VALUE date__rfc2822(VALUE); +VALUE date__httpdate(VALUE); +VALUE date__jisx0301(VALUE); + +/* + * call-seq: + * Date._iso8601(string) + * + * Return a hash of parsed elements. + */ +static VALUE +date_s__iso8601(VALUE klass, VALUE str) +{ + return date__iso8601(str); +} + +/* + * call-seq: + * Date.iso8601(string="-4712-01-01"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical ISO 8601 format. + */ +static VALUE +date_s_iso8601(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, sg; + + rb_scan_args(argc, argv, "02", &str, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01"); + case 1: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__iso8601(klass, str); + return d_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * Date._rfc3339(string) + * + * Return a hash of parsed elements. + */ +static VALUE +date_s__rfc3339(VALUE klass, VALUE str) +{ + return date__rfc3339(str); +} + +/* + * call-seq: + * Date.rfc3339(string="-4712-01-01T00:00:00+00:00"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical RFC 3339 format. + */ +static VALUE +date_s_rfc3339(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, sg; + + rb_scan_args(argc, argv, "02", &str, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + case 1: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__rfc3339(klass, str); + return d_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * Date._xmlschema(string) + * + * Return a hash of parsed elements. + */ +static VALUE +date_s__xmlschema(VALUE klass, VALUE str) +{ + return date__xmlschema(str); +} + +/* + * call-seq: + * Date.xmlschema(string="-4712-01-01"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical XML Schema format. + */ +static VALUE +date_s_xmlschema(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, sg; + + rb_scan_args(argc, argv, "02", &str, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01"); + case 1: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__xmlschema(klass, str); + return d_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * Date._rfc2822(string) + * Date._rfc822(string) + * + * Return a hash of parsed elements. + */ +static VALUE +date_s__rfc2822(VALUE klass, VALUE str) +{ + return date__rfc2822(str); +} + +/* + * call-seq: + * Date.rfc2822(string="Mon, 1 Jan -4712 00:00:00 +0000"[,start=ITALY]) + * Date.rfc822(string="Mon, 1 Jan -4712 00:00:00 +0000"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical RFC 2822 format. + */ +static VALUE +date_s_rfc2822(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, sg; + + rb_scan_args(argc, argv, "02", &str, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("Mon, 1 Jan -4712 00:00:00 +0000"); + case 1: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__rfc2822(klass, str); + return d_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * Date._httpdate(string) + * + * Return a hash of parsed elements. + */ +static VALUE +date_s__httpdate(VALUE klass, VALUE str) +{ + return date__httpdate(str); +} + +/* + * call-seq: + * Date.httpdate(string="Mon, 01 Jan -4712 00:00:00 GMT"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some RFC 2616 format. + */ +static VALUE +date_s_httpdate(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, sg; + + rb_scan_args(argc, argv, "02", &str, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("Mon, 01 Jan -4712 00:00:00 GMT"); + case 1: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__httpdate(klass, str); + return d_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * Date._jisx0301(string) + * + * Return a hash of parsed elements. + */ +static VALUE +date_s__jisx0301(VALUE klass, VALUE str) +{ + return date__jisx0301(str); +} + +/* + * call-seq: + * Date.jisx0301(string="-4712-01-01"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical JIS X 0301 format. + */ +static VALUE +date_s_jisx0301(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, sg; + + rb_scan_args(argc, argv, "02", &str, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01"); + case 1: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__jisx0301(klass, str); + return d_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: * d.ajd * * Get the date as an Astronomical Julian Day Number. @@ -1608,6 +3674,18 @@ d_lite_ajd(VALUE self) } } +#define c_iforward0(m) d_right_##m(self) +#define c_iforwardv(m) d_right_##m(argc, argv, self) +#define c_iforwardop(m) d_right_##m(self, other) + +static VALUE +d_right_amjd(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return rt_ajd_to_amjd(dat->r.ajd); +} + /* * call-seq: * d.amjd @@ -1619,13 +3697,40 @@ d_lite_amjd(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("amjd_r"); + return c_iforward0(amjd); { get_d_jd(dat); return rb_rational_new1(LONG2NUM(dat->l.jd - 2400001L)); } } +#define return_once(k, expr)\ +{\ + VALUE id, val;\ + get_d1(self);\ + id = ID2SYM(rb_intern(#k));\ + val = rb_hash_aref(dat->r.cache, id);\ + if (!NIL_P(val))\ + return val;\ + val = expr;\ + rb_hash_aset(dat->r.cache, id, val);\ + return val;\ +} + +static VALUE +d_right_daynum(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(daynum, rt_ajd_to_jd(dat->r.ajd, dat->r.of)); +} + +static VALUE +d_right_jd(VALUE self) +{ + return RARRAY_PTR(c_iforward0(daynum))[0]; +} + /* * call-seq: * d.jd @@ -1637,13 +3742,21 @@ d_lite_jd(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("jd_r"); + return c_iforward0(jd); { get_d_jd(dat); return INT2FIX(dat->l.jd); } } +static VALUE +d_right_mjd(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return rt_jd_to_mjd(d_right_jd(self)); +} + /* * call-seq: * d.mjd @@ -1655,13 +3768,21 @@ d_lite_mjd(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("mjd_r"); + return c_iforward0(mjd); { get_d_jd(dat); return LONG2NUM(dat->l.jd - 2400001L); } } +static VALUE +d_right_ld(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return rt_jd_to_ld(d_right_jd(self)); +} + /* * call-seq: * d.ld @@ -1673,13 +3794,27 @@ d_lite_ld(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("ld_r"); + return c_iforward0(ld); { get_d_jd(dat); return LONG2NUM(dat->l.jd - 2299160L); } } +static VALUE +d_right_civil(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(civil, rt_jd_to_civil(d_right_jd(self), dat->r.sg)); +} + +static VALUE +d_right_year(VALUE self) +{ + return RARRAY_PTR(c_iforward0(civil))[0]; +} + /* * call-seq: * d.year @@ -1691,13 +3826,27 @@ d_lite_year(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("year_r"); + return c_iforward0(year); { get_d_civil(dat); return INT2FIX(dat->l.year); } } +static VALUE +d_right_ordinal(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(ordinal, rt_jd_to_ordinal(d_right_jd(self), dat->r.sg)); +} + +static VALUE +d_right_yday(VALUE self) +{ + return RARRAY_PTR(c_iforward0(ordinal))[1]; +} + static const int yeartab[2][13] = { { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } @@ -1722,13 +3871,19 @@ d_lite_yday(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("yday_r"); + return c_iforward0(yday); { get_d_civil(dat); return INT2FIX(civil_to_yday(dat->l.year, dat->l.mon, dat->l.mday)); } } +static VALUE +d_right_mon(VALUE self) +{ + return RARRAY_PTR(c_iforward0(civil))[1]; +} + /* * call-seq: * d.mon @@ -1742,13 +3897,19 @@ d_lite_mon(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("mon_r"); + return c_iforward0(mon); { get_d_civil(dat); return INT2FIX(dat->l.mon); } } +static VALUE +d_right_mday(VALUE self) +{ + return RARRAY_PTR(c_iforward0(civil))[2]; +} + /* * call-seq: * d.mday @@ -1760,13 +3921,19 @@ d_lite_mday(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("mday_r"); + return c_iforward0(mday); { get_d_civil(dat); return INT2FIX(dat->l.mday); } } +static VALUE +d_right_day_fraction(VALUE self) +{ + return RARRAY_PTR(c_iforward0(daynum))[1]; +} + /* * call-seq: * d.day_fraction @@ -1778,18 +3945,33 @@ d_lite_day_fraction(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("day_fraction_r"); + return c_iforward0(day_fraction); return INT2FIX(0); } static VALUE +d_right_weeknum0(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(weeknum0, rt_jd_to_weeknum(d_right_jd(self), + INT2FIX(0), dat->r.sg)); +} + +static VALUE +d_right_wnum0(VALUE self) +{ + return RARRAY_PTR(c_iforward0(weeknum0))[1]; +} + +static VALUE d_lite_wnum0(VALUE self) { int ry, rw, rd; get_d1(self); if (!light_mode_p(dat)) - return iforward0("wnum0_r"); + return c_iforward0(wnum0); { get_d_jd(dat); jd_to_weeknum(dat->l.jd, 0, dat->l.sg, &ry, &rw, &rd); @@ -1798,13 +3980,28 @@ d_lite_wnum0(VALUE self) } static VALUE +d_right_weeknum1(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(weeknum1, rt_jd_to_weeknum(d_right_jd(self), + INT2FIX(1), dat->r.sg)); +} + +static VALUE +d_right_wnum1(VALUE self) +{ + return RARRAY_PTR(c_iforward0(weeknum1))[1]; +} + +static VALUE d_lite_wnum1(VALUE self) { int ry, rw, rd; get_d1(self); if (!light_mode_p(dat)) - return iforward0("wnum1_r"); + return c_iforward0(wnum1); { get_d_jd(dat); jd_to_weeknum(dat->l.jd, 1, dat->l.sg, &ry, &rw, &rd); @@ -1812,6 +4009,45 @@ d_lite_wnum1(VALUE self) } } +#ifndef NDEBUG +static VALUE +d_right_time(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(time, rt_day_fraction_to_time(d_right_day_fraction(self))); +} +#endif + +static VALUE +d_right_time_wo_sf(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(time_wo_sf, + rt_day_fraction_to_time_wo_sf(d_right_day_fraction(self))); +} + +static VALUE +d_right_time_sf(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(time_sf, f_mul(f_mod(d_right_day_fraction(self), + SECONDS_IN_DAY), + INT2FIX(DAY_IN_SECONDS))); +} + +static VALUE +d_right_hour(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[0]; +#else + return RARRAY_PTR(c_iforward0(time_wo_sf))[0]; +#endif +} + /* * call-seq: * d.hour @@ -1823,10 +4059,20 @@ d_lite_hour(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("hour_r"); + return c_iforward0(hour); return INT2FIX(0); } +static VALUE +d_right_min(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[1]; +#else + return RARRAY_PTR(c_iforward0(time_wo_sf))[1]; +#endif +} + /* * call-seq: * d.min @@ -1839,10 +4085,20 @@ d_lite_min(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("min_r"); + return c_iforward0(min); return INT2FIX(0); } +static VALUE +d_right_sec(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[2]; +#else + return RARRAY_PTR(c_iforward0(time_wo_sf))[2]; +#endif +} + /* * call-seq: * d.sec @@ -1855,10 +4111,20 @@ d_lite_sec(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("sec_r"); + return c_iforward0(sec); return INT2FIX(0); } +static VALUE +d_right_sec_fraction(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[3]; +#else + return c_iforward0(time_sf); +#endif +} + /* * call-seq: * d.sec_fraction @@ -1871,7 +4137,7 @@ d_lite_sec_fraction(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("sec_fraction_r"); + return c_iforward0(sec_fraction); return INT2FIX(0); } @@ -1890,6 +4156,36 @@ d_lite_offset(VALUE self) return INT2FIX(0); } +static VALUE +d_right_zone(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + { + int sign; + VALUE hh, mm, ss, fr; + + if (f_negative_p(dat->r.of)) { + sign = '-'; + fr = f_abs(dat->r.of); + } + else { + sign = '+'; + fr = dat->r.of; + } + ss = f_div(fr, SECONDS_IN_DAY); + + hh = f_idiv(ss, INT2FIX(HOUR_IN_SECONDS)); + ss = f_mod(ss, INT2FIX(HOUR_IN_SECONDS)); + + mm = f_idiv(ss, INT2FIX(MINUTE_IN_SECONDS)); + + return rb_enc_sprintf(rb_usascii_encoding(), + "%c%02d:%02d", + sign, NUM2INT(hh), NUM2INT(mm)); + } +} + /* * call-seq: * d.zone @@ -1901,10 +4197,24 @@ d_lite_zone(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("zone_r"); + return c_iforward0(zone); return rb_usascii_str_new2("+00:00"); } +static VALUE +d_right_commercial(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return_once(commercial, rt_jd_to_commercial(d_right_jd(self), dat->r.sg)); +} + +static VALUE +d_right_cwyear(VALUE self) +{ + return RARRAY_PTR(c_iforward0(commercial))[0]; +} + /* * call-seq: * d.cwyear @@ -1919,7 +4229,7 @@ d_lite_cwyear(VALUE self) get_d1(self); if (!light_mode_p(dat)) - return iforward0("cwyear_r"); + return c_iforward0(cwyear); { get_d_jd(dat); jd_to_commercial(dat->l.jd, dat->l.sg, &ry, &rw, &rd); @@ -1927,6 +4237,12 @@ d_lite_cwyear(VALUE self) } } +static VALUE +d_right_cweek(VALUE self) +{ + return RARRAY_PTR(c_iforward0(commercial))[1]; +} + /* * call-seq: * d.cweek @@ -1940,7 +4256,7 @@ d_lite_cweek(VALUE self) get_d1(self); if (!light_mode_p(dat)) - return iforward0("cweek_r"); + return c_iforward0(cweek); { get_d_jd(dat); jd_to_commercial(dat->l.jd, dat->l.sg, &ry, &rw, &rd); @@ -1948,6 +4264,12 @@ d_lite_cweek(VALUE self) } } +static VALUE +d_right_cwday(VALUE self) +{ + return RARRAY_PTR(c_iforward0(commercial))[2]; +} + /* * call-seq: * d.cwday @@ -1962,7 +4284,7 @@ d_lite_cwday(VALUE self) get_d1(self); if (!light_mode_p(dat)) - return iforward0("cwday_r"); + return c_iforward0(cwday); { get_d_jd(dat); w = jd_to_wday(dat->l.jd); @@ -1972,6 +4294,12 @@ d_lite_cwday(VALUE self) } } +static VALUE +d_right_wday(VALUE self) +{ + return rt_jd_to_wday(d_right_jd(self)); +} + /* * call-seq: * d.wday @@ -1984,7 +4312,7 @@ d_lite_wday(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("wday_r"); + return c_iforward0(wday); { get_d_jd(dat); return INT2FIX(jd_to_wday(dat->l.jd)); @@ -1993,6 +4321,112 @@ d_lite_wday(VALUE self) /* * call-seq: + * d.sunday? + * + * Is the current date Sunday? + */ +static VALUE +d_lite_sunday_p(VALUE self) +{ + return f_eqeq_p(d_lite_wday(self), INT2FIX(0)); +} + +/* + * call-seq: + * d.monday? + * + * Is the current date Monday? + */ +static VALUE +d_lite_monday_p(VALUE self) +{ + return f_eqeq_p(d_lite_wday(self), INT2FIX(1)); +} + +/* + * call-seq: + * d.tuesday? + * + * Is the current date Tuesday? + */ +static VALUE +d_lite_tuesday_p(VALUE self) +{ + return f_eqeq_p(d_lite_wday(self), INT2FIX(2)); +} + +/* + * call-seq: + * d.wednesday? + * + * Is the current date Wednesday? + */ +static VALUE +d_lite_wednesday_p(VALUE self) +{ + return f_eqeq_p(d_lite_wday(self), INT2FIX(3)); +} + +/* + * call-seq: + * d.thursday? + * + * Is the current date Thursday? + */ +static VALUE +d_lite_thursday_p(VALUE self) +{ + return f_eqeq_p(d_lite_wday(self), INT2FIX(4)); +} + +/* + * call-seq: + * d.friday? + * + * Is the current date Friday? + */ +static VALUE +d_lite_friday_p(VALUE self) +{ + return f_eqeq_p(d_lite_wday(self), INT2FIX(5)); +} + +/* + * call-seq: + * d.saturday? + * + * Is the current date Saturday? + */ +static VALUE +d_lite_saturday_p(VALUE self) +{ + return f_eqeq_p(d_lite_wday(self), INT2FIX(6)); +} + +#ifndef NDEBUG +static VALUE +generic_nth_kday_p(VALUE self, VALUE n, VALUE k) +{ + if (f_eqeq_p(k, f_wday(self)) && + f_equal_p(f_jd(self), rt_nth_kday_to_jd(f_year(self), + f_mon(self), + n, k, + f_start(self)))) + return Qtrue; + return Qfalse; +} +#endif + +static VALUE +d_right_julian_p(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return f_lt_p(d_right_jd(self), dat->r.sg); +} + +/* + * call-seq: * d.julian? * * Is the current date old-style (Julian Calendar)? @@ -2002,10 +4436,18 @@ d_lite_julian_p(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("julian_r?"); + return c_iforward0(julian_p); return Qfalse; } +static VALUE +d_right_gregorian_p(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return d_right_julian_p(self) ? Qfalse : Qtrue; +} + /* * call-seq: * d.gregorian? @@ -2017,10 +4459,33 @@ d_lite_gregorian_p(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("gregorian_r?"); + return c_iforward0(gregorian_p); return Qtrue; } +static VALUE +d_right_fix_style(VALUE self) +{ + if (d_right_julian_p(self)) + return DBL2NUM(JULIAN); + return DBL2NUM(GREGORIAN); +} + +static VALUE +d_right_leap_p(VALUE self) +{ + VALUE style, a; + + style = d_right_fix_style(self); + a = rt_jd_to_civil(f_sub(rt_civil_to_jd(d_right_year(self), + INT2FIX(3), INT2FIX(1), style), + INT2FIX(1)), + style); + if (f_eqeq_p(RARRAY_PTR(a)[2], INT2FIX(29))) + return Qtrue; + return Qfalse; +} + /* * call-seq: * d.leap? @@ -2032,7 +4497,7 @@ d_lite_leap_p(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("leap_r?"); + return c_iforward0(leap_p); { get_d_civil(dat); return leap_p(dat->l.year) ? Qtrue : Qfalse; @@ -2054,6 +4519,16 @@ d_lite_start(VALUE self) return DBL2NUM(dat->l.sg); } +static VALUE +d_right_new_start(int argc, VALUE *argv, VALUE self) +{ + get_d1(self); + return d_right_new(CLASS_OF(self), + d_lite_ajd(self), + d_lite_offset(self), + (argc >= 1) ? argv[0] : INT2FIX(ITALY)); +} + /* * call-seq: * d.new_start([start=Date::ITALY]) @@ -2069,26 +4544,112 @@ d_lite_new_start(int argc, VALUE *argv, VALUE self) get_d1(self); if (!light_mode_p(dat)) - return iforwardv("new_start_r"); + return c_iforwardv(new_start); rb_scan_args(argc, argv, "01", &vsg); - if (!NIL_P(vsg)) + sg = ITALY; + if (argc >= 1) sg = NUM2DBL(vsg); - else - sg = ITALY; { get_d_jd(dat); if (dat->l.jd < sg) - return iforwardv("new_start_r"); + return c_iforwardv(new_start); + + return d_lite_new_internal_wo_civil(CLASS_OF(self), + dat->l.jd, + sg, + HAVE_JD); + } +} + +/* + * call-seq: + * d.italy + * + * Create a copy of this Date object that uses the Italian/Catholic + * Day of Calendar Reform. + */ +static VALUE +d_lite_italy(VALUE self) +{ + VALUE argv[1]; + argv[0] = INT2FIX(ITALY); + return d_lite_new_start(1, argv, self); +} + +/* + * call-seq: + * d.england + * + * Create a copy of this Date object that uses the English/Colonial + * Day of Calendar Reform. + */ +static VALUE +d_lite_england(VALUE self) +{ + VALUE argv[1]; + argv[0] = INT2FIX(ENGLAND); + return d_lite_new_start(1, argv, self); +} + +/* + * call-seq: + * d.julian + * + * Create a copy of this Date object that always uses the Julian + * Calendar. + */ +static VALUE +d_lite_julian(VALUE self) +{ + VALUE argv[1]; + argv[0] = DBL2NUM(JULIAN); + return d_lite_new_start(1, argv, self); +} - return d_lite_s_new_internal_wo_civil(CLASS_OF(self), - dat->l.jd, - sg, - LIGHT_MODE | HAVE_JD); +/* + * call-seq: + * d.gregorian + * + * Create a copy of this Date object that always uses the Gregorian + * Calendar. + */ +static VALUE +d_lite_gregorian(VALUE self) +{ + VALUE argv[1]; + argv[0] = DBL2NUM(GREGORIAN); + return d_lite_new_start(1, argv, self); +} + +static VALUE +sof2nof(VALUE of) +{ + if (TYPE(of) == T_STRING) { + VALUE n = date_zone_to_diff(of); + if (NIL_P(n)) + of = INT2FIX(0); + else + of = rb_rational_new2(n, INT2FIX(DAY_IN_SECONDS)); } + else if (TYPE(of) == T_FLOAT) { + of = rb_rational_new2(f_truncate(f_mul(of, INT2FIX(DAY_IN_SECONDS))), + INT2FIX(DAY_IN_SECONDS)); + } + return of; +} + +static VALUE +d_right_new_offset(int argc, VALUE *argv, VALUE self) +{ + get_d1(self); + return d_right_new(CLASS_OF(self), + d_lite_ajd(self), + (argc >= 1) ? sof2nof(argv[0]) : INT2FIX(0), + d_lite_start(self)); } /* @@ -2106,25 +4667,42 @@ d_lite_new_offset(int argc, VALUE *argv, VALUE self) get_d1(self); if (!light_mode_p(dat)) - return iforwardv("new_offset_r"); + return c_iforwardv(new_offset); rb_scan_args(argc, argv, "01", &vof); if (NIL_P(vof)) rof = 0; else { + vof = sof2nof(vof); if (!daydiff_to_sec(vof, &rof) || rof != 0) - return iforwardv("new_offset_r"); + return c_iforwardv(new_offset); } { get_d_jd(dat); - return d_lite_s_new_internal_wo_civil(CLASS_OF(self), - dat->l.jd, - dat->l.sg, - LIGHT_MODE | HAVE_JD); + return d_lite_new_internal_wo_civil(CLASS_OF(self), + dat->l.jd, + dat->l.sg, + HAVE_JD); + } +} + +static VALUE +d_right_plus(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_d1(self); + if (TYPE(other) == T_FLOAT) + other = rb_rational_new2(f_round(f_mul(other, day_in_nanoseconds)), + day_in_nanoseconds); + return d_right_new(CLASS_OF(self), + f_add(d_lite_ajd(self), other), + d_lite_offset(self), + d_lite_start(self)); } + rb_raise(rb_eTypeError, "expected numeric"); } /* @@ -2147,7 +4725,7 @@ d_lite_plus(VALUE self, VALUE other) get_d1(self); if (!light_mode_p(dat)) - return iforwardop("plus_r"); + return c_iforwardop(plus); switch (TYPE(other)) { case T_FIXNUM: @@ -2159,10 +4737,11 @@ d_lite_plus(VALUE self, VALUE other) jd = dat->l.jd + FIX2LONG(other); if (LIGHTABLE_JD(jd) && jd >= dat->l.sg) - return d_lite_s_new_internal(CLASS_OF(self), - jd, dat->l.sg, - 0, 0, 0, - dat->l.flags & ~HAVE_CIVIL); + return d_lite_new_internal(CLASS_OF(self), + jd, dat->l.sg, + 0, 0, 0, + (dat->l.flags | HAVE_JD) & + ~HAVE_CIVIL); } break; case T_FLOAT: @@ -2174,10 +4753,32 @@ d_lite_plus(VALUE self, VALUE other) } break; } - return iforwardop("plus_r"); + return c_iforwardop(plus); } static VALUE +d_right_minus(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_d1(self); + if (TYPE(other) == T_FLOAT) + other = rb_rational_new2(f_round(f_mul(other, day_in_nanoseconds)), + day_in_nanoseconds); + return d_right_new(CLASS_OF(self), + f_sub(d_lite_ajd(self), other), + d_lite_offset(self), + d_lite_start(self)); + + } + else if (k_date_p(other)) { + return f_sub(d_lite_ajd(self), f_ajd(other)); + } + rb_raise(rb_eTypeError, "expected numeric"); +} + +static VALUE dt_right_minus(VALUE, VALUE); + +static VALUE minus_dd(VALUE self, VALUE other) { get_dt2_cast(self, other); @@ -2217,10 +4818,12 @@ minus_dd(VALUE self, VALUE other) r = f_add(r, rb_rational_new2(INT2FIX(df), INT2FIX(DAY_IN_SECONDS))); if (sf) - r = f_add(r, rb_rational_new2(INT2FIX(sf), day_in_nanoseconds)); + r = f_add(r, rb_rational_new2(LONG2NUM(sf), day_in_nanoseconds)); return r; } - return iforwardop("minus_r"); + if (!k_datetime_p(self)) + return d_right_minus(self, other); + return dt_right_minus(self, other); } /* @@ -2265,10 +4868,286 @@ d_lite_minus(VALUE self, VALUE other) case T_FLOAT: return d_lite_plus(self, DBL2NUM(-NUM2DBL(other))); } - return iforwardop("minus_r"); + return c_iforwardop(minus); +} + +/* + * call-seq: + * d.next_day([n=1]) + * + * Equivalent to d + n. + */ +static VALUE +d_lite_next_day(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return d_lite_plus(self, n); +} + +/* + * call-seq: + * d.prev_day([n=1]) + * + * Equivalent to d - n. + */ +static VALUE +d_lite_prev_day(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return d_lite_minus(self, n); +} + +/* + * call-seq: + * d.next + * + * Return a new Date one day after this one. + */ +static VALUE +d_lite_next(VALUE self) +{ + return d_lite_next_day(0, (VALUE *)NULL, self); +} + +/* + * call-seq: + * d >> n + * + * Return a new Date object that is +n+ months later than + * the current one. + * + * If the day-of-the-month of the current Date is greater + * than the last day of the target month, the day-of-the-month + * of the returned Date will be the last day of the target month. + */ +static VALUE +d_lite_rshift(VALUE self, VALUE other) +{ + VALUE t, y, m, d, sg, j; + + t = f_add3(f_mul(d_lite_year(self), INT2FIX(12)), + f_sub(d_lite_mon(self), INT2FIX(1)), + other); + y = f_idiv(t, INT2FIX(12)); + m = f_mod(t, INT2FIX(12)); + m = f_add(m, INT2FIX(1)); + d = d_lite_mday(self); + sg = d_lite_start(self); + + while (NIL_P(j = rt__valid_civil_p(y, m, d, sg))) { + d = f_sub(d, INT2FIX(1)); + if (f_lt_p(d, INT2FIX(1))) + rb_raise(rb_eArgError, "invalid date"); + } + return f_add(self, f_sub(j, d_lite_jd(self))); +} + +/* + * call-seq: + * d << n + * + * Return a new Date object that is +n+ months earlier than + * the current one. + * + * If the day-of-the-month of the current Date is greater + * than the last day of the target month, the day-of-the-month + * of the returned Date will be the last day of the target month. + */ +static VALUE +d_lite_lshift(VALUE self, VALUE other) +{ + return d_lite_rshift(self, f_negate(other)); +} + +/* + * call-seq: + * d.next_month([n=1]) + * + * Equivalent to d >> n + */ +static VALUE +d_lite_next_month(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return d_lite_rshift(self, n); +} + +/* + * call-seq: + * d.prev_month([n=1]) + * + * Equivalent to d << n + */ +static VALUE +d_lite_prev_month(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return d_lite_lshift(self, n); +} + +/* + * call-seq: + * d.next_year([n=1]) + * + * Equivalent to d >> (n * 12) + */ +static VALUE +d_lite_next_year(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return d_lite_rshift(self, f_mul(n, INT2FIX(12))); +} + +/* + * call-seq: + * d.prev_year([n=1]) + * + * Equivalent to d << (n * 12) + */ +static VALUE +d_lite_prev_year(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return d_lite_lshift(self, f_mul(n, INT2FIX(12))); +} + +/* + * call-seq: + * d.step(limit[, step=1]) + * d.step(limit[, step=1]){|date| ...} + * + * Step the current date forward +step+ days at a + * time (or backward, if +step+ is negative) until + * we reach +limit+ (inclusive), yielding the resultant + * date at each step. + */ +static VALUE +generic_step(int argc, VALUE *argv, VALUE self) +{ + VALUE limit, step, date; + + rb_scan_args(argc, argv, "11", &limit, &step); + + if (argc < 2) + step = INT2FIX(1); + +#if 0 + if (f_zero_p(step)) + rb_raise(rb_eArgError, "step can't be 0"); +#endif + + RETURN_ENUMERATOR(self, argc, argv); + + date = self; + switch (FIX2INT(f_cmp(step, INT2FIX(0)))) { + case -1: + while (f_ge_p(date, limit)) { + rb_yield(date); + date = f_add(date, step); + } + break; + case 0: + while (1) + rb_yield(date); + break; + case 1: + while (f_le_p(date, limit)) { + rb_yield(date); + date = f_add(date, step); + } + break; + default: + abort(); + } + return self; +} + +/* + * call-seq: + * d.upto(max) + * d.upto(max){|date| ...} + * + * Step forward one day at a time until we reach +max+ + * (inclusive), yielding each date as we go. + */ +static VALUE +generic_upto(VALUE self, VALUE max) +{ + VALUE date; + + RETURN_ENUMERATOR(self, 1, &max); + + date = self; + while (f_le_p(date, max)) { + rb_yield(date); + date = f_add(date, INT2FIX(1)); + } + return self; +} + +/* + * call-seq: + * d.downto(min) + * d.downto(min){|date| ...} + * + * Step backward one day at a time until we reach +min+ + * (inclusive), yielding each date as we go. + */ +static VALUE +generic_downto(VALUE self, VALUE min) +{ + VALUE date; + + RETURN_ENUMERATOR(self, 1, &min); + + date = self; + while (f_ge_p(date, min)) { + rb_yield(date); + date = f_add(date, INT2FIX(-1)); + } + return self; } static VALUE +d_right_cmp(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_d1(self); + return f_cmp(d_lite_ajd(self), other); + } + else if (k_date_p(other)) { + return f_cmp(d_lite_ajd(self), f_ajd(other)); + } + return rb_num_coerce_cmp(self, other, rb_intern("<=>")); +} + +static VALUE dt_right_cmp(VALUE, VALUE); + +static VALUE cmp_dd(VALUE self, VALUE other) { get_dt2_cast(self, other); @@ -2306,7 +5185,9 @@ cmp_dd(VALUE self, VALUE other) return INT2FIX(1); } } - return iforwardop("cmp_r"); + if (!k_datetime_p(self)) + return d_right_cmp(self, other); + return dt_right_cmp(self, other); } /* @@ -2378,9 +5259,24 @@ d_lite_cmp(VALUE self, VALUE other) } } } - return iforwardop("cmp_r"); + return c_iforwardop(cmp); +} + +static VALUE +d_right_equal(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_d1(self); + return f_eqeq_p(d_lite_jd(self), other); + } + else if (k_date_p(other)) { + return f_eqeq_p(d_lite_jd(self), f_jd(other)); + } + return rb_num_coerce_cmp(self, other, rb_intern("==")); } +static VALUE dt_right_equal(VALUE, VALUE); + static VALUE equal_dd(VALUE self, VALUE other) { @@ -2397,7 +5293,9 @@ equal_dd(VALUE self, VALUE other) return Qtrue; return Qfalse; } - return iforwardop("equal_r"); + if (!k_datetime_p(self)) + return d_right_equal(self, other); + return dt_right_equal(self, other); } /* @@ -2441,7 +5339,15 @@ d_lite_equal(VALUE self, VALUE other) } } } - return iforwardop("equal_r"); + return c_iforwardop(equal); +} + +static VALUE +d_right_eql_p(VALUE self, VALUE other) +{ + if (k_date_p(other) && f_eqeq_p(self, other)) + return Qtrue; + return Qfalse; } static VALUE @@ -2462,7 +5368,7 @@ eql_p_dd(VALUE self, VALUE other) return Qtrue; return Qfalse; } - return iforwardop("eql_r?"); + return c_iforwardop(eql_p); } /* @@ -2503,7 +5409,15 @@ d_lite_eql_p(VALUE self, VALUE other) } } } - return iforwardop("eql_r?"); + return c_iforwardop(eql_p); +} + +static VALUE +d_right_hash(VALUE self) +{ + get_d1(self); + assert(!light_mode_p(dat)); + return rb_hash(dat->r.ajd); } /* @@ -2517,10 +5431,26 @@ d_lite_hash(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("hash_r"); + return c_iforward0(hash); return rb_hash(d_lite_ajd(self)); } +static VALUE +d_right_to_s(VALUE self) +{ + VALUE a, argv[4]; + + get_d1(self); + assert(!light_mode_p(dat)); + + argv[0] = rb_usascii_str_new2("%.4d-%02d-%02d"); + a = d_right_civil(self); + argv[1] = RARRAY_PTR(a)[0]; + argv[2] = RARRAY_PTR(a)[1]; + argv[3] = RARRAY_PTR(a)[2]; + return rb_f_sprintf(4, argv); +} + /* * call-seq: * d.to_s @@ -2532,7 +5462,7 @@ d_lite_to_s(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("to_s_r"); + return c_iforward0(to_s); { get_d_civil(dat); return rb_enc_sprintf(rb_usascii_encoding(), @@ -2541,6 +5471,23 @@ d_lite_to_s(VALUE self) } } +static VALUE +d_right_inspect(VALUE self) +{ + VALUE argv[6]; + + get_d1(self); + assert(!light_mode_p(dat)); + + argv[0] = rb_usascii_str_new2("#<%s[R]: %s (%s,%s,%s)>"); + argv[1] = rb_class_name(CLASS_OF(self)); + argv[2] = d_right_to_s(self); + argv[3] = dat->r.ajd; + argv[4] = dat->r.of; + argv[5] = dat->r.sg; + return rb_f_sprintf(6, argv); +} + /* * call-seq: * d.inspect @@ -2552,7 +5499,7 @@ d_lite_inspect(VALUE self) { get_d1(self); if (!light_mode_p(dat)) - return iforward0("inspect_r"); + return c_iforward0(inspect); { get_d_civil(dat); get_d_jd(dat); @@ -2609,20 +5556,18 @@ d_lite_set_tmx(VALUE self, struct tmx *tmx) get_d1(self); if (!light_mode_p(dat)) { - tmx->year = iforward0("year_r"); - tmx->yday = FIX2INT(iforward0("yday_r")); - tmx->mon = FIX2INT(iforward0("mon_r")); - tmx->mday = FIX2INT(iforward0("mday_r")); - tmx->hour = FIX2INT(iforward0("hour_r")); - tmx->min = FIX2INT(iforward0("min_r")); - tmx->sec = FIX2INT(iforward0("sec_r")); - tmx->wday = FIX2INT(iforward0("wday_r")); - tmx->offset = INT2FIX(0); - tmx->zone = RSTRING_PTR(iforward0("zone_r")); - tmx->timev = f_mul(f_sub(dat->r.ajd, - rb_rational_new2(INT2FIX(4881175), - INT2FIX(2))), - INT2FIX(86400)); + tmx->year = d_right_year(self); + tmx->yday = FIX2INT(d_right_yday(self)); + tmx->mon = FIX2INT(d_right_mon(self)); + tmx->mday = FIX2INT(d_right_mday(self)); + tmx->hour = FIX2INT(d_right_hour(self)); + tmx->min = FIX2INT(d_right_min(self)); + tmx->sec = FIX2INT(d_right_sec(self)); + tmx->wday = FIX2INT(d_right_wday(self)); + tmx->offset = f_mul(dat->r.of, INT2FIX(DAY_IN_SECONDS)); + tmx->zone = RSTRING_PTR(d_right_zone(self)); + tmx->timev = f_mul(f_sub(dat->r.ajd, UNIX_EPOCH_IN_AJD), + INT2FIX(DAY_IN_SECONDS)); } else { get_d_jd(dat); @@ -2639,7 +5584,7 @@ d_lite_set_tmx(VALUE self, struct tmx *tmx) tmx->offset = INT2FIX(0); tmx->zone = "+00:00"; tmx->timev = f_mul(INT2FIX(dat->l.jd - 2440588), - INT2FIX(86400)); + INT2FIX(DAY_IN_SECONDS)); } } @@ -2648,7 +5593,6 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self, const char *default_fmt, void (*func)(VALUE, struct tmx *)) { - get_d1(self); { VALUE vfmt; const char *fmt; @@ -2695,6 +5639,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self, str = rb_str_new(buf, len); if (buf != buffer) xfree(buf); rb_enc_copy(str, vfmt); + OBJ_INFECT(str, vfmt); return str; } } @@ -2712,6 +5657,134 @@ d_lite_strftime(int argc, VALUE *argv, VALUE self) "%F", d_lite_set_tmx); } +static VALUE +strftimev(const char *fmt, VALUE self, + void (*func)(VALUE, struct tmx *)) +{ + char buffer[SMALLBUF], *buf = buffer; + struct tmx tmx; + long len; + VALUE str; + + (*func)(self, &tmx); + len = date_strftime_alloc(&buf, fmt, &tmx); + str = rb_usascii_str_new(buf, len); + if (buf != buffer) xfree(buf); + return str; +} + +/* + * call-seq: + * d.asctime + * d.ctime + * + * Equivalent to strftime('%c'). + * See also asctime(3) or ctime(3). + */ +static VALUE +d_lite_asctime(VALUE self) +{ + return strftimev("%c", self, d_lite_set_tmx); +} + +/* + * call-seq: + * d.iso8601 + * d.xmlschema + * + * Equivalent to strftime('%F'). + */ +static VALUE +d_lite_iso8601(VALUE self) +{ + return strftimev("%F", self, d_lite_set_tmx); +} + +/* + * call-seq: + * d.rfc3339 + * + * Equivalent to strftime('%FT%T%:z'). + */ +static VALUE +d_lite_rfc3339(VALUE self) +{ + return strftimev("%FT%T%:z", self, d_lite_set_tmx); +} + +/* + * call-seq: + * d.rfc2822 + * d.rfc822 + * + * Equivalent to strftime('%a, %-d %b %Y %T %z'). + */ +static VALUE +d_lite_rfc2822(VALUE self) +{ + return strftimev("%a, %-d %b %Y %T %z", self, d_lite_set_tmx); +} + +/* + * call-seq: + * d.httpdate + * + * Equivalent to strftime('%a, %d %b %Y %T GMT'). + * See also RFC 2616. + */ +static VALUE +d_lite_httpdate(VALUE self) +{ + VALUE argv[1], d; + + argv[0] = INT2FIX(0); + d = d_lite_new_offset(1, argv, self); + return strftimev("%a, %d %b %Y %T GMT", d, d_lite_set_tmx); +} + +static VALUE +gengo(VALUE jd, VALUE y, VALUE *a) +{ + if (f_lt_p(jd, INT2FIX(2405160))) + return 0; + if (f_lt_p(jd, INT2FIX(2419614))) { + a[0] = rb_usascii_str_new2("M%02d"); + a[1] = f_sub(y, INT2FIX(1867)); + } + else if (f_lt_p(jd, INT2FIX(2424875))) { + a[0] = rb_usascii_str_new2("T%02d"); + a[1] = f_sub(y, INT2FIX(1911)); + } + else if (f_lt_p(jd, INT2FIX(2447535))) { + a[0] = rb_usascii_str_new2("S%02d"); + a[1] = f_sub(y, INT2FIX(1925)); + } + else { + a[0] = rb_usascii_str_new2("H%02d"); + a[1] = f_sub(y, INT2FIX(1988)); + } + return 1; +} + +/* + * call-seq: + * d.jisx0301 + * + * Return a string as a JIS X 0301 format. + */ +static VALUE +d_lite_jisx0301(VALUE self) +{ + VALUE argv[2]; + + if (!gengo(d_lite_jd(self), + d_lite_year(self), + argv)) + return strftimev("%F", self, d_lite_set_tmx); + return f_add(rb_f_sprintf(2, argv), + strftimev(".%m.%d", self, d_lite_set_tmx)); +} + /* * call-seq: * d.marshal_dump @@ -2783,28 +5856,51 @@ d_lite_marshal_load(VALUE self, VALUE a) return self; } -static VALUE -d_right_cache(VALUE self) +/* datetime light */ + +static void +dt_right_gc_mark(union DateTimeData *dat) { - get_d1(self); - if (light_mode_p(dat)) - return Qnil; - return dat->r.cache; + assert(!light_mode_p(dat)); + rb_gc_mark(dat->r.ajd); + rb_gc_mark(dat->r.of); + rb_gc_mark(dat->r.sg); + rb_gc_mark(dat->r.cache); } -/* datetime light */ +#define dt_lite_gc_mark 0 + +inline static VALUE +dt_right_new_internal(VALUE klass, VALUE ajd, VALUE of, VALUE sg, + unsigned flags) +{ + union DateTimeData *dat; + VALUE obj; + + obj = Data_Make_Struct(klass, union DateTimeData, + dt_right_gc_mark, -1, dat); + + dat->r.ajd = ajd; + dat->r.of = of; + dat->r.sg = sg; + dat->r.cache = rb_hash_new(); + dat->r.flags = flags | DATETIME_OBJ; + + return obj; +} inline static VALUE -dt_lite_s_new_internal(VALUE klass, long jd, int df, - long sf, int of, double sg, - int y, int m, int d, - int h, int min, int s, - unsigned flags) +dt_lite_new_internal(VALUE klass, long jd, int df, + long sf, int of, double sg, + int y, int m, int d, + int h, int min, int s, + unsigned flags) { union DateTimeData *dat; VALUE obj; - obj = Data_Make_Struct(klass, union DateTimeData, 0, -1, dat); + obj = Data_Make_Struct(klass, union DateTimeData, + dt_lite_gc_mark, -1, dat); dat->l.jd = jd; dat->l.df = df; @@ -2817,67 +5913,227 @@ dt_lite_s_new_internal(VALUE klass, long jd, int df, dat->l.hour = h; dat->l.min = min; dat->l.sec = s; - dat->l.flags = flags; + dat->l.flags = flags | LIGHT_MODE | DATETIME_OBJ; return obj; } static VALUE -dt_lite_s_new_internal_wo_civil(VALUE klass, long jd, int df, - long sf, int of, double sg, - unsigned flags) +dt_lite_new_internal_wo_civil(VALUE klass, long jd, int df, + long sf, int of, double sg, + unsigned flags) { - return dt_lite_s_new_internal(klass, jd, df, sf, of, sg, + return dt_lite_new_internal(klass, jd, df, sf, of, sg, 0, 0, 0, 0, 0, 0, flags); } static VALUE dt_lite_s_alloc(VALUE klass) { - return dt_lite_s_new_internal_wo_civil(klass, 0, 0, 0, 0, 0, LIGHT_MODE); + return dt_lite_new_internal_wo_civil(klass, 0, 0, 0, 0, 0, 0); } static VALUE -dt_lite_s_new_l_bang(int argc, VALUE *argv, VALUE klass) +dt_right_new(VALUE klass, VALUE ajd, VALUE of, VALUE sg) { - VALUE vjd, vdf, vsf, vof, vsg; - long jd; + return dt_right_new_internal(klass, ajd, of, sg, 0); +} + +static VALUE +dt_lite_new(VALUE klass, VALUE jd, VALUE df, VALUE sf, VALUE of, VALUE sg) +{ + return dt_lite_new_internal_wo_civil(klass, + NUM2LONG(jd), + FIX2INT(df), + NUM2LONG(sf), + FIX2INT(of), + NUM2DBL(sg), + HAVE_JD | HAVE_DF); +} - rb_scan_args(argc, argv, "05", &vjd, &vdf, &vsf, &vof, &vsg); +static VALUE +dt_switch_new(VALUE klass, VALUE ajd, VALUE of, VALUE sg) +{ + VALUE t, jd, df, sf, ssf, odf, osf; - if (argc < 1) - vjd = INT2FIX(0); - if (argc < 2) - vdf = INT2FIX(0); - if (argc < 3) - vsf = INT2FIX(0); - if (argc < 4) - vof = INT2FIX(0); - if (argc < 5) - vsg = INT2FIX(0); + t = rt_ajd_to_jd(ajd, INT2FIX(0)); /* as utc */ + jd = RARRAY_PTR(t)[0]; + df = RARRAY_PTR(t)[1]; + + t = f_mul(df, INT2FIX(DAY_IN_SECONDS)); + df = f_idiv(t, INT2FIX(1)); + sf = f_mod(t, INT2FIX(1)); + + t = f_mul(sf, INT2FIX(SECOND_IN_NANOSECONDS)); + sf = f_idiv(t, INT2FIX(1)); + ssf = f_mod(t, INT2FIX(1)); + + t = f_mul(of, INT2FIX(DAY_IN_SECONDS)); + odf = f_truncate(t); + osf = f_remainder(t, INT2FIX(1)); + +#ifdef FORCE_RIGHT + if (1) +#else + if (!FIXNUM_P(jd) || + f_lt_p(jd, sg) || !f_zero_p(ssf) || !f_zero_p(osf) || + !LIGHTABLE_JD(NUM2LONG(jd))) +#endif + return dt_right_new(klass, ajd, of, sg); + else + return dt_lite_new(klass, jd, df, sf, odf, sg); +} + +#ifndef NDEBUG +static VALUE +dt_right_new_m(int argc, VALUE *argv, VALUE klass) +{ + VALUE ajd, of, sg; + + rb_scan_args(argc, argv, "03", &ajd, &of, &sg); + + switch (argc) { + case 0: + ajd = INT2FIX(0); + case 1: + of = INT2FIX(0); + case 2: + sg = INT2FIX(ITALY); + } + + return dt_right_new(klass, ajd, of, sg); +} + +static VALUE +dt_lite_new_m(int argc, VALUE *argv, VALUE klass) +{ + VALUE jd, df, sf, of, sg; + + rb_scan_args(argc, argv, "05", &jd, &df, &sf, &of, &sg); + + switch (argc) { + case 0: + jd = INT2FIX(0); + case 1: + df = INT2FIX(0); + case 2: + sf = INT2FIX(0); + case 3: + of = INT2FIX(0); + case 4: + sg = INT2FIX(ITALY); + } + + return dt_lite_new(klass, jd, df, sf, of, sg); +} + +static VALUE +dt_switch_new_m(int argc, VALUE *argv, VALUE klass) +{ + VALUE ajd, of, sg; + + rb_scan_args(argc, argv, "03", &ajd, &of, &sg); + + switch (argc) { + case 0: + ajd = INT2FIX(0); + case 1: + of = INT2FIX(0); + case 2: + sg = INT2FIX(ITALY); + } + + return dt_switch_new(klass, ajd, of, sg); +} +#endif + +static VALUE +dt_right_new_jd(VALUE klass, VALUE jd, VALUE fr, VALUE of, VALUE sg) +{ + return dt_right_new(klass, + rt_jd_to_ajd(jd, fr, of), + of, + sg); +} + +#ifndef NDEBUG +static VALUE +dt_lite_new_jd(VALUE klass, VALUE jd, VALUE fr, VALUE of, VALUE sg) +{ + VALUE n, df, sf; + + n = f_mul(fr, INT2FIX(DAY_IN_SECONDS)); + df = f_idiv(n, INT2FIX(1)); + sf = f_mod(n, INT2FIX(1)); + n = f_mul(sf, INT2FIX(SECOND_IN_NANOSECONDS)); + sf = f_idiv(n, INT2FIX(1)); + return dt_lite_new(klass, jd, df, sf, of, sg); +} +#endif - if (!FIXNUM_P(vjd) || - !FIXNUM_P(vdf) || - !FIXNUM_P(vsf) || - !FIXNUM_P(vof)) - rb_raise(rb_eArgError, "cannot create"); - jd = NUM2LONG(vjd); - if (!LIGHTABLE_JD(jd)) - rb_raise(rb_eArgError, "cannot create"); +static VALUE +dt_switch_new_jd(VALUE klass, VALUE jd, VALUE fr, VALUE of, VALUE sg) +{ + return dt_switch_new(klass, + rt_jd_to_ajd(jd, fr, of), + of, + sg); +} - return dt_lite_s_new_internal_wo_civil(klass, - jd, - FIX2INT(vdf), - FIX2INT(vsf), - FIX2INT(vof), - NUM2DBL(vsg), - LIGHT_MODE | HAVE_JD | HAVE_DF); +#ifndef NDEBUG +static VALUE +datetime_s_new_r_bang(int argc, VALUE *argv, VALUE klass) +{ + return dt_right_new_m(argc, argv, klass); } static VALUE datetime_s_new_l_bang(int argc, VALUE *argv, VALUE klass) { - return dt_lite_s_new_l_bang(argc, argv, klass); + return dt_lite_new_m(argc, argv, klass); +} + +static VALUE +datetime_s_new_bang(int argc, VALUE *argv, VALUE klass) +{ + return dt_switch_new_m(argc, argv, klass); +} +#endif + +#undef c_cforwardv +#define c_cforwardv(m) rt_datetime_s_##m(argc, argv, klass) + +static VALUE +rt_datetime_s_jd(int argc, VALUE *argv, VALUE klass) +{ + VALUE jd, h, min, s, of, sg, fr; + + rb_scan_args(argc, argv, "06", &jd, &h, &min, &s, &of, &sg); + + switch (argc) { + case 0: + jd = INT2FIX(0); + case 1: + h = INT2FIX(0); + case 2: + min = INT2FIX(0); + case 3: + s = INT2FIX(0); + case 4: + of = INT2FIX(0); + case 5: + sg = INT2FIX(ITALY); + } + + jd = rt__valid_jd_p(jd, sg); + fr = rt__valid_time_p(h, min, s); + + if (NIL_P(jd) || NIL_P(fr)) + rb_raise(rb_eArgError, "invalid date"); + + of = sof2nof(of); + + return dt_right_new_jd(klass, jd, fr, of, sg); } /* @@ -2906,24 +6162,26 @@ datetime_s_jd(int argc, VALUE *argv, VALUE klass) int h, min, s, rh, rmin, rs, rof; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(jd); +#endif + rb_scan_args(argc, argv, "06", &vjd, &vh, &vmin, &vs, &vof, &vsg); if (!FIXNUM_P(vjd)) - return cforwardv("jd_r"); - - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + return c_cforwardv(jd); jd = h = min = s = 0; rof = 0; + sg = ITALY; switch (argc) { case 6: + sg = NUM2DBL(vsg); case 5: + vof = sof2nof(vof); if (!daydiff_to_sec(vof, &rof)) - return cforwardv("jd_r"); + return c_cforwardv(jd); case 4: s = NUM2INT(vs); case 3: @@ -2933,21 +6191,56 @@ datetime_s_jd(int argc, VALUE *argv, VALUE klass) case 1: jd = NUM2LONG(vjd); if (!LIGHTABLE_JD(jd)) - return cforwardv("jd_r"); + return c_cforwardv(jd); } if (jd < sg) - return cforwardv("jd_r"); + return c_cforwardv(jd); if (!valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); - return dt_lite_s_new_internal(klass, - jd_local_to_utc(jd, - time_to_df(rh, rmin, rs), - rof), - 0, 0, rof, sg, 0, 0, 0, rh, rmin, rs, - LIGHT_MODE | HAVE_JD | HAVE_TIME); + return dt_lite_new_internal(klass, + jd_local_to_utc(jd, + time_to_df(rh, rmin, rs), + rof), + 0, 0, rof, sg, 0, 0, 0, rh, rmin, rs, + HAVE_JD | HAVE_TIME); +} + +static VALUE +rt_datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, d, h, min, s, of, sg, jd, fr; + + rb_scan_args(argc, argv, "07", &y, &d, &h, &min, &s, &of, &sg); + + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + d = INT2FIX(1); + case 2: + h = INT2FIX(0); + case 3: + min = INT2FIX(0); + case 4: + s = INT2FIX(0); + case 5: + of = INT2FIX(0); + case 6: + sg = INT2FIX(ITALY); + } + + jd = rt__valid_ordinal_p(y, d, sg); + fr = rt__valid_time_p(h, min, s); + + if (NIL_P(jd) || NIL_P(fr)) + rb_raise(rb_eArgError, "invalid date"); + + of = sof2nof(of); + + return dt_right_new_jd(klass, jd, fr, of, sg); } /* @@ -2976,6 +6269,10 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) int y, d, rd, h, min, s, rh, rmin, rs, rof; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(ordinal); +#endif + rb_scan_args(argc, argv, "07", &vy, &vd, &vh, &vmin, &vs, &vof, &vsg); if (!((NIL_P(vy) || FIXNUM_P(vy)) && @@ -2983,24 +6280,22 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) (NIL_P(vh) || FIXNUM_P(vh)) && (NIL_P(vmin) || FIXNUM_P(vmin)) && (NIL_P(vs) || FIXNUM_P(vs)))) - return cforwardv("ordinal_r"); - - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + return c_cforwardv(ordinal); y = -4712; d = 1; h = min = s = 0; rof = 0; + sg = ITALY; switch (argc) { case 7: + sg = NUM2DBL(vsg); case 6: + vof = sof2nof(vof); if (!daydiff_to_sec(vof, &rof)) - return cforwardv("ordinal_r"); + return c_cforwardv(ordinal); case 5: s = NUM2INT(vs); case 4: @@ -3012,7 +6307,7 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) case 1: y = NUM2INT(vy); if (!LIGHTABLE_YEAR(y)) - return cforwardv("ordinal_r"); + return c_cforwardv(ordinal); } { @@ -3025,16 +6320,53 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) rb_raise(rb_eArgError, "invalid date"); if (!LIGHTABLE_JD(jd) || !ns) - return cforwardv("ordinal_r"); + return c_cforwardv(ordinal); + + return dt_lite_new_internal(klass, + jd_local_to_utc(jd, + time_to_df(rh, rmin, rs), + rof), + 0, 0, rof, sg, + 0, 0, 0, rh, rmin, rs, + HAVE_JD | HAVE_TIME); + } +} + +static VALUE +rt_datetime_s_civil(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, m, d, h, min, s, of, sg, jd, fr; + + rb_scan_args(argc, argv, "08", &y, &m, &d, &h, &min, &s, &of, &sg); - return dt_lite_s_new_internal(klass, - jd_local_to_utc(jd, - time_to_df(rh, rmin, rs), - rof), - 0, 0, rof, sg, - 0, 0, 0, rh, rmin, rs, - LIGHT_MODE | HAVE_JD | HAVE_TIME); + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + m = INT2FIX(1); + case 2: + d = INT2FIX(1); + case 3: + h = INT2FIX(0); + case 4: + min = INT2FIX(0); + case 5: + s = INT2FIX(0); + case 6: + of = INT2FIX(0); + case 7: + sg = INT2FIX(ITALY); } + + jd = rt__valid_civil_p(y, m, d, sg); + fr = rt__valid_time_p(h, min, s); + + if (NIL_P(jd) || NIL_P(fr)) + rb_raise(rb_eArgError, "invalid date"); + + of = sof2nof(of); + + return dt_right_new_jd(klass, jd, fr, of, sg); } /* @@ -3064,6 +6396,10 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass) int y, m, d, rm, rd, h, min, s, rh, rmin, rs, rof; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(civil); +#endif + rb_scan_args(argc, argv, "08", &vy, &vm, &vd, &vh, &vmin, &vs, &vof, &vsg); if (!((NIL_P(vy) || FIXNUM_P(vy)) && @@ -3072,12 +6408,7 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass) (NIL_P(vh) || FIXNUM_P(vh)) && (NIL_P(vmin) || FIXNUM_P(vmin)) && (NIL_P(vs) || FIXNUM_P(vs)))) - return cforwardv("civil_r"); - - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + return c_cforwardv(civil); y = -4712; m = 1; @@ -3085,12 +6416,15 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass) h = min = s = 0; rof = 0; + sg = ITALY; switch (argc) { case 8: + sg = NUM2DBL(vsg); case 7: + vof = sof2nof(vof); if (!daydiff_to_sec(vof, &rof)) - return cforwardv("civil_r"); + return c_cforwardv(civil); case 6: s = NUM2INT(vs); case 5: @@ -3104,7 +6438,7 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass) case 1: y = NUM2INT(vy); if (!LIGHTABLE_YEAR(y)) - return cforwardv("civil_r"); + return c_cforwardv(civil); } if (isinf(sg) && sg < 0) { @@ -3113,9 +6447,9 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass) if (!valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); - return dt_lite_s_new_internal(klass, 0, 0, 0, rof, sg, - y, rm, rd, rh, rmin, rs, - LIGHT_MODE | HAVE_CIVIL | HAVE_TIME); + return dt_lite_new_internal(klass, 0, 0, 0, rof, sg, + y, rm, rd, rh, rmin, rs, + HAVE_CIVIL | HAVE_TIME); } else { long jd; @@ -3127,17 +6461,53 @@ datetime_s_civil(int argc, VALUE *argv, VALUE klass) rb_raise(rb_eArgError, "invalid date"); if (!LIGHTABLE_JD(jd) || !ns) - return cforwardv("civil_r"); + return c_cforwardv(civil); + + return dt_lite_new_internal(klass, + jd_local_to_utc(jd, + time_to_df(rh, rmin, rs), + rof), + 0, 0, rof, sg, + y, rm, rd, rh, rmin, rs, + HAVE_JD | HAVE_CIVIL | HAVE_TIME); + } +} + +static VALUE +rt_datetime_s_commercial(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, w, d, h, min, s, of, sg, jd, fr; - return dt_lite_s_new_internal(klass, - jd_local_to_utc(jd, - time_to_df(rh, rmin, rs), - rof), - 0, 0, rof, sg, - y, rm, rd, rh, rmin, rs, - LIGHT_MODE | HAVE_JD | - HAVE_CIVIL | HAVE_TIME); + rb_scan_args(argc, argv, "08", &y, &w, &d, &h, &min, &s, &of, &sg); + + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + w = INT2FIX(1); + case 2: + d = INT2FIX(1); + case 3: + h = INT2FIX(0); + case 4: + min = INT2FIX(0); + case 5: + s = INT2FIX(0); + case 6: + of = INT2FIX(0); + case 7: + sg = INT2FIX(ITALY); } + + jd = rt__valid_commercial_p(y, w, d, sg); + fr = rt__valid_time_p(h, min, s); + + if (NIL_P(jd) || NIL_P(fr)) + rb_raise(rb_eArgError, "invalid date"); + + of = sof2nof(of); + + return dt_right_new_jd(klass, jd, fr, of, sg); } /* @@ -3167,6 +6537,10 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass) int y, w, d, rw, rd, h, min, s, rh, rmin, rs, rof; double sg; +#ifdef FORCE_RIGHT + return c_cforwardv(commercial); +#endif + rb_scan_args(argc, argv, "08", &vy, &vw, &vd, &vh, &vmin, &vs, &vof, &vsg); if (!((NIL_P(vy) || FIXNUM_P(vy)) && @@ -3175,12 +6549,7 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass) (NIL_P(vh) || FIXNUM_P(vh)) && (NIL_P(vmin) || FIXNUM_P(vmin)) && (NIL_P(vs) || FIXNUM_P(vs)))) - return cforwardv("commercial_r"); - - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else - sg = ITALY; + return c_cforwardv(commercial); y = -4712; w = 1; @@ -3188,12 +6557,15 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass) h = min = s = 0; rof = 0; + sg = ITALY; switch (argc) { case 8: + sg = NUM2DBL(vsg); case 7: + vof = sof2nof(vof); if (!daydiff_to_sec(vof, &rof)) - return cforwardv("commercial_r"); + return c_cforwardv(commercial); case 6: s = NUM2INT(vs); case 5: @@ -3207,7 +6579,7 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass) case 1: y = NUM2INT(vy); if (!LIGHTABLE_CWYEAR(y)) - return cforwardv("commercial_r"); + return c_cforwardv(commercial); } { @@ -3220,18 +6592,256 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass) rb_raise(rb_eArgError, "invalid date"); if (!LIGHTABLE_JD(jd) || !ns) - return cforwardv("commercial_r"); + return c_cforwardv(commercial); + + return dt_lite_new_internal(klass, + jd_local_to_utc(jd, + time_to_df(rh, rmin, rs), + rof), + 0, 0, rof, sg, + 0, 0, 0, rh, rmin, rs, + HAVE_JD | HAVE_TIME); + } +} + +#ifndef NDEBUG +static VALUE +rt_datetime_s_weeknum(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, w, d, f, h, min, s, of, sg, jd, fr; + + rb_scan_args(argc, argv, "09", &y, &w, &d, &f, &h, &min, &s, &of, &sg); - return dt_lite_s_new_internal(klass, - jd_local_to_utc(jd, - time_to_df(rh, rmin, rs), - rof), - 0, 0, rof, sg, - 0, 0, 0, rh, rmin, rs, - LIGHT_MODE | HAVE_JD | HAVE_TIME); + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + w = INT2FIX(0); + case 2: + d = INT2FIX(1); + case 3: + f = INT2FIX(0); + case 4: + h = INT2FIX(0); + case 5: + min = INT2FIX(0); + case 6: + s = INT2FIX(0); + case 7: + of = INT2FIX(0); + case 8: + sg = INT2FIX(ITALY); } + + jd = rt__valid_weeknum_p(y, w, d, f, sg); + fr = rt__valid_time_p(h, min, s); + + if (NIL_P(jd) || NIL_P(fr)) + rb_raise(rb_eArgError, "invalid date"); + + of = sof2nof(of); + + return dt_right_new_jd(klass, jd, fr, of, sg); } +static VALUE +datetime_s_weeknum(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vw, vd, vf, vh, vmin, vs, vof, vsg; + int y, w, d, f, rw, rd, h, min, s, rh, rmin, rs, rof; + double sg; + +#ifdef FORCE_RIGHT + return c_cforwardv(weeknum); +#endif + + rb_scan_args(argc, argv, "09", &vy, &vw, &vd, &vf, + &vh, &vmin, &vs, &vof, &vsg); + + if (!((NIL_P(vy) || FIXNUM_P(vy)) && + (NIL_P(vw) || FIXNUM_P(vw)) && + (NIL_P(vd) || FIXNUM_P(vd)) && + (NIL_P(vf) || FIXNUM_P(vf)) && + (NIL_P(vh) || FIXNUM_P(vh)) && + (NIL_P(vmin) || FIXNUM_P(vmin)) && + (NIL_P(vs) || FIXNUM_P(vs)))) + return c_cforwardv(weeknum); + + y = -4712; + w = 0; + d = 1; + f = 0; + + h = min = s = 0; + rof = 0; + sg = ITALY; + + switch (argc) { + case 9: + sg = NUM2DBL(vsg); + case 8: + vof = sof2nof(vof); + if (!daydiff_to_sec(vof, &rof)) + return c_cforwardv(weeknum); + case 7: + s = NUM2INT(vs); + case 6: + min = NUM2INT(vmin); + case 5: + h = NUM2INT(vh); + case 4: + f = NUM2INT(vf); + case 3: + d = NUM2INT(vd); + case 2: + w = NUM2INT(vw); + case 1: + y = NUM2INT(vy); + if (!LIGHTABLE_YEAR(y)) + return c_cforwardv(weeknum); + } + + { + long jd; + int ns; + + if (!valid_weeknum_p(y, w, d, f, sg, &rw, &rd, &jd, &ns)) + rb_raise(rb_eArgError, "invalid date"); + if (!valid_time_p(h, min, s, &rh, &rmin, &rs)) + rb_raise(rb_eArgError, "invalid date"); + + if (!LIGHTABLE_JD(jd) || !ns) + return c_cforwardv(weeknum); + + return dt_lite_new_internal(klass, + jd_local_to_utc(jd, + time_to_df(rh, rmin, rs), + rof), + 0, 0, rof, sg, + 0, 0, 0, rh, rmin, rs, + HAVE_JD | HAVE_TIME); + } +} + +static VALUE +rt_datetime_s_nth_kday(int argc, VALUE *argv, VALUE klass) +{ + VALUE y, m, n, k, h, min, s, of, sg, jd, fr; + + rb_scan_args(argc, argv, "09", &y, &m, &n, &k, &h, &min, &s, &of, &sg); + + switch (argc) { + case 0: + y = INT2FIX(-4712); + case 1: + m = INT2FIX(1); + case 2: + n = INT2FIX(1); + case 3: + k = INT2FIX(1); + case 4: + h = INT2FIX(0); + case 5: + min = INT2FIX(0); + case 6: + s = INT2FIX(0); + case 7: + of = INT2FIX(0); + case 8: + sg = INT2FIX(ITALY); + } + + jd = rt__valid_nth_kday_p(y, m, n, k, sg); + fr = rt__valid_time_p(h, min, s); + + if (NIL_P(jd) || NIL_P(fr)) + rb_raise(rb_eArgError, "invalid date"); + + of = sof2nof(of); + + return dt_right_new_jd(klass, jd, fr, of, sg); +} + +static VALUE +datetime_s_nth_kday(int argc, VALUE *argv, VALUE klass) +{ + VALUE vy, vm, vn, vk, vh, vmin, vs, vof, vsg; + int y, m, n, k, rm, rn, rk, h, min, s, rh, rmin, rs, rof; + double sg; + +#ifdef FORCE_RIGHT + return c_cforwardv(nth_kday); +#endif + + rb_scan_args(argc, argv, "09", &vy, &vm, &vn, &vk, + &vh, &vmin, &vs, &vof, &vsg); + + if (!((NIL_P(vy) || FIXNUM_P(vy)) && + (NIL_P(vm) || FIXNUM_P(vm)) && + (NIL_P(vn) || FIXNUM_P(vn)) && + (NIL_P(vk) || FIXNUM_P(vk)) && + (NIL_P(vh) || FIXNUM_P(vh)) && + (NIL_P(vmin) || FIXNUM_P(vmin)) && + (NIL_P(vs) || FIXNUM_P(vs)))) + return c_cforwardv(nth_kday); + + y = -4712; + m = 1; + n = 1; + k = 1; + + h = min = s = 0; + rof = 0; + sg = ITALY; + + switch (argc) { + case 9: + sg = NUM2DBL(vsg); + case 8: + vof = sof2nof(vof); + if (!daydiff_to_sec(vof, &rof)) + return c_cforwardv(nth_kday); + case 7: + s = NUM2INT(vs); + case 6: + min = NUM2INT(vmin); + case 5: + h = NUM2INT(vh); + case 4: + k = NUM2INT(vk); + case 3: + n = NUM2INT(vn); + case 2: + m = NUM2INT(vm); + case 1: + y = NUM2INT(vy); + if (!LIGHTABLE_YEAR(y)) + return c_cforwardv(nth_kday); + } + + { + long jd; + int ns; + + if (!valid_nth_kday_p(y, m, n, k, sg, &rm, &rn, &rk, &jd, &ns)) + rb_raise(rb_eArgError, "invalid date"); + if (!valid_time_p(h, min, s, &rh, &rmin, &rs)) + rb_raise(rb_eArgError, "invalid date"); + + if (!LIGHTABLE_JD(jd) || !ns) + return c_cforwardv(nth_kday); + + return dt_lite_new_internal(klass, + jd_local_to_utc(jd, + time_to_df(rh, rmin, rs), + rof), + 0, 0, rof, sg, + 0, 0, 0, rh, rmin, rs, + HAVE_JD | HAVE_TIME); + } +} +#endif + /* * call-seq: * DateTime.now([start=Date::ITALY]) @@ -3252,15 +6862,15 @@ datetime_s_now(int argc, VALUE *argv, VALUE klass) #endif time_t sec; struct tm tm; - long sf; - int y, m, d, h, min, s, of; + long sf, of; + int y, m, d, h, min, s; rb_scan_args(argc, argv, "01", &vsg); - if (!NIL_P(vsg)) - sg = NUM2DBL(vsg); - else + if (argc < 1) sg = ITALY; + else + sg = NUM2DBL(vsg); #ifdef HAVE_CLOCK_GETTIME if (clock_gettime(CLOCK_REALTIME, &ts) == -1) @@ -3282,9 +6892,9 @@ datetime_s_now(int argc, VALUE *argv, VALUE klass) if (s == 60) s = 59; #ifdef HAVE_STRUCT_TM_TM_GMTOFF - of = (int)tm.tm_gmtoff; + of = tm.tm_gmtoff; #else - of = (int)-timezone; + of = -timezone; #endif #ifdef HAVE_CLOCK_GETTIME sf = ts.tv_nsec; @@ -3292,28 +6902,67 @@ datetime_s_now(int argc, VALUE *argv, VALUE klass) sf = tv.tv_usec * 1000; #endif +#ifdef FORCE_RIGHT + goto right; +#endif + if (!LIGHTABLE_YEAR(y)) - rb_raise(rb_eArgError, "cannot create"); + goto right; + + if (of < -DAY_IN_SECONDS || of > DAY_IN_SECONDS) + goto right; if (isinf(sg) && sg < 0) - return dt_lite_s_new_internal(klass, 0, 0, sf, of, sg, - y, m, d, h, min, s, - LIGHT_MODE | HAVE_CIVIL | HAVE_TIME); + return dt_lite_new_internal(klass, 0, 0, sf, (int)of, sg, + y, m, d, h, min, s, + HAVE_CIVIL | HAVE_TIME); else { long jd; int ns; civil_to_jd(y, m, d, sg, &jd, &ns); - return dt_lite_s_new_internal(klass, - jd_local_to_utc(jd, - time_to_df(h, min, s), - of), - 0, sf, of, sg, - y, m, d, h, min, s, - LIGHT_MODE | HAVE_JD | - HAVE_CIVIL | HAVE_TIME); + return dt_lite_new_internal(klass, + jd_local_to_utc(jd, + time_to_df(h, min, s), + of), + 0, sf, of, sg, + y, m, d, h, min, s, + HAVE_JD | HAVE_CIVIL | HAVE_TIME); } + right: + { + VALUE jd, fr, vof, ajd; + + jd = rt_civil_to_jd(INT2FIX(y), INT2FIX(m), INT2FIX(d), DBL2NUM(sg)); + fr = rt_time_to_day_fraction(INT2FIX(h), INT2FIX(min), INT2FIX(s)); + fr = f_add(fr, rb_rational_new(LONG2NUM(sf), day_in_nanoseconds)); + vof = rb_rational_new(LONG2NUM(of), INT2FIX(DAY_IN_SECONDS)); + ajd = rt_jd_to_ajd(jd, fr, vof); + return dt_right_new(klass, ajd, vof, DBL2NUM(sg)); + } +} + +static VALUE +dt_switch_new_by_frags(VALUE klass, VALUE hash, VALUE sg) +{ + VALUE jd, fr, of, t; + + hash = rt_rewrite_frags(hash); + hash = rt_complete_frags(klass, hash); + jd = rt__valid_date_frags_p(hash, sg); + fr = rt__valid_time_frags_p(hash); + if (NIL_P(jd) || NIL_P(fr)) + rb_raise(rb_eArgError, "invalid date"); + t = ref_hash("sec_fraction"); + if (!NIL_P(t)) + fr = f_add(fr, f_div(t, INT2FIX(DAY_IN_SECONDS))); + t = ref_hash("offset"); + if (NIL_P(t)) + of = INT2FIX(0); + else + of = rb_rational_new2(t, INT2FIX(DAY_IN_SECONDS)); + return dt_switch_new_jd(klass, jd, fr, of, sg); } /* @@ -3330,6 +6979,272 @@ datetime_s__strptime(int argc, VALUE *argv, VALUE klass) /* * call-seq: + * DateTime.strptime([string="-4712-01-01T00:00:00+00:00"[, format="%FT%T%z"[,start=ITALY]]]) + * + * Create a new DateTime object by parsing from a String + * according to a specified format. + * + * +str+ is a String holding a date-time representation. + * +fmt+ is the format that the date-time is in. See + * date/format.rb for details on supported formats. + * + * The default +str+ is '-4712-01-01T00:00:00+00:00', and the default + * +fmt+ is '%FT%T%z'. This gives midnight on Julian Day Number day 0. + * + * +sg+ specifies the Day of Calendar Reform. + * + * An ArgumentError will be raised if +str+ cannot be + * parsed. + */ +static VALUE +datetime_s_strptime(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, fmt, sg; + + rb_scan_args(argc, argv, "03", &str, &fmt, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + case 1: + fmt = rb_str_new2("%FT%T%z"); + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE argv2[2], hash; + + argv2[0] = str; + argv2[1] = fmt; + hash = date_s__strptime(2, argv2, klass); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * Date.parse(string="-4712-01-01T00:00:00+00:00"[, comp=true[,start=ITALY]]) + * + * Create a new DateTime object by parsing from a String, + * without specifying the format. + * + * +str+ is a String holding a date-time representation. + * +comp+ specifies whether to interpret 2-digit years + * as 19XX (>= 69) or 20XX (< 69); the default is to. + * The method will attempt to parse a date-time from the String + * using various heuristics; see #_parse in date/format.rb + * for more details. If parsing fails, an ArgumentError + * will be raised. + * + * The default +str+ is '-4712-01-01T00:00:00+00:00'; this is Julian + * Day Number day 0. + * + * +sg+ specifies the Day of Calendar Reform. + */ +static VALUE +datetime_s_parse(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE argv2[2], hash; + + argv2[0] = str; + argv2[1] = comp; + hash = date_s__parse(2, argv2, klass); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * DateTime.iso8601(string="-4712-01-01T00:00:00+00:00"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical ISO 8601 format. + */ +static VALUE +datetime_s_iso8601(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__iso8601(klass, str); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * DateTime.rfc3339(string="-4712-01-01T00:00:00+00:00"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical RFC 3339 format. + */ +static VALUE +datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__rfc3339(klass, str); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * DateTime.xmlschema(string="-4712-01-01T00:00:00+00:00"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical XML Schema format. + */ +static VALUE +datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__xmlschema(klass, str); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * DateTime.rfc2822(string="Mon, 1 Jan -4712 00:00:00 +0000"[,start=ITALY]) + * DateTime.rfc822(string="Mon, 1 Jan -4712 00:00:00 +0000"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical RFC 2822 format. + */ +static VALUE +datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("Mon, 1 Jan -4712 00:00:00 +0000"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__rfc2822(klass, str); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * DateTime.httpdate(string="Mon, 01 Jan -4712 00:00:00 GMT"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some RFC 2616 format. + */ +static VALUE +datetime_s_httpdate(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("Mon, 01 Jan -4712 00:00:00 GMT"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__httpdate(klass, str); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: + * DateTime.jisx0301(string="-4712-01-01T00:00:00+00:00"[,start=ITALY]) + * + * Create a new Date object by parsing from a String + * according to some typical JIS X 0301 format. + */ +static VALUE +datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass) +{ + VALUE str, comp, sg; + + rb_scan_args(argc, argv, "03", &str, &comp, &sg); + + switch (argc) { + case 0: + str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + case 1: + comp = Qtrue; + case 2: + sg = INT2FIX(ITALY); + } + + { + VALUE hash = date_s__jisx0301(klass, str); + return dt_switch_new_by_frags(klass, hash, sg); + } +} + +/* + * call-seq: * dt.ajd * * Get the date as an Astronomical Julian Day Number. @@ -3356,6 +7271,21 @@ dt_lite_ajd(VALUE self) } } +#undef c_iforward0 +#undef c_iforwardv +#undef c_iforwardop +#define c_iforward0(m) dt_right_##m(self) +#define c_iforwardv(m) dt_right_##m(argc, argv, self) +#define c_iforwardop(m) dt_right_##m(self, other) + +static VALUE +dt_right_amjd(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return rt_ajd_to_amjd(dat->r.ajd); +} + /* * call-seq: * dt.amjd @@ -3367,7 +7297,7 @@ dt_lite_amjd(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("amjd_r"); + return c_iforward0(amjd); { VALUE r; @@ -3384,6 +7314,34 @@ dt_lite_amjd(VALUE self) } } +#undef return_once +#define return_once(k, expr)\ +{\ + VALUE id, val;\ + get_dt1(self);\ + id = ID2SYM(rb_intern(#k));\ + val = rb_hash_aref(dat->r.cache, id);\ + if (!NIL_P(val))\ + return val;\ + val = expr;\ + rb_hash_aset(dat->r.cache, id, val);\ + return val;\ +} + +static VALUE +dt_right_daynum(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(daynum, rt_ajd_to_jd(dat->r.ajd, dat->r.of)); +} + +static VALUE +dt_right_jd(VALUE self) +{ + return RARRAY_PTR(c_iforward0(daynum))[0]; +} + /* * call-seq: * dt.jd @@ -3395,7 +7353,7 @@ dt_lite_jd(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("jd_r"); + return c_iforward0(jd); { get_dt_jd(dat); get_dt_df(dat); @@ -3403,6 +7361,14 @@ dt_lite_jd(VALUE self) } } +static VALUE +dt_right_mjd(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return rt_jd_to_mjd(dt_right_jd(self)); +} + /* * call-seq: * dt.mjd @@ -3414,7 +7380,7 @@ dt_lite_mjd(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("mjd_r"); + return c_iforward0(mjd); { get_dt_jd(dat); get_dt_df(dat); @@ -3422,6 +7388,14 @@ dt_lite_mjd(VALUE self) } } +static VALUE +dt_right_ld(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return rt_jd_to_ld(dt_right_jd(self)); +} + /* * call-seq: * dt.ld @@ -3433,7 +7407,7 @@ dt_lite_ld(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("ld_r"); + return c_iforward0(ld); { get_dt_jd(dat); get_dt_df(dat); @@ -3441,6 +7415,20 @@ dt_lite_ld(VALUE self) } } +static VALUE +dt_right_civil(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(civil, rt_jd_to_civil(dt_right_jd(self), dat->r.sg)); +} + +static VALUE +dt_right_year(VALUE self) +{ + return RARRAY_PTR(c_iforward0(civil))[0]; +} + /* * call-seq: * dt.year @@ -3452,13 +7440,27 @@ dt_lite_year(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("year_r"); + return c_iforward0(year); { get_dt_civil(dat); return INT2FIX(dat->l.year); } } +static VALUE +dt_right_ordinal(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(ordinal, rt_jd_to_ordinal(dt_right_jd(self), dat->r.sg)); +} + +static VALUE +dt_right_yday(VALUE self) +{ + return RARRAY_PTR(c_iforward0(ordinal))[1]; +} + /* * call-seq: * dt.yday @@ -3472,13 +7474,19 @@ dt_lite_yday(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("yday_r"); + return c_iforward0(yday); { get_dt_civil(dat); return INT2FIX(civil_to_yday(dat->l.year, dat->l.mon, dat->l.mday)); } } +static VALUE +dt_right_mon(VALUE self) +{ + return RARRAY_PTR(c_iforward0(civil))[1]; +} + /* * call-seq: * dt.mon @@ -3492,13 +7500,19 @@ dt_lite_mon(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("mon_r"); + return c_iforward0(mon); { get_dt_civil(dat); return INT2FIX(dat->l.mon); } } +static VALUE +dt_right_mday(VALUE self) +{ + return RARRAY_PTR(c_iforward0(civil))[2]; +} + /* * call-seq: * dt.mday @@ -3510,13 +7524,19 @@ dt_lite_mday(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("mday_r"); + return c_iforward0(mday); { get_dt_civil(dat); return INT2FIX(dat->l.mday); } } +static VALUE +dt_right_day_fraction(VALUE self) +{ + return RARRAY_PTR(c_iforward0(daynum))[1]; +} + /* * call-seq: * dt.day_fraction @@ -3528,7 +7548,7 @@ dt_lite_day_fraction(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("day_fraction_r"); + return c_iforward0(day_fraction); { get_dt_df(dat); return rb_rational_new2(INT2FIX(local_df(dat)), @@ -3537,13 +7557,28 @@ dt_lite_day_fraction(VALUE self) } static VALUE +dt_right_weeknum0(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(weeknum0, rt_jd_to_weeknum(dt_right_jd(self), + INT2FIX(0), dat->r.sg)); +} + +static VALUE +dt_right_wnum0(VALUE self) +{ + return RARRAY_PTR(c_iforward0(weeknum0))[1]; +} + +static VALUE dt_lite_wnum0(VALUE self) { int ry, rw, rd; get_dt1(self); if (!light_mode_p(dat)) - return iforward0("wnum0_r"); + return c_iforward0(wnum0); { get_dt_jd(dat); get_dt_df(dat); @@ -3553,13 +7588,28 @@ dt_lite_wnum0(VALUE self) } static VALUE +dt_right_weeknum1(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(weeknum1, rt_jd_to_weeknum(dt_right_jd(self), + INT2FIX(1), dat->r.sg)); +} + +static VALUE +dt_right_wnum1(VALUE self) +{ + return RARRAY_PTR(c_iforward0(weeknum1))[1]; +} + +static VALUE dt_lite_wnum1(VALUE self) { int ry, rw, rd; get_dt1(self); if (!light_mode_p(dat)) - return iforward0("wnum1_r"); + return c_iforward0(wnum1); { get_dt_jd(dat); get_dt_df(dat); @@ -3568,6 +7618,45 @@ dt_lite_wnum1(VALUE self) } } +#ifndef NDEBUG +static VALUE +dt_right_time(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(time, rt_day_fraction_to_time(dt_right_day_fraction(self))); +} +#endif + +static VALUE +dt_right_time_wo_sf(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(time_wo_sf, + rt_day_fraction_to_time_wo_sf(dt_right_day_fraction(self))); +} + +static VALUE +dt_right_time_sf(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(time_sf, f_mul(f_mod(dt_right_day_fraction(self), + SECONDS_IN_DAY), + INT2FIX(DAY_IN_SECONDS))); +} + +static VALUE +dt_right_hour(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[0]; +#else + return RARRAY_PTR(c_iforward0(time_wo_sf))[0]; +#endif +} + /* * call-seq: * dt.hour @@ -3579,13 +7668,23 @@ dt_lite_hour(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("hour_r"); + return c_iforward0(hour); { get_dt_time(dat); return INT2FIX(dat->l.hour); } } +static VALUE +dt_right_min(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[1]; +#else + return RARRAY_PTR(c_iforward0(time_wo_sf))[1]; +#endif +} + /* * call-seq: * dt.min @@ -3598,13 +7697,23 @@ dt_lite_min(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("min_r"); + return c_iforward0(min); { get_dt_time(dat); return INT2FIX(dat->l.min); } } +static VALUE +dt_right_sec(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[2]; +#else + return RARRAY_PTR(c_iforward0(time_wo_sf))[2]; +#endif +} + /* * call-seq: * dt.sec @@ -3617,13 +7726,23 @@ dt_lite_sec(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("sec_r"); + return c_iforward0(sec); { get_dt_time(dat); return INT2FIX(dat->l.sec); } } +static VALUE +dt_right_sec_fraction(VALUE self) +{ +#if 0 + return RARRAY_PTR(c_iforward0(time))[3]; +#else + return c_iforward0(time_sf); +#endif +} + /* * call-seq: * dt.sec_fraction @@ -3636,7 +7755,7 @@ dt_lite_sec_fraction(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("sec_fraction_r"); + return c_iforward0(sec_fraction); return rb_rational_new2(INT2FIX(dat->l.sf), INT2FIX(SECOND_IN_NANOSECONDS)); } @@ -3660,8 +7779,38 @@ dt_lite_offset(VALUE self) int a;\ s = (of < 0) ? '-' : '+';\ a = (of < 0) ? -of : of;\ - h = a / 3600;\ - m = a % 3600 / 60;\ + h = a / HOUR_IN_SECONDS;\ + m = a % HOUR_IN_SECONDS / MINUTE_IN_SECONDS;\ +} + +static VALUE +dt_right_zone(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + { + int sign; + VALUE hh, mm, ss, fr; + + if (f_negative_p(dat->r.of)) { + sign = '-'; + fr = f_abs(dat->r.of); + } + else { + sign = '+'; + fr = dat->r.of; + } + ss = f_div(fr, SECONDS_IN_DAY); + + hh = f_idiv(ss, INT2FIX(HOUR_IN_SECONDS)); + ss = f_mod(ss, INT2FIX(HOUR_IN_SECONDS)); + + mm = f_idiv(ss, INT2FIX(MINUTE_IN_SECONDS)); + + return rb_enc_sprintf(rb_usascii_encoding(), + "%c%02d:%02d", + sign, NUM2INT(hh), NUM2INT(mm)); + } } /* @@ -3677,11 +7826,25 @@ dt_lite_zone(VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforward0("zone_r"); + return c_iforward0(zone); decode_offset(dat->l.of, s, h, m); return rb_enc_sprintf(rb_usascii_encoding(), "%c%02d:%02d", s, h, m); } +static VALUE +dt_right_commercial(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return_once(commercial, rt_jd_to_commercial(dt_right_jd(self), dat->r.sg)); +} + +static VALUE +dt_right_cwyear(VALUE self) +{ + return RARRAY_PTR(c_iforward0(commercial))[0]; +} + /* * call-seq: * dt.cwyear @@ -3696,7 +7859,7 @@ dt_lite_cwyear(VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforward0("cwyear_r"); + return c_iforward0(cwyear); { get_dt_jd(dat); get_dt_df(dat); @@ -3705,6 +7868,12 @@ dt_lite_cwyear(VALUE self) } } +static VALUE +dt_right_cweek(VALUE self) +{ + return RARRAY_PTR(c_iforward0(commercial))[1]; +} + /* * call-seq: * dt.cweek @@ -3718,7 +7887,7 @@ dt_lite_cweek(VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforward0("cweek_r"); + return c_iforward0(cweek); { get_dt_jd(dat); get_dt_df(dat); @@ -3727,6 +7896,12 @@ dt_lite_cweek(VALUE self) } } +static VALUE +dt_right_cwday(VALUE self) +{ + return RARRAY_PTR(c_iforward0(commercial))[2]; +} + /* * call-seq: * dt.cwday @@ -3741,7 +7916,7 @@ dt_lite_cwday(VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforward0("cwday_r"); + return c_iforward0(cwday); { get_dt_jd(dat); get_dt_df(dat); @@ -3752,6 +7927,12 @@ dt_lite_cwday(VALUE self) } } +static VALUE +dt_right_wday(VALUE self) +{ + return rt_jd_to_wday(dt_right_jd(self)); +} + /* * call-seq: * dt.wday @@ -3766,7 +7947,7 @@ dt_lite_wday(VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforward0("wday_r"); + return c_iforward0(wday); { get_dt_jd(dat); get_dt_df(dat); @@ -3777,6 +7958,98 @@ dt_lite_wday(VALUE self) /* * call-seq: + * dt.monday? + * + * Is the current date Monday? + */ +static VALUE +dt_lite_sunday_p(VALUE self) +{ + return f_eqeq_p(dt_lite_wday(self), INT2FIX(0)); +} + +/* + * call-seq: + * dt.monday? + * + * Is the current date Monday? + */ +static VALUE +dt_lite_monday_p(VALUE self) +{ + return f_eqeq_p(dt_lite_wday(self), INT2FIX(1)); +} + +/* + * call-seq: + * dt.tuesday? + * + * Is the current date Tuesday? + */ +static VALUE +dt_lite_tuesday_p(VALUE self) +{ + return f_eqeq_p(dt_lite_wday(self), INT2FIX(2)); +} + +/* + * call-seq: + * dt.wednesday? + * + * Is the current date Wednesday? + */ +static VALUE +dt_lite_wednesday_p(VALUE self) +{ + return f_eqeq_p(dt_lite_wday(self), INT2FIX(3)); +} + +/* + * call-seq: + * dt.thursday? + * + * Is the current date Thursday? + */ +static VALUE +dt_lite_thursday_p(VALUE self) +{ + return f_eqeq_p(dt_lite_wday(self), INT2FIX(4)); +} + +/* + * call-seq: + * dt.friday? + * + * Is the current date Friday? + */ +static VALUE +dt_lite_friday_p(VALUE self) +{ + return f_eqeq_p(dt_lite_wday(self), INT2FIX(5)); +} + +/* + * call-seq: + * dt.saturday? + * + * Is the current date Saturday? + */ +static VALUE +dt_lite_saturday_p(VALUE self) +{ + return f_eqeq_p(dt_lite_wday(self), INT2FIX(6)); +} + +static VALUE +dt_right_julian_p(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return f_lt_p(dt_right_jd(self), dat->r.sg); +} + +/* + * call-seq: * dt.julian? * * Is the current date old-style (Julian Calendar)? @@ -3786,10 +8059,18 @@ dt_lite_julian_p(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("julian_r?"); + return c_iforward0(julian_p); return Qfalse; } +static VALUE +dt_right_gregorian_p(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return dt_right_julian_p(self) ? Qfalse : Qtrue; +} + /* * call-seq: * dt.gregorian? @@ -3801,10 +8082,33 @@ dt_lite_gregorian_p(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("gregorian_r?"); + return c_iforward0(gregorian_p); return Qtrue; } +static VALUE +dt_right_fix_style(VALUE self) +{ + if (dt_right_julian_p(self)) + return DBL2NUM(JULIAN); + return DBL2NUM(GREGORIAN); +} + +static VALUE +dt_right_leap_p(VALUE self) +{ + VALUE style, a; + + style = dt_right_fix_style(self); + a = rt_jd_to_civil(f_sub(rt_civil_to_jd(dt_right_year(self), + INT2FIX(3), INT2FIX(1), style), + INT2FIX(1)), + style); + if (f_eqeq_p(RARRAY_PTR(a)[2], INT2FIX(29))) + return Qtrue; + return Qfalse; +} + /* * call-seq: * dt.leap? @@ -3816,7 +8120,7 @@ dt_lite_leap_p(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("leap_r?"); + return c_iforward0(leap_p); { get_dt_civil(dat); return leap_p(dat->l.year) ? Qtrue : Qfalse; @@ -3838,6 +8142,16 @@ dt_lite_start(VALUE self) return DBL2NUM(dat->l.sg); } +static VALUE +dt_right_new_start(int argc, VALUE *argv, VALUE self) +{ + get_dt1(self); + return dt_right_new(CLASS_OF(self), + dt_lite_ajd(self), + dt_lite_offset(self), + (argc >= 1) ? argv[0] : INT2FIX(ITALY)); +} + /* * call-seq: * dt.new_start([start=Date::ITALY]) @@ -3853,34 +8167,103 @@ dt_lite_new_start(int argc, VALUE *argv, VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforwardv("new_start_r"); + return c_iforwardv(new_start); rb_scan_args(argc, argv, "01", &vsg); - if (!NIL_P(vsg)) + sg = ITALY; + if (argc >= 1) sg = NUM2DBL(vsg); - else - sg = ITALY; { get_dt_jd(dat); get_dt_df(dat); if (dat->l.jd < sg) - return iforwardv("new_start_r"); - - return dt_lite_s_new_internal_wo_civil(CLASS_OF(self), - dat->l.jd, - dat->l.df, - dat->l.sf, - dat->l.of, - sg, - LIGHT_MODE | HAVE_JD | HAVE_DF); + return c_iforwardv(new_start); + + return dt_lite_new_internal_wo_civil(CLASS_OF(self), + dat->l.jd, + dat->l.df, + dat->l.sf, + dat->l.of, + sg, + HAVE_JD | HAVE_DF); } } /* * call-seq: + * dt.italy + * + * Create a copy of this Date object that uses the Italian/Catholic + * Day of Calendar Reform. + */ +static VALUE +dt_lite_italy(VALUE self) +{ + VALUE argv[1]; + argv[0] = INT2FIX(ITALY); + return dt_lite_new_start(1, argv, self); +} + +/* + * call-seq: + * dt.england + * + * Create a copy of this Date object that uses the English/Colonial + * Day of Calendar Reform. + */ +static VALUE +dt_lite_england(VALUE self) +{ + VALUE argv[1]; + argv[0] = INT2FIX(ENGLAND); + return dt_lite_new_start(1, argv, self); +} + +/* + * call-seq: + * dt.julian + * + * Create a copy of this Date object that always uses the Julian + * Calendar. + */ +static VALUE +dt_lite_julian(VALUE self) +{ + VALUE argv[1]; + argv[0] = DBL2NUM(JULIAN); + return dt_lite_new_start(1, argv, self); +} + +/* + * call-seq: + * dt.gregorian + * + * Create a copy of this Date object that always uses the Gregorian + * Calendar. + */ +static VALUE +dt_lite_gregorian(VALUE self) +{ + VALUE argv[1]; + argv[0] = DBL2NUM(GREGORIAN); + return dt_lite_new_start(1, argv, self); +} + +static VALUE +dt_right_new_offset(int argc, VALUE *argv, VALUE self) +{ + get_dt1(self); + return dt_right_new(CLASS_OF(self), + dt_lite_ajd(self), + (argc >= 1) ? sof2nof(argv[0]) : INT2FIX(0), + dt_lite_start(self)); +} + +/* + * call-seq: * dt.new_offset([offset=0]) * * Create a copy of this Date object using a new offset. @@ -3894,31 +8277,48 @@ dt_lite_new_offset(int argc, VALUE *argv, VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforwardv("new_offset_r"); + return c_iforwardv(new_offset); rb_scan_args(argc, argv, "01", &vof); if (NIL_P(vof)) rof = 0; else { + vof = sof2nof(vof); if (!daydiff_to_sec(vof, &rof)) - return iforwardv("new_offset_r"); + return c_iforwardv(new_offset); } { get_dt_jd(dat); get_dt_df(dat); - return dt_lite_s_new_internal_wo_civil(CLASS_OF(self), - dat->l.jd, - dat->l.df, - dat->l.sf, - rof, - dat->l.sg, - LIGHT_MODE | HAVE_JD | HAVE_DF); + return dt_lite_new_internal_wo_civil(CLASS_OF(self), + dat->l.jd, + dat->l.df, + dat->l.sf, + rof, + dat->l.sg, + HAVE_JD | HAVE_DF); } } +static VALUE +dt_right_plus(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_dt1(self); + if (TYPE(other) == T_FLOAT) + other = rb_rational_new2(f_round(f_mul(other, day_in_nanoseconds)), + day_in_nanoseconds); + return dt_right_new(CLASS_OF(self), + f_add(dt_lite_ajd(self), other), + dt_lite_offset(self), + dt_lite_start(self)); + } + rb_raise(rb_eTypeError, "expected numeric"); +} + /* * call-seq: * dt + n @@ -3939,7 +8339,7 @@ dt_lite_plus(VALUE self, VALUE other) get_dt1(self); if (!light_mode_p(dat)) - return iforwardop("plus_r"); + return c_iforwardop(plus); switch (TYPE(other)) { case T_FIXNUM: @@ -3953,17 +8353,18 @@ dt_lite_plus(VALUE self, VALUE other) jd = dat->l.jd + FIX2LONG(other); if (LIGHTABLE_JD(jd) && jd >= dat->l.sg) - return dt_lite_s_new_internal(CLASS_OF(self), - jd, - dat->l.df, - dat->l.sf, - dat->l.of, - dat->l.sg, - 0, 0, 0, - dat->l.hour, - dat->l.min, - dat->l.sec, - dat->l.flags & ~HAVE_CIVIL); + return dt_lite_new_internal(CLASS_OF(self), + jd, + dat->l.df, + dat->l.sf, + dat->l.of, + dat->l.sg, + 0, 0, 0, + dat->l.hour, + dat->l.min, + dat->l.sec, + (dat->l.flags | HAVE_JD) & + ~HAVE_CIVIL); } break; case T_FLOAT: @@ -4022,23 +8423,43 @@ dt_lite_plus(VALUE self, VALUE other) jd = dat->l.jd + jd; if (LIGHTABLE_JD(jd) && jd >= dat->l.sg) - return dt_lite_s_new_internal(CLASS_OF(self), - (long)jd, - df, - sf, - dat->l.of, - dat->l.sg, - 0, 0, 0, - dat->l.hour, - dat->l.min, - dat->l.sec, - dat->l.flags & - ~HAVE_CIVIL & - ~HAVE_TIME); + return dt_lite_new_internal(CLASS_OF(self), + (long)jd, + df, + sf, + dat->l.of, + dat->l.sg, + 0, 0, 0, + dat->l.hour, + dat->l.min, + dat->l.sec, + (dat->l.flags | + HAVE_JD | HAVE_DF) & + ~HAVE_CIVIL & + ~HAVE_TIME); } break; } - return iforwardop("plus_r"); + return c_iforwardop(plus); +} + +static VALUE +dt_right_minus(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_dt1(self); + if (TYPE(other) == T_FLOAT) + other = rb_rational_new2(f_round(f_mul(other, day_in_nanoseconds)), + day_in_nanoseconds); + return dt_right_new(CLASS_OF(self), + f_sub(dt_lite_ajd(self), other), + dt_lite_offset(self), + dt_lite_start(self)); + } + else if (k_date_p(other)) { + return f_sub(dt_lite_ajd(self), f_ajd(other)); + } + rb_raise(rb_eTypeError, "expected numeric"); } /* @@ -4067,9 +8488,185 @@ dt_lite_minus(VALUE self, VALUE other) case T_FLOAT: return dt_lite_plus(self, DBL2NUM(-NUM2DBL(other))); } - return iforwardop("minus_r"); + return c_iforwardop(minus); +} + +/* + * call-seq: + * dt.next_day([n=1]) + * + * Equivalent to dt + n. + */ +static VALUE +dt_lite_next_day(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return dt_lite_plus(self, n); +} + +/* + * call-seq: + * dt.prev_day([n=1]) + * + * Equivalent to dt - n. + */ +static VALUE +dt_lite_prev_day(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return dt_lite_minus(self, n); +} + +/* + * call-seq: + * dt.next + * + * Return a new Date one day after this one. + */ +static VALUE +dt_lite_next(VALUE self) +{ + return dt_lite_next_day(0, (VALUE *)NULL, self); +} + +/* + * call-seq: + * dt >> n + * + * Return a new Date object that is +n+ months later than + * the current one. + * + * If the day-of-the-month of the current Date is greater + * than the last day of the target month, the day-of-the-month + * of the returned Date will be the last day of the target month. + */ +static VALUE +dt_lite_rshift(VALUE self, VALUE other) +{ + VALUE t, y, m, d, sg, j; + + t = f_add3(f_mul(dt_lite_year(self), INT2FIX(12)), + f_sub(dt_lite_mon(self), INT2FIX(1)), + other); + y = f_idiv(t, INT2FIX(12)); + m = f_mod(t, INT2FIX(12)); + m = f_add(m, INT2FIX(1)); + d = dt_lite_mday(self); + sg = dt_lite_start(self); + + while (NIL_P(j = rt__valid_civil_p(y, m, d, sg))) { + d = f_sub(d, INT2FIX(1)); + if (f_lt_p(d, INT2FIX(1))) + rb_raise(rb_eArgError, "invalid date"); + } + return f_add(self, f_sub(j, dt_lite_jd(self))); +} + +/* + * call-seq: + * dt << n + * + * Return a new Date object that is +n+ months earlier than + * the current one. + * + * If the day-of-the-month of the current Date is greater + * than the last day of the target month, the day-of-the-month + * of the returned Date will be the last day of the target month. + */ +static VALUE +dt_lite_lshift(VALUE self, VALUE other) +{ + return dt_lite_rshift(self, f_negate(other)); +} + +/* + * call-seq: + * dt.next_month([n=1]) + * + * Equivalent to dt >> n + */ +static VALUE +dt_lite_next_month(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return dt_lite_rshift(self, n); +} + +/* + * call-seq: + * dt.prev_month([n=1]) + * + * Equivalent to dt << n + */ +static VALUE +dt_lite_prev_month(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return dt_lite_lshift(self, n); +} + +/* + * call-seq: + * dt.next_year([n=1]) + * + * Equivalent to dt >> (n * 12) + */ +static VALUE +dt_lite_next_year(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return dt_lite_rshift(self, f_mul(n, INT2FIX(12))); +} + +/* + * call-seq: + * dt.prev_year([n=1]) + * + * Equivalent to dt << (n * 12) + */ +static VALUE +dt_lite_prev_year(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + if (argc < 1) + n = INT2FIX(1); + return dt_lite_lshift(self, f_mul(n, INT2FIX(12))); } +static VALUE +dt_right_cmp(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_dt1(self); + return f_cmp(dt_lite_ajd(self), other); + } + else if (k_date_p(other)) { + return f_cmp(dt_lite_ajd(self), f_ajd(other)); + } + return rb_num_coerce_cmp(self, other, rb_intern("<=>")); +} /* * call-seq: * dt <=> n @@ -4092,7 +8689,20 @@ dt_lite_cmp(VALUE self, VALUE other) { if (k_date_p(other)) return cmp_dd(self, other); - return iforwardop("cmp_r"); + return c_iforwardop(cmp); +} + +static VALUE +dt_right_equal(VALUE self, VALUE other) +{ + if (k_numeric_p(other)) { + get_dt1(self); + return f_eqeq_p(dt_lite_jd(self), other); + } + else if (k_date_p(other)) { + return f_eqeq_p(dt_lite_jd(self), f_jd(other)); + } + return rb_num_coerce_cmp(self, other, rb_intern("==")); } /* @@ -4111,7 +8721,15 @@ dt_lite_equal(VALUE self, VALUE other) { if (k_date_p(other)) return equal_dd(self, other); - return iforwardop("equal_r"); + return c_iforwardop(equal); +} + +static VALUE +dt_right_eql_p(VALUE self, VALUE other) +{ + if (k_date_p(other) && f_eqeq_p(self, other)) + return Qtrue; + return Qfalse; } /* @@ -4127,7 +8745,15 @@ dt_lite_eql_p(VALUE self, VALUE other) { if (k_date_p(other)) return eql_p_dd(self, other); - return iforwardop("eql_r?"); + return c_iforwardop(eql_p); +} + +static VALUE +dt_right_hash(VALUE self) +{ + get_dt1(self); + assert(!light_mode_p(dat)); + return rb_hash(dat->r.ajd); } /* @@ -4141,10 +8767,32 @@ dt_lite_hash(VALUE self) { get_dt1(self); if (!light_mode_p(dat)) - return iforward0("hash_r"); + return c_iforward0(hash); return rb_hash(dt_lite_ajd(self)); } +static VALUE +dt_right_to_s(VALUE self) +{ + VALUE a, b, c, argv[8]; + + get_dt1(self); + assert(!light_mode_p(dat)); + + argv[0] = rb_usascii_str_new2("%.4d-%02d-%02dT%02d:%02d:%02d%s"); + a = dt_right_civil(self); + b = dt_right_time_wo_sf(self); + c = dt_right_zone(self); + argv[1] = RARRAY_PTR(a)[0]; + argv[2] = RARRAY_PTR(a)[1]; + argv[3] = RARRAY_PTR(a)[2]; + argv[4] = RARRAY_PTR(b)[0]; + argv[5] = RARRAY_PTR(b)[1]; + argv[6] = RARRAY_PTR(b)[2]; + argv[7] = c; + return rb_f_sprintf(8, argv); +} + /* * call-seq: * dt.to_s @@ -4158,7 +8806,7 @@ dt_lite_to_s(VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforward0("to_s_r"); + return c_iforward0(to_s); { get_dt_civil(dat); get_dt_time(dat); @@ -4171,6 +8819,23 @@ dt_lite_to_s(VALUE self) } } +static VALUE +dt_right_inspect(VALUE self) +{ + VALUE argv[6]; + + get_dt1(self); + assert(!light_mode_p(dat)); + + argv[0] = rb_usascii_str_new2("#<%s[R]: %s (%s,%s,%s)>"); + argv[1] = rb_class_name(CLASS_OF(self)); + argv[2] = dt_right_to_s(self); + argv[3] = dat->r.ajd; + argv[4] = dat->r.of; + argv[5] = dat->r.sg; + return rb_f_sprintf(6, argv); +} + /* * call-seq: * dt.inspect @@ -4184,7 +8849,7 @@ dt_lite_inspect(VALUE self) get_dt1(self); if (!light_mode_p(dat)) - return iforward0("inspect_r"); + return c_iforward0(inspect); { get_dt_civil(dat); get_dt_time(dat); @@ -4210,20 +8875,18 @@ dt_lite_set_tmx(VALUE self, struct tmx *tmx) get_dt1(self); if (!light_mode_p(dat)) { - tmx->year = iforward0("year_r"); - tmx->yday = FIX2INT(iforward0("yday_r")); - tmx->mon = FIX2INT(iforward0("mon_r")); - tmx->mday = FIX2INT(iforward0("mday_r")); - tmx->hour = FIX2INT(iforward0("hour_r")); - tmx->min = FIX2INT(iforward0("min_r")); - tmx->sec = FIX2INT(iforward0("sec_r")); - tmx->wday = FIX2INT(iforward0("wday_r")); - tmx->offset = INT2FIX(0); - tmx->zone = RSTRING_PTR(iforward0("zone_r")); - tmx->timev = f_mul(f_sub(dat->r.ajd, - rb_rational_new2(INT2FIX(4881175), - INT2FIX(2))), - INT2FIX(86400)); + tmx->year = dt_right_year(self); + tmx->yday = FIX2INT(dt_right_yday(self)); + tmx->mon = FIX2INT(dt_right_mon(self)); + tmx->mday = FIX2INT(dt_right_mday(self)); + tmx->hour = FIX2INT(dt_right_hour(self)); + tmx->min = FIX2INT(dt_right_min(self)); + tmx->sec = FIX2INT(dt_right_sec(self)); + tmx->wday = FIX2INT(dt_right_wday(self)); + tmx->offset = f_mul(dat->r.of, INT2FIX(DAY_IN_SECONDS)); + tmx->zone = RSTRING_PTR(dt_right_zone(self)); + tmx->timev = f_mul(f_sub(dat->r.ajd, UNIX_EPOCH_IN_AJD), + INT2FIX(DAY_IN_SECONDS)); } else { get_dt_jd(dat); @@ -4241,10 +8904,8 @@ dt_lite_set_tmx(VALUE self, struct tmx *tmx) tmx->wday = jd_to_wday(local_jd(dat)); tmx->offset = INT2FIX(dat->l.of); tmx->zone = RSTRING_PTR(dt_lite_zone(self)); - tmx->timev = f_mul(f_sub(dt_lite_ajd(self), - rb_rational_new2(INT2FIX(4881175), - INT2FIX(2))), - INT2FIX(86400)); + tmx->timev = f_mul(f_sub(dt_lite_ajd(self), UNIX_EPOCH_IN_AJD), + INT2FIX(DAY_IN_SECONDS)); } } @@ -4261,6 +8922,134 @@ dt_lite_strftime(int argc, VALUE *argv, VALUE self) "%FT%T%:z", dt_lite_set_tmx); } +static VALUE +dt_lite_iso8601_timediv(VALUE self, VALUE n) +{ + VALUE f, fmt; + + if (f_lt_p(n, INT2FIX(1))) + f = rb_usascii_str_new2(""); + else { + VALUE argv[3]; + + argv[0] = rb_usascii_str_new2(".%0*d"); + argv[1] = n; + argv[2] = f_round(f_div(dt_lite_sec_fraction(self), + rb_rational_new2(INT2FIX(1), + f_expt(INT2FIX(10), n)))); + f = rb_f_sprintf(3, argv); + } + fmt = f_add3(rb_usascii_str_new2("T%T"), + f, + rb_usascii_str_new2("%:z")); + return strftimev(RSTRING_PTR(fmt), self, dt_lite_set_tmx); +} + +/* + * call-seq: + * dt.asctime + * dt.ctime + * + * Equivalent to strftime('%c'). + * See also asctime(3) or ctime(3). + */ +static VALUE +dt_lite_asctime(VALUE self) +{ + return strftimev("%c", self, dt_lite_set_tmx); +} + +/* + * call-seq: + * dt.iso8601([n=0]) + * dt.xmlschema([n=0]) + * + * Equivalent to strftime('%FT%T'). + * The optional argument n is length of fractional seconds. + */ +static VALUE +dt_lite_iso8601(int argc, VALUE *argv, VALUE self) +{ + VALUE n; + + rb_scan_args(argc, argv, "01", &n); + + if (argc < 1) + n = INT2FIX(0); + + return f_add(strftimev("%F", self, dt_lite_set_tmx), + dt_lite_iso8601_timediv(self, n)); +} + +/* + * call-seq: + * dt.rfc3339([n=0]) + * + * Equivalent to strftime('%FT%T'). + * The optional argument n is length of fractional seconds. + */ +static VALUE +dt_lite_rfc3339(int argc, VALUE *argv, VALUE self) +{ + return dt_lite_iso8601(argc, argv, self); +} + +/* + * call-seq: + * dt.rfc2822 + * dt.rfc822 + * + * Equivalent to strftime('%a, %-d %b %Y %T %z'). + */ +static VALUE +dt_lite_rfc2822(VALUE self) +{ + return strftimev("%a, %-d %b %Y %T %z", self, dt_lite_set_tmx); +} + +/* + * call-seq: + * dt.httpdate + * + * Equivalent to strftime('%a, %d %b %Y %T GMT'). + * See also RFC 2616. + */ +static VALUE +dt_lite_httpdate(VALUE self) +{ + VALUE argv[1], d; + + argv[0] = INT2FIX(0); + d = dt_lite_new_offset(1, argv, self); + return strftimev("%a, %d %b %Y %T GMT", d, dt_lite_set_tmx); +} + +/* + * call-seq: + * dt.jisx0301 + * + * Return a string as a JIS X 0301 format. + */ +static VALUE +dt_lite_jisx0301(int argc, VALUE *argv, VALUE self) +{ + VALUE n, argv2[2]; + + rb_scan_args(argc, argv, "01", &n); + + if (argc < 1) + n = INT2FIX(0); + + if (!gengo(dt_lite_jd(self), + dt_lite_year(self), + argv2)) + return f_add(strftimev("%F", self, dt_lite_set_tmx), + dt_lite_iso8601_timediv(self, n)); + return f_add(f_add(rb_f_sprintf(2, argv2), + strftimev(".%m.%d", self, dt_lite_set_tmx)), + dt_lite_iso8601_timediv(self, n)); +} + /* * call-seq: * dt.marshal_dump @@ -4313,12 +9102,12 @@ dt_lite_marshal_load(VALUE self, VALUE a) dat->r.of = RARRAY_PTR(a)[1]; dat->r.sg = RARRAY_PTR(a)[2]; dat->r.cache = rb_hash_new(); - dat->r.flags = 0; + dat->r.flags = DATETIME_OBJ; break; case 5: dat->l.jd = NUM2LONG(RARRAY_PTR(a)[0]); dat->l.df = FIX2INT(RARRAY_PTR(a)[1]); - dat->l.sf = FIX2INT(RARRAY_PTR(a)[2]); + dat->l.sf = NUM2LONG(RARRAY_PTR(a)[2]); dat->l.of = FIX2INT(RARRAY_PTR(a)[3]); dat->l.sg = NUM2DBL(RARRAY_PTR(a)[4]); dat->l.year = 0; @@ -4327,7 +9116,7 @@ dt_lite_marshal_load(VALUE self, VALUE a) dat->l.hour = 0; dat->l.min = 0; dat->l.sec = 0; - dat->l.flags = LIGHT_MODE | HAVE_JD | HAVE_DF; + dat->l.flags = LIGHT_MODE | HAVE_JD | HAVE_DF | DATETIME_OBJ; break; default: rb_raise(rb_eTypeError, "invalid size"); @@ -4342,22 +9131,261 @@ dt_lite_marshal_load(VALUE self, VALUE a) return self; } +/* conversions */ + +#define f_getlocal(x) rb_funcall(x, rb_intern("getlocal"), 0) +#define f_subsec(x) rb_funcall(x, rb_intern("subsec"), 0) +#define f_utc_offset(x) rb_funcall(x, rb_intern("utc_offset"), 0) +#define f_local3(x,y,m,d) rb_funcall(x, rb_intern("local"), 3, y, m, d) +#define f_utc6(x,y,m,d,h,min,s) rb_funcall(x, rb_intern("utc"), 6, y, m, d, h, min, s) + +/* + * call-seq: + * t.to_time + * + * Return a copy of self as local mode. + */ +static VALUE +time_to_time(VALUE self) +{ + return rb_funcall(self, rb_intern("getlocal"), 0); +} + +/* + * call-seq: + * t.to_date + * + * Return a Date object which denotes self. + */ +static VALUE +time_to_date(VALUE self) +{ + VALUE jd; + + jd = rt_civil_to_jd(f_year(self), f_mon(self), f_mday(self), + INT2FIX(ITALY)); + return d_switch_new_jd(cDate, jd, INT2FIX(ITALY)); +} + +/* + * call-seq: + * t.to_datetime + * + * Return a DateTime object which denotes self. + */ +static VALUE +time_to_datetime(VALUE self) +{ + VALUE jd, sec, fr, of; + + jd = rt_civil_to_jd(f_year(self), f_mon(self), f_mday(self), + INT2FIX(ITALY)); + sec = f_sec(self); + if (f_gt_p(sec, INT2FIX(59))) + sec = INT2FIX(59); + fr = rt_time_to_day_fraction(f_hour(self), f_min(self), sec); + fr = f_add(fr, rb_Rational(f_subsec(self), INT2FIX(DAY_IN_SECONDS))); + of = rb_rational_new2(f_utc_offset(self), INT2FIX(DAY_IN_SECONDS)); + return dt_switch_new_jd(cDateTime, jd, fr, of, INT2FIX(ITALY)); +} + +/* + * call-seq: + * d.to_time + * + * Return a Time object which denotes self. + */ +static VALUE +date_to_time(VALUE self) +{ + return f_local3(rb_cTime, + d_lite_year(self), + d_lite_mon(self), + d_lite_mday(self)); +} + +/* + * call-seq: + * d.to_date + * + * Return self; + */ +static VALUE +date_to_date(VALUE self) +{ + return self; +} + +/* + * call-seq: + * d.to_datetime + * + * Return a DateTime object which denotes self. + */ +static VALUE +date_to_datetime(VALUE self) +{ + return dt_switch_new_jd(cDateTime, + d_lite_jd(self), + INT2FIX(0), + d_lite_offset(self), + d_lite_start(self)); +} + +#ifndef NDEBUG static VALUE -dt_right_cache(VALUE self) +date_to_right(VALUE self) +{ + get_d1(self); + if (!light_mode_p(dat)) + return self; + return d_right_new(CLASS_OF(self), + d_lite_ajd(self), + d_lite_offset(self), + d_lite_start(self)); +} + +static VALUE +date_to_light(VALUE self) +{ + get_d1(self); + if (light_mode_p(dat)) + return self; + { + VALUE t, jd, df; + + t = rt_ajd_to_jd(dat->r.ajd, INT2FIX(0)); /* as utc */ + jd = RARRAY_PTR(t)[0]; + df = RARRAY_PTR(t)[1]; + + if (!LIGHTABLE_JD(NUM2LONG(jd))) + rb_raise(rb_eRangeError, "jd too big to convert into light"); + + if (!f_zero_p(df)) + rb_warning("day fraction is ignored"); + + if (!f_zero_p(dat->r.of)) + rb_warning("nonzero offset is ignored"); + + return d_lite_new(CLASS_OF(self), jd, dat->r.sg); + } +} +#endif + +/* + * call-seq: + * dt.to_time + * + * Return a Time object which denotes self. + */ +static VALUE +datetime_to_time(VALUE self) +{ + VALUE argv[1], d; + + argv[0] = INT2FIX(0); + d = dt_lite_new_offset(1, argv, self); + d = f_utc6(rb_cTime, + dt_lite_year(d), + dt_lite_mon(d), + dt_lite_mday(d), + dt_lite_hour(d), + dt_lite_min(d), + f_add(dt_lite_sec(d), dt_lite_sec_fraction(d))); + return f_getlocal(d); +} + +/* + * call-seq: + * dt.to_date + * + * Return a Date object which denotes self. + */ +static VALUE +datetime_to_date(VALUE self) +{ + return d_switch_new_jd(cDate, + dt_lite_jd(self), + dt_lite_start(self)); +} + +/* + * call-seq: + * dt.to_datetime + * + * Return self. + */ +static VALUE +datetime_to_datetime(VALUE self) +{ + return self; +} + +#ifndef NDEBUG +static VALUE +datetime_to_right(VALUE self) +{ + get_dt1(self); + if (!light_mode_p(dat)) + return self; + return dt_right_new(CLASS_OF(self), + dt_lite_ajd(self), + dt_lite_offset(self), + dt_lite_start(self)); +} + +static VALUE +datetime_to_light(VALUE self) { get_dt1(self); if (light_mode_p(dat)) - return Qnil; - return dat->r.cache; + return self; + { + VALUE t, jd, df, sf, odf; + + t = rt_ajd_to_jd(dat->r.ajd, INT2FIX(0)); /* as utc */ + jd = RARRAY_PTR(t)[0]; + df = RARRAY_PTR(t)[1]; + + if (!LIGHTABLE_JD(NUM2LONG(jd))) + rb_raise(rb_eRangeError, "jd too big to convert into light"); + + if (f_lt_p(dat->r.of, INT2FIX(-1)) || + f_gt_p(dat->r.of, INT2FIX(1))) + rb_raise(rb_eRangeError, "offset too big to convert into light"); + + t = f_mul(df, INT2FIX(DAY_IN_SECONDS)); + df = f_idiv(t, INT2FIX(1)); + sf = f_mod(t, INT2FIX(1)); + + t = f_mul(sf, INT2FIX(SECOND_IN_NANOSECONDS)); + sf = f_idiv(t, INT2FIX(1)); + + if (f_eqeq_p(t, sf)) + rb_warning("second fraction is truncated"); + + t = f_mul(dat->r.of, INT2FIX(DAY_IN_SECONDS)); + odf = f_truncate(t); + + if (f_eqeq_p(t, odf)) + rb_warning("offset is truncated"); + + return dt_lite_new(CLASS_OF(self), + jd, df, sf, odf, dat->r.sg); + } } +#endif #ifndef NDEBUG +/* tests */ + static int test_civil(long from, long to, double sg) { long j; - fprintf(stderr, "%ld...%ld (%ld) - %.0f\n", from, to, to - from, sg); + fprintf(stderr, "test_civil: %ld...%ld (%ld) - %.0f\n", + from, to, to - from, sg); for (j = from; j <= to; j++) { int y, m, d, ns; long rj; @@ -4397,7 +9425,8 @@ test_ordinal(long from, long to, double sg) { long j; - fprintf(stderr, "%ld...%ld (%ld) - %.0f\n", from, to, to - from, sg); + fprintf(stderr, "test_ordinal: %ld...%ld (%ld) - %.0f\n", + from, to, to - from, sg); for (j = from; j <= to; j++) { int y, d, ns; long rj; @@ -4437,7 +9466,8 @@ test_commercial(long from, long to, double sg) { long j; - fprintf(stderr, "%ld...%ld (%ld) - %.0f\n", from, to, to - from, sg); + fprintf(stderr, "test_commercial: %ld...%ld (%ld) - %.0f\n", + from, to, to - from, sg); for (j = from; j <= to; j++) { int y, w, d, ns; long rj; @@ -4477,7 +9507,8 @@ test_weeknum(long from, long to, int f, double sg) { long j; - fprintf(stderr, "%ld...%ld (%ld) - %.0f\n", from, to, to - from, sg); + fprintf(stderr, "test_weeknum: %ld...%ld (%ld) - %.0f\n", + from, to, to - from, sg); for (j = from; j <= to; j++) { int y, w, d, ns; long rj; @@ -4516,13 +9547,13 @@ date_s_test_weeknum(VALUE klass) return Qtrue; } - static int test_nth_kday(long from, long to, double sg) { long j; - fprintf(stderr, "%ld...%ld (%ld) - %.0f\n", from, to, to - from, sg); + fprintf(stderr, "test_nth_kday: %ld...%ld (%ld) - %.0f\n", + from, to, to - from, sg); for (j = from; j <= to; j++) { int y, m, n, k, ns; long rj; @@ -4557,6 +9588,119 @@ date_s_test_nth_kday(VALUE klass) return Qtrue; } +static int +print_emesg(VALUE x, VALUE y) +{ + VALUE argv[3]; + argv[0] = rb_usascii_str_new2("%s != %s\n"); + argv[1] = x; + argv[2] = y; + return fputs(RSTRING_PTR(rb_f_sprintf(3, argv)), stderr); +} + +static int +test_d_switch_new(long from, long to, double sg) +{ + long j; + + fprintf(stderr, "test_d_switch_new: %ld...%ld (%ld) - %.0f\n", + from, to, to - from, sg); + for (j = from; j <= to; j++) { + VALUE jd, ajd, rd, sd; + + jd = LONG2NUM(j); + ajd = rt_jd_to_ajd(jd, INT2FIX(0), INT2FIX(0)); + rd = d_right_new(cDate, ajd, INT2FIX(0), DBL2NUM(sg)); + sd = d_switch_new(cDate, ajd, INT2FIX(0), DBL2NUM(sg)); + if (!f_eqeq_p(rd, sd)) { + print_emesg(rd, sd); + return 0; + } + } + return 1; +} + +static VALUE +date_s_test_d_switch_new(VALUE klass) +{ + if (!test_d_switch_new(MIN_JD, MIN_JD + 366, GREGORIAN)) + return Qfalse; + if (!test_d_switch_new(2305814, 2598007, GREGORIAN)) + return Qfalse; + if (!test_d_switch_new(MAX_JD - 366, MAX_JD, GREGORIAN)) + return Qfalse; + + if (!test_d_switch_new(MIN_JD, MIN_JD + 366, ITALY)) + return Qfalse; + if (!test_d_switch_new(2305814, 2598007, ITALY)) + return Qfalse; + if (!test_d_switch_new(MAX_JD - 366, MAX_JD, ITALY)) + return Qfalse; + + return Qtrue; +} + +static int +test_dt_switch_new(long from, long to, double sg) +{ + long j; + + fprintf(stderr, "test_dt_switch_new: %ld...%ld (%ld) - %.0f\n", + from, to, to - from, sg); + for (j = from; j <= to; j++) { + VALUE jd, vof, ajd, rd, sd; + int of, df; + long sf; + + jd = LONG2NUM(j); + ajd = rt_jd_to_ajd(jd, INT2FIX(0), INT2FIX(0)); + rd = dt_right_new(cDateTime, ajd, INT2FIX(0), DBL2NUM(sg)); + sd = dt_switch_new(cDateTime, ajd, INT2FIX(0), DBL2NUM(sg)); + if (!f_eqeq_p(rd, sd)) { + print_emesg(rd, sd); + return 0; + } + + of = (int)((labs(j) % (DAY_IN_SECONDS * 2)) - DAY_IN_SECONDS); + vof = rb_rational_new2(INT2FIX(of), INT2FIX(DAY_IN_SECONDS)); + df = time_to_df(4, 5, 6); + sf = 123456789L; + ajd = rt_jd_to_ajd(jd, + f_add(rb_rational_new2(INT2FIX(df), + INT2FIX(DAY_IN_SECONDS)), + rb_rational_new2(LONG2NUM(sf), + day_in_nanoseconds)), + vof); + rd = dt_right_new(cDateTime, ajd, vof, DBL2NUM(sg)); + sd = dt_switch_new(cDateTime, ajd, vof, DBL2NUM(sg)); + if (!f_eqeq_p(rd, sd)) { + print_emesg(rd, sd); + return 0; + } + } + return 1; +} + +static VALUE +date_s_test_dt_switch_new(VALUE klass) +{ + if (!test_dt_switch_new(MIN_JD, MIN_JD + 366, GREGORIAN)) + return Qfalse; + if (!test_dt_switch_new(2305814, 2598007, GREGORIAN)) + return Qfalse; + if (!test_dt_switch_new(MAX_JD - 366, MAX_JD, GREGORIAN)) + return Qfalse; + + if (!test_dt_switch_new(MIN_JD, MIN_JD + 366, ITALY)) + return Qfalse; + if (!test_dt_switch_new(2305814, 2598007, ITALY)) + return Qfalse; + if (!test_dt_switch_new(MAX_JD - 366, MAX_JD, ITALY)) + return Qfalse; + + return Qtrue; +} + static VALUE date_s_test_all(VALUE klass) { @@ -4570,10 +9714,237 @@ date_s_test_all(VALUE klass) return Qfalse; if (date_s_test_nth_kday(klass) == Qfalse) return Qfalse; + if (date_s_test_d_switch_new(klass) == Qfalse) + return Qfalse; + if (date_s_test_dt_switch_new(klass) == Qfalse) + return Qfalse; return Qtrue; } #endif +static const char *monthnames[] = { + NULL, + "January", "February", "March", + "April", "May", "June", + "July", "August", "September", + "October", "November", "December" +}; + +static const char *abbr_monthnames[] = { + NULL, + "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" +}; + +static const char *daynames[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" +}; + +static const char *abbr_daynames[] = { + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" +}; + +static VALUE +mk_ary_of_str(long len, const char *a[]) +{ + VALUE o; + long i; + + o = rb_ary_new2(len); + for (i = 0; i < len; i++) { + VALUE e; + + if (!a[i]) + e = Qnil; + else { + e = rb_str_new2(a[i]); + rb_obj_freeze(e); + } + rb_ary_push(o, e); + } + rb_obj_freeze(o); + return o; +} + +/* + * date.rb - date and time library + * + * Author: Tadayoshi Funaba 1998-2011 + * + * Initial Documentation: William Webber <[email protected]> + * + * == Overview + * + * This file provides two classes for working with + * dates and times. + * + * The first class, Date, represents dates. + * It works with years, months, weeks, and days. + * See the Date class documentation for more details. + * + * The second, DateTime, extends Date to include hours, + * minutes, seconds, and fractions of a second. It + * provides basic support for time zones. See the + * DateTime class documentation for more details. + * + * === Ways of calculating the date. + * + * In common usage, the date is reckoned in years since or + * before the Common Era (CE/BCE, also known as AD/BC), then + * as a month and day-of-the-month within the current year. + * This is known as the *Civil* *Date*, and abbreviated + * as +civil+ in the Date class. + * + * Instead of year, month-of-the-year, and day-of-the-month, + * the date can also be reckoned in terms of year and + * day-of-the-year. This is known as the *Ordinal* *Date*, + * and is abbreviated as +ordinal+ in the Date class. (Note + * that referring to this as the Julian date is incorrect.) + * + * The date can also be reckoned in terms of year, week-of-the-year, + * and day-of-the-week. This is known as the *Commercial* + * *Date*, and is abbreviated as +commercial+ in the + * Date class. The commercial week runs Monday (day-of-the-week + * 1) to Sunday (day-of-the-week 7), in contrast to the civil + * week which runs Sunday (day-of-the-week 0) to Saturday + * (day-of-the-week 6). The first week of the commercial year + * starts on the Monday on or before January 1, and the commercial + * year itself starts on this Monday, not January 1. + * + * For scientific purposes, it is convenient to refer to a date + * simply as a day count, counting from an arbitrary initial + * day. The date first chosen for this was January 1, 4713 BCE. + * A count of days from this date is the *Julian* *Day* *Number* + * or *Julian* *Date*, which is abbreviated as +jd+ in the + * Date class. This is in local time, and counts from midnight + * on the initial day. The stricter usage is in UTC, and counts + * from midday on the initial day. This is referred to in the + * Date class as the *Astronomical* *Julian* *Day* *Number*, and + * abbreviated as +ajd+. In the Date class, the Astronomical + * Julian Day Number includes fractional days. + * + * Another absolute day count is the *Modified* *Julian* *Day* + * *Number*, which takes November 17, 1858 as its initial day. + * This is abbreviated as +mjd+ in the Date class. There + * is also an *Astronomical* *Modified* *Julian* *Day* *Number*, + * which is in UTC and includes fractional days. This is + * abbreviated as +amjd+ in the Date class. Like the Modified + * Julian Day Number (and unlike the Astronomical Julian + * Day Number), it counts from midnight. + * + * Alternative calendars such as the Ethiopic Solar Calendar, + * the Islamic Lunar Calendar, or the French Revolutionary Calendar + * are not supported by the Date class; nor are calendars that + * are based on an Era different from the Common Era, such as + * the Japanese Era. + * + * === Calendar Reform + * + * The standard civil year is 365 days long. However, the + * solar year is fractionally longer than this. To account + * for this, a *leap* *year* is occasionally inserted. This + * is a year with 366 days, the extra day falling on February 29. + * In the early days of the civil calendar, every fourth + * year without exception was a leap year. This way of + * reckoning leap years is the *Julian* *Calendar*. + * + * However, the solar year is marginally shorter than 365 1/4 + * days, and so the *Julian* *Calendar* gradually ran slow + * over the centuries. To correct this, every 100th year + * (but not every 400th year) was excluded as a leap year. + * This way of reckoning leap years, which we use today, is + * the *Gregorian* *Calendar*. + * + * The Gregorian Calendar was introduced at different times + * in different regions. The day on which it was introduced + * for a particular region is the *Day* *of* *Calendar* + * *Reform* for that region. This is abbreviated as +sg+ + * (for Start of Gregorian calendar) in the Date class. + * + * Two such days are of particular + * significance. The first is October 15, 1582, which was + * the Day of Calendar Reform for Italy and most Catholic + * countries. The second is September 14, 1752, which was + * the Day of Calendar Reform for England and its colonies + * (including what is now the United States). These two + * dates are available as the constants Date::ITALY and + * Date::ENGLAND, respectively. (By comparison, Germany and + * Holland, less Catholic than Italy but less stubborn than + * England, changed over in 1698; Sweden in 1753; Russia not + * till 1918, after the Revolution; and Greece in 1923. Many + * Orthodox churches still use the Julian Calendar. A complete + * list of Days of Calendar Reform can be found at + * http://www.polysyllabic.com/GregConv.html.) + * + * Switching from the Julian to the Gregorian calendar + * involved skipping a number of days to make up for the + * accumulated lag, and the later the switch was (or is) + * done, the more days need to be skipped. So in 1582 in Italy, + * 4th October was followed by 15th October, skipping 10 days; in 1752 + * in England, 2nd September was followed by 14th September, skipping + * 11 days; and if I decided to switch from Julian to Gregorian + * Calendar this midnight, I would go from 27th July 2003 (Julian) + * today to 10th August 2003 (Gregorian) tomorrow, skipping + * 13 days. The Date class is aware of this gap, and a supposed + * date that would fall in the middle of it is regarded as invalid. + * + * The Day of Calendar Reform is relevant to all date representations + * involving years. It is not relevant to the Julian Day Numbers, + * except for converting between them and year-based representations. + * + * In the Date and DateTime classes, the Day of Calendar Reform or + * +sg+ can be specified a number of ways. First, it can be as + * the Julian Day Number of the Day of Calendar Reform. Second, + * it can be using the constants Date::ITALY or Date::ENGLAND; these + * are in fact the Julian Day Numbers of the Day of Calendar Reform + * of the respective regions. Third, it can be as the constant + * Date::JULIAN, which means to always use the Julian Calendar. + * Finally, it can be as the constant Date::GREGORIAN, which means + * to always use the Gregorian Calendar. + * + * Note: in the Julian Calendar, New Years Day was March 25. The + * Date class does not follow this convention. + * + * === Offsets + * + * DateTime objects support a simple representation + * of offsets. Offsets are represented as an offset + * from UTC (UTC is not identical GMT; GMT is a historical term), + * as a fraction of a day. This offset is the + * how much local time is later (or earlier) than UTC. + * As you travel east, the offset increases until you + * reach the dateline in the middle of the Pacific Ocean; + * as you travel west, the offset decreases. This offset + * is abbreviated as +of+ in the Date class. + * + * This simple representation of offsets does not take + * into account the common practice of Daylight Savings + * Time or Summer Time. + * + * Most DateTime methods return the date and the + * time in local time. The two exceptions are + * #ajd() and #amjd(), which return the date and time + * in UTC time, including fractional days. + * + * The Date class does not support offsets, in that + * there is no way to create a Date object with non-utc offset. + * + * == Examples of use + * + * === Print out the date of every Sunday between two dates. + * + * def print_sundays(d1, d2) + * d1 += 1 until d1.sunday? + * d1.step(d2, 7) do |d| + * puts d.strftime('%B %-d') + * end + * end + * + * print_sundays(Date.new(2003, 4, 8), Date.new(2003, 5, 23)) + */ void Init_date_core(void) { @@ -4581,19 +9952,105 @@ Init_date_core(void) rzero = rb_rational_new1(INT2FIX(0)); rhalf = rb_rational_new2(INT2FIX(1), INT2FIX(2)); - day_in_nanoseconds = DAY_IN_NANOSECONDS; + +#if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS + day_in_nanoseconds = LONG2NUM((long)DAY_IN_SECONDS * + SECOND_IN_NANOSECONDS); +#elif defined HAVE_LONG_LONG + day_in_nanoseconds = LL2NUM((LONG_LONG)DAY_IN_SECONDS * + SECOND_IN_NANOSECONDS); +#else + day_in_nanoseconds = f_mul(INT2FIX(DAY_IN_SECONDS), + INT2FIX(SECOND_IN_NANOSECONDS)); +#endif rb_gc_register_mark_object(rzero); rb_gc_register_mark_object(rhalf); rb_gc_register_mark_object(day_in_nanoseconds); - /* date */ - + /* + * Class representing a date. + * + * See the documentation to the file date.rb for an overview. + * + * Internally, the date is represented as an Astronomical + * Julian Day Number, +ajd+. The Day of Calendar Reform, +sg+, is + * also stored, for conversions to other date formats. (There + * is also an +of+ field for a time zone offset, but this + * is only for the use of the DateTime subclass.) + * + * A new Date object is created using one of the object creation + * class methods named after the corresponding date format, and the + * arguments appropriate to that date format; for instance, + * Date::civil() (aliased to Date::new()) with year, month, + * and day-of-month, or Date::ordinal() with year and day-of-year. + * All of these object creation class methods also take the + * Day of Calendar Reform as an optional argument. + * + * Date objects are immutable once created. + * + * Once a Date has been created, date values + * can be retrieved for the different date formats supported + * using instance methods. For instance, #mon() gives the + * Civil month, #cwday() gives the Commercial day of the week, + * and #yday() gives the Ordinal day of the year. Date values + * can be retrieved in any format, regardless of what format + * was used to create the Date instance. + * + * The Date class includes the Comparable module, allowing + * date objects to be compared and sorted, ranges of dates + * to be created, and so forth. + */ cDate = rb_define_class("Date", rb_cObject); + rb_include_module(cDate, rb_mComparable); + + /* + * Full month names, in English. Months count from 1 to 12; a + * month's numerical representation indexed into this array + * gives the name of that month (hence the first element is nil). + */ + rb_define_const(cDate, "MONTHNAMES", mk_ary_of_str(13, monthnames)); + + /* Abbreviated month names, in English. */ + rb_define_const(cDate, "ABBR_MONTHNAMES", + mk_ary_of_str(13, abbr_monthnames)); + + /* + * Full names of days of the week, in English. Days of the week + * count from 0 to 6 (except in the commercial week); a day's numerical + * representation indexed into this array gives the name of that day. + */ + rb_define_const(cDate, "DAYNAMES", mk_ary_of_str(7, daynames)); + + /* Abbreviated day names, in English. */ + rb_define_const(cDate, "ABBR_DAYNAMES", mk_ary_of_str(7, abbr_daynames)); + + /* The Julian Day Number of the Day of Calendar Reform for Italy + * and the Catholic countries. + */ + rb_define_const(cDate, "ITALY", INT2FIX(ITALY)); + + /* The Julian Day Number of the Day of Calendar Reform for England + * and her Colonies. + */ + rb_define_const(cDate, "ENGLAND", INT2FIX(ENGLAND)); + /* A constant used to indicate that a Date should always use the + * Julian calendar. + */ + rb_define_const(cDate, "JULIAN", DBL2NUM(JULIAN)); + /* A constant used to indicate that a Date should always use the + * Gregorian calendar. + */ + rb_define_const(cDate, "GREGORIAN", DBL2NUM(GREGORIAN)); + rb_define_alloc_func(cDate, d_lite_s_alloc); + +#ifndef NDEBUG rb_define_singleton_method(cDate, "new_r!", date_s_new_r_bang, -1); rb_define_singleton_method(cDate, "new_l!", date_s_new_l_bang, -1); + rb_define_singleton_method(cDate, "new!", date_s_new_bang, -1); +#endif #ifndef NDEBUG rb_define_private_method(CLASS_OF(cDate), "_valid_jd?", @@ -4606,6 +10063,10 @@ Init_date_core(void) date_s__valid_civil_p, -1); rb_define_private_method(CLASS_OF(cDate), "_valid_commercial?", date_s__valid_commercial_p, -1); + rb_define_private_method(CLASS_OF(cDate), "_valid_weeknum?", + date_s__valid_weeknum_p, -1); + rb_define_private_method(CLASS_OF(cDate), "_valid_nth_kday?", + date_s__valid_nth_kday_p, -1); #endif rb_define_singleton_method(cDate, "valid_jd?", date_s_valid_jd_p, -1); @@ -4616,14 +10077,51 @@ Init_date_core(void) rb_define_singleton_method(cDate, "valid_commercial?", date_s_valid_commercial_p, -1); +#ifndef NDEBUG + rb_define_private_method(CLASS_OF(cDate), "valid_weeknum?", + date_s_valid_weeknum_p, -1); + rb_define_private_method(CLASS_OF(cDate), "valid_nth_kday?", + date_s_valid_nth_kday_p, -1); + rb_define_private_method(CLASS_OF(cDate), "zone_to_diff", + date_s_zone_to_diff, 1); +#endif + + rb_define_singleton_method(cDate, "julian_leap?", date_s_julian_leap_p, 1); + rb_define_singleton_method(cDate, "gregorian_leap?", + date_s_gregorian_leap_p, 1); + rb_define_singleton_method(cDate, "leap?", + date_s_gregorian_leap_p, 1); + rb_define_singleton_method(cDate, "jd", date_s_jd, -1); rb_define_singleton_method(cDate, "ordinal", date_s_ordinal, -1); rb_define_singleton_method(cDate, "civil", date_s_civil, -1); rb_define_singleton_method(cDate, "new", date_s_civil, -1); rb_define_singleton_method(cDate, "commercial", date_s_commercial, -1); + +#ifndef NDEBUG + rb_define_singleton_method(cDate, "weeknum", date_s_weeknum, -1); + rb_define_singleton_method(cDate, "nth_kday", date_s_nth_kday, -1); +#endif + rb_define_singleton_method(cDate, "today", date_s_today, -1); rb_define_singleton_method(cDate, "_strptime", date_s__strptime, -1); + rb_define_singleton_method(cDate, "strptime", date_s_strptime, -1); rb_define_singleton_method(cDate, "_parse", date_s__parse, -1); + rb_define_singleton_method(cDate, "parse", date_s_parse, -1); + rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, 1); + rb_define_singleton_method(cDate, "iso8601", date_s_iso8601, -1); + rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, 1); + rb_define_singleton_method(cDate, "rfc3339", date_s_rfc3339, -1); + rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, 1); + rb_define_singleton_method(cDate, "xmlschema", date_s_xmlschema, -1); + rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, 1); + rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, 1); + rb_define_singleton_method(cDate, "rfc2822", date_s_rfc2822, -1); + rb_define_singleton_method(cDate, "rfc822", date_s_rfc2822, -1); + rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, 1); + rb_define_singleton_method(cDate, "httpdate", date_s_httpdate, -1); + rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, 1); + rb_define_singleton_method(cDate, "jisx0301", date_s_jisx0301, -1); rb_define_method(cDate, "ajd", d_lite_ajd, 0); rb_define_method(cDate, "amjd", d_lite_amjd, 0); @@ -4658,17 +10156,50 @@ Init_date_core(void) rb_define_method(cDate, "wday", d_lite_wday, 0); + rb_define_method(cDate, "sunday?", d_lite_sunday_p, 0); + rb_define_method(cDate, "monday?", d_lite_monday_p, 0); + rb_define_method(cDate, "tuesday?", d_lite_tuesday_p, 0); + rb_define_method(cDate, "wednesday?", d_lite_wednesday_p, 0); + rb_define_method(cDate, "thursday?", d_lite_thursday_p, 0); + rb_define_method(cDate, "friday?", d_lite_friday_p, 0); + rb_define_method(cDate, "saturday?", d_lite_saturday_p, 0); + +#ifndef NDEBUG + rb_define_method(cDate, "nth_kday?", generic_nth_kday_p, 2); +#endif + rb_define_method(cDate, "julian?", d_lite_julian_p, 0); rb_define_method(cDate, "gregorian?", d_lite_gregorian_p, 0); rb_define_method(cDate, "leap?", d_lite_leap_p, 0); rb_define_method(cDate, "start", d_lite_start, 0); rb_define_method(cDate, "new_start", d_lite_new_start, -1); + rb_define_method(cDate, "italy", d_lite_italy, 0); + rb_define_method(cDate, "england", d_lite_england, 0); + rb_define_method(cDate, "julian", d_lite_julian, 0); + rb_define_method(cDate, "gregorian", d_lite_gregorian, 0); rb_define_private_method(cDate, "new_offset", d_lite_new_offset, -1); rb_define_method(cDate, "+", d_lite_plus, 1); rb_define_method(cDate, "-", d_lite_minus, 1); + rb_define_method(cDate, "next_day", d_lite_next_day, -1); + rb_define_method(cDate, "prev_day", d_lite_prev_day, -1); + rb_define_method(cDate, "next", d_lite_next, 0); + rb_define_method(cDate, "succ", d_lite_next, 0); + + rb_define_method(cDate, ">>", d_lite_rshift, 1); + rb_define_method(cDate, "<<", d_lite_lshift, 1); + + rb_define_method(cDate, "next_month", d_lite_next_month, -1); + rb_define_method(cDate, "prev_month", d_lite_prev_month, -1); + rb_define_method(cDate, "next_year", d_lite_next_year, -1); + rb_define_method(cDate, "prev_year", d_lite_prev_year, -1); + + rb_define_method(cDate, "step", generic_step, -1); + rb_define_method(cDate, "upto", generic_upto, 1); + rb_define_method(cDate, "downto", generic_downto, 1); + rb_define_method(cDate, "<=>", d_lite_cmp, 1); rb_define_method(cDate, "===", d_lite_equal, 1); rb_define_method(cDate, "eql?", d_lite_eql_p, 1); @@ -4678,17 +10209,30 @@ Init_date_core(void) rb_define_method(cDate, "inspect", d_lite_inspect, 0); rb_define_method(cDate, "strftime", d_lite_strftime, -1); + rb_define_method(cDate, "asctime", d_lite_asctime, 0); + rb_define_method(cDate, "ctime", d_lite_asctime, 0); + rb_define_method(cDate, "iso8601", d_lite_iso8601, 0); + rb_define_method(cDate, "xmlschema", d_lite_iso8601, 0); + rb_define_method(cDate, "rfc3339", d_lite_rfc3339, 0); + rb_define_method(cDate, "rfc2822", d_lite_rfc2822, 0); + rb_define_method(cDate, "rfc822", d_lite_rfc2822, 0); + rb_define_method(cDate, "httpdate", d_lite_httpdate, 0); + rb_define_method(cDate, "jisx0301", d_lite_jisx0301, 0); + rb_define_method(cDate, "marshal_dump", d_lite_marshal_dump, 0); rb_define_method(cDate, "marshal_load", d_lite_marshal_load, 1); - rb_define_private_method(cDate, "__ca__", d_right_cache, 0); - /* datetime */ cDateTime = rb_define_class("DateTime", cDate); rb_define_alloc_func(cDateTime, dt_lite_s_alloc); + +#ifndef NDEBUG + rb_define_singleton_method(cDateTime, "new_r!", datetime_s_new_r_bang, -1); rb_define_singleton_method(cDateTime, "new_l!", datetime_s_new_l_bang, -1); + rb_define_singleton_method(cDateTime, "new!", datetime_s_new_bang, -1); +#endif rb_undef_method(CLASS_OF(cDateTime), "today"); @@ -4698,9 +10242,35 @@ Init_date_core(void) rb_define_singleton_method(cDateTime, "new", datetime_s_civil, -1); rb_define_singleton_method(cDateTime, "commercial", datetime_s_commercial, -1); + +#ifndef NDEBUG + rb_define_singleton_method(cDateTime, "weeknum", + datetime_s_weeknum, -1); + rb_define_singleton_method(cDateTime, "nth_kday", + datetime_s_nth_kday, -1); +#endif + rb_define_singleton_method(cDateTime, "now", datetime_s_now, -1); rb_define_singleton_method(cDateTime, "_strptime", datetime_s__strptime, -1); + rb_define_singleton_method(cDateTime, "strptime", + datetime_s_strptime, -1); + rb_define_singleton_method(cDateTime, "parse", + datetime_s_parse, -1); + rb_define_singleton_method(cDateTime, "iso8601", + datetime_s_iso8601, -1); + rb_define_singleton_method(cDateTime, "rfc3339", + datetime_s_rfc3339, -1); + rb_define_singleton_method(cDateTime, "xmlschema", + datetime_s_xmlschema, -1); + rb_define_singleton_method(cDateTime, "rfc2822", + datetime_s_rfc2822, -1); + rb_define_singleton_method(cDateTime, "rfc822", + datetime_s_rfc2822, -1); + rb_define_singleton_method(cDateTime, "httpdate", + datetime_s_httpdate, -1); + rb_define_singleton_method(cDateTime, "jisx0301", + datetime_s_jisx0301, -1); rb_define_method(cDateTime, "ajd", dt_lite_ajd, 0); rb_define_method(cDateTime, "amjd", dt_lite_amjd, 0); @@ -4735,17 +10305,42 @@ Init_date_core(void) rb_define_method(cDateTime, "wday", dt_lite_wday, 0); + rb_define_method(cDateTime, "sunday?", dt_lite_sunday_p, 0); + rb_define_method(cDateTime, "monday?", dt_lite_monday_p, 0); + rb_define_method(cDateTime, "tuesday?", dt_lite_tuesday_p, 0); + rb_define_method(cDateTime, "wednesday?", dt_lite_wednesday_p, 0); + rb_define_method(cDateTime, "thursday?", dt_lite_thursday_p, 0); + rb_define_method(cDateTime, "friday?", dt_lite_friday_p, 0); + rb_define_method(cDateTime, "saturday?", dt_lite_saturday_p, 0); + rb_define_method(cDateTime, "julian?", dt_lite_julian_p, 0); rb_define_method(cDateTime, "gregorian?", dt_lite_gregorian_p, 0); rb_define_method(cDateTime, "leap?", dt_lite_leap_p, 0); rb_define_method(cDateTime, "start", dt_lite_start, 0); rb_define_method(cDateTime, "new_start", dt_lite_new_start, -1); + rb_define_method(cDateTime, "italy", dt_lite_italy, 0); + rb_define_method(cDateTime, "england", dt_lite_england, 0); + rb_define_method(cDateTime, "julian", dt_lite_julian, 0); + rb_define_method(cDateTime, "gregorian", dt_lite_gregorian, 0); rb_define_method(cDateTime, "new_offset", dt_lite_new_offset, -1); rb_define_method(cDateTime, "+", dt_lite_plus, 1); rb_define_method(cDateTime, "-", dt_lite_minus, 1); + rb_define_method(cDateTime, "next_day", dt_lite_next_day, -1); + rb_define_method(cDateTime, "prev_day", dt_lite_prev_day, -1); + rb_define_method(cDateTime, "next", dt_lite_next, 0); + rb_define_method(cDateTime, "succ", dt_lite_next, 0); + + rb_define_method(cDateTime, ">>", dt_lite_rshift, 1); + rb_define_method(cDateTime, "<<", dt_lite_lshift, 1); + + rb_define_method(cDateTime, "next_month", dt_lite_next_month, -1); + rb_define_method(cDateTime, "prev_month", dt_lite_prev_month, -1); + rb_define_method(cDateTime, "next_year", dt_lite_next_year, -1); + rb_define_method(cDateTime, "prev_year", dt_lite_prev_year, -1); + rb_define_method(cDateTime, "<=>", dt_lite_cmp, 1); rb_define_method(cDateTime, "===", dt_lite_equal, 1); rb_define_method(cDateTime, "eql?", dt_lite_eql_p, 1); @@ -4755,18 +10350,56 @@ Init_date_core(void) rb_define_method(cDateTime, "inspect", dt_lite_inspect, 0); rb_define_method(cDateTime, "strftime", dt_lite_strftime, -1); + rb_define_method(cDateTime, "asctime", dt_lite_asctime, 0); + rb_define_method(cDateTime, "ctime", dt_lite_asctime, 0); + rb_define_method(cDateTime, "iso8601", dt_lite_iso8601, -1); + rb_define_method(cDateTime, "xmlschema", dt_lite_iso8601, -1); + rb_define_method(cDateTime, "rfc3339", dt_lite_rfc3339, -1); + rb_define_method(cDateTime, "rfc2822", dt_lite_rfc2822, 0); + rb_define_method(cDateTime, "rfc822", dt_lite_rfc2822, 0); + rb_define_method(cDateTime, "httpdate", dt_lite_httpdate, 0); + rb_define_method(cDateTime, "jisx0301", dt_lite_jisx0301, -1); + rb_define_method(cDateTime, "marshal_dump", dt_lite_marshal_dump, 0); rb_define_method(cDateTime, "marshal_load", dt_lite_marshal_load, 1); - rb_define_private_method(cDateTime, "__ca__", dt_right_cache, 0); + /* conversions */ + + rb_define_method(rb_cTime, "to_time", time_to_time, 0); + rb_define_method(rb_cTime, "to_date", time_to_date, 0); + rb_define_method(rb_cTime, "to_datetime", time_to_datetime, 0); + + rb_define_method(cDate, "to_time", date_to_time, 0); + rb_define_method(cDate, "to_date", date_to_date, 0); + rb_define_method(cDate, "to_datetime", date_to_datetime, 0); #ifndef NDEBUG + rb_define_method(cDate, "to_right", date_to_right, 0); + rb_define_method(cDate, "to_light", date_to_light, 0); +#endif + + rb_define_method(cDateTime, "to_time", datetime_to_time, 0); + rb_define_method(cDateTime, "to_date", datetime_to_date, 0); + rb_define_method(cDateTime, "to_datetime", datetime_to_datetime, 0); + +#ifndef NDEBUG + rb_define_method(cDateTime, "to_right", datetime_to_right, 0); + rb_define_method(cDateTime, "to_light", datetime_to_light, 0); +#endif + +#ifndef NDEBUG + /* tests */ + rb_define_singleton_method(cDate, "test_civil", date_s_test_civil, 0); rb_define_singleton_method(cDate, "test_ordinal", date_s_test_ordinal, 0); rb_define_singleton_method(cDate, "test_commercial", date_s_test_commercial, 0); rb_define_singleton_method(cDate, "test_weeknum", date_s_test_weeknum, 0); rb_define_singleton_method(cDate, "test_nth_kday", date_s_test_nth_kday, 0); + rb_define_singleton_method(cDate, "test_d_switch_new", + date_s_test_d_switch_new, 0); + rb_define_singleton_method(cDate, "test_dt_switch_new", + date_s_test_dt_switch_new, 0); rb_define_singleton_method(cDate, "test_all", date_s_test_all, 0); #endif } diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c index 9a84c0be83..f08e99b02b 100644 --- a/ext/date/date_parse.c +++ b/ext/date/date_parse.c @@ -14,6 +14,7 @@ #define f_sub(x,y) rb_funcall(x, '-', 1, y) #define f_mul(x,y) rb_funcall(x, '*', 1, y) #define f_div(x,y) rb_funcall(x, '/', 1, y) +#define f_idiv(x,y) rb_funcall(x, rb_intern("div"), 1, y) #define f_mod(x,y) rb_funcall(x, '%', 1, y) #define f_expt(x,y) rb_funcall(x, rb_intern("**"), 1, y) @@ -33,8 +34,6 @@ #define f_aset2(o,i,j,v) rb_funcall(o, rb_intern("[]="), 3, i, j, v) #define f_sub_bang(s,r,x) rb_funcall(s, rb_intern("sub!"), 2, r, x) #define f_gsub_bang(s,r,x) rb_funcall(s, rb_intern("gsub!"), 2, r, x) -#define f_split(s,p) rb_funcall(s, rb_intern("split"), 1, p) -#define f_downcase(x) rb_funcall(x, rb_intern("downcase"), 0) #define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k)), v) #define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k))) @@ -213,6 +212,8 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc) set_hash("_comp", c); } +#define DAYS "sunday|monday|tuesday|wednesday|thursday|friday|saturday" +#define MONTHS "january|february|march|april|may|june|july|august|september|october|november|december" #define ABBR_DAYS "sun|mon|tue|wed|thu|fri|sat" #define ABBR_MONTHS "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec" @@ -262,6 +263,277 @@ subs(VALUE str, VALUE pat, VALUE hash, int (*cb)(VALUE, VALUE)) return 1; } +struct zone { + const char *name; + int offset; +}; + +static struct zone zones_source[] = { + {"ut", 0*3600}, {"gmt", 0*3600}, {"est", -5*3600}, {"edt", -4*3600}, + {"cst", -6*3600}, {"cdt", -5*3600}, {"mst", -7*3600}, {"mdt", -6*3600}, + {"pst", -8*3600}, {"pdt", -7*3600}, + {"a", 1*3600}, {"b", 2*3600}, {"c", 3*3600}, {"d", 4*3600}, + {"e", 5*3600}, {"f", 6*3600}, {"g", 7*3600}, {"h", 8*3600}, + {"i", 9*3600}, {"k", 10*3600}, {"l", 11*3600}, {"m", 12*3600}, + {"n", -1*3600}, {"o", -2*3600}, {"p", -3*3600}, {"q", -4*3600}, + {"r", -5*3600}, {"s", -6*3600}, {"t", -7*3600}, {"u", -8*3600}, + {"v", -9*3600}, {"w", -10*3600}, {"x", -11*3600}, {"y", -12*3600}, + {"z", 0*3600}, + + {"utc", 0*3600}, {"wet", 0*3600}, + {"at", -2*3600}, {"brst",-2*3600}, {"ndt", -(2*3600+1800)}, + {"art", -3*3600}, {"adt", -3*3600}, {"brt", -3*3600}, {"clst",-3*3600}, + {"nst", -(3*3600+1800)}, + {"ast", -4*3600}, {"clt", -4*3600}, + {"akdt",-8*3600}, {"ydt", -8*3600}, + {"akst",-9*3600}, {"hadt",-9*3600}, {"hdt", -9*3600}, {"yst", -9*3600}, + {"ahst",-10*3600},{"cat",-10*3600}, {"hast",-10*3600},{"hst",-10*3600}, + {"nt", -11*3600}, + {"idlw",-12*3600}, + {"bst", 1*3600}, {"cet", 1*3600}, {"fwt", 1*3600}, {"met", 1*3600}, + {"mewt", 1*3600}, {"mez", 1*3600}, {"swt", 1*3600}, {"wat", 1*3600}, + {"west", 1*3600}, + {"cest", 2*3600}, {"eet", 2*3600}, {"fst", 2*3600}, {"mest", 2*3600}, + {"mesz", 2*3600}, {"sast", 2*3600}, {"sst", 2*3600}, + {"bt", 3*3600}, {"eat", 3*3600}, {"eest", 3*3600}, {"msk", 3*3600}, + {"msd", 4*3600}, {"zp4", 4*3600}, + {"zp5", 5*3600}, {"ist", (5*3600+1800)}, + {"zp6", 6*3600}, + {"wast", 7*3600}, + {"cct", 8*3600}, {"sgt", 8*3600}, {"wadt", 8*3600}, + {"jst", 9*3600}, {"kst", 9*3600}, + {"east",10*3600}, {"gst", 10*3600}, + {"eadt",11*3600}, + {"idle",12*3600}, {"nzst",12*3600}, {"nzt", 12*3600}, + {"nzdt",13*3600}, + + {"afghanistan", 16200}, {"alaskan", -32400}, + {"arab", 10800}, {"arabian", 14400}, + {"arabic", 10800}, {"atlantic", -14400}, + {"aus central", 34200}, {"aus eastern", 36000}, + {"azores", -3600}, {"canada central", -21600}, + {"cape verde", -3600}, {"caucasus", 14400}, + {"cen. australia", 34200}, {"central america", -21600}, + {"central asia", 21600}, {"central europe", 3600}, + {"central european", 3600}, {"central pacific", 39600}, + {"central", -21600}, {"china", 28800}, + {"dateline", -43200}, {"e. africa", 10800}, + {"e. australia", 36000}, {"e. europe", 7200}, + {"e. south america", -10800}, {"eastern", -18000}, + {"egypt", 7200}, {"ekaterinburg", 18000}, + {"fiji", 43200}, {"fle", 7200}, + {"greenland", -10800}, {"greenwich", 0}, + {"gtb", 7200}, {"hawaiian", -36000}, + {"india", 19800}, {"iran", 12600}, + {"jerusalem", 7200}, {"korea", 32400}, + {"mexico", -21600}, {"mid-atlantic", -7200}, + {"mountain", -25200}, {"myanmar", 23400}, + {"n. central asia", 21600}, {"nepal", 20700}, + {"new zealand", 43200}, {"newfoundland", -12600}, + {"north asia east", 28800}, {"north asia", 25200}, + {"pacific sa", -14400}, {"pacific", -28800}, + {"romance", 3600}, {"russian", 10800}, + {"sa eastern", -10800}, {"sa pacific", -18000}, + {"sa western", -14400}, {"samoa", -39600}, + {"se asia", 25200}, {"malay peninsula", 28800}, + {"south africa", 7200}, {"sri lanka", 21600}, + {"taipei", 28800}, {"tasmania", 36000}, + {"tokyo", 32400}, {"tonga", 46800}, + {"us eastern", -18000}, {"us mountain", -25200}, + {"vladivostok", 36000}, {"w. australia", 28800}, + {"w. central africa", 3600}, {"w. europe", 3600}, + {"west asia", 18000}, {"west pacific", 36000}, + {"yakutsk", 32400} +}; + +VALUE +date_zone_to_diff(VALUE str) +{ + VALUE offset = Qnil; + + long l, i; + char *s, *dest, *d; + int sp = 1; + + l = RSTRING_LEN(str); + s = RSTRING_PTR(str); + + dest = d = ALLOC_N(char, l + 1); + + for (i = 0; i < l; i++) { + if (isspace(s[i]) || s[i] == '\0') { + if (!sp) + *d++ = ' '; + sp = 1; + } + else { + if (isalpha(s[i])) + *d++ = tolower(s[i]); + else + *d++ = s[i]; + sp = 0; + } + } + if (d > dest) { + if (*(d - 1) == ' ') + --d; + *d = '\0'; + } + str = rb_str_new2(dest); + { +#define STD " standard time" +#define DST " daylight time" + char *ss, *ds; + long sl, dl; + int dst = 0; + + sl = RSTRING_LEN(str) - (sizeof STD - 1); + ss = RSTRING_PTR(str) + sl; + dl = RSTRING_LEN(str) - (sizeof DST - 1); + ds = RSTRING_PTR(str) + dl; + + if (strcmp(ss, STD) == 0) { + str = rb_str_new(RSTRING_PTR(str), sl); + } + else if (strcmp(ds, DST) == 0) { + str = rb_str_new(RSTRING_PTR(str), dl); + dst = 1; + } +#undef STD +#undef DST + else { +#define DST " dst" + char *ds; + long dl; + + dl = RSTRING_LEN(str) - (sizeof DST - 1); + ds = RSTRING_PTR(str) + dl; + + if (strcmp(ds, DST) == 0) { + str = rb_str_new(RSTRING_PTR(str), dl); + dst = 1; + } +#undef DST + } + { + static VALUE zones = Qnil; + + if (NIL_P(zones)) { + int i; + + zones = rb_hash_new(); + rb_gc_register_mark_object(zones); + for (i = 0; i < (int)sizeof_array(zones_source); i++) { + VALUE name = rb_str_new2(zones_source[i].name); + VALUE offset = INT2FIX(zones_source[i].offset); + rb_hash_aset(zones, name, offset); + } + } + + offset = f_aref(zones, str); + if (!NIL_P(offset)) { + if (dst) + offset = f_add(offset, INT2FIX(3600)); + goto ok; + } + } + { + char *s, *p; + VALUE sign; + VALUE hour = Qnil, min = Qnil, sec = Qnil; + + s = RSTRING_PTR(str); + + if (strncmp(s, "gmt", 3) == 0 || + strncmp(s, "utc", 3) == 0) + s += 3; + if (issign(*s)) { + sign = rb_str_new(s, 1); + s++; + + str = rb_str_new2(s); + + if (p = strchr(s, ':')) { + hour = rb_str_new(s, p - s); + s = ++p; + if (p = strchr(s, ':')) { + min = rb_str_new(s, p - s); + s = ++p; + if (p = strchr(s, ':')) { + sec = rb_str_new(s, p - s); + } + else + sec = rb_str_new2(s); + } + else + min = rb_str_new2(s); + goto num; + } + if (strpbrk(RSTRING_PTR(str), ",.")) { + char *a, *b; + + a = ALLOC_N(char, RSTRING_LEN(str) + 1); + strcpy(a, RSTRING_PTR(str)); + b = strpbrk(a, ",."); + *b = '\0'; + b++; + + hour = cstr2num(a); + min = f_mul(rb_rational_new2 + (cstr2num(b), + f_expt(INT2FIX(10), + LONG2NUM((long)strlen(b)))), + INT2FIX(60)); + goto num; + } + { + const char *cs = RSTRING_PTR(str); + long cl = RSTRING_LEN(str); + + if (cl % 2) { + if (cl >= 1) + hour = rb_str_new(&cs[0], 1); + if (cl >= 3) + min = rb_str_new(&cs[1], 2); + if (cl >= 5) + min = rb_str_new(&cs[3], 2); + } + else { + if (cl >= 2) + hour = rb_str_new(&cs[0], 2); + if (cl >= 4) + min = rb_str_new(&cs[2], 2); + if (cl >= 6) + sec = rb_str_new(&cs[4], 2); + } + goto num; + } + num: + if (NIL_P(hour)) + offset = INT2FIX(0); + else { + if (TYPE(hour) == T_STRING) + hour = str2num(hour); + offset = f_mul(hour, INT2FIX(3600)); + } + if (!NIL_P(min)) { + if (TYPE(min) == T_STRING) + min = str2num(min); + offset = f_add(offset, f_mul(min, INT2FIX(60))); + } + if (!NIL_P(sec)) + offset = f_add(offset, str2num(sec)); + if (!NIL_P(sign) && + RSTRING_LEN(sign) == 1 && + *RSTRING_PTR(sign) == '-') + offset = f_negate(offset); + } + } + } + ok: + return offset; +} + static int day_num(VALUE s) { @@ -289,7 +561,7 @@ parse_day_cb(VALUE m, VALUE hash) { VALUE s; - s = f_aref(m, INT2FIX(1)); + s = rb_reg_nth_match(1, m); set_hash("wday", INT2FIX(day_num(s))); return 1; } @@ -309,24 +581,24 @@ parse_time2_cb(VALUE m, VALUE hash) { VALUE h, min, s, f, p; - h = f_aref(m, INT2FIX(1)); + h = rb_reg_nth_match(1, m); h = str2num(h); - min = f_aref(m, INT2FIX(2)); + min = rb_reg_nth_match(2, m); if (!NIL_P(min)) min = str2num(min); - s = f_aref(m, INT2FIX(3)); + s = rb_reg_nth_match(3, m); if (!NIL_P(s)) s = str2num(s); - f = f_aref(m, INT2FIX(4)); + f = rb_reg_nth_match(4, m); if (!NIL_P(f)) f = rb_rational_new2(str2num(f), f_expt(INT2FIX(10), LONG2NUM(RSTRING_LEN(f)))); - p = f_aref(m, INT2FIX(5)); + p = rb_reg_nth_match(5, m); if (!NIL_P(p)) { int ih = NUM2INT(h); @@ -361,8 +633,8 @@ parse_time_cb(VALUE m, VALUE hash) static VALUE pat = Qnil; VALUE s1, s2; - s1 = f_aref(m, INT2FIX(1)); - s2 = f_aref(m, INT2FIX(2)); + s1 = rb_reg_nth_match(1, m); + s2 = rb_reg_nth_match(2, m); if (!NIL_P(s2)) set_hash("zone", s2); @@ -421,10 +693,10 @@ parse_eu_cb(VALUE m, VALUE hash) { VALUE y, mon, d, b; - d = f_aref(m, INT2FIX(1)); - mon = f_aref(m, INT2FIX(2)); - b = f_aref(m, INT2FIX(3)); - y = f_aref(m, INT2FIX(4)); + d = rb_reg_nth_match(1, m); + mon = rb_reg_nth_match(2, m); + b = rb_reg_nth_match(3, m); + y = rb_reg_nth_match(4, m); mon = INT2FIX(mon_num(mon)); @@ -458,10 +730,10 @@ parse_us_cb(VALUE m, VALUE hash) { VALUE y, mon, d, b; - mon = f_aref(m, INT2FIX(1)); - d = f_aref(m, INT2FIX(2)); - b = f_aref(m, INT2FIX(3)); - y = f_aref(m, INT2FIX(4)); + mon = rb_reg_nth_match(1, m); + d = rb_reg_nth_match(2, m); + b = rb_reg_nth_match(3, m); + y = rb_reg_nth_match(4, m); mon = INT2FIX(mon_num(mon)); @@ -495,9 +767,9 @@ parse_iso_cb(VALUE m, VALUE hash) { VALUE y, mon, d; - y = f_aref(m, INT2FIX(1)); - mon = f_aref(m, INT2FIX(2)); - d = f_aref(m, INT2FIX(3)); + y = rb_reg_nth_match(1, m); + mon = rb_reg_nth_match(2, m); + d = rb_reg_nth_match(3, m); s3e(hash, y, mon, d, 0); return 1; @@ -518,9 +790,9 @@ parse_iso21_cb(VALUE m, VALUE hash) { VALUE y, w, d; - y = f_aref(m, INT2FIX(1)); - w = f_aref(m, INT2FIX(2)); - d = f_aref(m, INT2FIX(3)); + y = rb_reg_nth_match(1, m); + w = rb_reg_nth_match(2, m); + d = rb_reg_nth_match(3, m); if (!NIL_P(y)) set_hash("cwyear", str2num(y)); @@ -547,7 +819,7 @@ parse_iso22_cb(VALUE m, VALUE hash) { VALUE d; - d = f_aref(m, INT2FIX(1)); + d = rb_reg_nth_match(1, m); set_hash("cwday", str2num(d)); return 1; } @@ -567,8 +839,8 @@ parse_iso23_cb(VALUE m, VALUE hash) { VALUE mon, d; - mon = f_aref(m, INT2FIX(1)); - d = f_aref(m, INT2FIX(2)); + mon = rb_reg_nth_match(1, m); + d = rb_reg_nth_match(2, m); if (!NIL_P(mon)) set_hash("mon", str2num(mon)); @@ -592,8 +864,8 @@ parse_iso24_cb(VALUE m, VALUE hash) { VALUE mon, d; - mon = f_aref(m, INT2FIX(1)); - d = f_aref(m, INT2FIX(2)); + mon = rb_reg_nth_match(1, m); + d = rb_reg_nth_match(2, m); set_hash("mon", str2num(mon)); if (!NIL_P(d)) @@ -617,8 +889,8 @@ parse_iso25_cb(VALUE m, VALUE hash) { VALUE y, d; - y = f_aref(m, INT2FIX(1)); - d = f_aref(m, INT2FIX(2)); + y = rb_reg_nth_match(1, m); + d = rb_reg_nth_match(2, m); set_hash("year", str2num(y)); set_hash("yday", str2num(d)); @@ -647,7 +919,7 @@ parse_iso26_cb(VALUE m, VALUE hash) { VALUE d; - d = f_aref(m, INT2FIX(1)); + d = rb_reg_nth_match(1, m); set_hash("yday", str2num(d)); return 1; @@ -690,23 +962,32 @@ parse_iso2(VALUE str, VALUE hash) } static int +gengo(int c) +{ + int e; + + switch (c) { + case 'M': case 'm': e = 1867; break; + case 'T': case 't': e = 1911; break; + case 'S': case 's': e = 1925; break; + case 'H': case 'h': e = 1988; break; + default: e = 0; break; + } + return e; +} + +static int parse_jis_cb(VALUE m, VALUE hash) { VALUE e, y, mon, d; int ep; - e = f_aref(m, INT2FIX(1)); - y = f_aref(m, INT2FIX(2)); - mon = f_aref(m, INT2FIX(3)); - d = f_aref(m, INT2FIX(4)); - - switch (*RSTRING_PTR(e)) { - case 'M': case 'm': ep = 1867; break; - case 'T': case 't': ep = 1911; break; - case 'S': case 's': ep = 1925; break; - case 'H': case 'h': ep = 1988; break; - default: ep = 0; break; - } + e = rb_reg_nth_match(1, m); + y = rb_reg_nth_match(2, m); + mon = rb_reg_nth_match(3, m); + d = rb_reg_nth_match(4, m); + + ep = gengo(*RSTRING_PTR(e)); set_hash("year", f_add(str2num(y), INT2FIX(ep))); set_hash("mon", str2num(mon)); @@ -730,9 +1011,9 @@ parse_vms11_cb(VALUE m, VALUE hash) { VALUE y, mon, d; - d = f_aref(m, INT2FIX(1)); - mon = f_aref(m, INT2FIX(2)); - y = f_aref(m, INT2FIX(3)); + d = rb_reg_nth_match(1, m); + mon = rb_reg_nth_match(2, m); + y = rb_reg_nth_match(3, m); mon = INT2FIX(mon_num(mon)); @@ -757,9 +1038,9 @@ parse_vms12_cb(VALUE m, VALUE hash) { VALUE y, mon, d; - mon = f_aref(m, INT2FIX(1)); - d = f_aref(m, INT2FIX(2)); - y = f_aref(m, INT2FIX(3)); + mon = rb_reg_nth_match(1, m); + d = rb_reg_nth_match(2, m); + y = rb_reg_nth_match(3, m); mon = INT2FIX(mon_num(mon)); @@ -797,9 +1078,9 @@ parse_sla_cb(VALUE m, VALUE hash) { VALUE y, mon, d; - y = f_aref(m, INT2FIX(1)); - mon = f_aref(m, INT2FIX(2)); - d = f_aref(m, INT2FIX(3)); + y = rb_reg_nth_match(1, m); + mon = rb_reg_nth_match(2, m); + d = rb_reg_nth_match(3, m); s3e(hash, y, mon, d, 0); return 1; @@ -821,9 +1102,9 @@ parse_dot_cb(VALUE m, VALUE hash) { VALUE y, mon, d; - y = f_aref(m, INT2FIX(1)); - mon = f_aref(m, INT2FIX(2)); - d = f_aref(m, INT2FIX(3)); + y = rb_reg_nth_match(1, m); + mon = rb_reg_nth_match(2, m); + d = rb_reg_nth_match(3, m); s3e(hash, y, mon, d, 0); return 1; @@ -845,7 +1126,7 @@ parse_year_cb(VALUE m, VALUE hash) { VALUE y; - y = f_aref(m, INT2FIX(1)); + y = rb_reg_nth_match(1, m); set_hash("year", str2num(y)); return 1; } @@ -865,7 +1146,7 @@ parse_mon_cb(VALUE m, VALUE hash) { VALUE mon; - mon = f_aref(m, INT2FIX(1)); + mon = rb_reg_nth_match(1, m); set_hash("mon", INT2FIX(mon_num(mon))); return 1; } @@ -885,7 +1166,7 @@ parse_mday_cb(VALUE m, VALUE hash) { VALUE d; - d = f_aref(m, INT2FIX(1)); + d = rb_reg_nth_match(1, m); set_hash("mday", str2num(d)); return 1; } @@ -915,8 +1196,6 @@ n2i(const char *s, long f, long w) return v; } -VALUE date_zone_to_diff(VALUE); - static int parse_ddd_cb(VALUE m, VALUE hash) { @@ -924,11 +1203,11 @@ parse_ddd_cb(VALUE m, VALUE hash) const char *cs2, *cs3, *cs5; long l2, l3, l4, l5; - s1 = f_aref(m, INT2FIX(1)); - s2 = f_aref(m, INT2FIX(2)); - s3 = f_aref(m, INT2FIX(3)); - s4 = f_aref(m, INT2FIX(4)); - s5 = f_aref(m, INT2FIX(5)); + s1 = rb_reg_nth_match(1, m); + s2 = rb_reg_nth_match(2, m); + s3 = rb_reg_nth_match(3, m); + s4 = rb_reg_nth_match(4, m); + s5 = rb_reg_nth_match(5, m); cs2 = RSTRING_PTR(s2); l2 = RSTRING_LEN(s2); @@ -1172,7 +1451,7 @@ parse_frag_cb(VALUE m, VALUE hash) { VALUE s, n; - s = f_aref(m, INT2FIX(1)); + s = rb_reg_nth_match(1, m); if (!NIL_P(ref_hash("hour")) && NIL_P(ref_hash("mday"))) { n = str2num(s); @@ -1334,6 +1613,784 @@ date__parse(VALUE str, VALUE comp) return hash; } +static VALUE +comp_year69(VALUE y) +{ + if (f_ge_p(y, INT2FIX(69))) + return f_add(y, INT2FIX(1900)); + return f_add(y, INT2FIX(2000)); +} + +static VALUE +comp_year50(VALUE y) +{ + if (f_ge_p(y, INT2FIX(50))) + return f_add(y, INT2FIX(1900)); + return f_add(y, INT2FIX(2000)); +} + +static VALUE +sec_fraction(VALUE f) +{ + return rb_rational_new2(str2num(f), + f_expt(INT2FIX(10), + LONG2NUM(RSTRING_LEN(f)))); +} + +#define SNUM 14 + +static int +iso8601_ext_datetime_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1], y; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + if (!NIL_P(s[3])) { + set_hash("mday", str2num(s[3])); + if (strcmp(RSTRING_PTR(s[1]), "-") != 0) { + y = str2num(s[1]); + if (RSTRING_LEN(s[1]) < 4) + y = comp_year69(y); + set_hash("year", y); + } + if (NIL_P(s[2])) { + if (strcmp(RSTRING_PTR(s[1]), "-") != 0) + return 0; + } + else + set_hash("mon", str2num(s[2])); + } + else if (!NIL_P(s[5])) { + set_hash("yday", str2num(s[5])); + if (!NIL_P(s[4])) { + y = str2num(s[4]); + if (RSTRING_LEN(s[4]) < 4) + y = comp_year69(y); + set_hash("year", y); + } + } + else if (!NIL_P(s[8])) { + set_hash("cweek", str2num(s[7])); + set_hash("cwday", str2num(s[8])); + if (!NIL_P(s[6])) { + y = str2num(s[6]); + if (RSTRING_LEN(s[6]) < 4) + y = comp_year69(y); + set_hash("cwyear", y); + } + } + else if (!NIL_P(s[9])) { + set_hash("cwday", str2num(s[9])); + } + if (!NIL_P(s[10])) { + set_hash("hour", str2num(s[10])); + set_hash("min", str2num(s[11])); + if (!NIL_P(s[12])) + set_hash("sec", str2num(s[12])); + } + if (!NIL_P(s[13])) { + set_hash("sec_fraction", sec_fraction(s[13])); + } + if (!NIL_P(s[14])) { + set_hash("zone", s[14]); + set_hash("offset", date_zone_to_diff(s[14])); + } + + return 1; +} + +static int +iso8601_ext_datetime(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(?:([-+]?\\d{2,}|-)-(\\d{2})?-(\\d{2})|" + "([-+]?\\d{2,})?-(\\d{3})|" + "(\\d{4}|\\d{2})?-w(\\d{2})-(\\d)|" + "-w-(\\d))" + "(?:t" + "(\\d{2}):(\\d{2})(?::(\\d{2})(?:[,.](\\d+))?)?" + "(z|[-+]\\d{2}(?::?\\d{2})?)?)?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, iso8601_ext_datetime_cb); +} + +#undef SNUM +#define SNUM 17 + +static int +iso8601_bas_datetime_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1], y; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + if (!NIL_P(s[3])) { + set_hash("mday", str2num(s[3])); + if (strcmp(RSTRING_PTR(s[1]), "--") != 0) { + y = str2num(s[1]); + if (RSTRING_LEN(s[1]) < 4) + y = comp_year69(y); + set_hash("year", y); + } + if (*RSTRING_PTR(s[2]) == '-') { + if (strcmp(RSTRING_PTR(s[1]), "--") != 0) + return 0; + } + else + set_hash("mon", str2num(s[2])); + } + else if (!NIL_P(s[5])) { + set_hash("yday", str2num(s[5])); + y = str2num(s[4]); + if (RSTRING_LEN(s[4]) < 4) + y = comp_year69(y); + set_hash("year", y); + } + else if (!NIL_P(s[6])) { + set_hash("yday", str2num(s[6])); + } + else if (!NIL_P(s[9])) { + set_hash("cweek", str2num(s[8])); + set_hash("cwday", str2num(s[9])); + y = str2num(s[7]); + if (RSTRING_LEN(s[7]) < 4) + y = comp_year69(y); + set_hash("cwyear", y); + } + else if (!NIL_P(s[11])) { + set_hash("cweek", str2num(s[10])); + set_hash("cwday", str2num(s[11])); + } + else if (!NIL_P(s[12])) { + set_hash("cwday", str2num(s[12])); + } + if (!NIL_P(s[13])) { + set_hash("hour", str2num(s[13])); + set_hash("min", str2num(s[14])); + if (!NIL_P(s[15])) + set_hash("sec", str2num(s[15])); + } + if (!NIL_P(s[16])) { + set_hash("sec_fraction", sec_fraction(s[16])); + } + if (!NIL_P(s[17])) { + set_hash("zone", s[17]); + set_hash("offset", date_zone_to_diff(s[17])); + } + + return 1; +} + +static int +iso8601_bas_datetime(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(?:([-+]?(?:\\d{4}|\\d{2})|--)(\\d{2}|-)(\\d{2})|" + "([-+]?(?:\\d{4}|\\d{2}))(\\d{3})|" + "-(\\d{3})|" + "(\\d{4}|\\d{2})w(\\d{2})(\\d)|" + "-w(\\d{2})(\\d)|" + "-w-(\\d))" + "(?:t?" + "(\\d{2})(\\d{2})(?:(\\d{2})(?:[,.](\\d+))?)?" + "(z|[-+]\\d{2}(?:\\d{2})?)?)?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, iso8601_bas_datetime_cb); +} + +#undef SNUM +#define SNUM 5 + +static int +iso8601_ext_time_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("hour", str2num(s[1])); + set_hash("min", str2num(s[2])); + if (!NIL_P(s[3])) + set_hash("sec", str2num(s[3])); + if (!NIL_P(s[4])) + set_hash("sec_fraction", sec_fraction(s[4])); + if (!NIL_P(s[5])) { + set_hash("zone", s[5]); + set_hash("offset", date_zone_to_diff(s[5])); + } + + return 1; +} + +#define iso8601_bas_time_cb iso8601_ext_time_cb + +static int +iso8601_ext_time(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(?:(\\d{2}):(\\d{2})(?::(\\d{2})(?:[,.](\\d+))?)?" + "(z|[-+]\\d{2}(:?\\d{2})?)?)?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, iso8601_ext_time_cb); +} + +static int +iso8601_bas_time(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(?:(\\d{2})(\\d{2})(?:(\\d{2})(?:[,.](\\d+))?)?" + "(z|[-+]\\d{2}(\\d{2})?)?)?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, iso8601_bas_time_cb); +} + +VALUE +date__iso8601(VALUE str) +{ + VALUE backref, hash; + + backref = rb_backref_get(); + rb_match_busy(backref); + + hash = rb_hash_new(); + + if (iso8601_ext_datetime(str, hash)) + goto ok; + if (iso8601_bas_datetime(str, hash)) + goto ok; + if (iso8601_ext_time(str, hash)) + goto ok; + if (iso8601_bas_time(str, hash)) + goto ok; + + ok: + rb_backref_set(backref); + + return hash; +} + +#undef SNUM +#define SNUM 8 + +static int +rfc3339_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("year", str2num(s[1])); + set_hash("mon", str2num(s[2])); + set_hash("mday", str2num(s[3])); + set_hash("hour", str2num(s[4])); + set_hash("min", str2num(s[5])); + set_hash("sec", str2num(s[6])); + set_hash("zone", s[8]); + set_hash("offset", date_zone_to_diff(s[8])); + if (!NIL_P(s[7])) + set_hash("sec_fraction", sec_fraction(s[7])); + + return 1; +} + +static int +rfc3339(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(-?\\d{4})-(\\d{2})-(\\d{2})" + "(?:t|\\s)" + "(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?" + "(z|[-+]\\d{2}:\\d{2})\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, rfc3339_cb); +} + +VALUE +date__rfc3339(VALUE str) +{ + VALUE backref, hash; + + backref = rb_backref_get(); + rb_match_busy(backref); + + hash = rb_hash_new(); + rfc3339(str, hash); + rb_backref_set(backref); + return hash; +} + +#undef SNUM +#define SNUM 8 + +static int +xmlschema_datetime_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("year", str2num(s[1])); + if (!NIL_P(s[2])) + set_hash("mon", str2num(s[2])); + if (!NIL_P(s[3])) + set_hash("mday", str2num(s[3])); + if (!NIL_P(s[4])) + set_hash("hour", str2num(s[4])); + if (!NIL_P(s[5])) + set_hash("min", str2num(s[5])); + if (!NIL_P(s[6])) + set_hash("sec", str2num(s[6])); + if (!NIL_P(s[7])) + set_hash("sec_fraction", sec_fraction(s[7])); + if (!NIL_P(s[8])) { + set_hash("zone", s[8]); + set_hash("offset", date_zone_to_diff(s[8])); + } + + return 1; +} + +static int +xmlschema_datetime(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(-?\\d{4,})(?:-(\\d{2})(?:-(\\d{2}))?)?" + "(?:t" + "(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?)?" + "(z|[-+]\\d{2}:\\d{2})?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, xmlschema_datetime_cb); +} + +#undef SNUM +#define SNUM 5 + +static int +xmlschema_time_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("hour", str2num(s[1])); + set_hash("min", str2num(s[2])); + if (!NIL_P(s[3])) + set_hash("sec", str2num(s[3])); + if (!NIL_P(s[4])) + set_hash("sec_fraction", sec_fraction(s[4])); + if (!NIL_P(s[5])) { + set_hash("zone", s[5]); + set_hash("offset", date_zone_to_diff(s[5])); + } + + return 1; +} + +static int +xmlschema_time(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?" + "(z|[-+]\\d{2}:\\d{2})?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, xmlschema_time_cb); +} + +#undef SNUM +#define SNUM 4 + +static int +xmlschema_trunc_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + if (!NIL_P(s[1])) + set_hash("mon", str2num(s[1])); + if (!NIL_P(s[2])) + set_hash("mday", str2num(s[2])); + if (!NIL_P(s[3])) + set_hash("mday", str2num(s[3])); + if (!NIL_P(s[4])) { + set_hash("zone", s[4]); + set_hash("offset", date_zone_to_diff(s[4])); + } + + return 1; +} + +static int +xmlschema_trunc(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(?:--(\\d{2})(?:-(\\d{2}))?|---(\\d{2}))" + "(z|[-+]\\d{2}:\\d{2})?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, xmlschema_trunc_cb); +} + +VALUE +date__xmlschema(VALUE str) +{ + VALUE backref, hash; + + backref = rb_backref_get(); + rb_match_busy(backref); + + hash = rb_hash_new(); + + if (xmlschema_datetime(str, hash)) + goto ok; + if (xmlschema_time(str, hash)) + goto ok; + if (xmlschema_trunc(str, hash)) + goto ok; + + ok: + rb_backref_set(backref); + + return hash; +} + +#undef SNUM +#define SNUM 8 + +static int +rfc2822_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1], y; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("wday", INT2FIX(day_num(s[1]))); + set_hash("mday", str2num(s[2])); + set_hash("mon", INT2FIX(mon_num(s[3]))); + y = str2num(s[4]); + if (RSTRING_LEN(s[4]) < 4) + y = comp_year50(y); + set_hash("year", y); + set_hash("hour", str2num(s[5])); + set_hash("min", str2num(s[6])); + if (!NIL_P(s[7])) + set_hash("sec", str2num(s[7])); + set_hash("zone", s[8]); + set_hash("offset", date_zone_to_diff(s[8])); + + return 1; +} + +static int +rfc2822(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(?:(" ABBR_DAYS ")\\s*,\\s+)?" + "(\\d{1,2})\\s+" + "(" ABBR_MONTHS ")\\s+" + "(-?\\d{2,})\\s+" + "(\\d{2}):(\\d{2})(?::(\\d{2}))?\\s*" + "([-+]\\d{4}|ut|gmt|e[sd]t|c[sd]t|m[sd]t|p[sd]t|[a-ik-z])\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, rfc2822_cb); +} + +VALUE +date__rfc2822(VALUE str) +{ + VALUE backref, hash; + + backref = rb_backref_get(); + rb_match_busy(backref); + + hash = rb_hash_new(); + rfc2822(str, hash); + rb_backref_set(backref); + return hash; +} + +#undef SNUM +#define SNUM 8 + +static int +httpdate_type1_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("wday", INT2FIX(day_num(s[1]))); + set_hash("mday", str2num(s[2])); + set_hash("mon", INT2FIX(mon_num(s[3]))); + set_hash("year", str2num(s[4])); + set_hash("hour", str2num(s[5])); + set_hash("min", str2num(s[6])); + set_hash("sec", str2num(s[7])); + set_hash("zone", s[8]); + set_hash("offset", INT2FIX(0)); + + return 1; +} + +static int +httpdate_type1(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(" ABBR_DAYS ")\\s*,\\s+" + "(\\d{2})\\s+" + "(" ABBR_MONTHS ")\\s+" + "(-?\\d{4})\\s+" + "(\\d{2}):(\\d{2}):(\\d{2})\\s+" + "(gmt)\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, httpdate_type1_cb); +} + +#undef SNUM +#define SNUM 8 + +static int +httpdate_type2_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1], y; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("wday", INT2FIX(day_num(s[1]))); + set_hash("mday", str2num(s[2])); + set_hash("mon", INT2FIX(mon_num(s[3]))); + y = str2num(s[4]); + if (f_ge_p(y, INT2FIX(0)) && f_le_p(y, INT2FIX(99))) + y = comp_year69(y); + set_hash("year", y); + set_hash("hour", str2num(s[5])); + set_hash("min", str2num(s[6])); + set_hash("sec", str2num(s[7])); + set_hash("zone", s[8]); + set_hash("offset", INT2FIX(0)); + + return 1; +} + +static int +httpdate_type2(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(" DAYS ")\\s*,\\s+" + "(\\d{2})\\s*-\\s*" + "(" ABBR_MONTHS ")\\s*-\\s*" + "(\\d{2})\\s+" + "(\\d{2}):(\\d{2}):(\\d{2})\\s+" + "(gmt)\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, httpdate_type2_cb); +} + +#undef SNUM +#define SNUM 7 + +static int +httpdate_type3_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + set_hash("wday", INT2FIX(day_num(s[1]))); + set_hash("mon", INT2FIX(mon_num(s[2]))); + set_hash("mday", str2num(s[3])); + set_hash("hour", str2num(s[4])); + set_hash("min", str2num(s[5])); + set_hash("sec", str2num(s[6])); + set_hash("year", str2num(s[7])); + + return 1; +} + +static int +httpdate_type3(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*(" ABBR_DAYS ")\\s+" + "(" ABBR_MONTHS ")\\s+" + "(\\d{1,2})\\s+" + "(\\d{2}):(\\d{2}):(\\d{2})\\s+" + "(\\d{4})\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, httpdate_type3_cb); +} + +VALUE +date__httpdate(VALUE str) +{ + VALUE backref, hash; + + backref = rb_backref_get(); + rb_match_busy(backref); + + hash = rb_hash_new(); + + if (httpdate_type1(str, hash)) + goto ok; + if (httpdate_type2(str, hash)) + goto ok; + if (httpdate_type3(str, hash)) + goto ok; + + ok: + rb_backref_set(backref); + + return hash; +} + +#undef SNUM +#define SNUM 9 + +static int +jisx0301_cb(VALUE m, VALUE hash) +{ + VALUE s[SNUM + 1]; + int ep; + + { + int i; + s[0] = Qnil; + for (i = 1; i <= SNUM; i++) + s[i] = rb_reg_nth_match(i, m); + } + + ep = gengo(NIL_P(s[1]) ? 'h' : *RSTRING_PTR(s[1])); + set_hash("year", f_add(str2num(s[2]), INT2FIX(ep))); + set_hash("mon", str2num(s[3])); + set_hash("mday", str2num(s[4])); + if (!NIL_P(s[5])) { + set_hash("hour", str2num(s[5])); + if (!NIL_P(s[6])) + set_hash("min", str2num(s[6])); + if (!NIL_P(s[7])) + set_hash("sec", str2num(s[7])); + } + if (!NIL_P(s[8])) + set_hash("sec_fraction", sec_fraction(s[8])); + if (!NIL_P(s[9])) { + set_hash("zone", s[9]); + set_hash("offset", date_zone_to_diff(s[9])); + } + + return 1; +} + +static int +jisx0301(VALUE str, VALUE hash) +{ + static const char pat_source[] = + "\\A\\s*([mtsh])?(\\d{2})\\.(\\d{2})\\.(\\d{2})" + "(?:t" + "(?:(\\d{2}):(\\d{2})(?::(\\d{2})(?:[,.](\\d*))?)?" + "(z|[-+]\\d{2}(?::?\\d{2})?)?)?)?\\s*\\z"; + static VALUE pat = Qnil; + + REGCOMP_I(pat); + SUBS(str, pat, jisx0301_cb); +} + +VALUE +date__jisx0301(VALUE str) +{ + VALUE backref, hash; + + backref = rb_backref_get(); + rb_match_busy(backref); + + hash = rb_hash_new(); + if (jisx0301(str, hash)) + goto ok; + hash = date__iso8601(str); + + ok: + rb_backref_set(backref); + return hash; +} + /* Local variables: c-file-style: "ruby" diff --git a/ext/date/date_strftime.c b/ext/date/date_strftime.c index 2b20f310f9..3596b52235 100644 --- a/ext/date/date_strftime.c +++ b/ext/date/date_strftime.c @@ -602,14 +602,12 @@ date_strftime_with_tmx(char *s, size_t maxsize, const char *format, continue; #endif - #ifdef VMS_EXT case 'v': /* date as dd-bbb-YYYY */ STRFTIME("%e-%^b-%4Y"); continue; #endif - #ifdef POSIX2_DATE case 'C': FMTV('0', 2, "d", div(tmx->year, INT2FIX(100))); @@ -677,7 +675,6 @@ date_strftime_with_tmx(char *s, size_t maxsize, const char *format, #endif /* ISO_DATE_EXT */ - case 'L': w = 3; goto subsec; @@ -834,7 +831,6 @@ isleap(long year) return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); } - static void tmx2tm_noyear(const struct tmx *tmx, struct tm *result) { diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c index 897f5f8807..80b9e03278 100644 --- a/ext/date/date_strptime.c +++ b/ext/date/date_strptime.c @@ -40,6 +40,7 @@ static const char *extz_pats[] = { #define f_sub(x,y) rb_funcall(x, '-', 1, y) #define f_mul(x,y) rb_funcall(x, '*', 1, y) #define f_div(x,y) rb_funcall(x, '/', 1, y) +#define f_idiv(x,y) rb_funcall(x, rb_intern("div"), 1, y) #define f_mod(x,y) rb_funcall(x, '%', 1, y) #define f_expt(x,y) rb_funcall(x, rb_intern("**"), 1, y) @@ -559,7 +560,6 @@ date__strptime_internal(const char *str, size_t slen, goto matched; } - case 'Z': case 'z': { @@ -585,7 +585,7 @@ date__strptime_internal(const char *str, size_t slen, if (!NIL_P(m)) { VALUE s, l, o; - s = f_aref(m, INT2FIX(1)); + s = rb_reg_nth_match(1, m); l = f_end(m, INT2FIX(0)); o = date_zone_to_diff(s); si += NUM2LONG(l); diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb index c6448017f7..d235d76e6f 100644 --- a/ext/date/lib/date.rb +++ b/ext/date/lib/date.rb @@ -1,239 +1,10 @@ -# -# date.rb - date and time library -# -# Author: Tadayoshi Funaba 1998-2011 -# -# Documentation: William Webber <[email protected]> -# -# == Overview -# -# This file provides two classes for working with -# dates and times. -# -# The first class, Date, represents dates. -# It works with years, months, weeks, and days. -# See the Date class documentation for more details. -# -# The second, DateTime, extends Date to include hours, -# minutes, seconds, and fractions of a second. It -# provides basic support for time zones. See the -# DateTime class documentation for more details. -# -# === Ways of calculating the date. -# -# In common usage, the date is reckoned in years since or -# before the Common Era (CE/BCE, also known as AD/BC), then -# as a month and day-of-the-month within the current year. -# This is known as the *Civil* *Date*, and abbreviated -# as +civil+ in the Date class. -# -# Instead of year, month-of-the-year, and day-of-the-month, -# the date can also be reckoned in terms of year and -# day-of-the-year. This is known as the *Ordinal* *Date*, -# and is abbreviated as +ordinal+ in the Date class. (Note -# that referring to this as the Julian date is incorrect.) -# -# The date can also be reckoned in terms of year, week-of-the-year, -# and day-of-the-week. This is known as the *Commercial* -# *Date*, and is abbreviated as +commercial+ in the -# Date class. The commercial week runs Monday (day-of-the-week -# 1) to Sunday (day-of-the-week 7), in contrast to the civil -# week which runs Sunday (day-of-the-week 0) to Saturday -# (day-of-the-week 6). The first week of the commercial year -# starts on the Monday on or before January 1, and the commercial -# year itself starts on this Monday, not January 1. -# -# For scientific purposes, it is convenient to refer to a date -# simply as a day count, counting from an arbitrary initial -# day. The date first chosen for this was January 1, 4713 BCE. -# A count of days from this date is the *Julian* *Day* *Number* -# or *Julian* *Date*, which is abbreviated as +jd+ in the -# Date class. This is in local time, and counts from midnight -# on the initial day. The stricter usage is in UTC, and counts -# from midday on the initial day. This is referred to in the -# Date class as the *Astronomical* *Julian* *Day* *Number*, and -# abbreviated as +ajd+. In the Date class, the Astronomical -# Julian Day Number includes fractional days. -# -# Another absolute day count is the *Modified* *Julian* *Day* -# *Number*, which takes November 17, 1858 as its initial day. -# This is abbreviated as +mjd+ in the Date class. There -# is also an *Astronomical* *Modified* *Julian* *Day* *Number*, -# which is in UTC and includes fractional days. This is -# abbreviated as +amjd+ in the Date class. Like the Modified -# Julian Day Number (and unlike the Astronomical Julian -# Day Number), it counts from midnight. -# -# Alternative calendars such as the Ethiopic Solar Calendar, -# the Islamic Lunar Calendar, or the French Revolutionary Calendar -# are not supported by the Date class; nor are calendars that -# are based on an Era different from the Common Era, such as -# the Japanese Era. -# -# === Calendar Reform -# -# The standard civil year is 365 days long. However, the -# solar year is fractionally longer than this. To account -# for this, a *leap* *year* is occasionally inserted. This -# is a year with 366 days, the extra day falling on February 29. -# In the early days of the civil calendar, every fourth -# year without exception was a leap year. This way of -# reckoning leap years is the *Julian* *Calendar*. -# -# However, the solar year is marginally shorter than 365 1/4 -# days, and so the *Julian* *Calendar* gradually ran slow -# over the centuries. To correct this, every 100th year -# (but not every 400th year) was excluded as a leap year. -# This way of reckoning leap years, which we use today, is -# the *Gregorian* *Calendar*. -# -# The Gregorian Calendar was introduced at different times -# in different regions. The day on which it was introduced -# for a particular region is the *Day* *of* *Calendar* -# *Reform* for that region. This is abbreviated as +sg+ -# (for Start of Gregorian calendar) in the Date class. -# -# Two such days are of particular -# significance. The first is October 15, 1582, which was -# the Day of Calendar Reform for Italy and most Catholic -# countries. The second is September 14, 1752, which was -# the Day of Calendar Reform for England and its colonies -# (including what is now the United States). These two -# dates are available as the constants Date::ITALY and -# Date::ENGLAND, respectively. (By comparison, Germany and -# Holland, less Catholic than Italy but less stubborn than -# England, changed over in 1698; Sweden in 1753; Russia not -# till 1918, after the Revolution; and Greece in 1923. Many -# Orthodox churches still use the Julian Calendar. A complete -# list of Days of Calendar Reform can be found at -# http://www.polysyllabic.com/GregConv.html.) -# -# Switching from the Julian to the Gregorian calendar -# involved skipping a number of days to make up for the -# accumulated lag, and the later the switch was (or is) -# done, the more days need to be skipped. So in 1582 in Italy, -# 4th October was followed by 15th October, skipping 10 days; in 1752 -# in England, 2nd September was followed by 14th September, skipping -# 11 days; and if I decided to switch from Julian to Gregorian -# Calendar this midnight, I would go from 27th July 2003 (Julian) -# today to 10th August 2003 (Gregorian) tomorrow, skipping -# 13 days. The Date class is aware of this gap, and a supposed -# date that would fall in the middle of it is regarded as invalid. -# -# The Day of Calendar Reform is relevant to all date representations -# involving years. It is not relevant to the Julian Day Numbers, -# except for converting between them and year-based representations. -# -# In the Date and DateTime classes, the Day of Calendar Reform or -# +sg+ can be specified a number of ways. First, it can be as -# the Julian Day Number of the Day of Calendar Reform. Second, -# it can be using the constants Date::ITALY or Date::ENGLAND; these -# are in fact the Julian Day Numbers of the Day of Calendar Reform -# of the respective regions. Third, it can be as the constant -# Date::JULIAN, which means to always use the Julian Calendar. -# Finally, it can be as the constant Date::GREGORIAN, which means -# to always use the Gregorian Calendar. -# -# Note: in the Julian Calendar, New Years Day was March 25. The -# Date class does not follow this convention. -# -# === Offsets -# -# DateTime objects support a simple representation -# of offsets. Offsets are represented as an offset -# from UTC (UTC is not identical GMT; GMT is a historical term), -# as a fraction of a day. This offset is the -# how much local time is later (or earlier) than UTC. -# As you travel east, the offset increases until you -# reach the dateline in the middle of the Pacific Ocean; -# as you travel west, the offset decreases. This offset -# is abbreviated as +of+ in the Date class. -# -# This simple representation of offsets does not take -# into account the common practice of Daylight Savings -# Time or Summer Time. -# -# Most DateTime methods return the date and the -# time in local time. The two exceptions are -# #ajd() and #amjd(), which return the date and time -# in UTC time, including fractional days. -# -# The Date class does not support offsets, in that -# there is no way to create a Date object with non-utc offset. -# -# == Examples of use -# -# === Print out the date of every Sunday between two dates. -# -# def print_sundays(d1, d2) -# d1 += 1 until d1.sunday? -# d1.step(d2, 7) do |d| -# puts d.strftime('%B %-d') -# end -# end -# -# print_sundays(Date.new(2003, 4, 8), Date.new(2003, 5, 23)) +# date.rb: Written by Tadayoshi Funaba 1998-2011 +require 'date_core' require 'date/format' -# Class representing a date. -# -# See the documentation to the file date.rb for an overview. -# -# Internally, the date is represented as an Astronomical -# Julian Day Number, +ajd+. The Day of Calendar Reform, +sg+, is -# also stored, for conversions to other date formats. (There -# is also an +of+ field for a time zone offset, but this -# is only for the use of the DateTime subclass.) -# -# A new Date object is created using one of the object creation -# class methods named after the corresponding date format, and the -# arguments appropriate to that date format; for instance, -# Date::civil() (aliased to Date::new()) with year, month, -# and day-of-month, or Date::ordinal() with year and day-of-year. -# All of these object creation class methods also take the -# Day of Calendar Reform as an optional argument. -# -# Date objects are immutable once created. -# -# Once a Date has been created, date values -# can be retrieved for the different date formats supported -# using instance methods. For instance, #mon() gives the -# Civil month, #cwday() gives the Commercial day of the week, -# and #yday() gives the Ordinal day of the year. Date values -# can be retrieved in any format, regardless of what format -# was used to create the Date instance. -# -# The Date class includes the Comparable module, allowing -# date objects to be compared and sorted, ranges of dates -# to be created, and so forth. class Date - include Comparable - - # Full month names, in English. Months count from 1 to 12; a - # month's numerical representation indexed into this array - # gives the name of that month (hence the first element is nil). - MONTHNAMES = [nil] + %w(January February March April May June July - August September October November December) - - # Full names of days of the week, in English. Days of the week - # count from 0 to 6 (except in the commercial week); a day's numerical - # representation indexed into this array gives the name of that day. - DAYNAMES = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday) - - # Abbreviated month names, in English. - ABBR_MONTHNAMES = [nil] + %w(Jan Feb Mar Apr May Jun - Jul Aug Sep Oct Nov Dec) - - # Abbreviated day names, in English. - ABBR_DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat) - - [MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYNAMES].each do |xs| - xs.each{|x| x.freeze unless x.nil?}.freeze - end - - # now only for marshal dumped class Infinity < Numeric # :nodoc: include Comparable @@ -287,1361 +58,4 @@ class Date end - # The Julian Day Number of the Day of Calendar Reform for Italy - # and the Catholic countries. - ITALY = 2299161 # 1582-10-15 - - # The Julian Day Number of the Day of Calendar Reform for England - # and her Colonies. - ENGLAND = 2361222 # 1752-09-14 - - # A constant used to indicate that a Date should always use the - # Julian calendar. - JULIAN = Float::INFINITY - - # A constant used to indicate that a Date should always use the - # Gregorian calendar. - GREGORIAN = -Float::INFINITY - - HALF_DAYS_IN_DAY = Rational(1, 2) # :nodoc: - HOURS_IN_DAY = Rational(1, 24) # :nodoc: - MINUTES_IN_DAY = Rational(1, 1440) # :nodoc: - SECONDS_IN_DAY = Rational(1, 86400) # :nodoc: - MILLISECONDS_IN_DAY = Rational(1, 86400*10**3) # :nodoc: - NANOSECONDS_IN_DAY = Rational(1, 86400*10**9) # :nodoc: - MILLISECONDS_IN_SECOND = Rational(1, 10**3) # :nodoc: - NANOSECONDS_IN_SECOND = Rational(1, 10**9) # :nodoc: - - MJD_EPOCH_IN_AJD = Rational(4800001, 2) # 1858-11-17 # :nodoc: - UNIX_EPOCH_IN_AJD = Rational(4881175, 2) # 1970-01-01 # :nodoc: - MJD_EPOCH_IN_CJD = 2400001 # :nodoc: - UNIX_EPOCH_IN_CJD = 2440588 # :nodoc: - LD_EPOCH_IN_CJD = 2299160 # :nodoc: - - t = Module.new do - - private - - def find_fdoy(y, sg) # :nodoc: - j = nil - 1.upto(31) do |d| - break if j = _valid_civil_r?(y, 1, d, sg) - end - j - end - - def find_ldoy(y, sg) # :nodoc: - j = nil - 31.downto(1) do |d| - break if j = _valid_civil_r?(y, 12, d, sg) - end - j - end - - def find_fdom(y, m, sg) # :nodoc: - j = nil - 1.upto(31) do |d| - break if j = _valid_civil_r?(y, m, d, sg) - end - j - end - - def find_ldom(y, m, sg) # :nodoc: - j = nil - 31.downto(1) do |d| - break if j = _valid_civil_r?(y, m, d, sg) - end - j - end - - # Convert an Ordinal Date to a Julian Day Number. - # - # +y+ and +d+ are the year and day-of-year to convert. - # +sg+ specifies the Day of Calendar Reform. - # - # Returns the corresponding Julian Day Number. - def ordinal_to_jd(y, d, sg=GREGORIAN) # :nodoc: - find_fdoy(y, sg) + d - 1 - end - - # Convert a Julian Day Number to an Ordinal Date. - # - # +jd+ is the Julian Day Number to convert. - # +sg+ specifies the Day of Calendar Reform. - # - # Returns the corresponding Ordinal Date as - # [year, day_of_year] - def jd_to_ordinal(jd, sg=GREGORIAN) # :nodoc: - y = jd_to_civil(jd, sg)[0] - j = find_fdoy(y, sg) - doy = jd - j + 1 - return y, doy - end - - # Convert a Civil Date to a Julian Day Number. - # +y+, +m+, and +d+ are the year, month, and day of the - # month. +sg+ specifies the Day of Calendar Reform. - # - # Returns the corresponding Julian Day Number. - def civil_to_jd(y, m, d, sg=GREGORIAN) # :nodoc: - if m <= 2 - y -= 1 - m += 12 - end - a = (y / 100.0).floor - b = 2 - a + (a / 4.0).floor - jd = (365.25 * (y + 4716)).floor + - (30.6001 * (m + 1)).floor + - d + b - 1524 - if jd < sg - jd -= b - end - jd - end - - # Convert a Julian Day Number to a Civil Date. +jd+ is - # the Julian Day Number. +sg+ specifies the Day of - # Calendar Reform. - # - # Returns the corresponding [year, month, day_of_month] - # as a three-element array. - def jd_to_civil(jd, sg=GREGORIAN) # :nodoc: - if jd < sg - a = jd - else - x = ((jd - 1867216.25) / 36524.25).floor - a = jd + 1 + x - (x / 4.0).floor - end - b = a + 1524 - c = ((b - 122.1) / 365.25).floor - d = (365.25 * c).floor - e = ((b - d) / 30.6001).floor - dom = b - d - (30.6001 * e).floor - if e <= 13 - m = e - 1 - y = c - 4716 - else - m = e - 13 - y = c - 4715 - end - return y, m, dom - end - - # Convert a Commercial Date to a Julian Day Number. - # - # +y+, +w+, and +d+ are the (commercial) year, week of the year, - # and day of the week of the Commercial Date to convert. - # +sg+ specifies the Day of Calendar Reform. - def commercial_to_jd(y, w, d, sg=GREGORIAN) # :nodoc: - j = find_fdoy(y, sg) + 3 - (j - (((j - 1) + 1) % 7)) + - 7 * (w - 1) + - (d - 1) - end - - # Convert a Julian Day Number to a Commercial Date - # - # +jd+ is the Julian Day Number to convert. - # +sg+ specifies the Day of Calendar Reform. - # - # Returns the corresponding Commercial Date as - # [commercial_year, week_of_year, day_of_week] - def jd_to_commercial(jd, sg=GREGORIAN) # :nodoc: - a = jd_to_civil(jd - 3, sg)[0] - j = commercial_to_jd(a + 1, 1, 1, sg) - if jd >= j - y = a + 1 - else - j = commercial_to_jd(a, 1, 1, sg) - y = a - end - w = 1 + ((jd - j) / 7).floor - d = (jd + 1) % 7 - d = 7 if d == 0 - return y, w, d - end - - def weeknum_to_jd(y, w, d, f=0, sg=GREGORIAN) # :nodoc: - a = find_fdoy(y, sg) + 6 - (a - ((a - f) + 1) % 7 - 7) + 7 * w + d - end - - def jd_to_weeknum(jd, f=0, sg=GREGORIAN) # :nodoc: - y, _, d = jd_to_civil(jd, sg) - a = find_fdoy(y, sg) + 6 - w, d = (jd - (a - ((a - f) + 1) % 7) + 7).divmod(7) - return y, w, d - end - - def nth_kday_to_jd(y, m, n, k, sg=GREGORIAN) # :nodoc: - j = if n > 0 - find_fdom(y, m, sg) - 1 - else - find_ldom(y, m, sg) + 7 - end - (j - (((j - k) + 1) % 7)) + 7 * n - end - - def jd_to_nth_kday(jd, sg=GREGORIAN) # :nodoc: - y, m, = jd_to_civil(jd, sg) - j = find_fdom(y, m, sg) - return y, m, ((jd - j) / 7).floor + 1, jd_to_wday(jd) - end - - # Convert an Astronomical Julian Day Number to a (civil) Julian - # Day Number. - # - # +ajd+ is the Astronomical Julian Day Number to convert. - # +of+ is the offset from UTC as a fraction of a day (defaults to 0). - # - # Returns the (civil) Julian Day Number as [day_number, - # fraction] where +fraction+ is always 1/2. - def ajd_to_jd(ajd, of=0) (ajd + of + HALF_DAYS_IN_DAY).divmod(1) end # :nodoc: - - # Convert a (civil) Julian Day Number to an Astronomical Julian - # Day Number. - # - # +jd+ is the Julian Day Number to convert, and +fr+ is a - # fractional day. - # +of+ is the offset from UTC as a fraction of a day (defaults to 0). - # - # Returns the Astronomical Julian Day Number as a single - # numeric value. - def jd_to_ajd(jd, fr, of=0) jd + fr - of - HALF_DAYS_IN_DAY end # :nodoc: - - # Convert a fractional day +fr+ to [hours, minutes, seconds, - # fraction_of_a_second] - def day_fraction_to_time(fr) # :nodoc: - ss, fr = fr.divmod(SECONDS_IN_DAY) # 4p - h, ss = ss.divmod(3600) - min, s = ss.divmod(60) - return h, min, s, fr * 86400 - end - - def day_fraction_to_time_wo_sf(fr) # :nodoc: - ss = fr.div(SECONDS_IN_DAY) # 4p - h, ss = ss.divmod(3600) - min, s = ss.divmod(60) - return h, min, s - end - - # Convert an +h+ hour, +min+ minutes, +s+ seconds period - # to a fractional day. - begin - Rational(Rational(1, 2), 2) # a challenge - - def time_to_day_fraction(h, min, s) - Rational(h * 3600 + min * 60 + s, 86400) # 4p - end - rescue - def time_to_day_fraction(h, min, s) - if Integer === h && Integer === min && Integer === s - Rational(h * 3600 + min * 60 + s, 86400) # 4p - else - (h * 3600 + min * 60 + s).to_r/86400 # 4p - end - end - end - - # Convert an Astronomical Modified Julian Day Number to an - # Astronomical Julian Day Number. - def amjd_to_ajd(amjd) amjd + MJD_EPOCH_IN_AJD end # :nodoc: - - # Convert an Astronomical Julian Day Number to an - # Astronomical Modified Julian Day Number. - def ajd_to_amjd(ajd) ajd - MJD_EPOCH_IN_AJD end # :nodoc: - - # Convert a Modified Julian Day Number to a Julian - # Day Number. - def mjd_to_jd(mjd) mjd + MJD_EPOCH_IN_CJD end # :nodoc: - - # Convert a Julian Day Number to a Modified Julian Day - # Number. - def jd_to_mjd(jd) jd - MJD_EPOCH_IN_CJD end # :nodoc: - - # Convert a count of the number of days since the adoption - # of the Gregorian Calendar (in Italy) to a Julian Day Number. - def ld_to_jd(ld) ld + LD_EPOCH_IN_CJD end # :nodoc: - - # Convert a Julian Day Number to the number of days since - # the adoption of the Gregorian Calendar (in Italy). - def jd_to_ld(jd) jd - LD_EPOCH_IN_CJD end # :nodoc: - - # Convert a Julian Day Number to the day of the week. - # - # Sunday is day-of-week 0; Saturday is day-of-week 6. - def jd_to_wday(jd) (jd + 1) % 7 end # :nodoc: - - # Is +jd+ a valid Julian Day Number? - # - # If it is, returns it. In fact, any value is treated as a valid - # Julian Day Number. - def _valid_jd_r? (jd, sg=GREGORIAN) jd end # :nodoc: - - # Do the year +y+ and day-of-year +d+ make a valid Ordinal Date? - # Returns the corresponding Julian Day Number if they do, or - # nil if they don't. - # - # +d+ can be a negative number, in which case it counts backwards - # from the end of the year (-1 being the last day of the year). - # No year wraparound is performed, however, so valid values of - # +d+ are -365 .. -1, 1 .. 365 on a non-leap-year, - # -366 .. -1, 1 .. 366 on a leap year. - # A date falling in the period skipped in the Day of Calendar Reform - # adjustment is not valid. - # - # +sg+ specifies the Day of Calendar Reform. - def _valid_ordinal_r? (y, d, sg=GREGORIAN) # :nodoc: - if d < 0 - return unless j = find_ldoy(y, sg) - ny, nd = jd_to_ordinal(j + d + 1, sg) - return unless ny == y - d = nd - end - jd = ordinal_to_jd(y, d, sg) - return unless [y, d] == jd_to_ordinal(jd, sg) - jd - end - - # Do year +y+, month +m+, and day-of-month +d+ make a - # valid Civil Date? Returns the corresponding Julian - # Day Number if they do, nil if they don't. - # - # +m+ and +d+ can be negative, in which case they count - # backwards from the end of the year and the end of the - # month respectively. No wraparound is performed, however, - # and invalid values cause an ArgumentError to be raised. - # A date falling in the period skipped in the Day of Calendar - # Reform adjustment is not valid. - # - # +sg+ specifies the Day of Calendar Reform. - def _valid_civil_r? (y, m, d, sg=GREGORIAN) # :nodoc: - if m < 0 - m += 13 - end - if d < 0 - return unless j = find_ldom(y, m, sg) - ny, nm, nd = jd_to_civil(j + d + 1, sg) - return unless [ny, nm] == [y, m] - d = nd - end - jd = civil_to_jd(y, m, d, sg) - return unless [y, m, d] == jd_to_civil(jd, sg) - jd - end - - # Do year +y+, week-of-year +w+, and day-of-week +d+ make a - # valid Commercial Date? Returns the corresponding Julian - # Day Number if they do, nil if they don't. - # - # Monday is day-of-week 1; Sunday is day-of-week 7. - # - # +w+ and +d+ can be negative, in which case they count - # backwards from the end of the year and the end of the - # week respectively. No wraparound is performed, however, - # and invalid values cause an ArgumentError to be raised. - # A date falling in the period skipped in the Day of Calendar - # Reform adjustment is not valid. - # - # +sg+ specifies the Day of Calendar Reform. - def _valid_commercial_r? (y, w, d, sg=GREGORIAN) # :nodoc: - if d < 0 - d += 8 - end - if w < 0 - ny, nw, = - jd_to_commercial(commercial_to_jd(y + 1, 1, 1, sg) + w * 7, sg) - return unless ny == y - w = nw - end - jd = commercial_to_jd(y, w, d, sg) - return unless [y, w, d] == jd_to_commercial(jd, sg) - jd - end - - def _valid_weeknum_r? (y, w, d, f, sg=GREGORIAN) # :nodoc: - if d < 0 - d += 7 - end - if w < 0 - ny, nw, = - jd_to_weeknum(weeknum_to_jd(y + 1, 1, f, f, sg) + w * 7, f, sg) - return unless ny == y - w = nw - end - jd = weeknum_to_jd(y, w, d, f, sg) - return unless [y, w, d] == jd_to_weeknum(jd, f, sg) - jd - end - - def _valid_nth_kday_r? (y, m, n, k, sg=GREGORIAN) # :nodoc: - if k < 0 - k += 7 - end - if n < 0 - ny, nm = (y * 12 + m).divmod(12) - nm, = (nm + 1) .divmod(1) - ny, nm, nn, = - jd_to_nth_kday(nth_kday_to_jd(ny, nm, 1, k, sg) + n * 7, sg) - return unless [ny, nm] == [y, m] - n = nn - end - jd = nth_kday_to_jd(y, m, n, k, sg) - return unless [y, m, n, k] == jd_to_nth_kday(jd, sg) - jd - end - - # Do hour +h+, minute +min+, and second +s+ constitute a valid time? - # - # If they do, returns their value as a fraction of a day. If not, - # returns nil. - # - # The 24-hour clock is used. Negative values of +h+, +min+, and - # +sec+ are treating as counting backwards from the end of the - # next larger unit (e.g. a +min+ of -2 is treated as 58). No - # wraparound is performed. - def _valid_time_r? (h, min, s) # :nodoc: - h += 24 if h < 0 - min += 60 if min < 0 - s += 60 if s < 0 - return unless ((0...24) === h && - (0...60) === min && - (0...60) === s) || - (24 == h && - 0 == min && - 0 == s) - time_to_day_fraction(h, min, s) - end - - end - - extend t - include t - - # Is a year a leap year in the Julian calendar? - # - # All years divisible by 4 are leap years in the Julian calendar. - def self.julian_leap? (y) y % 4 == 0 end - - # Is a year a leap year in the Gregorian calendar? - # - # All years divisible by 4 are leap years in the Gregorian calendar, - # except for years divisible by 100 and not by 400. - def self.gregorian_leap? (y) y % 4 == 0 && y % 100 != 0 || y % 400 == 0 end - - class << self; alias_method :leap?, :gregorian_leap? end - - def self.new!(ajd=0, of=0, sg=ITALY) - jd, df = ajd_to_jd(ajd, 0) - if !(Fixnum === jd) || - jd < sg || df !=0 || of != 0 || - jd < -327 || jd > 366963925 - return new_r!(ajd, of, sg) - end - new_l!(jd, sg) - end - - def self.jd_r(jd=0, sg=ITALY) # :nodoc: - jd = _valid_jd_r?(jd, sg) - new_r!(jd_to_ajd(jd, 0, 0), 0, sg) - end - - private_class_method :jd_r - - def self.ordinal_r(y=-4712, d=1, sg=ITALY) # :nodoc: - unless jd = _valid_ordinal_r?(y, d, sg) - raise ArgumentError, 'invalid date' - end - new_r!(jd_to_ajd(jd, 0, 0), 0, sg) - end - - private_class_method :ordinal_r - - def self.civil_r(y=-4712, m=1, d=1, sg=ITALY) # :nodoc: - unless jd = _valid_civil_r?(y, m, d, sg) - raise ArgumentError, 'invalid date' - end - new_r!(jd_to_ajd(jd, 0, 0), 0, sg) - end - - private_class_method :civil_r - - def self.commercial_r(y=-4712, w=1, d=1, sg=ITALY) # :nodoc: - unless jd = _valid_commercial_r?(y, w, d, sg) - raise ArgumentError, 'invalid date' - end - new_r!(jd_to_ajd(jd, 0, 0), 0, sg) - end - - private_class_method :commercial_r - - def self.weeknum(y=-4712, w=0, d=1, f=0, sg=ITALY) - unless jd = _valid_weeknum_r?(y, w, d, f, sg) - raise ArgumentError, 'invalid date' - end - new!(jd_to_ajd(jd, 0, 0), 0, sg) - end - - private_class_method :weeknum - - def self.nth_kday(y=-4712, m=1, n=1, k=1, sg=ITALY) - unless jd = _valid_nth_kday_r?(y, m, n, k, sg) - raise ArgumentError, 'invalid date' - end - new!(jd_to_ajd(jd, 0, 0), 0, sg) - end - - private_class_method :nth_kday - - def self.rewrite_frags(elem) # :nodoc: - elem ||= {} - if seconds = elem[:seconds] - d, fr = seconds.divmod(86400) - h, fr = fr.divmod(3600) - min, fr = fr.divmod(60) - s, fr = fr.divmod(1) - elem[:jd] = UNIX_EPOCH_IN_CJD + d - elem[:hour] = h - elem[:min] = min - elem[:sec] = s - elem[:sec_fraction] = fr - elem.delete(:seconds) - elem.delete(:offset) - end - elem - end - - private_class_method :rewrite_frags - - def self.complete_frags(elem) # :nodoc: - i = 0 - g = [[:time, [:hour, :min, :sec]], - [nil, [:jd]], - [:ordinal, [:year, :yday, :hour, :min, :sec]], - [:civil, [:year, :mon, :mday, :hour, :min, :sec]], - [:commercial, [:cwyear, :cweek, :cwday, :hour, :min, :sec]], - [:wday, [:wday, :hour, :min, :sec]], - [:wnum0, [:year, :wnum0, :wday, :hour, :min, :sec]], - [:wnum1, [:year, :wnum1, :wday, :hour, :min, :sec]], - [nil, [:cwyear, :cweek, :wday, :hour, :min, :sec]], - [nil, [:year, :wnum0, :cwday, :hour, :min, :sec]], - [nil, [:year, :wnum1, :cwday, :hour, :min, :sec]]]. - collect{|k, a| e = elem.values_at(*a).compact; [k, a, e]}. - select{|k, a, e| e.size > 0}. - sort_by{|k, a, e| [e.size, i -= 1]}.last - - d = nil - - if g && g[0] && (g[1].size - g[2].size) != 0 - d ||= Date.today - - case g[0] - when :ordinal - elem[:year] ||= d.year - elem[:yday] ||= 1 - when :civil - g[1].each do |e| - break if elem[e] - elem[e] = d.__send__(e) - end - elem[:mon] ||= 1 - elem[:mday] ||= 1 - when :commercial - g[1].each do |e| - break if elem[e] - elem[e] = d.__send__(e) - end - elem[:cweek] ||= 1 - elem[:cwday] ||= 1 - when :wday - elem[:jd] ||= (d - d.wday + elem[:wday]).jd - when :wnum0 - g[1].each do |e| - break if elem[e] - elem[e] = d.__send__(e) - end - elem[:wnum0] ||= 0 - elem[:wday] ||= 0 - when :wnum1 - g[1].each do |e| - break if elem[e] - elem[e] = d.__send__(e) - end - elem[:wnum1] ||= 0 - elem[:wday] ||= 1 - end - end - - if g && g[0] == :time - if self <= DateTime - d ||= Date.today - elem[:jd] ||= d.jd - end - end - - elem[:hour] ||= 0 - elem[:min] ||= 0 - elem[:sec] ||= 0 - elem[:sec] = [elem[:sec], 59].min - - elem - end - - private_class_method :complete_frags - - def self.valid_date_frags?(elem, sg) # :nodoc: - catch :jd do - a = elem.values_at(:jd) - if a.all? - if jd = _valid_jd_r?(*(a << sg)) - throw :jd, jd - end - end - - a = elem.values_at(:year, :yday) - if a.all? - if jd = _valid_ordinal_r?(*(a << sg)) - throw :jd, jd - end - end - - a = elem.values_at(:year, :mon, :mday) - if a.all? - if jd = _valid_civil_r?(*(a << sg)) - throw :jd, jd - end - end - - a = elem.values_at(:cwyear, :cweek, :cwday) - if a[2].nil? && elem[:wday] - a[2] = elem[:wday].nonzero? || 7 - end - if a.all? - if jd = _valid_commercial_r?(*(a << sg)) - throw :jd, jd - end - end - - a = elem.values_at(:year, :wnum0, :wday) - if a[2].nil? && elem[:cwday] - a[2] = elem[:cwday] % 7 - end - if a.all? - if jd = _valid_weeknum_r?(*(a << 0 << sg)) - throw :jd, jd - end - end - - a = elem.values_at(:year, :wnum1, :wday) - if a[2] - a[2] = (a[2] - 1) % 7 - end - if a[2].nil? && elem[:cwday] - a[2] = (elem[:cwday] - 1) % 7 - end - if a.all? - if jd = _valid_weeknum_r?(*(a << 1 << sg)) - throw :jd, jd - end - end - end - end - - private_class_method :valid_date_frags? - - def self.valid_time_frags? (elem) # :nodoc: - h, min, s = elem.values_at(:hour, :min, :sec) - _valid_time_r?(h, min, s) - end - - private_class_method :valid_time_frags? - - def self.new_by_frags(elem, sg) # :nodoc: - elem = rewrite_frags(elem) - elem = complete_frags(elem) - unless jd = valid_date_frags?(elem, sg) - raise ArgumentError, 'invalid date' - end - new!(jd_to_ajd(jd, 0, 0), 0, sg) - end - - private_class_method :new_by_frags - - # Create a new Date object by parsing from a String - # according to a specified format. - # - # +str+ is a String holding a date representation. - # +fmt+ is the format that the date is in. See - # date/format.rb for details on supported formats. - # - # The default +str+ is '-4712-01-01', and the default - # +fmt+ is '%F', which means Year-Month-Day_of_Month. - # This gives Julian Day Number day 0. - # - # +sg+ specifies the Day of Calendar Reform. - # - # An ArgumentError will be raised if +str+ cannot be - # parsed. - def self.strptime(str='-4712-01-01', fmt='%F', sg=ITALY) - elem = _strptime(str, fmt) - new_by_frags(elem, sg) - end - - # Create a new Date object by parsing from a String, - # without specifying the format. - # - # +str+ is a String holding a date representation. - # +comp+ specifies whether to interpret 2-digit years - # as 19XX (>= 69) or 20XX (< 69); the default is to. - # The method will attempt to parse a date from the String - # using various heuristics; see #_parse in date/format.rb - # for more details. If parsing fails, an ArgumentError - # will be raised. - # - # The default +str+ is '-4712-01-01'; this is Julian - # Day Number day 0. - # - # +sg+ specifies the Day of Calendar Reform. - def self.parse(str='-4712-01-01', comp=true, sg=ITALY) - elem = _parse(str, comp) - new_by_frags(elem, sg) - end - - def self.iso8601(str='-4712-01-01', sg=ITALY) # :nodoc: - elem = _iso8601(str) - new_by_frags(elem, sg) - end - - def self.rfc3339(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc: - elem = _rfc3339(str) - new_by_frags(elem, sg) - end - - def self.xmlschema(str='-4712-01-01', sg=ITALY) # :nodoc: - elem = _xmlschema(str) - new_by_frags(elem, sg) - end - - def self.rfc2822(str='Mon, 1 Jan -4712 00:00:00 +0000', sg=ITALY) # :nodoc: - elem = _rfc2822(str) - new_by_frags(elem, sg) - end - - class << self; alias_method :rfc822, :rfc2822 end - - def self.httpdate(str='Mon, 01 Jan -4712 00:00:00 GMT', sg=ITALY) # :nodoc: - elem = _httpdate(str) - new_by_frags(elem, sg) - end - - def self.jisx0301(str='-4712-01-01', sg=ITALY) # :nodoc: - elem = _jisx0301(str) - new_by_frags(elem, sg) - end - - class << self - - def once(*ids) # :nodoc: -- restricted - for id in ids - module_eval <<-"end;" - alias_method :__#{id.object_id}__, :#{id.to_s} - private :__#{id.object_id}__ - def #{id.to_s}(*args) - __ca__[#{id.object_id}] ||= __#{id.object_id}__(*args) - end - end; - end - end # <<dummy - - private :once - - end - - def amjd_r() ajd_to_amjd(ajd) end - - once :amjd_r - - def daynum() ajd_to_jd(ajd, offset) end - - once :daynum - private :daynum - - def jd_r() daynum[0] end # :nodoc: - def day_fraction_r() daynum[1] end # :nodoc: - def mjd_r() jd_to_mjd(jd) end # :nodoc: - def ld_r() jd_to_ld(jd) end # :nodoc: - - once :jd_r, :day_fraction_r, :mjd_r, :ld_r - private :jd_r, :day_fraction_r, :mjd_r, :ld_r - - def civil() jd_to_civil(jd, start) end # :nodoc: - def ordinal() jd_to_ordinal(jd, start) end # :nodoc: - def commercial() jd_to_commercial(jd, start) end # :nodoc: - - def weeknum0() jd_to_weeknum(jd, 0, start) end # :nodoc: - def weeknum1() jd_to_weeknum(jd, 1, start) end # :nodoc: - - once :civil, :ordinal, :commercial, :weeknum0, :weeknum1 - private :civil, :ordinal, :commercial, :weeknum0, :weeknum1 - - def year_r() civil[0] end # :nodoc: - def yday_r() ordinal[1] end # :nodoc: - def mon_r() civil[1] end # :nodoc: - def mday_r() civil[2] end # :nodoc: - - private :year_r, :yday_r, :mon_r, :mday_r - - def wnum0_r() weeknum0[1] end # :nodoc: - def wnum1_r() weeknum1[1] end # :nodoc: - - private :wnum0_r, :wnum1_r - - def time() day_fraction_to_time(day_fraction) end # :nodoc: - def time_wo_sf() day_fraction_to_time_wo_sf(day_fraction) end # :nodoc: - def time_sf() day_fraction % SECONDS_IN_DAY * 86400 end # :nodoc: - - once :time, :time_wo_sf, :time_sf - private :time, :time_wo_sf, :time_sf - - def hour_r() time_wo_sf[0] end # :nodoc: # 4p - def min_r() time_wo_sf[1] end # :nodoc: # 4p - def sec_r() time_wo_sf[2] end # :nodoc: # 4p - def sec_fraction_r() time_sf end # 4p - - private :hour_r, :min_r, :sec_r, :sec_fraction_r - - def zone_r # :nodoc: # 4p - strftime('%:z') - sign = if offset < 0 then '-' else '+' end - fr = offset.abs - ss = fr.div(SECONDS_IN_DAY) - hh, ss = ss.divmod(3600) - mm = ss.div(60) - format('%s%02d:%02d', sign, hh, mm) - end - - private :zone_r - - def cwyear_r() commercial[0] end # :nodoc: - def cweek_r() commercial[1] end # :nodoc: - def cwday_r() commercial[2] end # :nodoc: - - private :cwyear_r, :cweek_r, :cwday_r - - def wday_r() jd_to_wday(jd) end # :nodoc: - - once :wday_r - private :wday_r - -=begin - MONTHNAMES.each_with_index do |n, i| - if n - define_method(n.downcase + '?'){mon == i} - end - end -=end - - DAYNAMES.each_with_index do |n, i| - define_method(n.downcase + '?'){wday == i} - end - - def nth_kday? (n, k) # :nodoc: - k == wday && jd === nth_kday_to_jd(year, mon, n, k, start) - end - - private :nth_kday? - - def julian_r? () jd < start end # :nodoc: - def gregorian_r? () !julian? end # :nodoc: - - once :julian_r?, :gregorian_r? - private :julian_r?, :gregorian_r? - - def fix_style # :nodoc: - if julian? - then self.class::JULIAN - else self.class::GREGORIAN end - end - - private :fix_style - - def leap_r? # :nodoc: - jd_to_civil(civil_to_jd(year, 3, 1, fix_style) - 1, - fix_style)[-1] == 29 - end - - once :leap_r? - private :leap_r? - - def new_start_r(sg=self.class::ITALY) self.class.new_r!(ajd, offset, sg) end # :nodoc: - - private :new_start_r - - # Create a copy of this Date object that uses the Italian/Catholic - # Day of Calendar Reform. - def italy() new_start(self.class::ITALY) end - - # Create a copy of this Date object that uses the English/Colonial - # Day of Calendar Reform. - def england() new_start(self.class::ENGLAND) end - - # Create a copy of this Date object that always uses the Julian - # Calendar. - def julian() new_start(self.class::JULIAN) end - - # Create a copy of this Date object that always uses the Gregorian - # Calendar. - def gregorian() new_start(self.class::GREGORIAN) end - - def new_offset_r(of=0) # :nodoc: - if String === of - of = Rational(zone_to_diff(of) || 0, 86400) - elsif Float === of - of = Rational((of * 86400).round, 86400) - end - self.class.new_r!(ajd, of, start) - end - - private :new_offset_r - - def plus_r (n) # :nodoc: - case n - when Numeric - if Float === n - n = Rational((n * 86400000000000).round, 86400000000000) - end - return self.class.new_r!(ajd + n, offset, start) - end - raise TypeError, 'expected numeric' - end - - private :plus_r - - def minus_r (x) # :nodoc: - case x - when Numeric - if Float === x - x = Rational((x * 86400000000000).round, 86400000000000) - end - return self.class.new_r!(ajd - x, offset, start) - when Date - return ajd - x.ajd - end - raise TypeError, 'expected numeric or date' - end - - private :minus_r - - def cmp_r (other) # :nodoc: - case other - when Numeric; return ajd <=> other - when Date; return ajd <=> other.ajd - else - begin - l, r = other.coerce(self) - return l <=> r - rescue NoMethodError - end - end - nil - end - - private :cmp_r - - def equal_r (other) # :nodoc: - case other - when Numeric; return jd == other - when Date; return jd == other.jd - else - begin - l, r = other.coerce(self) - return l === r - rescue NoMethodError - end - end - false - end - - private :equal_r - - def next_day(n=1) self + n end - def prev_day(n=1) self - n end - - # Return a new Date one day after this one. - def next() next_day end - - alias_method :succ, :next - - # Return a new Date object that is +n+ months later than - # the current one. - # - # If the day-of-the-month of the current Date is greater - # than the last day of the target month, the day-of-the-month - # of the returned Date will be the last day of the target month. - def >> (n) - y, m = (year * 12 + (mon - 1) + n).divmod(12) - m, = (m + 1) .divmod(1) - d = mday - until jd2 = _valid_civil_r?(y, m, d, start) - d -= 1 - raise ArgumentError, 'invalid date' unless d > 0 - end - self + (jd2 - jd) - end - - # Return a new Date object that is +n+ months earlier than - # the current one. - # - # If the day-of-the-month of the current Date is greater - # than the last day of the target month, the day-of-the-month - # of the returned Date will be the last day of the target month. - def << (n) self >> -n end - - def next_month(n=1) self >> n end - def prev_month(n=1) self << n end - - def next_year(n=1) self >> n * 12 end - def prev_year(n=1) self << n * 12 end - - require 'enumerator' - - # Step the current date forward +step+ days at a - # time (or backward, if +step+ is negative) until - # we reach +limit+ (inclusive), yielding the resultant - # date at each step. - def step(limit, step=1) # :yield: date -=begin - if step.zero? - raise ArgumentError, "step can't be 0" - end -=end - unless block_given? - return to_enum(:step, limit, step) - end - da = self - op = %w(- <= >=)[step <=> 0] - while da.__send__(op, limit) - yield da - da += step - end - self - end - - # Step forward one day at a time until we reach +max+ - # (inclusive), yielding each date as we go. - def upto(max, &block) # :yield: date - step(max, +1, &block) - end - - # Step backward one day at a time until we reach +min+ - # (inclusive), yielding each date as we go. - def downto(min, &block) # :yield: date - step(min, -1, &block) - end - - def eql_r? (other) Date === other && self == other end # :nodoc: - - private :eql_r? - - def hash_r() ajd.hash end # :nodoc: - - private :hash_r - - def inspect_r # :nodoc: - format('#<%s[R]: %s (%s,%s,%s)>', self.class, to_s_r, ajd, offset, start) - end - - private :inspect_r - - def to_s_r() format('%.4d-%02d-%02d', year, mon, mday) end # :nodoc: # 4p - - private :to_s_r - end - -# Class representing a date and time. -# -# See the documentation to the file date.rb for an overview. -# -# DateTime objects are immutable once created. -# -# == Other methods. -# -# The following methods are defined in Date, but declared private -# there. They are made public in DateTime. They are documented -# here. -# -# === hour() -# -# Get the hour-of-the-day of the time. This is given -# using the 24-hour clock, counting from midnight. The first -# hour after midnight is hour 0; the last hour of the day is -# hour 23. -# -# === min() -# -# Get the minute-of-the-hour of the time. -# -# === sec() -# -# Get the second-of-the-minute of the time. -# -# === sec_fraction() -# -# Get the fraction of a second of the time. This is returned as -# a +Rational+. -# -# === zone() -# -# Get the time zone as a String. This is representation of the -# time offset such as "+10:00". -# -# === offset() -# -# Get the time zone offset as a fraction of a day. This is returned -# as a +Rational+. -# -# === new_offset(of=0) -# -# Create a new DateTime object, identical to the current one, except -# with a new time zone offset of +of+. +of+ is the new offset from -# UTC as a fraction of a day. -# -class DateTime < Date - - def self.new!(ajd=0, of=0, sg=ITALY) - jd, df = ajd_to_jd(ajd, 0) - df, sf = (df * 86400).divmod(1) - sf, ssf = (sf * 1000000000).divmod(1) - odf, osf = (of * 86400).divmod(1) - if !(Fixnum === jd) || - jd < sg || ssf != 0 || osf != 0 || - jd < -327 || jd > 366963925 - return new_r!(ajd, of, sg) - end - new_l!(jd, df, sf, odf, sg) - end - - def self.jd_r(jd=0, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc: - unless (jd = _valid_jd_r?(jd, sg)) && - (fr = _valid_time_r?(h, min, s)) - raise ArgumentError, 'invalid date' - end - if String === of - of = Rational(zone_to_diff(of) || 0, 86400) - elsif Float === of - of = Rational((of * 86400).round, 86400) - end - new_r!(jd_to_ajd(jd, fr, of), of, sg) - end - - private_class_method :jd_r - - def self.ordinal_r(y=-4712, d=1, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc: - unless (jd = _valid_ordinal_r?(y, d, sg)) && - (fr = _valid_time_r?(h, min, s)) - raise ArgumentError, 'invalid date' - end - if String === of - of = Rational(zone_to_diff(of) || 0, 86400) - elsif Float === of - of = Rational((of * 86400).round, 86400) - end - new_r!(jd_to_ajd(jd, fr, of), of, sg) - end - - private_class_method :ordinal_r - - def self.civil_r(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc: - unless (jd = _valid_civil_r?(y, m, d, sg)) && - (fr = _valid_time_r?(h, min, s)) - raise ArgumentError, 'invalid date' - end - if String === of - of = Rational(zone_to_diff(of) || 0, 86400) - elsif Float === of - of = Rational((of * 86400).round, 86400) - end - new_r!(jd_to_ajd(jd, fr, of), of, sg) - end - - private_class_method :civil_r - - def self.commercial_r(y=-4712, w=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc: - unless (jd = _valid_commercial_r?(y, w, d, sg)) && - (fr = _valid_time_r?(h, min, s)) - raise ArgumentError, 'invalid date' - end - if String === of - of = Rational(zone_to_diff(of) || 0, 86400) - elsif Float === of - of = Rational((of * 86400).round, 86400) - end - new_r!(jd_to_ajd(jd, fr, of), of, sg) - end - - private_class_method :commercial_r - - def self.weeknum(y=-4712, w=0, d=1, f=0, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc: - unless (jd = _valid_weeknum_r?(y, w, d, f, sg)) && - (fr = _valid_time_r?(h, min, s)) - raise ArgumentError, 'invalid date' - end - if String === of - of = Rational(zone_to_diff(of) || 0, 86400) - elsif Float === of - of = Rational((of * 86400).round, 86400) - end - new!(jd_to_ajd(jd, fr, of), of, sg) - end - - private_class_method :weeknum - - def self.nth_kday(y=-4712, m=1, n=1, k=1, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc: - unless (jd = _valid_nth_kday_r?(y, m, n, k, sg)) && - (fr = _valid_time_r?(h, min, s)) - raise ArgumentError, 'invalid date' - end - if String === of - of = Rational(zone_to_diff(of) || 0, 86400) - elsif Float === of - of = Rational((of * 86400).round, 86400) - end - new!(jd_to_ajd(jd, fr, of), of, sg) - end - - private_class_method :nth_kday - - def self.new_by_frags(elem, sg) # :nodoc: - elem = rewrite_frags(elem) - elem = complete_frags(elem) - unless (jd = valid_date_frags?(elem, sg)) && - (fr = valid_time_frags?(elem)) - raise ArgumentError, 'invalid date' - end - fr += (elem[:sec_fraction] || 0) / 86400 - of = Rational(elem[:offset] || 0, 86400) - new!(jd_to_ajd(jd, fr, of), of, sg) - end - - private_class_method :new_by_frags - - # Create a new DateTime object by parsing from a String - # according to a specified format. - # - # +str+ is a String holding a date-time representation. - # +fmt+ is the format that the date-time is in. See - # date/format.rb for details on supported formats. - # - # The default +str+ is '-4712-01-01T00:00:00+00:00', and the default - # +fmt+ is '%FT%T%z'. This gives midnight on Julian Day Number day 0. - # - # +sg+ specifies the Day of Calendar Reform. - # - # An ArgumentError will be raised if +str+ cannot be - # parsed. - def self.strptime(str='-4712-01-01T00:00:00+00:00', fmt='%FT%T%z', sg=ITALY) - elem = _strptime(str, fmt) - new_by_frags(elem, sg) - end - - # Create a new DateTime object by parsing from a String, - # without specifying the format. - # - # +str+ is a String holding a date-time representation. - # +comp+ specifies whether to interpret 2-digit years - # as 19XX (>= 69) or 20XX (< 69); the default is to. - # The method will attempt to parse a date-time from the String - # using various heuristics; see #_parse in date/format.rb - # for more details. If parsing fails, an ArgumentError - # will be raised. - # - # The default +str+ is '-4712-01-01T00:00:00+00:00'; this is Julian - # Day Number day 0. - # - # +sg+ specifies the Day of Calendar Reform. - def self.parse(str='-4712-01-01T00:00:00+00:00', comp=true, sg=ITALY) - elem = _parse(str, comp) - new_by_frags(elem, sg) - end - - def self.iso8601(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc: - elem = _iso8601(str) - new_by_frags(elem, sg) - end - - def self.xmlschema(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc: - elem = _xmlschema(str) - new_by_frags(elem, sg) - end - - def self.rfc2822(str='Mon, 1 Jan -4712 00:00:00 +0000', sg=ITALY) # :nodoc: - elem = _rfc2822(str) - new_by_frags(elem, sg) - end - - class << self; alias_method :rfc822, :rfc2822 end - - def self.httpdate(str='Mon, 01 Jan -4712 00:00:00 GMT', sg=ITALY) # :nodoc: - elem = _httpdate(str) - new_by_frags(elem, sg) - end - - def self.jisx0301(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc: - elem = _jisx0301(str) - new_by_frags(elem, sg) - end - - def to_s_r # :nodoc: # 4p - format('%.4d-%02d-%02dT%02d:%02d:%02d%s', - year, mon, mday, hour, min, sec, zone) - end - - private :to_s_r - -end - -class Time - - def to_time() getlocal end - - def to_date - jd = Date.__send__(:civil_to_jd, year, mon, mday, Date::ITALY) - Date.new!(Date.__send__(:jd_to_ajd, jd, 0, 0), 0, Date::ITALY) - end - - def to_datetime - jd = DateTime.__send__(:civil_to_jd, year, mon, mday, DateTime::ITALY) - fr = DateTime.__send__(:time_to_day_fraction, hour, min, [sec, 59].min) + - Rational(subsec, 86400) - of = Rational(utc_offset, 86400) - DateTime.new!(DateTime.__send__(:jd_to_ajd, jd, fr, of), - of, DateTime::ITALY) - end - -end - -class Date - - def to_time() Time.local(year, mon, mday) end - def to_date() self end - def to_datetime() DateTime.new!(jd_to_ajd(jd, 0, 0), offset, start) end - -end - -class DateTime < Date - - def to_time - d = new_offset(0) - d.instance_eval do - Time.utc(year, mon, mday, hour, min, sec + - sec_fraction) - end. - getlocal - end - - def to_date() Date.new!(jd_to_ajd(jd, 0, 0), 0, start) end - def to_datetime() self end - -end - -require 'date_core' diff --git a/ext/date/lib/date/format.rb b/ext/date/lib/date/format.rb index 3da4c40f51..892e7aacaa 100644 --- a/ext/date/lib/date/format.rb +++ b/ext/date/lib/date/format.rb @@ -1,584 +1 @@ -#-- # format.rb: Written by Tadayoshi Funaba 1999-2011 -#++ - -class Date - - module Format # :nodoc: - - MONTHS = { - 'january' => 1, 'february' => 2, 'march' => 3, 'april' => 4, - 'may' => 5, 'june' => 6, 'july' => 7, 'august' => 8, - 'september'=> 9, 'october' =>10, 'november' =>11, 'december' =>12 - } - - DAYS = { - 'sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday'=> 3, - 'thursday' => 4, 'friday' => 5, 'saturday' => 6 - } - - ABBR_MONTHS = { - 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, - 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, - 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12 - } - - ABBR_DAYS = { - 'sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3, - 'thu' => 4, 'fri' => 5, 'sat' => 6 - } - - ZONES = { - 'ut' => 0*3600, 'gmt' => 0*3600, 'est' => -5*3600, 'edt' => -4*3600, - 'cst' => -6*3600, 'cdt' => -5*3600, 'mst' => -7*3600, 'mdt' => -6*3600, - 'pst' => -8*3600, 'pdt' => -7*3600, - 'a' => 1*3600, 'b' => 2*3600, 'c' => 3*3600, 'd' => 4*3600, - 'e' => 5*3600, 'f' => 6*3600, 'g' => 7*3600, 'h' => 8*3600, - 'i' => 9*3600, 'k' => 10*3600, 'l' => 11*3600, 'm' => 12*3600, - 'n' => -1*3600, 'o' => -2*3600, 'p' => -3*3600, 'q' => -4*3600, - 'r' => -5*3600, 's' => -6*3600, 't' => -7*3600, 'u' => -8*3600, - 'v' => -9*3600, 'w' =>-10*3600, 'x' =>-11*3600, 'y' =>-12*3600, - 'z' => 0*3600, - - 'utc' => 0*3600, 'wet' => 0*3600, - 'at' => -2*3600, 'brst'=> -2*3600, 'ndt' => -(2*3600+1800), - 'art' => -3*3600, 'adt' => -3*3600, 'brt' => -3*3600, 'clst'=> -3*3600, - 'nst' => -(3*3600+1800), - 'ast' => -4*3600, 'clt' => -4*3600, - 'akdt'=> -8*3600, 'ydt' => -8*3600, - 'akst'=> -9*3600, 'hadt'=> -9*3600, 'hdt' => -9*3600, 'yst' => -9*3600, - 'ahst'=>-10*3600, 'cat' =>-10*3600, 'hast'=>-10*3600, 'hst' =>-10*3600, - 'nt' =>-11*3600, - 'idlw'=>-12*3600, - 'bst' => 1*3600, 'cet' => 1*3600, 'fwt' => 1*3600, 'met' => 1*3600, - 'mewt'=> 1*3600, 'mez' => 1*3600, 'swt' => 1*3600, 'wat' => 1*3600, - 'west'=> 1*3600, - 'cest'=> 2*3600, 'eet' => 2*3600, 'fst' => 2*3600, 'mest'=> 2*3600, - 'mesz'=> 2*3600, 'sast'=> 2*3600, 'sst' => 2*3600, - 'bt' => 3*3600, 'eat' => 3*3600, 'eest'=> 3*3600, 'msk' => 3*3600, - 'msd' => 4*3600, 'zp4' => 4*3600, - 'zp5' => 5*3600, 'ist' => (5*3600+1800), - 'zp6' => 6*3600, - 'wast'=> 7*3600, - 'cct' => 8*3600, 'sgt' => 8*3600, 'wadt'=> 8*3600, - 'jst' => 9*3600, 'kst' => 9*3600, - 'east'=> 10*3600, 'gst' => 10*3600, - 'eadt'=> 11*3600, - 'idle'=> 12*3600, 'nzst'=> 12*3600, 'nzt' => 12*3600, - 'nzdt'=> 13*3600, - - 'afghanistan' => 16200, 'alaskan' => -32400, - 'arab' => 10800, 'arabian' => 14400, - 'arabic' => 10800, 'atlantic' => -14400, - 'aus central' => 34200, 'aus eastern' => 36000, - 'azores' => -3600, 'canada central' => -21600, - 'cape verde' => -3600, 'caucasus' => 14400, - 'cen. australia' => 34200, 'central america' => -21600, - 'central asia' => 21600, 'central europe' => 3600, - 'central european' => 3600, 'central pacific' => 39600, - 'central' => -21600, 'china' => 28800, - 'dateline' => -43200, 'e. africa' => 10800, - 'e. australia' => 36000, 'e. europe' => 7200, - 'e. south america' => -10800, 'eastern' => -18000, - 'egypt' => 7200, 'ekaterinburg' => 18000, - 'fiji' => 43200, 'fle' => 7200, - 'greenland' => -10800, 'greenwich' => 0, - 'gtb' => 7200, 'hawaiian' => -36000, - 'india' => 19800, 'iran' => 12600, - 'jerusalem' => 7200, 'korea' => 32400, - 'mexico' => -21600, 'mid-atlantic' => -7200, - 'mountain' => -25200, 'myanmar' => 23400, - 'n. central asia' => 21600, 'nepal' => 20700, - 'new zealand' => 43200, 'newfoundland' => -12600, - 'north asia east' => 28800, 'north asia' => 25200, - 'pacific sa' => -14400, 'pacific' => -28800, - 'romance' => 3600, 'russian' => 10800, - 'sa eastern' => -10800, 'sa pacific' => -18000, - 'sa western' => -14400, 'samoa' => -39600, - 'se asia' => 25200, 'malay peninsula' => 28800, - 'south africa' => 7200, 'sri lanka' => 21600, - 'taipei' => 28800, 'tasmania' => 36000, - 'tokyo' => 32400, 'tonga' => 46800, - 'us eastern' => -18000, 'us mountain' => -25200, - 'vladivostok' => 36000, 'w. australia' => 28800, - 'w. central africa' => 3600, 'w. europe' => 3600, - 'west asia' => 18000, 'west pacific' => 36000, - 'yakutsk' => 32400 - } - - [MONTHS, DAYS, ABBR_MONTHS, ABBR_DAYS, ZONES].each do |x| - x.freeze - end - - end - - # Returns a String representing this Date in the form: - # Sun Sep 30 00:00:00 1977. - def asctime() strftime('%c') end - - alias_method :ctime, :asctime - - # Returns a String representing this Date in the form: - # 1977-09-30. - # - # This form is in accordance with - # ISO8601[http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/date_and_time_format.htm]. - def iso8601() strftime('%F') end - - # Returns a String representing this Date in the form: - # 1977-09-30T00:00:00+00:00 - # - # This form is in accordance with the format for a timestamp in - # RFC3339[http://www.ietf.org/rfc/rfc3339.txt]. - def rfc3339() strftime('%FT%T%:z') end - - # Synonym for Date.iso8601. - def xmlschema() iso8601 end # :nodoc: - - # Returns a String representing this Date in the form: - # Fri, 30 Sep 1977 00:00:00 +0000 - # - # This form is in accordance with the format for a full - # date and time specification in - # RFC2822[http://www.ietf.org/rfc/rfc2822.txt]. - def rfc2822() strftime('%a, %-d %b %Y %T %z') end - - alias_method :rfc822, :rfc2822 - - def httpdate() new_offset(0).strftime('%a, %d %b %Y %T GMT') end # :nodoc: - - # Returns a String representing this Date in a form - # that is in accordance with the Japanese specification - # JIS X 0301. - def jisx0301 - if jd < 2405160 - strftime('%F') - else - case jd - when 2405160...2419614 - g = 'M%02d' % (year - 1867) - when 2419614...2424875 - g = 'T%02d' % (year - 1911) - when 2424875...2447535 - g = 'S%02d' % (year - 1925) - else - g = 'H%02d' % (year - 1988) - end - g + strftime('.%m.%d') - end - end - - def self._iso8601(str) # :nodoc: - if /\A\s*(?:([-+]?\d{2,}|-)-(\d{2})?-(\d{2})| - ([-+]?\d{2,})?-(\d{3})| - (\d{4}|\d{2})?-w(\d{2})-(\d)| - -w-(\d)) - (?:t - (\d{2}):(\d{2})(?::(\d{2})(?:[,.](\d+))?)? - (z|[-+]\d{2}(?::?\d{2})?)?)?\s*\z/ix =~ str - if $3 - e = { - :mday => $3.to_i - } - if $1 != '-' - y = $1.to_i - if $1.size < 4 - y += if y >= 69 then 1900 else 2000 end - end - e[:year] = y - end - if $2.nil? - return if $1 != '-' - else - e[:mon] = $2.to_i - end - elsif $5 - e = { - :yday => $5.to_i - } - if $4 - y = $4.to_i - if $4.size < 4 - y += if y >= 69 then 1900 else 2000 end - end - e[:year] = y - end - elsif $8 - e = { - :cweek => $7.to_i, - :cwday => $8.to_i - } - if $6 - y = $6.to_i - if $6.size < 4 - y += if y >= 69 then 1900 else 2000 end - end - e[:cwyear] = y - end - elsif $9 - e = { - :cwday => $9.to_i - } - end - if $10 - e[:hour] = $10.to_i - e[:min] = $11.to_i - e[:sec] = $12.to_i if $12 - end - if $13 - e[:sec_fraction] = Rational($13.to_i, 10**$13.size) - end - if $14 - e[:zone] = $14 - e[:offset] = zone_to_diff($14) - end - e - elsif /\A\s*(?:([-+]?(?:\d{4}|\d{2})|--)(\d{2}|-)(\d{2})| - ([-+]?(?:\d{4}|\d{2}))(\d{3})| - -(\d{3})| - (\d{4}|\d{2})w(\d{2})(\d)| - -w(\d{2})(\d)| - -w-(\d)) - (?:t? - (\d{2})(\d{2})(?:(\d{2})(?:[,.](\d+))?)? - (z|[-+]\d{2}(?:\d{2})?)?)?\s*\z/ix =~ str - if $3 - e = { - :mday => $3.to_i - } - if $1 != '--' - y = $1.to_i - if $1.size < 4 - y += if y >= 69 then 1900 else 2000 end - end - e[:year] = y - end - if $2 == '-' - return if $1 != '--' - else - e[:mon] = $2.to_i - end - elsif $5 - e = { - :yday => $5.to_i - } - y = $4.to_i - if $4.size < 4 - y += if y >= 69 then 1900 else 2000 end - end - e[:year] = y - elsif $6 - e = { - :yday => $6.to_i - } - elsif $9 - e = { - :cweek => $8.to_i, - :cwday => $9.to_i - } - y = $7.to_i - if $7.size < 4 - y += if y >= 69 then 1900 else 2000 end - end - e[:cwyear] = y - elsif $11 - e = { - :cweek => $10.to_i, - :cwday => $11.to_i - } - elsif $12 - e = { - :cwday => $12.to_i - } - end - if $13 - e[:hour] = $13.to_i - e[:min] = $14.to_i - e[:sec] = $15.to_i if $15 - end - if $16 - e[:sec_fraction] = Rational($16.to_i, 10**$16.size) - end - if $17 - e[:zone] = $17 - e[:offset] = zone_to_diff($17) - end - e - elsif /\A\s*(?:(\d{2}):(\d{2})(?::(\d{2})(?:[,.](\d+))?)? - (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/ix =~ str - e = {} - e[:hour] = $1.to_i if $1 - e[:min] = $2.to_i if $2 - e[:sec] = $3.to_i if $3 - if $4 - e[:sec_fraction] = Rational($4.to_i, 10**$4.size) - end - if $5 - e[:zone] = $5 - e[:offset] = zone_to_diff($5) - end - e - elsif /\A\s*(?:(\d{2})(\d{2})(?:(\d{2})(?:[,.](\d+))?)? - (z|[-+]\d{2}(\d{2})?)?)?\s*\z/ix =~ str - e = {} - e[:hour] = $1.to_i if $1 - e[:min] = $2.to_i if $2 - e[:sec] = $3.to_i if $3 - if $4 - e[:sec_fraction] = Rational($4.to_i, 10**$4.size) - end - if $5 - e[:zone] = $5 - e[:offset] = zone_to_diff($5) - end - e - end - end - - def self._rfc3339(str) # :nodoc: - if /\A\s*(-?\d{4})-(\d{2})-(\d{2}) # allow minus, anyway - (?:t|\s) - (\d{2}):(\d{2}):(\d{2})(?:\.(\d+))? - (z|[-+]\d{2}:\d{2})\s*\z/ix =~ str - e = { - :year => $1.to_i, - :mon => $2.to_i, - :mday => $3.to_i, - :hour => $4.to_i, - :min => $5.to_i, - :sec => $6.to_i, - :zone => $8, - :offset => zone_to_diff($8) - } - e[:sec_fraction] = Rational($7.to_i, 10**$7.size) if $7 - e - end - end - - def self._xmlschema(str) # :nodoc: - if /\A\s*(-?\d{4,})(?:-(\d{2})(?:-(\d{2}))?)? - (?:t - (\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)? - (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str - e = {} - e[:year] = $1.to_i - e[:mon] = $2.to_i if $2 - e[:mday] = $3.to_i if $3 - e[:hour] = $4.to_i if $4 - e[:min] = $5.to_i if $5 - e[:sec] = $6.to_i if $6 - e[:sec_fraction] = Rational($7.to_i, 10**$7.size) if $7 - if $8 - e[:zone] = $8 - e[:offset] = zone_to_diff($8) - end - e - elsif /\A\s*(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))? - (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str - e = {} - e[:hour] = $1.to_i if $1 - e[:min] = $2.to_i if $2 - e[:sec] = $3.to_i if $3 - e[:sec_fraction] = Rational($4.to_i, 10**$4.size) if $4 - if $5 - e[:zone] = $5 - e[:offset] = zone_to_diff($5) - end - e - elsif /\A\s*(?:--(\d{2})(?:-(\d{2}))?|---(\d{2})) - (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str - e = {} - e[:mon] = $1.to_i if $1 - e[:mday] = $2.to_i if $2 - e[:mday] = $3.to_i if $3 - if $4 - e[:zone] = $4 - e[:offset] = zone_to_diff($4) - end - e - end - end - - def self._rfc2822(str) # :nodoc: - if /\A\s*(?:(#{Format::ABBR_DAYS.keys.join('|')})\s*,\s+)? - (\d{1,2})\s+ - (#{Format::ABBR_MONTHS.keys.join('|')})\s+ - (-?\d{2,})\s+ # allow minus, anyway - (\d{2}):(\d{2})(?::(\d{2}))?\s* - ([-+]\d{4}|ut|gmt|e[sd]t|c[sd]t|m[sd]t|p[sd]t|[a-ik-z])\s*\z/iox =~ str - y = $4.to_i - if $4.size < 4 - y += if y >= 50 then 1900 else 2000 end - end - e = { - :wday => Format::ABBR_DAYS[$1.downcase], - :mday => $2.to_i, - :mon => Format::ABBR_MONTHS[$3.downcase], - :year => y, - :hour => $5.to_i, - :min => $6.to_i, - :zone => $8, - :offset => zone_to_diff($8) - } - e[:sec] = $7.to_i if $7 - e - end - end - - class << self; alias_method :_rfc822, :_rfc2822 end - - def self._httpdate(str) # :nodoc: - if /\A\s*(#{Format::ABBR_DAYS.keys.join('|')})\s*,\s+ - (\d{2})\s+ - (#{Format::ABBR_MONTHS.keys.join('|')})\s+ - (-?\d{4})\s+ # allow minus, anyway - (\d{2}):(\d{2}):(\d{2})\s+ - (gmt)\s*\z/iox =~ str - { - :wday => Format::ABBR_DAYS[$1.downcase], - :mday => $2.to_i, - :mon => Format::ABBR_MONTHS[$3.downcase], - :year => $4.to_i, - :hour => $5.to_i, - :min => $6.to_i, - :sec => $7.to_i, - :zone => $8, - :offset => 0 - } - elsif /\A\s*(#{Format::DAYS.keys.join('|')})\s*,\s+ - (\d{2})\s*-\s* - (#{Format::ABBR_MONTHS.keys.join('|')})\s*-\s* - (\d{2})\s+ - (\d{2}):(\d{2}):(\d{2})\s+ - (gmt)\s*\z/iox =~ str - y = $4.to_i - if y >= 0 && y <= 99 - y += if y >= 69 then 1900 else 2000 end - end - { - :wday => Format::DAYS[$1.downcase], - :mday => $2.to_i, - :mon => Format::ABBR_MONTHS[$3.downcase], - :year => y, - :hour => $5.to_i, - :min => $6.to_i, - :sec => $7.to_i, - :zone => $8, - :offset => 0 - } - elsif /\A\s*(#{Format::ABBR_DAYS.keys.join('|')})\s+ - (#{Format::ABBR_MONTHS.keys.join('|')})\s+ - (\d{1,2})\s+ - (\d{2}):(\d{2}):(\d{2})\s+ - (\d{4})\s*\z/iox =~ str - { - :wday => Format::ABBR_DAYS[$1.downcase], - :mon => Format::ABBR_MONTHS[$2.downcase], - :mday => $3.to_i, - :hour => $4.to_i, - :min => $5.to_i, - :sec => $6.to_i, - :year => $7.to_i - } - end - end - - def self._jisx0301(str) # :nodoc: - if /\A\s*([mtsh])?(\d{2})\.(\d{2})\.(\d{2}) - (?:t - (?:(\d{2}):(\d{2})(?::(\d{2})(?:[,.](\d*))?)? - (z|[-+]\d{2}(?::?\d{2})?)?)?)?\s*\z/ix =~ str - era = { - 'm'=>1867, - 't'=>1911, - 's'=>1925, - 'h'=>1988 - }[$1 ? $1.downcase : 'h'] - e = { - :year => $2.to_i + era, - :mon => $3.to_i, - :mday => $4.to_i - } - if $5 - e[:hour] = $5.to_i - e[:min] = $6.to_i if $6 - e[:sec] = $7.to_i if $7 - end - if $8 - e[:sec_fraction] = Rational($8.to_i, 10**$8.size) - end - if $9 - e[:zone] = $9 - e[:offset] = zone_to_diff($9) - end - e - else - _iso8601(str) - end - end - - def self.zone_to_diff(zone) # :nodoc: - zone = zone.downcase - if zone.sub!(/\s+(standard|daylight)\s+time\z/, '') - dst = $1 == 'daylight' - else - dst = zone.sub!(/\s+dst\z/, '') - end - if Format::ZONES.include?(zone) - offset = Format::ZONES[zone] - offset += 3600 if dst - elsif zone.sub!(/\A(?:gmt|utc?)?([-+])/, '') - sign = $1 - if zone.include?(':') - hour, min, sec, = zone.split(':') - elsif zone.include?(',') || zone.include?('.') - hour, fr, = zone.split(/[,.]/) - min = Rational(fr.to_i, 10**fr.size) * 60 - else - if (zone.size % 2) == 1 - hour = zone[0,1] - min = zone[1,2] - sec = zone[3,2] - else - hour = zone[0,2] - min = zone[2,2] - sec = zone[4,2] - end - end - offset = hour.to_i * 3600 + min.to_i * 60 + sec.to_i - offset *= -1 if sign == '-' - end - offset - end - - private_class_method :zone_to_diff - -end - -class DateTime < Date - - def iso8601_timediv(n) # :nodoc: - strftime('T%T' + - if n < 1 - '' - else - '.%0*d' % [n, (sec_fraction / Rational(1, 10**n)).round] - end + - '%:z') - end - - private :iso8601_timediv - - def iso8601(n=0) - super() + iso8601_timediv(n) - end - - def rfc3339(n=0) iso8601(n) end - - def xmlschema(n=0) iso8601(n) end # :nodoc: - - def jisx0301(n=0) - super() + iso8601_timediv(n) - end - -end |