/* date_core.c: Coded by Tadayoshi Funaba 2010-2014 */ #include "ruby.h" #include "ruby/encoding.h" #include "ruby/util.h" #include #include #if defined(HAVE_SYS_TIME_H) #include #endif #define NDEBUG #include #ifdef RUBY_EXTCONF_H #include RUBY_EXTCONF_H #endif #define USE_PACK static ID id_cmp, id_le_p, id_ge_p, id_eqeq_p; static VALUE cDate, cDateTime; static VALUE half_days_in_day, day_in_nanoseconds; static double positive_inf, negative_inf; #define f_boolcast(x) ((x) ? Qtrue : Qfalse) #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 f_quo(x,y) rb_funcall(x, rb_intern("quo"), 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_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_to_i(x) rb_funcall(x, rb_intern("to_i"), 0) #define f_to_r(x) rb_funcall(x, rb_intern("to_r"), 0) #define f_to_s(x) rb_funcall(x, rb_intern("to_s"), 0) #define f_inspect(x) rb_funcall(x, rb_intern("inspect"), 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) inline static VALUE f_cmp(VALUE x, VALUE y) { if (FIXNUM_P(x) && FIXNUM_P(y)) { long c = FIX2LONG(x) - FIX2LONG(y); if (c > 0) c = 1; else if (c < 0) c = -1; return INT2FIX(c); } return rb_funcall(x, id_cmp, 1, y); } inline static VALUE f_lt_p(VALUE x, VALUE y) { if (FIXNUM_P(x) && FIXNUM_P(y)) return f_boolcast(FIX2LONG(x) < FIX2LONG(y)); return rb_funcall(x, '<', 1, y); } inline static VALUE f_gt_p(VALUE x, VALUE y) { if (FIXNUM_P(x) && FIXNUM_P(y)) return f_boolcast(FIX2LONG(x) > FIX2LONG(y)); return rb_funcall(x, '>', 1, y); } inline static VALUE f_le_p(VALUE x, VALUE y) { if (FIXNUM_P(x) && FIXNUM_P(y)) return f_boolcast(FIX2LONG(x) <= FIX2LONG(y)); return rb_funcall(x, id_le_p, 1, y); } inline static VALUE f_ge_p(VALUE x, VALUE y) { if (FIXNUM_P(x) && FIXNUM_P(y)) return f_boolcast(FIX2LONG(x) >= FIX2LONG(y)); return rb_funcall(x, rb_intern(">="), 1, y); } inline static VALUE f_eqeq_p(VALUE x, VALUE y) { if (FIXNUM_P(x) && FIXNUM_P(y)) return f_boolcast(FIX2LONG(x) == FIX2LONG(y)); return rb_funcall(x, rb_intern("=="), 1, y); } inline static VALUE f_zero_p(VALUE x) { switch (TYPE(x)) { case T_FIXNUM: return f_boolcast(FIX2LONG(x) == 0); case T_BIGNUM: return Qfalse; case T_RATIONAL: { VALUE num = rb_rational_num(x); return f_boolcast(FIXNUM_P(num) && FIX2LONG(num) == 0); } } return rb_funcall(x, id_eqeq_p, 1, INT2FIX(0)); } #define f_nonzero_p(x) (!f_zero_p(x)) inline static VALUE f_negative_p(VALUE x) { if (FIXNUM_P(x)) return f_boolcast(FIX2LONG(x) < 0); return rb_funcall(x, '<', 1, INT2FIX(0)); } #define f_positive_p(x) (!f_negative_p(x)) #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) /* copied from time.c */ #define NDIV(x,y) (-(-((x)+1)/(y))-1) #define NMOD(x,y) ((y)-(-((x)+1)%(y))-1) #define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d)) #define MOD(n,d) ((n)<0 ? NMOD((n),(d)) : (n)%(d)) #define HAVE_JD (1 << 0) #define HAVE_DF (1 << 1) #define HAVE_CIVIL (1 << 2) #define HAVE_TIME (1 << 3) #define COMPLEX_DAT (1 << 7) #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 complex_dat_p(x) ((x)->flags & COMPLEX_DAT) #define simple_dat_p(x) (!complex_dat_p(x)) #define ITALY 2299161 /* 1582-10-15 */ #define ENGLAND 2361222 /* 1752-09-14 */ #define JULIAN positive_inf #define GREGORIAN negative_inf #define DEFAULT_SG ITALY #define UNIX_EPOCH_IN_CJD INT2FIX(2440588) /* 1970-01-01 */ #define MINUTE_IN_SECONDS 60 #define HOUR_IN_SECONDS 3600 #define DAY_IN_SECONDS 86400 #define SECOND_IN_MILLISECONDS 1000 #define SECOND_IN_NANOSECONDS 1000000000 #define JC_PERIOD0 1461 /* 365.25 * 4 */ #define GC_PERIOD0 146097 /* 365.2425 * 400 */ #define CM_PERIOD0 71149239 /* (lcm 7 1461 146097) */ #define CM_PERIOD (0xfffffff / CM_PERIOD0 * CM_PERIOD0) #define CM_PERIOD_JCY (CM_PERIOD / JC_PERIOD0 * 4) #define CM_PERIOD_GCY (CM_PERIOD / GC_PERIOD0 * 400) #define REFORM_BEGIN_YEAR 1582 #define REFORM_END_YEAR 1930 #define REFORM_BEGIN_JD 2298874 /* ns 1582-01-01 */ #define REFORM_END_JD 2426355 /* os 1930-12-31 */ #ifdef USE_PACK #define SEC_WIDTH 6 #define MIN_WIDTH 6 #define HOUR_WIDTH 5 #define MDAY_WIDTH 5 #define MON_WIDTH 4 #define SEC_SHIFT 0 #define MIN_SHIFT SEC_WIDTH #define HOUR_SHIFT (MIN_WIDTH + SEC_WIDTH) #define MDAY_SHIFT (HOUR_WIDTH + MIN_WIDTH + SEC_WIDTH) #define MON_SHIFT (MDAY_WIDTH + HOUR_WIDTH + MIN_WIDTH + SEC_WIDTH) #define PK_MASK(x) ((1 << (x)) - 1) #define EX_SEC(x) (((x) >> SEC_SHIFT) & PK_MASK(SEC_WIDTH)) #define EX_MIN(x) (((x) >> MIN_SHIFT) & PK_MASK(MIN_WIDTH)) #define EX_HOUR(x) (((x) >> HOUR_SHIFT) & PK_MASK(HOUR_WIDTH)) #define EX_MDAY(x) (((x) >> MDAY_SHIFT) & PK_MASK(MDAY_WIDTH)) #define EX_MON(x) (((x) >> MON_SHIFT) & PK_MASK(MON_WIDTH)) #define PACK5(m,d,h,min,s) \ (((m) << MON_SHIFT) | ((d) << MDAY_SHIFT) |\ ((h) << HOUR_SHIFT) | ((min) << MIN_SHIFT) | ((s) << SEC_SHIFT)) #define PACK2(m,d) \ (((m) << MON_SHIFT) | ((d) << MDAY_SHIFT)) #endif #ifdef HAVE_FLOAT_H #include #endif #if defined(FLT_RADIX) && defined(FLT_MANT_DIG) && FLT_RADIX == 2 && FLT_MANT_DIG > 22 #define date_sg_t float #else #define date_sg_t double #endif /* A set of nth, jd, df and sf denote ajd + 1/2. Each ajd begin at * noon of GMT (assume equal to UTC). However, this begins at * midnight. */ struct SimpleDateData { unsigned flags; VALUE nth; /* not always canonicalized */ int jd; /* as utc */ /* df is zero */ /* sf is zero */ /* of is zero */ date_sg_t sg; /* 2298874..2426355 or -/+oo -- at most 22 bits */ /* decoded as utc=local */ int year; /* truncated */ #ifndef USE_PACK int mon; int mday; /* hour is zero */ /* min is zero */ /* sec is zero */ #else /* packed civil */ unsigned pc; #endif }; struct ComplexDateData { unsigned flags; VALUE nth; /* not always canonicalized */ int jd; /* as utc */ int df; /* as utc, in secs */ VALUE sf; /* in nano secs */ int of; /* in secs */ date_sg_t sg; /* 2298874..2426355 or -/+oo -- at most 22 bits */ /* decoded as local */ int year; /* truncated */ #ifndef USE_PACK int mon; int mday; int hour; int min; int sec; #else /* packed civil */ unsigned pc; #endif }; union DateData { unsigned flags; struct SimpleDateData s; struct ComplexDateData c; }; #define get_d1(x)\ union DateData *dat;\ TypedData_Get_Struct(x, union DateData, &d_lite_type, dat); #define get_d1a(x)\ union DateData *adat;\ TypedData_Get_Struct(x, union DateData, &d_lite_type, adat); #define get_d1b(x)\ union DateData *bdat;\ TypedData_Get_Struct(x, union DateData, &d_lite_type, bdat); #define get_d2(x,y)\ union DateData *adat, *bdat;\ TypedData_Get_Struct(x, union DateData, &d_lite_type, adat);\ TypedData_Get_Struct(y, union DateData, &d_lite_type, bdat); inline static VALUE canon(VALUE x) { if (RB_TYPE_P(x, T_RATIONAL)) { VALUE den = rb_rational_den(x); if (FIXNUM_P(den) && FIX2LONG(den) == 1) return rb_rational_num(x); } return x; } #ifndef USE_PACK #define set_to_simple(obj, x, _nth, _jd ,_sg, _year, _mon, _mday, _flags) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth)); \ (x)->jd = _jd;\ (x)->sg = (date_sg_t)(_sg);\ (x)->year = _year;\ (x)->mon = _mon;\ (x)->mday = _mday;\ (x)->flags = _flags;\ } #else #define set_to_simple(obj, x, _nth, _jd ,_sg, _year, _mon, _mday, _flags) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth)); \ (x)->jd = _jd;\ (x)->sg = (date_sg_t)(_sg);\ (x)->year = _year;\ (x)->pc = PACK2(_mon, _mday);\ (x)->flags = _flags;\ } #endif #ifndef USE_PACK #define set_to_complex(obj, x, _nth, _jd ,_df, _sf, _of, _sg,\ _year, _mon, _mday, _hour, _min, _sec, _flags) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth));\ (x)->jd = _jd;\ (x)->df = _df;\ RB_OBJ_WRITE((obj), &(x)->sf, canon(_sf));\ (x)->of = _of;\ (x)->sg = (date_sg_t)(_sg);\ (x)->year = _year;\ (x)->mon = _mon;\ (x)->mday = _mday;\ (x)->hour = _hour;\ (x)->min = _min;\ (x)->sec = _sec;\ (x)->flags = _flags;\ } #else #define set_to_complex(obj, x, _nth, _jd ,_df, _sf, _of, _sg,\ _year, _mon, _mday, _hour, _min, _sec, _flags) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, canon(_nth));\ (x)->jd = _jd;\ (x)->df = _df;\ RB_OBJ_WRITE((obj), &(x)->sf, canon(_sf));\ (x)->of = _of;\ (x)->sg = (date_sg_t)(_sg);\ (x)->year = _year;\ (x)->pc = PACK5(_mon, _mday, _hour, _min, _sec);\ (x)->flags = _flags;\ } #endif #ifndef USE_PACK #define copy_simple_to_complex(obj, x, y) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\ (x)->jd = (y)->jd;\ (x)->df = 0;\ (x)->sf = INT2FIX(0);\ (x)->of = 0;\ (x)->sg = (date_sg_t)((y)->sg);\ (x)->year = (y)->year;\ (x)->mon = (y)->mon;\ (x)->mday = (y)->mday;\ (x)->hour = 0;\ (x)->min = 0;\ (x)->sec = 0;\ (x)->flags = (y)->flags;\ } #else #define copy_simple_to_complex(obj, x, y) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\ (x)->jd = (y)->jd;\ (x)->df = 0;\ RB_OBJ_WRITE((obj), &(x)->sf, INT2FIX(0));\ (x)->of = 0;\ (x)->sg = (date_sg_t)((y)->sg);\ (x)->year = (y)->year;\ (x)->pc = PACK5(EX_MON((y)->pc), EX_MDAY((y)->pc), 0, 0, 0);\ (x)->flags = (y)->flags;\ } #endif #ifndef USE_PACK #define copy_complex_to_simple(obj, x, y) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\ (x)->jd = (y)->jd;\ (x)->sg = (date_sg_t)((y)->sg);\ (x)->year = (y)->year;\ (x)->mon = (y)->mon;\ (x)->mday = (y)->mday;\ (x)->flags = (y)->flags;\ } #else #define copy_complex_to_simple(obj, x, y) \ {\ RB_OBJ_WRITE((obj), &(x)->nth, (y)->nth);\ (x)->jd = (y)->jd;\ (x)->sg = (date_sg_t)((y)->sg);\ (x)->year = (y)->year;\ (x)->pc = PACK2(EX_MON((y)->pc), EX_MDAY((y)->pc));\ (x)->flags = (y)->flags;\ } #endif /* base */ static int c_valid_civil_p(int, int, int, double, int *, int *, int *, int *); static int c_find_fdoy(int y, double sg, int *rjd, int *ns) { int d, rm, rd; for (d = 1; d < 31; d++) if (c_valid_civil_p(y, 1, d, sg, &rm, &rd, rjd, ns)) return 1; return 0; } static int c_find_ldoy(int y, double sg, int *rjd, int *ns) { int i, rm, rd; for (i = 0; i < 30; i++) if (c_valid_civil_p(y, 12, 31 - i, sg, &rm, &rd, rjd, ns)) return 1; return 0; } #ifndef NDEBUG static int c_find_fdom(int y, int m, double sg, int *rjd, int *ns) { int d, rm, rd; for (d = 1; d < 31; d++) if (c_valid_civil_p(y, m, d, sg, &rm, &rd, rjd, ns)) return 1; return 0; } #endif static int c_find_ldom(int y, int m, double sg, int *rjd, int *ns) { int i, rm, rd; for (i = 0; i < 30; i++) if (c_valid_civil_p(y, m, 31 - i, sg, &rm, &rd, rjd, ns)) return 1; return 0; } static void c_civil_to_jd(int y, int m, int d, double sg, int *rjd, int *ns) { double a, b, jd; if (m <= 2) { y -= 1; m += 12; } a = floor(y / 100.0); b = 2 - a + floor(a / 4.0); jd = floor(365.25 * (y + 4716)) + floor(30.6001 * (m + 1)) + d + b - 1524; if (jd < sg) { jd -= b; *ns = 0; } else *ns = 1; *rjd = (int)jd; } static void c_jd_to_civil(int jd, double sg, int *ry, int *rm, int *rdom) { double x, a, b, c, d, e, y, m, dom; if (jd < sg) a = jd; else { x = floor((jd - 1867216.25) / 36524.25); a = jd + 1 + x - floor(x / 4.0); } b = a + 1524; c = floor((b - 122.1) / 365.25); d = floor(365.25 * c); e = floor((b - d) / 30.6001); dom = b - d - floor(30.6001 * e); if (e <= 13) { m = e - 1; y = c - 4716; } else { m = e - 13; y = c - 4715; } *ry = (int)y; *rm = (int)m; *rdom = (int)dom; } static void c_ordinal_to_jd(int y, int d, double sg, int *rjd, int *ns) { int ns2; c_find_fdoy(y, sg, rjd, &ns2); *rjd += d - 1; *ns = (*rjd < sg) ? 0 : 1; } static void c_jd_to_ordinal(int jd, double sg, int *ry, int *rd) { int rm2, rd2, rjd, ns; c_jd_to_civil(jd, sg, ry, &rm2, &rd2); c_find_fdoy(*ry, sg, &rjd, &ns); *rd = (jd - rjd) + 1; } static void c_commercial_to_jd(int y, int w, int d, double sg, int *rjd, int *ns) { int rjd2, ns2; c_find_fdoy(y, sg, &rjd2, &ns2); rjd2 += 3; *rjd = (rjd2 - MOD((rjd2 - 1) + 1, 7)) + 7 * (w - 1) + (d - 1); *ns = (*rjd < sg) ? 0 : 1; } static void c_jd_to_commercial(int jd, double sg, int *ry, int *rw, int *rd) { int ry2, rm2, rd2, a, rjd2, ns2; c_jd_to_civil(jd - 3, sg, &ry2, &rm2, &rd2); a = ry2; c_commercial_to_jd(a + 1, 1, 1, sg, &rjd2, &ns2); if (jd >= rjd2) *ry = a + 1; else { c_commercial_to_jd(a, 1, 1, sg, &rjd2, &ns2); *ry = a; } *rw = 1 + DIV(jd - rjd2, 7); *rd = MOD(jd + 1, 7); if (*rd == 0) *rd = 7; } static void c_weeknum_to_jd(int y, int w, int d, int f, double sg, int *rjd, int *ns) { int rjd2, ns2; c_find_fdoy(y, sg, &rjd2, &ns2); rjd2 += 6; *rjd = (rjd2 - MOD(((rjd2 - f) + 1), 7) - 7) + 7 * w + d; *ns = (*rjd < sg) ? 0 : 1; } static void c_jd_to_weeknum(int jd, int f, double sg, int *ry, int *rw, int *rd) { int rm, rd2, rjd, ns, j; c_jd_to_civil(jd, sg, ry, &rm, &rd2); c_find_fdoy(*ry, sg, &rjd, &ns); rjd += 6; j = jd - (rjd - MOD((rjd - f) + 1, 7)) + 7; *rw = (int)DIV(j, 7); *rd = (int)MOD(j, 7); } #ifndef NDEBUG static void c_nth_kday_to_jd(int y, int m, int n, int k, double sg, int *rjd, int *ns) { int rjd2, ns2; if (n > 0) { c_find_fdom(y, m, sg, &rjd2, &ns2); rjd2 -= 1; } else { c_find_ldom(y, m, sg, &rjd2, &ns2); rjd2 += 7; } *rjd = (rjd2 - MOD((rjd2 - k) + 1, 7)) + 7 * n; *ns = (*rjd < sg) ? 0 : 1; } #endif inline static int c_jd_to_wday(int jd) { return MOD(jd + 1, 7); } #ifndef NDEBUG static void c_jd_to_nth_kday(int jd, double sg, int *ry, int *rm, int *rn, int *rk) { int rd, rjd, ns2; c_jd_to_civil(jd, sg, ry, rm, &rd); c_find_fdom(*ry, *rm, sg, &rjd, &ns2); *rn = DIV(jd - rjd, 7) + 1; *rk = c_jd_to_wday(jd); } #endif static int c_valid_ordinal_p(int y, int d, double sg, int *rd, int *rjd, int *ns) { int ry2, rd2; if (d < 0) { int rjd2, ns2; if (!c_find_ldoy(y, sg, &rjd2, &ns2)) return 0; c_jd_to_ordinal(rjd2 + d + 1, sg, &ry2, &rd2); if (ry2 != y) return 0; d = rd2; } c_ordinal_to_jd(y, d, sg, rjd, ns); c_jd_to_ordinal(*rjd, sg, &ry2, &rd2); if (ry2 != y || rd2 != d) return 0; return 1; } static const int monthtab[2][13] = { { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; inline static int c_julian_leap_p(int y) { return MOD(y, 4) == 0; } inline static int c_gregorian_leap_p(int y) { return (MOD(y, 4) == 0 && y % 100 != 0) || MOD(y, 400) == 0; } static int c_julian_last_day_of_month(int y, int m) { assert(m >= 1 && m <= 12); return monthtab[c_julian_leap_p(y) ? 1 : 0][m]; } static int c_gregorian_last_day_of_month(int y, int m) { assert(m >= 1 && m <= 12); return monthtab[c_gregorian_leap_p(y) ? 1 : 0][m]; } static int c_valid_julian_p(int y, int m, int d, int *rm, int *rd) { int last; if (m < 0) m += 13; if (m < 1 || m > 12) return 0; last = c_julian_last_day_of_month(y, m); if (d < 0) d = last + d + 1; if (d < 1 || d > last) return 0; *rm = m; *rd = d; return 1; } static int c_valid_gregorian_p(int y, int m, int d, int *rm, int *rd) { int last; if (m < 0) m += 13; if (m < 1 || m > 12) return 0; last = c_gregorian_last_day_of_month(y, m); if (d < 0) d = last + d + 1; if (d < 1 || d > last) return 0; *rm = m; *rd = d; return 1; } static int c_valid_civil_p(int y, int m, int d, double sg, int *rm, int *rd, int *rjd, int *ns) { int ry; if (m < 0) m += 13; if (d < 0) { if (!c_find_ldom(y, m, sg, rjd, ns)) return 0; c_jd_to_civil(*rjd + d + 1, sg, &ry, rm, rd); if (ry != y || *rm != m) return 0; d = *rd; } c_civil_to_jd(y, m, d, sg, rjd, ns); c_jd_to_civil(*rjd, sg, &ry, rm, rd); if (ry != y || *rm != m || *rd != d) return 0; return 1; } static int c_valid_commercial_p(int y, int w, int d, double sg, int *rw, int *rd, int *rjd, int *ns) { int ns2, ry2, rw2, rd2; if (d < 0) d += 8; if (w < 0) { int rjd2; c_commercial_to_jd(y + 1, 1, 1, sg, &rjd2, &ns2); c_jd_to_commercial(rjd2 + w * 7, sg, &ry2, &rw2, &rd2); if (ry2 != y) return 0; w = rw2; } c_commercial_to_jd(y, w, d, sg, rjd, ns); c_jd_to_commercial(*rjd, sg, &ry2, rw, rd); if (y != ry2 || w != *rw || d != *rd) return 0; return 1; } static int c_valid_weeknum_p(int y, int w, int d, int f, double sg, int *rw, int *rd, int *rjd, int *ns) { int ns2, ry2, rw2, rd2; if (d < 0) d += 7; if (w < 0) { int rjd2; c_weeknum_to_jd(y + 1, 1, f, f, sg, &rjd2, &ns2); c_jd_to_weeknum(rjd2 + w * 7, f, sg, &ry2, &rw2, &rd2); if (ry2 != y) return 0; w = rw2; } c_weeknum_to_jd(y, w, d, f, sg, rjd, ns); c_jd_to_weeknum(*rjd, f, sg, &ry2, rw, rd); if (y != ry2 || w != *rw || d != *rd) return 0; return 1; } #ifndef NDEBUG static int c_valid_nth_kday_p(int y, int m, int n, int k, double sg, int *rm, int *rn, int *rk, int *rjd, int *ns) { int ns2, ry2, rm2, rn2, rk2; if (k < 0) k += 7; if (n < 0) { int t, ny, nm, rjd2; t = y * 12 + m; ny = DIV(t, 12); nm = MOD(t, 12) + 1; c_nth_kday_to_jd(ny, nm, 1, k, sg, &rjd2, &ns2); c_jd_to_nth_kday(rjd2 + n * 7, sg, &ry2, &rm2, &rn2, &rk2); if (ry2 != y || rm2 != m) return 0; n = rn2; } c_nth_kday_to_jd(y, m, n, k, sg, rjd, ns); c_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 c_valid_time_p(int h, int min, int s, int *rh, int *rmin, int *rs) { if (h < 0) h += 24; if (min < 0) min += 60; if (s < 0) s += 60; *rh = h; *rmin = min; *rs = s; return !(h < 0 || h > 24 || min < 0 || min > 59 || s < 0 || s > 59 || (h == 24 && (min > 0 || s > 0))); } inline static int c_valid_start_p(double sg) { if (isnan(sg)) return 0; if (isinf(sg)) return 1; if (sg < REFORM_BEGIN_JD || sg > REFORM_END_JD) return 0; return 1; } inline static int df_local_to_utc(int df, int of) { df -= of; if (df < 0) df += DAY_IN_SECONDS; else if (df >= DAY_IN_SECONDS) df -= DAY_IN_SECONDS; return df; } inline static int df_utc_to_local(int df, int of) { df += of; if (df < 0) df += DAY_IN_SECONDS; else if (df >= DAY_IN_SECONDS) df -= DAY_IN_SECONDS; return df; } inline static int jd_local_to_utc(int jd, int df, int of) { df -= of; if (df < 0) jd -= 1; else if (df >= DAY_IN_SECONDS) jd += 1; return jd; } inline static int jd_utc_to_local(int jd, int df, int of) { df += of; if (df < 0) jd -= 1; else if (df >= DAY_IN_SECONDS) jd += 1; return jd; } inline static int time_to_df(int h, int min, int s) { return h * HOUR_IN_SECONDS + min * MINUTE_IN_SECONDS + s; } inline static void df_to_time(int df, int *h, int *min, int *s) { *h = df / HOUR_IN_SECONDS; df %= HOUR_IN_SECONDS; *min = df / MINUTE_IN_SECONDS; *s = df % MINUTE_IN_SECONDS; } static VALUE sec_to_day(VALUE s) { if (FIXNUM_P(s)) return rb_rational_new2(s, INT2FIX(DAY_IN_SECONDS)); return f_quo(s, INT2FIX(DAY_IN_SECONDS)); } inline static VALUE isec_to_day(int s) { return sec_to_day(INT2FIX(s)); } static VALUE ns_to_day(VALUE n) { if (FIXNUM_P(n)) return rb_rational_new2(n, day_in_nanoseconds); return f_quo(n, day_in_nanoseconds); } #ifndef NDEBUG static VALUE ms_to_sec(VALUE m) { if (FIXNUM_P(m)) return rb_rational_new2(m, INT2FIX(SECOND_IN_MILLISECONDS)); return f_quo(m, INT2FIX(SECOND_IN_MILLISECONDS)); } #endif static VALUE ns_to_sec(VALUE n) { if (FIXNUM_P(n)) return rb_rational_new2(n, INT2FIX(SECOND_IN_NANOSECONDS)); return f_quo(n, INT2FIX(SECOND_IN_NANOSECONDS)); } #ifndef NDEBUG inline static VALUE ins_to_day(int n) { return ns_to_day(INT2FIX(n)); } #endif static int safe_mul_p(VALUE x, long m) { long ix; if (!FIXNUM_P(x)) return 0; ix = FIX2LONG(x); if (ix < 0) { if (ix <= (FIXNUM_MIN / m)) return 0; } else { if (ix >= (FIXNUM_MAX / m)) return 0; } return 1; } static VALUE day_to_sec(VALUE d) { if (safe_mul_p(d, DAY_IN_SECONDS)) return LONG2FIX(FIX2LONG(d) * DAY_IN_SECONDS); return f_mul(d, INT2FIX(DAY_IN_SECONDS)); } #ifndef NDEBUG static VALUE day_to_ns(VALUE d) { return f_mul(d, day_in_nanoseconds); } #endif static VALUE sec_to_ms(VALUE s) { if (safe_mul_p(s, SECOND_IN_MILLISECONDS)) return LONG2FIX(FIX2LONG(s) * SECOND_IN_MILLISECONDS); return f_mul(s, INT2FIX(SECOND_IN_MILLISECONDS)); } static VALUE sec_to_ns(VALUE s) { if (safe_mul_p(s, SECOND_IN_NANOSECONDS)) return LONG2FIX(FIX2LONG(s) * SECOND_IN_NANOSECONDS); return f_mul(s, INT2FIX(SECOND_IN_NANOSECONDS)); } #ifndef NDEBUG static VALUE isec_to_ns(int s) { return sec_to_ns(INT2FIX(s)); } #endif static VALUE div_day(VALUE d, VALUE *f) { if (f) *f = f_mod(d, INT2FIX(1)); return f_floor(d); } static VALUE div_df(VALUE d, VALUE *f) { VALUE s = day_to_sec(d); if (f) *f = f_mod(s, INT2FIX(1)); return f_floor(s); } #ifndef NDEBUG static VALUE div_sf(VALUE s, VALUE *f) { VALUE n = sec_to_ns(s); if (f) *f = f_mod(n, INT2FIX(1)); return f_floor(n); } #endif static void decode_day(VALUE d, VALUE *jd, VALUE *df, VALUE *sf) { VALUE f; *jd = div_day(d, &f); *df = div_df(f, &f); *sf = sec_to_ns(f); } inline static double s_virtual_sg(union DateData *x) { if (isinf(x->s.sg)) return x->s.sg; if (f_zero_p(x->s.nth)) return x->s.sg; else if (f_negative_p(x->s.nth)) return positive_inf; return negative_inf; } inline static double c_virtual_sg(union DateData *x) { if (isinf(x->c.sg)) return x->c.sg; if (f_zero_p(x->c.nth)) return x->c.sg; else if (f_negative_p(x->c.nth)) return positive_inf; return negative_inf; } inline static double m_virtual_sg(union DateData *x) { if (simple_dat_p(x)) return s_virtual_sg(x); else return c_virtual_sg(x); } #define canonicalize_jd(_nth, _jd) \ {\ if (_jd < 0) {\ _nth = f_sub(_nth, INT2FIX(1));\ _jd += CM_PERIOD;\ }\ if (_jd >= CM_PERIOD) {\ _nth = f_add(_nth, INT2FIX(1));\ _jd -= CM_PERIOD;\ }\ } inline static void canonicalize_s_jd(VALUE obj, union DateData *x) { int j = x->s.jd; VALUE nth = x->s.nth; assert(have_jd_p(x)); canonicalize_jd(nth, x->s.jd); RB_OBJ_WRITE(obj, &x->s.nth, nth); if (x->s.jd != j) x->flags &= ~HAVE_CIVIL; } inline static void get_s_jd(union DateData *x) { assert(simple_dat_p(x)); if (!have_jd_p(x)) { int jd, ns; assert(have_civil_p(x)); #ifndef USE_PACK c_civil_to_jd(x->s.year, x->s.mon, x->s.mday, s_virtual_sg(x), &jd, &ns); #else c_civil_to_jd(x->s.year, EX_MON(x->s.pc), EX_MDAY(x->s.pc), s_virtual_sg(x), &jd, &ns); #endif x->s.jd = jd; x->s.flags |= HAVE_JD; } } inline static void get_s_civil(union DateData *x) { assert(simple_dat_p(x)); if (!have_civil_p(x)) { int y, m, d; assert(have_jd_p(x)); c_jd_to_civil(x->s.jd, s_virtual_sg(x), &y, &m, &d); x->s.year = y; #ifndef USE_PACK x->s.mon = m; x->s.mday = d; #else x->s.pc = PACK2(m, d); #endif x->s.flags |= HAVE_CIVIL; } } inline static void get_c_df(union DateData *x) { assert(complex_dat_p(x)); if (!have_df_p(x)) { assert(have_time_p(x)); #ifndef USE_PACK x->c.df = df_local_to_utc(time_to_df(x->c.hour, x->c.min, x->c.sec), x->c.of); #else x->c.df = df_local_to_utc(time_to_df(EX_HOUR(x->c.pc), EX_MIN(x->c.pc), EX_SEC(x->c.pc)), x->c.of); #endif x->c.flags |= HAVE_DF; } } inline static void get_c_time(union DateData *x) { assert(complex_dat_p(x)); if (!have_time_p(x)) { #ifndef USE_PACK int r; assert(have_df_p(x)); r = df_utc_to_local(x->c.df, x->c.of); df_to_time(r, &x->c.hour, &x->c.min, &x->c.sec); x->c.flags |= HAVE_TIME; #else int r, m, d, h, min, s; assert(have_df_p(x)); m = EX_MON(x->c.pc); d = EX_MDAY(x->c.pc); r = df_utc_to_local(x->c.df, x->c.of); df_to_time(r, &h, &min, &s); x->c.pc = PACK5(m, d, h, min, s); x->c.flags |= HAVE_TIME; #endif } } inline static void canonicalize_c_jd(VALUE obj, union DateData *x) { int j = x->c.jd; VALUE nth = x->c.nth; assert(have_jd_p(x)); canonicalize_jd(nth, x->c.jd); RB_OBJ_WRITE(obj, &x->c.nth, nth); if (x->c.jd != j) x->flags &= ~HAVE_CIVIL; } inline static void get_c_jd(union DateData *x) { assert(complex_dat_p(x)); if (!have_jd_p(x)) { int jd, ns; assert(have_civil_p(x)); #ifndef USE_PACK c_civil_to_jd(x->c.year, x->c.mon, x->c.mday, c_virtual_sg(x), &jd, &ns); #else c_civil_to_jd(x->c.year, EX_MON(x->c.pc), EX_MDAY(x->c.pc), c_virtual_sg(x), &jd, &ns); #endif get_c_time(x); #ifndef USE_PACK x->c.jd = jd_local_to_utc(jd, time_to_df(x->c.hour, x->c.min, x->c.sec), x->c.of); #else x->c.jd = jd_local_to_utc(jd, time_to_df(EX_HOUR(x->c.pc), EX_MIN(x->c.pc), EX_SEC(x->c.pc)), x->c.of); #endif x->c.flags |= HAVE_JD; } } inline static void get_c_civil(union DateData *x) { assert(complex_dat_p(x)); if (!have_civil_p(x)) { #ifndef USE_PACK int jd, y, m, d; #else int jd, y, m, d, h, min, s; #endif assert(have_jd_p(x)); get_c_df(x); jd = jd_utc_to_local(x->c.jd, x->c.df, x->c.of); c_jd_to_civil(jd, c_virtual_sg(x), &y, &m, &d); x->c.year = y; #ifndef USE_PACK x->c.mon = m; x->c.mday = d; #else h = EX_HOUR(x->c.pc); min = EX_MIN(x->c.pc); s = EX_SEC(x->c.pc); x->c.pc = PACK5(m, d, h, min, s); #endif x->c.flags |= HAVE_CIVIL; } } inline static int local_jd(union DateData *x) { assert(complex_dat_p(x)); assert(have_jd_p(x)); assert(have_df_p(x)); return jd_utc_to_local(x->c.jd, x->c.df, x->c.of); } inline static int local_df(union DateData *x) { assert(complex_dat_p(x)); assert(have_df_p(x)); return df_utc_to_local(x->c.df, x->c.of); } static void decode_year(VALUE y, double style, VALUE *nth, int *ry) { int period; VALUE t; period = (style < 0) ? CM_PERIOD_GCY : CM_PERIOD_JCY; if (FIXNUM_P(y)) { long iy, it, inth; iy = FIX2LONG(y); if (iy >= (FIXNUM_MAX - 4712)) goto big; it = iy + 4712; /* shift */ inth = DIV(it, ((long)period)); *nth = LONG2FIX(inth); if (inth) it = MOD(it, ((long)period)); *ry = (int)it - 4712; /* unshift */ return; } big: t = f_add(y, INT2FIX(4712)); /* shift */ *nth = f_idiv(t, INT2FIX(period)); if (f_nonzero_p(*nth)) t = f_mod(t, INT2FIX(period)); *ry = FIX2INT(t) - 4712; /* unshift */ } static void encode_year(VALUE nth, int y, double style, VALUE *ry) { int period; VALUE t; period = (style < 0) ? CM_PERIOD_GCY : CM_PERIOD_JCY; if (f_zero_p(nth)) *ry = INT2FIX(y); else { t = f_mul(INT2FIX(period), nth); t = f_add(t, INT2FIX(y)); *ry = t; } } static void decode_jd(VALUE jd, VALUE *nth, int *rjd) { assert(FIXNUM_P(jd) || RB_TYPE_P(jd, T_BIGNUM)); *nth = f_idiv(jd, INT2FIX(CM_PERIOD)); if (f_zero_p(*nth)) { assert(FIXNUM_P(jd)); *rjd = FIX2INT(jd); return; } *rjd = FIX2INT(f_mod(jd, INT2FIX(CM_PERIOD))); } static void encode_jd(VALUE nth, int jd, VALUE *rjd) { if (f_zero_p(nth)) { *rjd = INT2FIX(jd); return; } *rjd = f_add(f_mul(INT2FIX(CM_PERIOD), nth), INT2FIX(jd)); } inline static double guess_style(VALUE y, double sg) /* -/+oo or zero */ { double style = 0; if (isinf(sg)) style = sg; else if (!FIXNUM_P(y)) style = f_positive_p(y) ? negative_inf : positive_inf; else { long iy = FIX2LONG(y); assert(FIXNUM_P(y)); if (iy < REFORM_BEGIN_YEAR) style = positive_inf; else if (iy > REFORM_END_YEAR) style = negative_inf; } return style; } inline static void m_canonicalize_jd(VALUE obj, union DateData *x) { if (simple_dat_p(x)) { get_s_jd(x); canonicalize_s_jd(obj, x); } else { get_c_jd(x); canonicalize_c_jd(obj, x); } } inline static VALUE m_nth(union DateData *x) { if (simple_dat_p(x)) return x->s.nth; else { get_c_civil(x); return x->c.nth; } } inline static int m_jd(union DateData *x) { if (simple_dat_p(x)) { get_s_jd(x); return x->s.jd; } else { get_c_jd(x); return x->c.jd; } } static VALUE m_real_jd(union DateData *x) { VALUE nth, rjd; int jd; nth = m_nth(x); jd = m_jd(x); encode_jd(nth, jd, &rjd); return rjd; } static int m_local_jd(union DateData *x) { if (simple_dat_p(x)) { get_s_jd(x); return x->s.jd; } else { get_c_jd(x); get_c_df(x); return local_jd(x); } } static VALUE m_real_local_jd(union DateData *x) { VALUE nth, rjd; int jd; nth = m_nth(x); jd = m_local_jd(x); encode_jd(nth, jd, &rjd); return rjd; } inline static int m_df(union DateData *x) { if (simple_dat_p(x)) return 0; else { get_c_df(x); return x->c.df; } } #ifndef NDEBUG static VALUE m_df_in_day(union DateData *x) { return isec_to_day(m_df(x)); } #endif static int m_local_df(union DateData *x) { if (simple_dat_p(x)) return 0; else { get_c_df(x); return local_df(x); } } #ifndef NDEBUG static VALUE m_local_df_in_day(union DateData *x) { return isec_to_day(m_local_df(x)); } #endif inline static VALUE m_sf(union DateData *x) { if (simple_dat_p(x)) return INT2FIX(0); else return x->c.sf; } #ifndef NDEBUG static VALUE m_sf_in_day(union DateData *x) { return ns_to_day(m_sf(x)); } #endif static VALUE m_sf_in_sec(union DateData *x) { return ns_to_sec(m_sf(x)); } static VALUE m_fr(union DateData *x) { if (simple_dat_p(x)) return INT2FIX(0); else { int df; VALUE sf, fr; df = m_local_df(x); sf = m_sf(x); fr = isec_to_day(df); if (f_nonzero_p(sf)) fr = f_add(fr, ns_to_day(sf)); return fr; } } #define HALF_DAYS_IN_SECONDS (DAY_IN_SECONDS / 2) static VALUE m_ajd(union DateData *x) { VALUE r, sf; int df; if (simple_dat_p(x)) { r = m_real_jd(x); if (FIXNUM_P(r) && FIX2LONG(r) <= (FIXNUM_MAX / 2)) { long ir = FIX2LONG(r); ir = ir * 2 - 1; return rb_rational_new2(LONG2FIX(ir), INT2FIX(2)); } else return rb_rational_new2(f_sub(f_mul(r, INT2FIX(2)), INT2FIX(1)), INT2FIX(2)); } r = m_real_jd(x); df = m_df(x); df -= HALF_DAYS_IN_SECONDS; if (df) r = f_add(r, isec_to_day(df)); sf = m_sf(x); if (f_nonzero_p(sf)) r = f_add(r, ns_to_day(sf)); return r; } static VALUE m_amjd(union DateData *x) { VALUE r, sf; int df; r = m_real_jd(x); if (FIXNUM_P(r) && FIX2LONG(r) >= (FIXNUM_MIN + 2400001)) { long ir = FIX2LONG(r); ir -= 2400001; r = rb_rational_new1(LONG2FIX(ir)); } else r = rb_rational_new1(f_sub(m_real_jd(x), INT2FIX(2400001))); if (simple_dat_p(x)) return r; df = m_df(x); if (df) r = f_add(r, isec_to_day(df)); sf = m_sf(x); if (f_nonzero_p(sf)) r = f_add(r, ns_to_day(sf)); return r; } inline static int m_of(union DateData *x) { if (simple_dat_p(x)) return 0; else { get_c_jd(x); return x->c.of; } } static VALUE m_of_in_day(union DateData *x) { return isec_to_day(m_of(x)); } inline static double m_sg(union DateData *x) { if (simple_dat_p(x)) return x->s.sg; else { get_c_jd(x); return x->c.sg; } } static int m_julian_p(union DateData *x) { int jd; double sg; if (simple_dat_p(x)) { get_s_jd(x); jd = x->s.jd; sg = s_virtual_sg(x); } else { get_c_jd(x); jd = x->c.jd; sg = c_virtual_sg(x); } if (isinf(sg)) return sg == positive_inf; return jd < sg; } inline static int m_gregorian_p(union DateData *x) { return !m_julian_p(x); } inline static int m_proleptic_julian_p(union DateData *x) { double sg; sg = m_sg(x); if (isinf(sg) && sg > 0) return 1; return 0; } inline static int m_proleptic_gregorian_p(union DateData *x) { double sg; sg = m_sg(x); if (isinf(sg) && sg < 0) return 1; return 0; } inline static int m_year(union DateData *x) { if (simple_dat_p(x)) { get_s_civil(x); return x->s.year; } else { get_c_civil(x); return x->c.year; } } static VALUE m_real_year(union DateData *x) { VALUE nth, ry; int year; nth = m_nth(x); year = m_year(x); if (f_zero_p(nth)) return INT2FIX(year); encode_year(nth, year, m_gregorian_p(x) ? -1 : +1, &ry); return ry; } inline static int m_mon(union DateData *x) { if (simple_dat_p(x)) { get_s_civil(x); #ifndef USE_PACK return x->s.mon; #else return EX_MON(x->s.pc); #endif } else { get_c_civil(x); #ifndef USE_PACK return x->c.mon; #else return EX_MON(x->c.pc); #endif } } inline static int m_mday(union DateData *x) { if (simple_dat_p(x)) { get_s_civil(x); #ifndef USE_PACK return x->s.mday; #else return EX_MDAY(x->s.pc); #endif } else { get_c_civil(x); #ifndef USE_PACK return x->c.mday; #else return EX_MDAY(x->c.pc); #endif } } 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 } }; static int c_julian_to_yday(int y, int m, int d) { assert(m >= 1 && m <= 12); return yeartab[c_julian_leap_p(y) ? 1 : 0][m] + d; } static int c_gregorian_to_yday(int y, int m, int d) { assert(m >= 1 && m <= 12); return yeartab[c_gregorian_leap_p(y) ? 1 : 0][m] + d; } static int m_yday(union DateData *x) { int jd, ry, rd; double sg; jd = m_local_jd(x); sg = m_virtual_sg(x); /* !=m_sg() */ if (m_proleptic_gregorian_p(x) || (jd - sg) > 366) return c_gregorian_to_yday(m_year(x), m_mon(x), m_mday(x)); if (m_proleptic_julian_p(x)) return c_julian_to_yday(m_year(x), m_mon(x), m_mday(x)); c_jd_to_ordinal(jd, sg, &ry, &rd); return rd; } static int m_wday(union DateData *x) { return c_jd_to_wday(m_local_jd(x)); } static int m_cwyear(union DateData *x) { int ry, rw, rd; c_jd_to_commercial(m_local_jd(x), m_virtual_sg(x), /* !=m_sg() */ &ry, &rw, &rd); return ry; } static VALUE m_real_cwyear(union DateData *x) { VALUE nth, ry; int year; nth = m_nth(x); year = m_cwyear(x); if (f_zero_p(nth)) return INT2FIX(year); encode_year(nth, year, m_gregorian_p(x) ? -1 : +1, &ry); return ry; } static int m_cweek(union DateData *x) { int ry, rw, rd; c_jd_to_commercial(m_local_jd(x), m_virtual_sg(x), /* !=m_sg() */ &ry, &rw, &rd); return rw; } static int m_cwday(union DateData *x) { int w; w = m_wday(x); if (w == 0) w = 7; return w; } static int m_wnumx(union DateData *x, int f) { int ry, rw, rd; c_jd_to_weeknum(m_local_jd(x), f, m_virtual_sg(x), /* !=m_sg() */ &ry, &rw, &rd); return rw; } static int m_wnum0(union DateData *x) { return m_wnumx(x, 0); } static int m_wnum1(union DateData *x) { return m_wnumx(x, 1); } inline static int m_hour(union DateData *x) { if (simple_dat_p(x)) return 0; else { get_c_time(x); #ifndef USE_PACK return x->c.hour; #else return EX_HOUR(x->c.pc); #endif } } inline static int m_min(union DateData *x) { if (simple_dat_p(x)) return 0; else { get_c_time(x); #ifndef USE_PACK return x->c.min; #else return EX_MIN(x->c.pc); #endif } } inline static int m_sec(union DateData *x) { if (simple_dat_p(x)) return 0; else { get_c_time(x); #ifndef USE_PACK return x->c.sec; #else return EX_SEC(x->c.pc); #endif } } #define decode_offset(of,s,h,m)\ {\ int a;\ s = (of < 0) ? '-' : '+';\ a = (of < 0) ? -of : of;\ h = a / HOUR_IN_SECONDS;\ m = a % HOUR_IN_SECONDS / MINUTE_IN_SECONDS;\ } static VALUE of2str(int of) { int s, h, m; decode_offset(of, s, h, m); return rb_enc_sprintf(rb_usascii_encoding(), "%c%02d:%02d", s, h, m); } static VALUE m_zone(union DateData *x) { if (simple_dat_p(x)) return rb_usascii_str_new2("+00:00"); return of2str(m_of(x)); } inline static VALUE f_kind_of_p(VALUE x, VALUE c) { return rb_obj_is_kind_of(x, c); } inline static VALUE k_date_p(VALUE x) { return f_kind_of_p(x, cDate); } inline static VALUE k_numeric_p(VALUE x) { return f_kind_of_p(x, rb_cNumeric); } inline static VALUE k_rational_p(VALUE x) { return f_kind_of_p(x, rb_cRational); } static inline void expect_numeric(VALUE x) { if (!k_numeric_p(x)) rb_raise(rb_eTypeError, "expected numeric"); } #ifndef NDEBUG static void civil_to_jd(VALUE y, int m, int d, double sg, VALUE *nth, int *ry, int *rjd, int *ns) { double style = guess_style(y, sg); if (style == 0) { int jd; c_civil_to_jd(FIX2INT(y), m, d, sg, &jd, ns); decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); c_civil_to_jd(*ry, m, d, style, rjd, ns); } } static void jd_to_civil(VALUE jd, double sg, VALUE *nth, int *rjd, int *ry, int *rm, int *rd) { decode_jd(jd, nth, rjd); c_jd_to_civil(*rjd, sg, ry, rm, rd); } static void ordinal_to_jd(VALUE y, int d, double sg, VALUE *nth, int *ry, int *rjd, int *ns) { double style = guess_style(y, sg); if (style == 0) { int jd; c_ordinal_to_jd(FIX2INT(y), d, sg, &jd, ns); decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); c_ordinal_to_jd(*ry, d, style, rjd, ns); } } static void jd_to_ordinal(VALUE jd, double sg, VALUE *nth, int *rjd, int *ry, int *rd) { decode_jd(jd, nth, rjd); c_jd_to_ordinal(*rjd, sg, ry, rd); } static void commercial_to_jd(VALUE y, int w, int d, double sg, VALUE *nth, int *ry, int *rjd, int *ns) { double style = guess_style(y, sg); if (style == 0) { int jd; c_commercial_to_jd(FIX2INT(y), w, d, sg, &jd, ns); decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); c_commercial_to_jd(*ry, w, d, style, rjd, ns); } } static void jd_to_commercial(VALUE jd, double sg, VALUE *nth, int *rjd, int *ry, int *rw, int *rd) { decode_jd(jd, nth, rjd); c_jd_to_commercial(*rjd, sg, ry, rw, rd); } static void weeknum_to_jd(VALUE y, int w, int d, int f, double sg, VALUE *nth, int *ry, int *rjd, int *ns) { double style = guess_style(y, sg); if (style == 0) { int jd; c_weeknum_to_jd(FIX2INT(y), w, d, f, sg, &jd, ns); decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); c_weeknum_to_jd(*ry, w, d, f, style, rjd, ns); } } static void jd_to_weeknum(VALUE jd, int f, double sg, VALUE *nth, int *rjd, int *ry, int *rw, int *rd) { decode_jd(jd, nth, rjd); c_jd_to_weeknum(*rjd, f, sg, ry, rw, rd); } static void nth_kday_to_jd(VALUE y, int m, int n, int k, double sg, VALUE *nth, int *ry, int *rjd, int *ns) { double style = guess_style(y, sg); if (style == 0) { int jd; c_nth_kday_to_jd(FIX2INT(y), m, n, k, sg, &jd, ns); decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); c_nth_kday_to_jd(*ry, m, n, k, style, rjd, ns); } } static void jd_to_nth_kday(VALUE jd, double sg, VALUE *nth, int *rjd, int *ry, int *rm, int *rn, int *rk) { decode_jd(jd, nth, rjd); c_jd_to_nth_kday(*rjd, sg, ry, rm, rn, rk); } #endif static int valid_ordinal_p(VALUE y, int d, double sg, VALUE *nth, int *ry, int *rd, int *rjd, int *ns) { double style = guess_style(y, sg); int r; if (style == 0) { int jd; r = c_valid_ordinal_p(FIX2INT(y), d, sg, rd, &jd, ns); if (!r) return 0; decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); r = c_valid_ordinal_p(*ry, d, style, rd, rjd, ns); } return r; } static int valid_gregorian_p(VALUE y, int m, int d, VALUE *nth, int *ry, int *rm, int *rd) { decode_year(y, -1, nth, ry); return c_valid_gregorian_p(*ry, m, d, rm, rd); } static int valid_civil_p(VALUE y, int m, int d, double sg, VALUE *nth, int *ry, int *rm, int *rd, int *rjd, int *ns) { double style = guess_style(y, sg); int r; if (style == 0) { int jd; r = c_valid_civil_p(FIX2INT(y), m, d, sg, rm, rd, &jd, ns); if (!r) return 0; decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); if (style < 0) r = c_valid_gregorian_p(*ry, m, d, rm, rd); else r = c_valid_julian_p(*ry, m, d, rm, rd); if (!r) return 0; c_civil_to_jd(*ry, *rm, *rd, style, rjd, ns); } return r; } static int valid_commercial_p(VALUE y, int w, int d, double sg, VALUE *nth, int *ry, int *rw, int *rd, int *rjd, int *ns) { double style = guess_style(y, sg); int r; if (style == 0) { int jd; r = c_valid_commercial_p(FIX2INT(y), w, d, sg, rw, rd, &jd, ns); if (!r) return 0; decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); r = c_valid_commercial_p(*ry, w, d, style, rw, rd, rjd, ns); } return r; } static int valid_weeknum_p(VALUE y, int w, int d, int f, double sg, VALUE *nth, int *ry, int *rw, int *rd, int *rjd, int *ns) { double style = guess_style(y, sg); int r; if (style == 0) { int jd; r = c_valid_weeknum_p(FIX2INT(y), w, d, f, sg, rw, rd, &jd, ns); if (!r) return 0; decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); r = c_valid_weeknum_p(*ry, w, d, f, style, rw, rd, rjd, ns); } return r; } #ifndef NDEBUG static int valid_nth_kday_p(VALUE y, int m, int n, int k, double sg, VALUE *nth, int *ry, int *rm, int *rn, int *rk, int *rjd, int *ns) { double style = guess_style(y, sg); int r; if (style == 0) { int jd; r = c_valid_nth_kday_p(FIX2INT(y), m, n, k, sg, rm, rn, rk, &jd, ns); if (!r) return 0; decode_jd(INT2FIX(jd), nth, rjd); if (f_zero_p(*nth)) *ry = FIX2INT(y); else { VALUE nth2; decode_year(y, *ns ? -1 : +1, &nth2, ry); } } else { decode_year(y, style, nth, ry); r = c_valid_nth_kday_p(*ry, m, n, k, style, rm, rn, rk, rjd, ns); } return r; } #endif VALUE date_zone_to_diff(VALUE); static int offset_to_sec(VALUE vof, int *rof) { switch (TYPE(vof)) { case T_FIXNUM: { long n; n = FIX2LONG(vof); if (n != -1 && n != 0 && n != 1) return 0; *rof = (int)n * DAY_IN_SECONDS; return 1; } case T_FLOAT: { double n; n = RFLOAT_VALUE(vof) * DAY_IN_SECONDS; if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS) return 0; *rof = (int)round(n); if (*rof != n) rb_warning("fraction of offset is ignored"); return 1; } default: expect_numeric(vof); vof = f_to_r(vof); #ifdef CANONICALIZATION_FOR_MATHN if (!k_rational_p(vof)) return offset_to_sec(vof, rof); #endif /* fall through */ case T_RATIONAL: { VALUE vs, vn, vd; long n; vs = day_to_sec(vof); #ifdef CANONICALIZATION_FOR_MATHN if (!k_rational_p(vs)) { if (!FIXNUM_P(vs)) return 0; n = FIX2LONG(vs); if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS) return 0; *rof = (int)n; return 1; } #endif vn = rb_rational_num(vs); vd = rb_rational_den(vs); if (FIXNUM_P(vn) && FIXNUM_P(vd) && (FIX2LONG(vd) == 1)) n = FIX2LONG(vn); else { vn = f_round(vs); if (!f_eqeq_p(vn, vs)) rb_warning("fraction of offset is ignored"); if (!FIXNUM_P(vn)) return 0; n = FIX2LONG(vn); if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS) return 0; } *rof = (int)n; return 1; } case T_STRING: { VALUE vs = date_zone_to_diff(vof); long n; if (!FIXNUM_P(vs)) return 0; n = FIX2LONG(vs); if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS) return 0; *rof = (int)n; return 1; } } return 0; } /* date */ #define valid_sg(sg) \ {\ if (!c_valid_start_p(sg)) {\ sg = 0;\ rb_warning("invalid start is ignored");\ }\ } static VALUE valid_jd_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { double sg = NUM2DBL(argv[1]); valid_sg(sg); return argv[0]; } #ifndef NDEBUG static VALUE date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass) { VALUE vjd, vsg; VALUE argv2[2]; rb_scan_args(argc, argv, "11", &vjd, &vsg); argv2[0] = vjd; if (argc < 2) argv2[1] = DBL2NUM(GREGORIAN); else argv2[1] = vsg; return valid_jd_sub(2, argv2, klass, 1); } #endif /* * call-seq: * Date.valid_jd?(jd[, start=Date::ITALY]) -> bool * * Just returns true. It's nonsense, but is for symmetry. * * Date.valid_jd?(2451944) #=> true * * See also jd. */ static VALUE date_s_valid_jd_p(int argc, VALUE *argv, VALUE klass) { VALUE vjd, vsg; VALUE argv2[2]; rb_scan_args(argc, argv, "11", &vjd, &vsg); argv2[0] = vjd; if (argc < 2) argv2[1] = INT2FIX(DEFAULT_SG); else argv2[1] = vsg; if (NIL_P(valid_jd_sub(2, argv2, klass, 0))) return Qfalse; return Qtrue; } static VALUE valid_civil_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { VALUE nth, y; int m, d, ry, rm, rd; double sg; y = argv[0]; m = NUM2INT(argv[1]); d = NUM2INT(argv[2]); sg = NUM2DBL(argv[3]); valid_sg(sg); if (!need_jd && (guess_style(y, sg) < 0)) { if (!valid_gregorian_p(y, m, d, &nth, &ry, &rm, &rd)) return Qnil; return INT2FIX(0); /* dummy */ } else { int rjd, ns; VALUE rjd2; if (!valid_civil_p(y, m, d, sg, &nth, &ry, &rm, &rd, &rjd, &ns)) return Qnil; if (!need_jd) return INT2FIX(0); /* dummy */ encode_jd(nth, rjd, &rjd2); return rjd2; } } #ifndef NDEBUG static VALUE date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass) { VALUE vy, vm, vd, vsg; VALUE argv2[4]; rb_scan_args(argc, argv, "31", &vy, &vm, &vd, &vsg); argv2[0] = vy; argv2[1] = vm; argv2[2] = vd; if (argc < 4) argv2[3] = DBL2NUM(GREGORIAN); else argv2[3] = vsg; return valid_civil_sub(4, argv2, klass, 1); } #endif /* * call-seq: * Date.valid_civil?(year, month, mday[, start=Date::ITALY]) -> bool * Date.valid_date?(year, month, mday[, start=Date::ITALY]) -> bool * * Returns true if the given calendar date is valid, and false if not. * * Date.valid_date?(2001,2,3) #=> true * Date.valid_date?(2001,2,29) #=> false * * See also jd and civil. */ static VALUE date_s_valid_civil_p(int argc, VALUE *argv, VALUE klass) { VALUE vy, vm, vd, vsg; VALUE argv2[4]; rb_scan_args(argc, argv, "31", &vy, &vm, &vd, &vsg); argv2[0] = vy; argv2[1] = vm; argv2[2] = vd; if (argc < 4) argv2[3] = INT2FIX(DEFAULT_SG); else argv2[3] = vsg; if (NIL_P(valid_civil_sub(4, argv2, klass, 0))) return Qfalse; return Qtrue; } static VALUE valid_ordinal_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { VALUE nth, y; int d, ry, rd; double sg; y = argv[0]; d = NUM2INT(argv[1]); sg = NUM2DBL(argv[2]); valid_sg(sg); { int rjd, ns; VALUE rjd2; if (!valid_ordinal_p(y, d, sg, &nth, &ry, &rd, &rjd, &ns)) return Qnil; if (!need_jd) return INT2FIX(0); /* dummy */ encode_jd(nth, rjd, &rjd2); return rjd2; } } #ifndef NDEBUG static VALUE date_s__valid_ordinal_p(int argc, VALUE *argv, VALUE klass) { VALUE vy, vd, vsg; VALUE argv2[3]; rb_scan_args(argc, argv, "21", &vy, &vd, &vsg); argv2[0] = vy; argv2[1] = vd; if (argc < 3) argv2[2] = DBL2NUM(GREGORIAN); else argv2[2] = vsg; return valid_ordinal_sub(3, argv2, klass, 1); } #endif /* * call-seq: * Date.valid_ordinal?(year, yday[, start=Date::ITALY]) -> bool * * Returns true if the given ordinal date is valid, and false if not. * * Date.valid_ordinal?(2001,34) #=> true * Date.valid_ordinal?(2001,366) #=> false * * See also jd and ordinal. */ static VALUE date_s_valid_ordinal_p(int argc, VALUE *argv, VALUE klass) { VALUE vy, vd, vsg; VALUE argv2[3]; rb_scan_args(argc, argv, "21", &vy, &vd, &vsg); argv2[0] = vy; argv2[1] = vd; if (argc < 3) argv2[2] = INT2FIX(DEFAULT_SG); else argv2[2] = vsg; if (NIL_P(valid_ordinal_sub(3, argv2, klass, 0))) return Qfalse; return Qtrue; } static VALUE valid_commercial_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { VALUE nth, y; int w, d, ry, rw, rd; double sg; y = argv[0]; w = NUM2INT(argv[1]); d = NUM2INT(argv[2]); sg = NUM2DBL(argv[3]); valid_sg(sg); { int rjd, ns; VALUE rjd2; if (!valid_commercial_p(y, w, d, sg, &nth, &ry, &rw, &rd, &rjd, &ns)) return Qnil; if (!need_jd) return INT2FIX(0); /* dummy */ encode_jd(nth, rjd, &rjd2); return rjd2; } } #ifndef NDEBUG static VALUE date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass) { VALUE vy, vw, vd, vsg; VALUE argv2[4]; rb_scan_args(argc, argv, "31", &vy, &vw, &vd, &vsg); argv2[0] = vy; argv2[1] = vw; argv2[2] = vd; if (argc < 4) argv2[3] = DBL2NUM(GREGORIAN); else argv2[3] = vsg; return valid_commercial_sub(4, argv2, klass, 1); } #endif /* * call-seq: * Date.valid_commercial?(cwyear, cweek, cwday[, start=Date::ITALY]) -> bool * * Returns true if the given week date is valid, and false if not. * * Date.valid_commercial?(2001,5,6) #=> true * Date.valid_commercial?(2001,5,8) #=> false * * See also jd and commercial. */ static VALUE date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass) { VALUE vy, vw, vd, vsg; VALUE argv2[4]; rb_scan_args(argc, argv, "31", &vy, &vw, &vd, &vsg); argv2[0] = vy; argv2[1] = vw; argv2[2] = vd; if (argc < 4) argv2[3] = INT2FIX(DEFAULT_SG); else argv2[3] = vsg; if (NIL_P(valid_commercial_sub(4, argv2, klass, 0))) return Qfalse; return Qtrue; } #ifndef NDEBUG static VALUE valid_weeknum_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { VALUE nth, y; int w, d, f, ry, rw, rd; double sg; y = argv[0]; w = NUM2INT(argv[1]); d = NUM2INT(argv[2]); f = NUM2INT(argv[3]); sg = NUM2DBL(argv[4]); valid_sg(sg); { int rjd, ns; VALUE rjd2; if (!valid_weeknum_p(y, w, d, f, sg, &nth, &ry, &rw, &rd, &rjd, &ns)) return Qnil; if (!need_jd) return INT2FIX(0); /* dummy */ encode_jd(nth, rjd, &rjd2); return rjd2; } } 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, 1); } 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(DEFAULT_SG); else argv2[4] = vsg; if (NIL_P(valid_weeknum_sub(5, argv2, klass, 0))) return Qfalse; return Qtrue; } static VALUE valid_nth_kday_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { VALUE nth, y; int m, n, k, ry, rm, rn, rk; double sg; y = argv[0]; m = NUM2INT(argv[1]); n = NUM2INT(argv[2]); k = NUM2INT(argv[3]); sg = NUM2DBL(argv[4]); { int rjd, ns; VALUE rjd2; if (!valid_nth_kday_p(y, m, n, k, sg, &nth, &ry, &rm, &rn, &rk, &rjd, &ns)) return Qnil; if (!need_jd) return INT2FIX(0); /* dummy */ encode_jd(nth, rjd, &rjd2); return rjd2; } } 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, 1); } 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(DEFAULT_SG); else argv2[4] = vsg; if (NIL_P(valid_nth_kday_sub(5, argv2, klass, 0))) 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) -> bool * * Returns true if the given year is a leap year of the proleptic * Julian calendar. * * Date.julian_leap?(1900) #=> true * Date.julian_leap?(1901) #=> false */ static VALUE date_s_julian_leap_p(VALUE klass, VALUE y) { VALUE nth; int ry; decode_year(y, +1, &nth, &ry); return f_boolcast(c_julian_leap_p(ry)); } /* * call-seq: * Date.gregorian_leap?(year) -> bool * Date.leap?(year) -> bool * * Returns true if the given year is a leap year of the proleptic * Gregorian calendar. * * Date.gregorian_leap?(1900) #=> false * Date.gregorian_leap?(2000) #=> true */ static VALUE date_s_gregorian_leap_p(VALUE klass, VALUE y) { VALUE nth; int ry; decode_year(y, -1, &nth, &ry); return f_boolcast(c_gregorian_leap_p(ry)); } static void d_lite_gc_mark(void *ptr) { union DateData *dat = ptr; if (simple_dat_p(dat)) rb_gc_mark(dat->s.nth); else { rb_gc_mark(dat->c.nth); rb_gc_mark(dat->c.sf); } } static size_t d_lite_memsize(const void *ptr) { const union DateData *dat = ptr; return complex_dat_p(dat) ? sizeof(struct ComplexDateData) : sizeof(struct SimpleDateData); } static const rb_data_type_t d_lite_type = { "Date", {d_lite_gc_mark, RUBY_TYPED_DEFAULT_FREE, d_lite_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED, }; inline static VALUE d_simple_new_internal(VALUE klass, VALUE nth, int jd, double sg, int y, int m, int d, unsigned flags) { struct SimpleDateData *dat; VALUE obj; obj = TypedData_Make_Struct(klass, struct SimpleDateData, &d_lite_type, dat); set_to_simple(obj, dat, nth, jd, sg, y, m, d, flags & ~COMPLEX_DAT); assert(have_jd_p(dat) || have_civil_p(dat)); return obj; } inline static VALUE d_complex_new_internal(VALUE klass, VALUE nth, int jd, int df, VALUE sf, int of, double sg, int y, int m, int d, int h, int min, int s, unsigned flags) { struct ComplexDateData *dat; VALUE obj; obj = TypedData_Make_Struct(klass, struct ComplexDateData, &d_lite_type, dat); set_to_complex(obj, dat, nth, jd, df, sf, of, sg, y, m, d, h, min, s, flags | COMPLEX_DAT); assert(have_jd_p(dat) || have_civil_p(dat)); assert(have_df_p(dat) || have_time_p(dat)); return obj; } static VALUE d_lite_s_alloc_simple(VALUE klass) { return d_simple_new_internal(klass, INT2FIX(0), 0, DEFAULT_SG, 0, 0, 0, HAVE_JD); } static VALUE d_lite_s_alloc_complex(VALUE klass) { return d_complex_new_internal(klass, INT2FIX(0), 0, 0, INT2FIX(0), 0, DEFAULT_SG, 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF); } static VALUE d_lite_s_alloc(VALUE klass) { return d_lite_s_alloc_complex(klass); } static void old_to_new(VALUE ajd, VALUE of, VALUE sg, VALUE *rnth, int *rjd, int *rdf, VALUE *rsf, int *rof, double *rsg) { VALUE jd, df, sf, of2, t; decode_day(f_add(ajd, half_days_in_day), &jd, &df, &sf); t = day_to_sec(of); of2 = f_round(t); if (!f_eqeq_p(of2, t)) rb_warning("fraction of offset is ignored"); decode_jd(jd, rnth, rjd); *rdf = NUM2INT(df); *rsf = sf; *rof = NUM2INT(of2); *rsg = NUM2DBL(sg); if (*rdf < 0 || *rdf >= DAY_IN_SECONDS) rb_raise(rb_eArgError, "invalid day fraction"); if (f_lt_p(*rsf, INT2FIX(0)) || f_ge_p(*rsf, INT2FIX(SECOND_IN_NANOSECONDS))) if (*rof < -DAY_IN_SECONDS || *rof > DAY_IN_SECONDS) { *rof = 0; rb_warning("invalid offset is ignored"); } if (!c_valid_start_p(*rsg)) { *rsg = DEFAULT_SG; rb_warning("invalid start is ignored"); } } #ifndef NDEBUG static VALUE date_s_new_bang(int argc, VALUE *argv, VALUE klass) { VALUE ajd, of, sg, nth, sf; int jd, df, rof; double rsg; 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(DEFAULT_SG); } old_to_new(ajd, of, sg, &nth, &jd, &df, &sf, &rof, &rsg); if (!df && f_zero_p(sf) && !rof) return d_simple_new_internal(klass, nth, jd, rsg, 0, 0, 0, HAVE_JD); else return d_complex_new_internal(klass, nth, jd, df, sf, rof, rsg, 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF); } #endif inline static int wholenum_p(VALUE x) { if (FIXNUM_P(x)) return 1; switch (TYPE(x)) { case T_BIGNUM: return 1; case T_FLOAT: { double d = RFLOAT_VALUE(x); return round(d) == d; } break; case T_RATIONAL: { VALUE den = rb_rational_den(x); return FIXNUM_P(den) && FIX2LONG(den) == 1; } break; } return 0; } inline static VALUE to_integer(VALUE x) { if (FIXNUM_P(x) || RB_TYPE_P(x, T_BIGNUM)) return x; return f_to_i(x); } inline static VALUE d_trunc(VALUE d, VALUE *fr) { VALUE rd; if (wholenum_p(d)) { rd = to_integer(d); *fr = INT2FIX(0); } else { rd = f_idiv(d, INT2FIX(1)); *fr = f_mod(d, INT2FIX(1)); } return rd; } #define jd_trunc d_trunc #define k_trunc d_trunc inline static VALUE h_trunc(VALUE h, VALUE *fr) { VALUE rh; if (wholenum_p(h)) { rh = to_integer(h); *fr = INT2FIX(0); } else { rh = f_idiv(h, INT2FIX(1)); *fr = f_mod(h, INT2FIX(1)); *fr = f_quo(*fr, INT2FIX(24)); } return rh; } inline static VALUE min_trunc(VALUE min, VALUE *fr) { VALUE rmin; if (wholenum_p(min)) { rmin = to_integer(min); *fr = INT2FIX(0); } else { rmin = f_idiv(min, INT2FIX(1)); *fr = f_mod(min, INT2FIX(1)); *fr = f_quo(*fr, INT2FIX(1440)); } return rmin; } inline static VALUE s_trunc(VALUE s, VALUE *fr) { VALUE rs; if (wholenum_p(s)) { rs = to_integer(s); *fr = INT2FIX(0); } else { rs = f_idiv(s, INT2FIX(1)); *fr = f_mod(s, INT2FIX(1)); *fr = f_quo(*fr, INT2FIX(86400)); } return rs; } #define num2num_with_frac(s,n) \ {\ s = s##_trunc(v##s, &fr);\ if (f_nonzero_p(fr)) {\ if (argc > n)\ rb_raise(rb_eArgError, "invalid fraction");\ fr2 = fr;\ }\ } #define num2int_with_frac(s,n) \ {\ s = NUM2INT(s##_trunc(v##s, &fr));\ if (f_nonzero_p(fr)) {\ if (argc > n)\ rb_raise(rb_eArgError, "invalid fraction");\ fr2 = fr;\ }\ } #define canon24oc() \ {\ if (rh == 24) {\ rh = 0;\ fr2 = f_add(fr2, INT2FIX(1));\ }\ } #define add_frac() \ {\ if (f_nonzero_p(fr2))\ ret = d_lite_plus(ret, fr2);\ } #define val2sg(vsg,dsg) \ {\ dsg = NUM2DBL(vsg);\ if (!c_valid_start_p(dsg)) {\ dsg = DEFAULT_SG;\ rb_warning("invalid start is ignored");\ }\ } static VALUE d_lite_plus(VALUE, VALUE); /* * call-seq: * Date.jd([jd=0[, start=Date::ITALY]]) -> date * * Creates a date object denoting the given chronological Julian day * number. * * Date.jd(2451944) #=> # * Date.jd(2451945) #=> # * Date.jd(0) #=> # * * See also new. */ static VALUE date_s_jd(int argc, VALUE *argv, VALUE klass) { VALUE vjd, vsg, jd, fr, fr2, ret; double sg; rb_scan_args(argc, argv, "02", &vjd, &vsg); jd = INT2FIX(0); fr2 = INT2FIX(0); sg = DEFAULT_SG; switch (argc) { case 2: val2sg(vsg, sg); case 1: num2num_with_frac(jd, positive_inf); } { VALUE nth; int rjd; decode_jd(jd, &nth, &rjd); ret = d_simple_new_internal(klass, nth, rjd, sg, 0, 0, 0, HAVE_JD); } add_frac(); return ret; } /* * call-seq: * Date.ordinal([year=-4712[, yday=1[, start=Date::ITALY]]]) -> date * * Creates a date object denoting the given ordinal date. * * The day of year should be a negative or a positive number (as a * relative day from the end of year when negative). It should not be * zero. * * Date.ordinal(2001) #=> # * Date.ordinal(2001,34) #=> # * Date.ordinal(2001,-1) #=> # * * See also jd and new. */ static VALUE date_s_ordinal(int argc, VALUE *argv, VALUE klass) { VALUE vy, vd, vsg, y, fr, fr2, ret; int d; double sg; rb_scan_args(argc, argv, "03", &vy, &vd, &vsg); y = INT2FIX(-4712); d = 1; fr2 = INT2FIX(0); sg = DEFAULT_SG; switch (argc) { case 3: val2sg(vsg, sg); case 2: num2int_with_frac(d, positive_inf); case 1: y = vy; } { VALUE nth; int ry, rd, rjd, ns; if (!valid_ordinal_p(y, d, sg, &nth, &ry, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); ret = d_simple_new_internal(klass, nth, rjd, sg, 0, 0, 0, HAVE_JD); } add_frac(); return ret; } /* * call-seq: * Date.civil([year=-4712[, month=1[, mday=1[, start=Date::ITALY]]]]) -> date * Date.new([year=-4712[, month=1[, mday=1[, start=Date::ITALY]]]]) -> date * * Creates a date object denoting the given calendar date. * * In this class, BCE years are counted astronomically. Thus, the * year before the year 1 is the year zero, and the year preceding the * year zero is the year -1. The month and the day of month should be * a negative or a positive number (as a relative month/day from the * end of year/month when negative). They should not be zero. * * The last argument should be a Julian day number which denotes the * day of calendar reform. Date::ITALY (2299161=1582-10-15), * Date::ENGLAND (2361222=1752-09-14), Date::GREGORIAN (the proleptic * Gregorian calendar) and Date::JULIAN (the proleptic Julian * calendar) can be specified as a day of calendar reform. * * Date.new(2001) #=> # * Date.new(2001,2,3) #=> # * Date.new(2001,2,-1) #=> # * * See also jd. */ static VALUE date_s_civil(int argc, VALUE *argv, VALUE klass) { VALUE vy, vm, vd, vsg, y, fr, fr2, ret; int m, d; double sg; rb_scan_args(argc, argv, "04", &vy, &vm, &vd, &vsg); y = INT2FIX(-4712); m = 1; d = 1; fr2 = INT2FIX(0); sg = DEFAULT_SG; switch (argc) { case 4: val2sg(vsg, sg); case 3: num2int_with_frac(d, positive_inf); case 2: m = NUM2INT(vm); case 1: y = vy; } if (guess_style(y, sg) < 0) { VALUE nth; int ry, rm, rd; if (!valid_gregorian_p(y, m, d, &nth, &ry, &rm, &rd)) rb_raise(rb_eArgError, "invalid date"); ret = d_simple_new_internal(klass, nth, 0, sg, ry, rm, rd, HAVE_CIVIL); } else { VALUE nth; int ry, rm, rd, rjd, ns; if (!valid_civil_p(y, m, d, sg, &nth, &ry, &rm, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); ret = d_simple_new_internal(klass, nth, rjd, sg, ry, rm, rd, HAVE_JD | HAVE_CIVIL); } add_frac(); return ret; } /* * call-seq: * Date.commercial([cwyear=-4712[, cweek=1[, cwday=1[, start=Date::ITALY]]]]) -> date * * Creates a date object denoting the given week date. * * The week and the day of week should be a negative or a positive * number (as a relative week/day from the end of year/week when * negative). They should not be zero. * * Date.commercial(2001) #=> # * Date.commercial(2002) #=> # * Date.commercial(2001,5,6) #=> # * * See also jd and new. */ static VALUE date_s_commercial(int argc, VALUE *argv, VALUE klass) { VALUE vy, vw, vd, vsg, y, fr, fr2, ret; int w, d; double sg; rb_scan_args(argc, argv, "04", &vy, &vw, &vd, &vsg); y = INT2FIX(-4712); w = 1; d = 1; fr2 = INT2FIX(0); sg = DEFAULT_SG; switch (argc) { case 4: val2sg(vsg, sg); case 3: num2int_with_frac(d, positive_inf); case 2: w = NUM2INT(vw); case 1: y = vy; } { VALUE nth; int ry, rw, rd, rjd, ns; if (!valid_commercial_p(y, w, d, sg, &nth, &ry, &rw, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); ret = d_simple_new_internal(klass, nth, rjd, sg, 0, 0, 0, HAVE_JD); } add_frac(); return ret; } #ifndef NDEBUG static VALUE date_s_weeknum(int argc, VALUE *argv, VALUE klass) { VALUE vy, vw, vd, vf, vsg, y, fr, fr2, ret; int w, d, f; double sg; rb_scan_args(argc, argv, "05", &vy, &vw, &vd, &vf, &vsg); y = INT2FIX(-4712); w = 0; d = 1; f = 0; fr2 = INT2FIX(0); sg = DEFAULT_SG; switch (argc) { case 5: val2sg(vsg, sg); case 4: f = NUM2INT(vf); case 3: num2int_with_frac(d, positive_inf); case 2: w = NUM2INT(vw); case 1: y = vy; } { VALUE nth; int ry, rw, rd, rjd, ns; if (!valid_weeknum_p(y, w, d, f, sg, &nth, &ry, &rw, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); ret = d_simple_new_internal(klass, nth, rjd, sg, 0, 0, 0, HAVE_JD); } add_frac(); return ret; } static VALUE date_s_nth_kday(int argc, VALUE *argv, VALUE klass) { VALUE vy, vm, vn, vk, vsg, y, fr, fr2, ret; int m, n, k; double sg; rb_scan_args(argc, argv, "05", &vy, &vm, &vn, &vk, &vsg); y = INT2FIX(-4712); m = 1; n = 1; k = 1; fr2 = INT2FIX(0); sg = DEFAULT_SG; switch (argc) { case 5: val2sg(vsg, sg); case 4: num2int_with_frac(k, positive_inf); case 3: n = NUM2INT(vn); case 2: m = NUM2INT(vm); case 1: y = vy; } { VALUE nth; int ry, rm, rn, rk, rjd, ns; if (!valid_nth_kday_p(y, m, n, k, sg, &nth, &ry, &rm, &rn, &rk, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); ret = d_simple_new_internal(klass, nth, rjd, sg, 0, 0, 0, HAVE_JD); } add_frac(); return ret; } #endif #if !defined(HAVE_GMTIME_R) static struct tm* gmtime_r(const time_t *t, struct tm *tm) { auto struct tm *tmp = gmtime(t); if (tmp) *tm = *tmp; return tmp; } static struct tm* localtime_r(const time_t *t, struct tm *tm) { auto struct tm *tmp = localtime(t); if (tmp) *tm = *tmp; return tmp; } #endif static void set_sg(union DateData *, double); /* * call-seq: * Date.today([start=Date::ITALY]) -> date * * Date.today #=> # * * Creates a date object denoting the present day. */ static VALUE date_s_today(int argc, VALUE *argv, VALUE klass) { VALUE vsg, nth, ret; double sg; time_t t; struct tm tm; int y, ry, m, d; rb_scan_args(argc, argv, "01", &vsg); if (argc < 1) sg = DEFAULT_SG; else val2sg(vsg, sg); if (time(&t) == -1) rb_sys_fail("time"); tzset(); if (!localtime_r(&t, &tm)) rb_sys_fail("localtime"); y = tm.tm_year + 1900; m = tm.tm_mon + 1; d = tm.tm_mday; decode_year(INT2FIX(y), -1, &nth, &ry); ret = d_simple_new_internal(klass, nth, 0, GREGORIAN, ry, m, d, HAVE_CIVIL); { get_d1(ret); set_sg(dat, sg); } return ret; } #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; seconds = ref_hash("seconds"); if (!NIL_P(seconds)) { VALUE offset, d, h, min, s, fr; offset = ref_hash("offset"); if (!NIL_P(offset)) seconds = f_add(seconds, offset); 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"); } return hash; } #define sym(x) ID2SYM(rb_intern(x)) static VALUE d_lite_year(VALUE); static VALUE d_lite_wday(VALUE); static VALUE d_lite_jd(VALUE); static VALUE rt_complete_frags(VALUE klass, VALUE hash) { static VALUE tab = Qnil; int g; long e; VALUE k, a, 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); } { long i, eno = 0, idx = 0; for (i = 0; i < RARRAY_LEN(tab); i++) { VALUE x, a; x = RARRAY_AREF(tab, i); a = RARRAY_AREF(x, 1); { long j, n = 0; for (j = 0; j < RARRAY_LEN(a); j++) if (!NIL_P(ref_hash0(RARRAY_AREF(a, j)))) n++; if (n > eno) { eno = n; idx = i; } } } if (eno == 0) g = 0; else { g = 1; k = RARRAY_AREF(RARRAY_AREF(tab, idx), 0); a = RARRAY_AREF(RARRAY_AREF(tab, idx), 1); e = eno; } } d = Qnil; if (g && !NIL_P(k) && (RARRAY_LEN(a) - e)) { if (k == sym("ordinal")) { if (NIL_P(ref_hash("year"))) { if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); set_hash("year", d_lite_year(d)); } if (NIL_P(ref_hash("yday"))) set_hash("yday", INT2FIX(1)); } else if (k == sym("civil")) { long i; for (i = 0; i < RARRAY_LEN(a); i++) { VALUE e = RARRAY_AREF(a, i); if (!NIL_P(ref_hash0(e))) break; if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); 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")) { long i; for (i = 0; i < RARRAY_LEN(a); i++) { VALUE e = RARRAY_AREF(a, i); if (!NIL_P(ref_hash0(e))) break; if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); 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")) { if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); set_hash("jd", d_lite_jd(f_add(f_sub(d, d_lite_wday(d)), ref_hash("wday")))); } else if (k == sym("wnum0")) { long i; for (i = 0; i < RARRAY_LEN(a); i++) { VALUE e = RARRAY_AREF(a, i); if (!NIL_P(ref_hash0(e))) break; if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); 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")) { long i; for (i = 0; i < RARRAY_LEN(a); i++) { VALUE e = RARRAY_AREF(a, i); if (!NIL_P(ref_hash0(e))) break; if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); 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 (g && k == 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", d_lite_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; } 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 nth, rjd2; int ry, rd, rjd, ns; if (!valid_ordinal_p(y, NUM2INT(d), NUM2DBL(sg), &nth, &ry, &rd, &rjd, &ns)) return Qnil; encode_jd(nth, rjd, &rjd2); return rjd2; } static VALUE rt__valid_civil_p(VALUE y, VALUE m, VALUE d, VALUE sg) { VALUE nth, rjd2; int ry, rm, rd, rjd, ns; if (!valid_civil_p(y, NUM2INT(m), NUM2INT(d), NUM2DBL(sg), &nth, &ry, &rm, &rd, &rjd, &ns)) return Qnil; encode_jd(nth, rjd, &rjd2); return rjd2; } static VALUE rt__valid_commercial_p(VALUE y, VALUE w, VALUE d, VALUE sg) { VALUE nth, rjd2; int ry, rw, rd, rjd, ns; if (!valid_commercial_p(y, NUM2INT(w), NUM2INT(d), NUM2DBL(sg), &nth, &ry, &rw, &rd, &rjd, &ns)) return Qnil; encode_jd(nth, rjd, &rjd2); return rjd2; } static VALUE rt__valid_weeknum_p(VALUE y, VALUE w, VALUE d, VALUE f, VALUE sg) { VALUE nth, rjd2; int ry, rw, rd, rjd, ns; if (!valid_weeknum_p(y, NUM2INT(w), NUM2INT(d), NUM2INT(f), NUM2DBL(sg), &nth, &ry, &rw, &rd, &rjd, &ns)) return Qnil; encode_jd(nth, rjd, &rjd2); return rjd2; } static VALUE rt__valid_date_frags_p(VALUE hash, VALUE sg) { { VALUE vjd; if (!NIL_P(vjd = ref_hash("jd"))) { VALUE jd = rt__valid_jd_p(vjd, sg); if (!NIL_P(jd)) return jd; } } { VALUE year, yday; if (!NIL_P(yday = ref_hash("yday")) && !NIL_P(year = ref_hash("year"))) { VALUE jd = rt__valid_ordinal_p(year, yday, sg); if (!NIL_P(jd)) return jd; } } { VALUE year, mon, mday; if (!NIL_P(mday = ref_hash("mday")) && !NIL_P(mon = ref_hash("mon")) && !NIL_P(year = ref_hash("year"))) { VALUE jd = rt__valid_civil_p(year, mon, mday, sg); if (!NIL_P(jd)) return jd; } } { VALUE year, week, wday; wday = ref_hash("cwday"); if (NIL_P(wday)) { wday = ref_hash("wday"); if (!NIL_P(wday)) if (f_zero_p(wday)) wday = INT2FIX(7); } if (!NIL_P(wday) && !NIL_P(week = ref_hash("cweek")) && !NIL_P(year = ref_hash("cwyear"))) { VALUE jd = rt__valid_commercial_p(year, week, wday, sg); if (!NIL_P(jd)) return jd; } } { VALUE year, week, wday; wday = ref_hash("wday"); if (NIL_P(wday)) { wday = ref_hash("cwday"); if (!NIL_P(wday)) if (f_eqeq_p(wday, INT2FIX(7))) wday = INT2FIX(0); } if (!NIL_P(wday) && !NIL_P(week = ref_hash("wnum0")) && !NIL_P(year = ref_hash("year"))) { VALUE jd = rt__valid_weeknum_p(year, week, wday, INT2FIX(0), sg); if (!NIL_P(jd)) return jd; } } { VALUE year, week, wday; wday = ref_hash("wday"); if (NIL_P(wday)) wday = ref_hash("cwday"); if (!NIL_P(wday)) wday = f_mod(f_sub(wday, INT2FIX(1)), INT2FIX(7)); if (!NIL_P(wday) && !NIL_P(week = ref_hash("wnum1")) && !NIL_P(year = ref_hash("year"))) { VALUE jd = rt__valid_weeknum_p(year, week, wday, INT2FIX(1), sg); if (!NIL_P(jd)) return jd; } } return Qnil; } static VALUE d_new_by_frags(VALUE klass, VALUE hash, VALUE sg) { VALUE jd; if (!c_valid_start_p(NUM2DBL(sg))) { sg = INT2FIX(DEFAULT_SG); rb_warning("invalid start is ignored"); } if (NIL_P(hash)) rb_raise(rb_eArgError, "invalid date"); if (NIL_P(ref_hash("jd")) && NIL_P(ref_hash("yday")) && !NIL_P(ref_hash("year")) && !NIL_P(ref_hash("mon")) && !NIL_P(ref_hash("mday"))) jd = rt__valid_civil_p(ref_hash("year"), ref_hash("mon"), ref_hash("mday"), sg); else { 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"); { VALUE nth; int rjd; decode_jd(jd, &nth, &rjd); return d_simple_new_internal(klass, nth, rjd, NUM2DBL(sg), 0, 0, 0, HAVE_JD); } } VALUE date__strptime(const char *str, size_t slen, const char *fmt, size_t flen, VALUE hash); static VALUE date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, const char *default_fmt) { VALUE vstr, vfmt, hash; const char *str, *fmt; size_t slen, flen; rb_scan_args(argc, argv, "11", &vstr, &vfmt); StringValue(vstr); if (!rb_enc_str_asciicompat_p(vstr)) rb_raise(rb_eArgError, "string should have ASCII compatible encoding"); str = RSTRING_PTR(vstr); slen = RSTRING_LEN(vstr); if (argc < 2) { fmt = default_fmt; flen = strlen(default_fmt); } else { StringValue(vfmt); if (!rb_enc_str_asciicompat_p(vfmt)) rb_raise(rb_eArgError, "format should have ASCII compatible encoding"); fmt = RSTRING_PTR(vfmt); flen = RSTRING_LEN(vfmt); } hash = rb_hash_new(); if (NIL_P(date__strptime(str, slen, fmt, flen, hash))) return Qnil; { VALUE zone = ref_hash("zone"); VALUE left = ref_hash("leftover"); if (!NIL_P(zone)) { rb_enc_copy(zone, vstr); OBJ_INFECT(zone, vstr); set_hash("zone", zone); } if (!NIL_P(left)) { rb_enc_copy(left, vstr); OBJ_INFECT(left, vstr); set_hash("leftover", left); } } return hash; } /* * call-seq: * Date._strptime(string[, format='%F']) -> hash * * Parses the given representation of date and time with the given * template, and returns a hash of parsed elements. _strptime does * not support specification of flags and width unlike strftime. * * Date._strptime('2001-02-03', '%Y-%m-%d') * #=> {:year=>2001, :mon=>2, :mday=>3} * * See also strptime(3) and strftime. */ static VALUE 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]]]) -> date * * Parses the given representation of date and time with the given * template, and creates a date object. strptime does not support * specification of flags and width unlike strftime. * * Date.strptime('2001-02-03', '%Y-%m-%d') #=> # * Date.strptime('03-02-2001', '%d-%m-%Y') #=> # * Date.strptime('2001-034', '%Y-%j') #=> # * Date.strptime('2001-W05-6', '%G-W%V-%u') #=> # * Date.strptime('2001 04 6', '%Y %U %w') #=> # * Date.strptime('2001 05 6', '%Y %W %u') #=> # * Date.strptime('sat3feb01', '%a%d%b%y') #=> # * * See also strptime(3) and strftime. */ 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(DEFAULT_SG); } { VALUE argv2[2], hash; argv2[0] = str; argv2[1] = fmt; hash = date_s__strptime(2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } VALUE date__parse(VALUE str, VALUE comp); static VALUE date_s__parse_internal(int argc, VALUE *argv, VALUE klass) { VALUE vstr, vcomp, hash; rb_scan_args(argc, argv, "11", &vstr, &vcomp); StringValue(vstr); if (!rb_enc_str_asciicompat_p(vstr)) rb_raise(rb_eArgError, "string should have ASCII compatible encoding"); if (argc < 2) vcomp = Qtrue; hash = date__parse(vstr, vcomp); { VALUE zone = ref_hash("zone"); if (!NIL_P(zone)) { rb_enc_copy(zone, vstr); OBJ_INFECT(zone, vstr); set_hash("zone", zone); } } return hash; } /* * call-seq: * Date._parse(string[, comp=true]) -> hash * * Parses the given representation of date and time, and returns a * hash of parsed elements. This method does not function as a * validator. * * If the optional second argument is true and the detected year is in * the range "00" to "99", considers the year a 2-digit form and makes * it full. * * Date._parse('2001-02-03') #=> {:year=>2001, :mon=>2, :mday=>3} */ static VALUE date_s__parse(int argc, VALUE *argv, VALUE klass) { return date_s__parse_internal(argc, argv, klass); } /* * call-seq: * Date.parse(string='-4712-01-01'[, comp=true[, start=ITALY]]) -> date * * Parses the given representation of date and time, and creates a * date object. This method does not function as a validator. * * If the optional second argument is true and the detected year is in * the range "00" to "99", considers the year a 2-digit form and makes * it full. * * Date.parse('2001-02-03') #=> # * Date.parse('20010203') #=> # * Date.parse('3rd Feb 2001') #=> # */ 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(DEFAULT_SG); } { VALUE argv2[2], hash; argv2[0] = str; argv2[1] = comp; hash = date_s__parse(2, argv2, klass); return d_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) -> hash * * Returns 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]) -> date * * Creates a new Date object by parsing from a string according to * some typical ISO 8601 formats. * * Date.iso8601('2001-02-03') #=> # * Date.iso8601('20010203') #=> # * Date.iso8601('2001-W05-6') #=> # */ 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(DEFAULT_SG); } { VALUE hash = date_s__iso8601(klass, str); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: * Date._rfc3339(string) -> hash * * Returns 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]) -> date * * Creates a new Date object by parsing from a string according to * some typical RFC 3339 formats. * * Date.rfc3339('2001-02-03T04:05:06+07:00') #=> # */ 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(DEFAULT_SG); } { VALUE hash = date_s__rfc3339(klass, str); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: * Date._xmlschema(string) -> hash * * Returns 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]) -> date * * Creates a new Date object by parsing from a string according to * some typical XML Schema formats. * * Date.xmlschema('2001-02-03') #=> # */ 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(DEFAULT_SG); } { VALUE hash = date_s__xmlschema(klass, str); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: * Date._rfc2822(string) -> hash * Date._rfc822(string) -> hash * * Returns 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 * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=ITALY]) -> date * * Creates a new Date object by parsing from a string according to * some typical RFC 2822 formats. * * Date.rfc2822('Sat, 3 Feb 2001 00:00:00 +0000') * #=> # */ 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(DEFAULT_SG); } { VALUE hash = date_s__rfc2822(klass, str); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: * Date._httpdate(string) -> hash * * Returns 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]) -> date * * Creates a new Date object by parsing from a string according to * some RFC 2616 format. * * Date.httpdate('Sat, 03 Feb 2001 00:00:00 GMT') * #=> # */ 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(DEFAULT_SG); } { VALUE hash = date_s__httpdate(klass, str); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: * Date._jisx0301(string) -> hash * * Returns 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]) -> date * * Creates a new Date object by parsing from a string according to * some typical JIS X 0301 formats. * * Date.jisx0301('H13.02.03') #=> # */ 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(DEFAULT_SG); } { VALUE hash = date_s__jisx0301(klass, str); return d_new_by_frags(klass, hash, sg); } } static VALUE dup_obj(VALUE self) { get_d1a(self); if (simple_dat_p(adat)) { VALUE new = d_lite_s_alloc_simple(rb_obj_class(self)); { get_d1b(new); bdat->s = adat->s; RB_OBJ_WRITTEN(new, Qundef, bdat->s.nth); return new; } } else { VALUE new = d_lite_s_alloc_complex(rb_obj_class(self)); { get_d1b(new); bdat->c = adat->c; RB_OBJ_WRITTEN(new, Qundef, bdat->c.nth); RB_OBJ_WRITTEN(new, Qundef, bdat->c.sf); return new; } } } static VALUE dup_obj_as_complex(VALUE self) { get_d1a(self); if (simple_dat_p(adat)) { VALUE new = d_lite_s_alloc_complex(rb_obj_class(self)); { get_d1b(new); copy_simple_to_complex(new, &bdat->c, &adat->s); bdat->c.flags |= HAVE_DF | COMPLEX_DAT; return new; } } else { VALUE new = d_lite_s_alloc_complex(rb_obj_class(self)); { get_d1b(new); bdat->c = adat->c; RB_OBJ_WRITTEN(new, Qundef, bdat->c.nth); RB_OBJ_WRITTEN(new, Qundef, bdat->c.sf); return new; } } } #define val2off(vof,iof) \ {\ if (!offset_to_sec(vof, &iof)) {\ iof = 0;\ rb_warning("invalid offset is ignored");\ }\ } #ifndef NDEBUG static VALUE d_lite_initialize(int argc, VALUE *argv, VALUE self) { VALUE jd, vjd, vdf, sf, vsf, vof, vsg; int df, of; double sg; rb_check_frozen(self); rb_check_trusted(self); rb_scan_args(argc, argv, "05", &vjd, &vdf, &vsf, &vof, &vsg); jd = INT2FIX(0); df = 0; sf = INT2FIX(0); of = 0; sg = DEFAULT_SG; switch (argc) { case 5: val2sg(vsg, sg); case 4: val2off(vof, of); case 3: sf = vsf; if (f_lt_p(sf, INT2FIX(0)) || f_ge_p(sf, INT2FIX(SECOND_IN_NANOSECONDS))) rb_raise(rb_eArgError, "invalid second fraction"); case 2: df = NUM2INT(vdf); if (df < 0 || df >= DAY_IN_SECONDS) rb_raise(rb_eArgError, "invalid day fraction"); case 1: jd = vjd; } { VALUE nth; int rjd; get_d1(self); decode_jd(jd, &nth, &rjd); if (!df && f_zero_p(sf) && !of) { set_to_simple(self, &dat->s, nth, rjd, sg, 0, 0, 0, HAVE_JD); } else { if (!complex_dat_p(dat)) rb_raise(rb_eArgError, "cannot load complex into simple"); set_to_complex(&dat->c, nth, rjd, df, sf, of, sg, 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF | COMPLEX_DAT); } } return self; } #endif /* :nodoc: */ static VALUE d_lite_initialize_copy(VALUE copy, VALUE date) { rb_check_frozen(copy); rb_check_trusted(copy); if (copy == date) return copy; { get_d2(copy, date); if (simple_dat_p(bdat)) { adat->s = bdat->s; adat->s.flags &= ~COMPLEX_DAT; } else { if (!complex_dat_p(adat)) rb_raise(rb_eArgError, "cannot load complex into simple"); adat->c = bdat->c; adat->c.flags |= COMPLEX_DAT; } } return copy; } #ifndef NDEBUG static VALUE d_lite_fill(VALUE self) { get_d1(self); if (simple_dat_p(dat)) { get_s_jd(dat); get_s_civil(dat); } else { get_c_jd(dat); get_c_civil(dat); get_c_df(dat); get_c_time(dat); } return self; } #endif /* * call-seq: * d.ajd -> rational * * Returns the astronomical Julian day number. This is a fractional * number, which is not adjusted by the offset. * * DateTime.new(2001,2,3,4,5,6,'+7').ajd #=> (11769328217/4800) * DateTime.new(2001,2,2,14,5,6,'-7').ajd #=> (11769328217/4800) */ static VALUE d_lite_ajd(VALUE self) { get_d1(self); return m_ajd(dat); } /* * call-seq: * d.amjd -> rational * * Returns the astronomical modified Julian day number. This is * a fractional number, which is not adjusted by the offset. * * DateTime.new(2001,2,3,4,5,6,'+7').amjd #=> (249325817/4800) * DateTime.new(2001,2,2,14,5,6,'-7').amjd #=> (249325817/4800) */ static VALUE d_lite_amjd(VALUE self) { get_d1(self); return m_amjd(dat); } /* * call-seq: * d.jd -> integer * * Returns the Julian day number. This is a whole number, which is * adjusted by the offset as the local time. * * DateTime.new(2001,2,3,4,5,6,'+7').jd #=> 2451944 * DateTime.new(2001,2,3,4,5,6,'-7').jd #=> 2451944 */ static VALUE d_lite_jd(VALUE self) { get_d1(self); return m_real_local_jd(dat); } /* * call-seq: * d.mjd -> integer * * Returns the modified Julian day number. This is a whole number, * which is adjusted by the offset as the local time. * * DateTime.new(2001,2,3,4,5,6,'+7').mjd #=> 51943 * DateTime.new(2001,2,3,4,5,6,'-7').mjd #=> 51943 */ static VALUE d_lite_mjd(VALUE self) { get_d1(self); return f_sub(m_real_local_jd(dat), INT2FIX(2400001)); } /* * call-seq: * d.ld -> integer * * Returns the Lilian day number. This is a whole number, which is * adjusted by the offset as the local time. * * Date.new(2001,2,3).ld #=> 152784 */ static VALUE d_lite_ld(VALUE self) { get_d1(self); return f_sub(m_real_local_jd(dat), INT2FIX(2299160)); } /* * call-seq: * d.year -> integer * * Returns the year. * * Date.new(2001,2,3).year #=> 2001 * (Date.new(1,1,1) - 1).year #=> 0 */ static VALUE d_lite_year(VALUE self) { get_d1(self); return m_real_year(dat); } /* * call-seq: * d.yday -> fixnum * * Returns the day of the year (1-366). * * Date.new(2001,2,3).yday #=> 34 */ static VALUE d_lite_yday(VALUE self) { get_d1(self); return INT2FIX(m_yday(dat)); } /* * call-seq: * d.mon -> fixnum * d.month -> fixnum * * Returns the month (1-12). * * Date.new(2001,2,3).mon #=> 2 */ static VALUE d_lite_mon(VALUE self) { get_d1(self); return INT2FIX(m_mon(dat)); } /* * call-seq: * d.mday -> fixnum * d.day -> fixnum * * Returns the day of the month (1-31). * * Date.new(2001,2,3).mday #=> 3 */ static VALUE d_lite_mday(VALUE self) { get_d1(self); return INT2FIX(m_mday(dat)); } /* * call-seq: * d.day_fraction -> rational * * Returns the fractional part of the day. * * DateTime.new(2001,2,3,12).day_fraction #=> (1/2) */ static VALUE d_lite_day_fraction(VALUE self) { get_d1(self); if (simple_dat_p(dat)) return INT2FIX(0); return m_fr(dat); } /* * call-seq: * d.cwyear -> integer * * Returns the calendar week based year. * * Date.new(2001,2,3).cwyear #=> 2001 * Date.new(2000,1,1).cwyear #=> 1999 */ static VALUE d_lite_cwyear(VALUE self) { get_d1(self); return m_real_cwyear(dat); } /* * call-seq: * d.cweek -> fixnum * * Returns the calendar week number (1-53). * * Date.new(2001,2,3).cweek #=> 5 */ static VALUE d_lite_cweek(VALUE self) { get_d1(self); return INT2FIX(m_cweek(dat)); } /* * call-seq: * d.cwday -> fixnum * * Returns the day of calendar week (1-7, Monday is 1). * * Date.new(2001,2,3).cwday #=> 6 */ static VALUE d_lite_cwday(VALUE self) { get_d1(self); return INT2FIX(m_cwday(dat)); } #ifndef NDEBUG static VALUE d_lite_wnum0(VALUE self) { get_d1(self); return INT2FIX(m_wnum0(dat)); } static VALUE d_lite_wnum1(VALUE self) { get_d1(self); return INT2FIX(m_wnum1(dat)); } #endif /* * call-seq: * d.wday -> fixnum * * Returns the day of week (0-6, Sunday is zero). * * Date.new(2001,2,3).wday #=> 6 */ static VALUE d_lite_wday(VALUE self) { get_d1(self); return INT2FIX(m_wday(dat)); } /* * call-seq: * d.sunday? -> bool * * Returns true if the date is Sunday. */ static VALUE d_lite_sunday_p(VALUE self) { get_d1(self); return f_boolcast(m_wday(dat) == 0); } /* * call-seq: * d.monday? -> bool * * Returns true if the date is Monday. */ static VALUE d_lite_monday_p(VALUE self) { get_d1(self); return f_boolcast(m_wday(dat) == 1); } /* * call-seq: * d.tuesday? -> bool * * Returns true if the date is Tuesday. */ static VALUE d_lite_tuesday_p(VALUE self) { get_d1(self); return f_boolcast(m_wday(dat) == 2); } /* * call-seq: * d.wednesday? -> bool * * Returns true if the date is Wednesday. */ static VALUE d_lite_wednesday_p(VALUE self) { get_d1(self); return f_boolcast(m_wday(dat) == 3); } /* * call-seq: * d.thursday? -> bool * * Returns true if the date is Thursday. */ static VALUE d_lite_thursday_p(VALUE self) { get_d1(self); return f_boolcast(m_wday(dat) == 4); } /* * call-seq: * d.friday? -> bool * * Returns true if the date is Friday. */ static VALUE d_lite_friday_p(VALUE self) { get_d1(self); return f_boolcast(m_wday(dat) == 5); } /* * call-seq: * d.saturday? -> bool * * Returns true if the date is Saturday. */ static VALUE d_lite_saturday_p(VALUE self) { get_d1(self); return f_boolcast(m_wday(dat) == 6); } #ifndef NDEBUG static VALUE d_lite_nth_kday_p(VALUE self, VALUE n, VALUE k) { int rjd, ns; get_d1(self); if (NUM2INT(k) != m_wday(dat)) return Qfalse; c_nth_kday_to_jd(m_year(dat), m_mon(dat), NUM2INT(n), NUM2INT(k), m_virtual_sg(dat), /* !=m_sg() */ &rjd, &ns); if (m_local_jd(dat) != rjd) return Qfalse; return Qtrue; } #endif /* * call-seq: * d.hour -> fixnum * * Returns the hour (0-23). * * DateTime.new(2001,2,3,4,5,6).hour #=> 4 */ static VALUE d_lite_hour(VALUE self) { get_d1(self); return INT2FIX(m_hour(dat)); } /* * call-seq: * d.min -> fixnum * d.minute -> fixnum * * Returns the minute (0-59). * * DateTime.new(2001,2,3,4,5,6).min #=> 5 */ static VALUE d_lite_min(VALUE self) { get_d1(self); return INT2FIX(m_min(dat)); } /* * call-seq: * d.sec -> fixnum * d.second -> fixnum * * Returns the second (0-59). * * DateTime.new(2001,2,3,4,5,6).sec #=> 6 */ static VALUE d_lite_sec(VALUE self) { get_d1(self); return INT2FIX(m_sec(dat)); } /* * call-seq: * d.sec_fraction -> rational * d.second_fraction -> rational * * Returns the fractional part of the second. * * DateTime.new(2001,2,3,4,5,6.5).sec_fraction #=> (1/2) */ static VALUE d_lite_sec_fraction(VALUE self) { get_d1(self); return m_sf_in_sec(dat); } /* * call-seq: * d.offset -> rational * * Returns the offset. * * DateTime.parse('04pm+0730').offset #=> (5/16) */ static VALUE d_lite_offset(VALUE self) { get_d1(self); return m_of_in_day(dat); } /* * call-seq: * d.zone -> string * * Returns the timezone. * * DateTime.parse('04pm+0730').zone #=> "+07:30" */ static VALUE d_lite_zone(VALUE self) { get_d1(self); return m_zone(dat); } /* * call-seq: * d.julian? -> bool * * Retruns true if the date is before the day of calendar reform. * * Date.new(1582,10,15).julian? #=> false * (Date.new(1582,10,15) - 1).julian? #=> true */ static VALUE d_lite_julian_p(VALUE self) { get_d1(self); return f_boolcast(m_julian_p(dat)); } /* * call-seq: * d.gregorian? -> bool * * Retunrs true if the date is on or after the day of calendar reform. * * Date.new(1582,10,15).gregorian? #=> true * (Date.new(1582,10,15) - 1).gregorian? #=> false */ static VALUE d_lite_gregorian_p(VALUE self) { get_d1(self); return f_boolcast(m_gregorian_p(dat)); } /* * call-seq: * d.leap? -> bool * * Returns true if the year is a leap year. * * Date.new(2000).leap? #=> true * Date.new(2001).leap? #=> false */ static VALUE d_lite_leap_p(VALUE self) { int rjd, ns, ry, rm, rd; get_d1(self); if (m_gregorian_p(dat)) return f_boolcast(c_gregorian_leap_p(m_year(dat))); c_civil_to_jd(m_year(dat), 3, 1, m_virtual_sg(dat), &rjd, &ns); c_jd_to_civil(rjd - 1, m_virtual_sg(dat), &ry, &rm, &rd); return f_boolcast(rd == 29); } /* * call-seq: * d.start -> float * * Returns the Julian day number denoting the day of calendar reform. * * Date.new(2001,2,3).start #=> 2299161.0 * Date.new(2001,2,3,Date::GREGORIAN).start #=> -Infinity */ static VALUE d_lite_start(VALUE self) { get_d1(self); return DBL2NUM(m_sg(dat)); } static void clear_civil(union DateData *x) { if (simple_dat_p(x)) { x->s.year = 0; #ifndef USE_PACK x->s.mon = 0; x->s.mday = 0; #else x->s.pc = 0; #endif x->s.flags &= ~HAVE_CIVIL; } else { x->c.year = 0; #ifndef USE_PACK x->c.mon = 0; x->c.mday = 0; x->c.hour = 0; x->c.min = 0; x->c.sec = 0; #else x->c.pc = 0; #endif x->c.flags &= ~(HAVE_CIVIL | HAVE_TIME); } } static void set_sg(union DateData *x, double sg) { if (simple_dat_p(x)) { get_s_jd(x); clear_civil(x); x->s.sg = (date_sg_t)sg; } else { get_c_jd(x); get_c_df(x); clear_civil(x); x->c.sg = (date_sg_t)sg; } } static VALUE dup_obj_with_new_start(VALUE obj, double sg) { volatile VALUE dup = dup_obj(obj); { get_d1(dup); set_sg(dat, sg); } return dup; } /* * call-seq: * d.new_start([start=Date::ITALY]) -> date * * Duplicates self and resets its the day of calendar reform. * * d = Date.new(1582,10,15) * d.new_start(Date::JULIAN) #=> # */ static VALUE d_lite_new_start(int argc, VALUE *argv, VALUE self) { VALUE vsg; double sg; rb_scan_args(argc, argv, "01", &vsg); sg = DEFAULT_SG; if (argc >= 1) val2sg(vsg, sg); return dup_obj_with_new_start(self, sg); } /* * call-seq: * d.italy -> date * * This method is equivalent to new_start(Date::ITALY). */ static VALUE d_lite_italy(VALUE self) { return dup_obj_with_new_start(self, ITALY); } /* * call-seq: * d.england -> date * * This method is equivalent to new_start(Date::ENGLAND). */ static VALUE d_lite_england(VALUE self) { return dup_obj_with_new_start(self, ENGLAND); } /* * call-seq: * d.julian -> date * * This method is equivalent to new_start(Date::JULIAN). */ static VALUE d_lite_julian(VALUE self) { return dup_obj_with_new_start(self, JULIAN); } /* * call-seq: * d.gregorian -> date * * This method is equivalent to new_start(Date::GREGORIAN). */ static VALUE d_lite_gregorian(VALUE self) { return dup_obj_with_new_start(self, GREGORIAN); } static void set_of(union DateData *x, int of) { assert(complex_dat_p(x)); get_c_jd(x); get_c_df(x); clear_civil(x); x->c.of = of; } static VALUE dup_obj_with_new_offset(VALUE obj, int of) { volatile VALUE dup = dup_obj_as_complex(obj); { get_d1(dup); set_of(dat, of); } return dup; } /* * call-seq: * d.new_offset([offset=0]) -> date * * Duplicates self and resets its offset. * * d = DateTime.new(2001,2,3,4,5,6,'-02:00') * #=> # * d.new_offset('+09:00') #=> # */ static VALUE d_lite_new_offset(int argc, VALUE *argv, VALUE self) { VALUE vof; int rof; rb_scan_args(argc, argv, "01", &vof); rof = 0; if (argc >= 1) val2off(vof, rof); return dup_obj_with_new_offset(self, rof); } /* * call-seq: * d + other -> date * * Returns a date object pointing other days after self. The other * should be a numeric value. If the other is flonum, assumes its * precision is at most nanosecond. * * Date.new(2001,2,3) + 1 #=> # * DateTime.new(2001,2,3) + Rational(1,2) * #=> # * DateTime.new(2001,2,3) + Rational(-1,2) * #=> # * DateTime.jd(0,12) + DateTime.new(2001,2,3).ajd * #=> # */ static VALUE d_lite_plus(VALUE self, VALUE other) { get_d1(self); switch (TYPE(other)) { case T_FIXNUM: { VALUE nth; long t; int jd; nth = m_nth(dat); t = FIX2LONG(other); if (DIV(t, CM_PERIOD)) { nth = f_add(nth, INT2FIX(DIV(t, CM_PERIOD))); t = MOD(t, CM_PERIOD); } if (!t) jd = m_jd(dat); else { jd = m_jd(dat) + (int)t; canonicalize_jd(nth, jd); } if (simple_dat_p(dat)) return d_simple_new_internal(rb_obj_class(self), nth, jd, dat->s.sg, 0, 0, 0, (dat->s.flags | HAVE_JD) & ~HAVE_CIVIL); else return d_complex_new_internal(rb_obj_class(self), nth, jd, dat->c.df, dat->c.sf, dat->c.of, dat->c.sg, 0, 0, 0, #ifndef USE_PACK dat->c.hour, dat->c.min, dat->c.sec, #else EX_HOUR(dat->c.pc), EX_MIN(dat->c.pc), EX_SEC(dat->c.pc), #endif (dat->c.flags | HAVE_JD) & ~HAVE_CIVIL); } break; case T_BIGNUM: { VALUE nth; int jd, s; if (f_positive_p(other)) s = +1; else { s = -1; other = f_negate(other); } nth = f_idiv(other, INT2FIX(CM_PERIOD)); jd = FIX2INT(f_mod(other, INT2FIX(CM_PERIOD))); if (s < 0) { nth = f_negate(nth); jd = -jd; } if (!jd) jd = m_jd(dat); else { jd = m_jd(dat) + jd; canonicalize_jd(nth, jd); } if (f_zero_p(nth)) nth = m_nth(dat); else nth = f_add(m_nth(dat), nth); if (simple_dat_p(dat)) return d_simple_new_internal(rb_obj_class(self), nth, jd, dat->s.sg, 0, 0, 0, (dat->s.flags | HAVE_JD) & ~HAVE_CIVIL); else return d_complex_new_internal(rb_obj_class(self), nth, jd, dat->c.df, dat->c.sf, dat->c.of, dat->c.sg, 0, 0, 0, #ifndef USE_PACK dat->c.hour, dat->c.min, dat->c.sec, #else EX_HOUR(dat->c.pc), EX_MIN(dat->c.pc), EX_SEC(dat->c.pc), #endif (dat->c.flags | HAVE_JD) & ~HAVE_CIVIL); } break; case T_FLOAT: { double jd, o, tmp; int s, df; VALUE nth, sf; o = RFLOAT_VALUE(other); if (o > 0) s = +1; else { s = -1; o = -o; } o = modf(o, &tmp); if (!floor(tmp / CM_PERIOD)) { nth = INT2FIX(0); jd = (int)tmp; } else { double i, f; f = modf(tmp / CM_PERIOD, &i); nth = f_floor(DBL2NUM(i)); jd = (int)(f * CM_PERIOD); } o *= DAY_IN_SECONDS; o = modf(o, &tmp); df = (int)tmp; o *= SECOND_IN_NANOSECONDS; sf = INT2FIX((int)round(o)); if (s < 0) { jd = -jd; df = -df; sf = f_negate(sf); } if (f_zero_p(sf)) sf = m_sf(dat); else { sf = f_add(m_sf(dat), sf); if (f_lt_p(sf, INT2FIX(0))) { df -= 1; sf = f_add(sf, INT2FIX(SECOND_IN_NANOSECONDS)); } else if (f_ge_p(sf, INT2FIX(SECOND_IN_NANOSECONDS))) { df += 1; sf = f_sub(sf, INT2FIX(SECOND_IN_NANOSECONDS)); } } if (!df) df = m_df(dat); else { df = m_df(dat) + df; if (df < 0) { jd -= 1; df += DAY_IN_SECONDS; } else if (df >= DAY_IN_SECONDS) { jd += 1; df -= DAY_IN_SECONDS; } } if (!jd) jd = m_jd(dat); else { jd = m_jd(dat) + jd; canonicalize_jd(nth, jd); } if (f_zero_p(nth)) nth = m_nth(dat); else nth = f_add(m_nth(dat), nth); if (!df && f_zero_p(sf) && !m_of(dat)) return d_simple_new_internal(rb_obj_class(self), nth, (int)jd, m_sg(dat), 0, 0, 0, (dat->s.flags | HAVE_JD) & ~(HAVE_CIVIL | HAVE_TIME | COMPLEX_DAT)); else return d_complex_new_internal(rb_obj_class(self), nth, (int)jd, df, sf, m_of(dat), m_sg(dat), 0, 0, 0, 0, 0, 0, (dat->c.flags | HAVE_JD | HAVE_DF) & ~(HAVE_CIVIL | HAVE_TIME)); } break; default: expect_numeric(other); other = f_to_r(other); #ifdef CANONICALIZATION_FOR_MATHN if (!k_rational_p(other)) return d_lite_plus(self, other); #endif /* fall through */ case T_RATIONAL: { VALUE nth, sf, t; int jd, df, s; if (wholenum_p(other)) return d_lite_plus(self, rb_rational_num(other)); if (f_positive_p(other)) s = +1; else { s = -1; other = f_negate(other); } nth = f_idiv(other, INT2FIX(CM_PERIOD)); t = f_mod(other, INT2FIX(CM_PERIOD)); jd = FIX2INT(f_idiv(t, INT2FIX(1))); t = f_mod(t, INT2FIX(1)); t = f_mul(t, INT2FIX(DAY_IN_SECONDS)); df = FIX2INT(f_idiv(t, INT2FIX(1))); t = f_mod(t, INT2FIX(1)); sf = f_mul(t, INT2FIX(SECOND_IN_NANOSECONDS)); if (s < 0) { nth = f_negate(nth); jd = -jd; df = -df; sf = f_negate(sf); } if (f_zero_p(sf)) sf = m_sf(dat); else { sf = f_add(m_sf(dat), sf); if (f_lt_p(sf, INT2FIX(0))) { df -= 1; sf = f_add(sf, INT2FIX(SECOND_IN_NANOSECONDS)); } else if (f_ge_p(sf, INT2FIX(SECOND_IN_NANOSECONDS))) { df += 1; sf = f_sub(sf, INT2FIX(SECOND_IN_NANOSECONDS)); } } if (!df) df = m_df(dat); else { df = m_df(dat) + df; if (df < 0) { jd -= 1; df += DAY_IN_SECONDS; } else if (df >= DAY_IN_SECONDS) { jd += 1; df -= DAY_IN_SECONDS; } } if (!jd) jd = m_jd(dat); else { jd = m_jd(dat) + jd; canonicalize_jd(nth, jd); } if (f_zero_p(nth)) nth = m_nth(dat); else nth = f_add(m_nth(dat), nth); if (!df && f_zero_p(sf) && !m_of(dat)) return d_simple_new_internal(rb_obj_class(self), nth, jd, m_sg(dat), 0, 0, 0, (dat->s.flags | HAVE_JD) & ~(HAVE_CIVIL | HAVE_TIME | COMPLEX_DAT)); else return d_complex_new_internal(rb_obj_class(self), nth, jd, df, sf, m_of(dat), m_sg(dat), 0, 0, 0, 0, 0, 0, (dat->c.flags | HAVE_JD | HAVE_DF) & ~(HAVE_CIVIL | HAVE_TIME)); } break; } } static VALUE minus_dd(VALUE self, VALUE other) { get_d2(self, other); { int d, df; VALUE n, sf, r; n = f_sub(m_nth(adat), m_nth(bdat)); d = m_jd(adat) - m_jd(bdat); df = m_df(adat) - m_df(bdat); sf = f_sub(m_sf(adat), m_sf(bdat)); canonicalize_jd(n, d); if (df < 0) { d -= 1; df += DAY_IN_SECONDS; } else if (df >= DAY_IN_SECONDS) { d += 1; df -= DAY_IN_SECONDS; } if (f_lt_p(sf, INT2FIX(0))) { df -= 1; sf = f_add(sf, INT2FIX(SECOND_IN_NANOSECONDS)); } else if (f_ge_p(sf, INT2FIX(SECOND_IN_NANOSECONDS))) { df += 1; sf = f_sub(sf, INT2FIX(SECOND_IN_NANOSECONDS)); } if (f_zero_p(n)) r = INT2FIX(0); else r = f_mul(n, INT2FIX(CM_PERIOD)); if (d) r = f_add(r, rb_rational_new1(INT2FIX(d))); if (df) r = f_add(r, isec_to_day(df)); if (f_nonzero_p(sf)) r = f_add(r, ns_to_day(sf)); if (RB_TYPE_P(r, T_RATIONAL)) return r; return rb_rational_new1(r); } } /* * call-seq: * d - other -> date or rational * * Returns the difference between the two dates if the other is a date * object. If the other is a numeric value, returns a date object * pointing other days before self. If the other is flonum, assumes * its precision is at most nanosecond. * * Date.new(2001,2,3) - 1 #=> # * DateTime.new(2001,2,3) - Rational(1,2) * #=> # * Date.new(2001,2,3) - Date.new(2001) * #=> (33/1) * DateTime.new(2001,2,3) - DateTime.new(2001,2,2,12) * #=> (1/2) */ static VALUE d_lite_minus(VALUE self, VALUE other) { if (k_date_p(other)) return minus_dd(self, other); switch (TYPE(other)) { case T_FIXNUM: return d_lite_plus(self, LONG2NUM(-FIX2LONG(other))); case T_FLOAT: return d_lite_plus(self, DBL2NUM(-RFLOAT_VALUE(other))); default: expect_numeric(other); /* fall through */ case T_BIGNUM: case T_RATIONAL: return d_lite_plus(self, f_negate(other)); } } /* * call-seq: * d.next_day([n=1]) -> date * * This method is 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]) -> date * * This method is 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.succ -> date * d.next -> date * * Returns a date object denoting the following day. */ static VALUE d_lite_next(VALUE self) { return d_lite_next_day(0, (VALUE *)NULL, self); } /* * call-seq: * d >> n -> date * * Returns a date object pointing n months after self. The n should * be a numeric value. * * Date.new(2001,2,3) >> 1 #=> # * Date.new(2001,1,31) >> 1 #=> # * Date.new(2001,2,3) >> -2 #=> # */ static VALUE d_lite_rshift(VALUE self, VALUE other) { VALUE t, y, nth, rjd2; int m, d, rjd; double sg; get_d1(self); t = f_add3(f_mul(m_real_year(dat), INT2FIX(12)), INT2FIX(m_mon(dat) - 1), other); if (FIXNUM_P(t)) { long it = FIX2LONG(t); y = LONG2NUM(DIV(it, 12)); it = MOD(it, 12); m = (int)it + 1; } else { y = f_idiv(t, INT2FIX(12)); t = f_mod(t, INT2FIX(12)); m = FIX2INT(t) + 1; } d = m_mday(dat); sg = m_sg(dat); while (1) { int ry, rm, rd, ns; if (valid_civil_p(y, m, d, sg, &nth, &ry, &rm, &rd, &rjd, &ns)) break; if (--d < 1) rb_raise(rb_eArgError, "invalid date"); } encode_jd(nth, rjd, &rjd2); return d_lite_plus(self, f_sub(rjd2, m_real_local_jd(dat))); } /* * call-seq: * d << n -> date * * Returns a date object pointing n months before self. The n should * be a numeric value. * * Date.new(2001,2,3) << 1 #=> # * Date.new(2001,1,31) << 11 #=> # * Date.new(2001,2,3) << -1 #=> # */ static VALUE d_lite_lshift(VALUE self, VALUE other) { expect_numeric(other); return d_lite_rshift(self, f_negate(other)); } /* * call-seq: * d.next_month([n=1]) -> date * * This method is 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]) -> date * * This method is 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]) -> date * * This method is 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]) -> date * * This method is 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))); } static VALUE d_lite_cmp(VALUE, VALUE); /* * call-seq: * d.step(limit[, step=1]) -> enumerator * d.step(limit[, step=1]){|date| ...} -> self * * Iterates evaluation of the given block, which takes a date object. * The limit should be a date object. * * Date.new(2001).step(Date.new(2001,-1,-1)).select{|d| d.sunday?}.size * #=> 52 */ static VALUE d_lite_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 (FIX2INT(d_lite_cmp(date, limit)) >= 0) { rb_yield(date); date = d_lite_plus(date, step); } break; case 0: while (1) rb_yield(date); break; case 1: while (FIX2INT(d_lite_cmp(date, limit)) <= 0) { rb_yield(date); date = d_lite_plus(date, step); } break; default: abort(); } return self; } /* * call-seq: * d.upto(max) -> enumerator * d.upto(max){|date| ...} -> self * * This method is equivalent to step(max, 1){|date| ...}. */ static VALUE d_lite_upto(VALUE self, VALUE max) { VALUE date; RETURN_ENUMERATOR(self, 1, &max); date = self; while (FIX2INT(d_lite_cmp(date, max)) <= 0) { rb_yield(date); date = d_lite_plus(date, INT2FIX(1)); } return self; } /* * call-seq: * d.downto(min) -> enumerator * d.downto(min){|date| ...} -> self * * This method is equivalent to step(min, -1){|date| ...}. */ static VALUE d_lite_downto(VALUE self, VALUE min) { VALUE date; RETURN_ENUMERATOR(self, 1, &min); date = self; while (FIX2INT(d_lite_cmp(date, min)) >= 0) { rb_yield(date); date = d_lite_plus(date, INT2FIX(-1)); } return self; } static VALUE cmp_gen(VALUE self, VALUE other) { get_d1(self); if (k_numeric_p(other)) return f_cmp(m_ajd(dat), other); else if (k_date_p(other)) return f_cmp(m_ajd(dat), f_ajd(other)); return rb_num_coerce_cmp(self, other, rb_intern("<=>")); } static VALUE cmp_dd(VALUE self, VALUE other) { get_d2(self, other); { VALUE a_nth, b_nth, a_sf, b_sf; int a_jd, b_jd, a_df, b_df; m_canonicalize_jd(self, adat); m_canonicalize_jd(other, bdat); a_nth = m_nth(adat); b_nth = m_nth(bdat); if (f_eqeq_p(a_nth, b_nth)) { a_jd = m_jd(adat); b_jd = m_jd(bdat); if (a_jd == b_jd) { a_df = m_df(adat); b_df = m_df(bdat); if (a_df == b_df) { a_sf = m_sf(adat); b_sf = m_sf(bdat); if (f_eqeq_p(a_sf, b_sf)) { return INT2FIX(0); } else if (f_lt_p(a_sf, b_sf)) { return INT2FIX(-1); } else { return INT2FIX(1); } } else if (a_df < b_df) { return INT2FIX(-1); } else { return INT2FIX(1); } } else if (a_jd < b_jd) { return INT2FIX(-1); } else { return INT2FIX(1); } } else if (f_lt_p(a_nth, b_nth)) { return INT2FIX(-1); } else { return INT2FIX(1); } } } /* * call-seq: * d <=> other -> -1, 0, +1 or nil * * Compares the two dates and returns -1, zero, 1 or nil. The other * should be a date object or a numeric value as an astronomical * Julian day number. * * Date.new(2001,2,3) <=> Date.new(2001,2,4) #=> -1 * Date.new(2001,2,3) <=> Date.new(2001,2,3) #=> 0 * Date.new(2001,2,3) <=> Date.new(2001,2,2) #=> 1 * Date.new(2001,2,3) <=> Object.new #=> nil * Date.new(2001,2,3) <=> Rational(4903887,2)#=> 0 * * See also Comparable. */ static VALUE d_lite_cmp(VALUE self, VALUE other) { if (!k_date_p(other)) return cmp_gen(self, other); { get_d2(self, other); if (!(simple_dat_p(adat) && simple_dat_p(bdat) && m_gregorian_p(adat) == m_gregorian_p(bdat))) return cmp_dd(self, other); { VALUE a_nth, b_nth; int a_jd, b_jd; m_canonicalize_jd(self, adat); m_canonicalize_jd(other, bdat); a_nth = m_nth(adat); b_nth = m_nth(bdat); if (f_eqeq_p(a_nth, b_nth)) { a_jd = m_jd(adat); b_jd = m_jd(bdat); if (a_jd == b_jd) { return INT2FIX(0); } else if (a_jd < b_jd) { return INT2FIX(-1); } else { return INT2FIX(1); } } else if (f_lt_p(a_nth, b_nth)) { return INT2FIX(-1); } else { return INT2FIX(1); } } } } static VALUE equal_gen(VALUE self, VALUE other) { get_d1(self); if (k_numeric_p(other)) return f_eqeq_p(m_real_local_jd(dat), other); else if (k_date_p(other)) return f_eqeq_p(m_real_local_jd(dat), f_jd(other)); return rb_num_coerce_cmp(self, other, rb_intern("==")); } /* * call-seq: * d === other -> bool * * Returns true if they are the same day. * * Date.new(2001,2,3) === Date.new(2001,2,3) * #=> true * Date.new(2001,2,3) === Date.new(2001,2,4) * #=> false * DateTime.new(2001,2,3) === DateTime.new(2001,2,3,12) * #=> true * DateTime.new(2001,2,3) === DateTime.new(2001,2,3,0,0,0,'+24:00') * #=> true * DateTime.new(2001,2,3) === DateTime.new(2001,2,4,0,0,0,'+24:00') * #=> false */ static VALUE d_lite_equal(VALUE self, VALUE other) { if (!k_date_p(other)) return equal_gen(self, other); { get_d2(self, other); if (!(m_gregorian_p(adat) == m_gregorian_p(bdat))) return equal_gen(self, other); { VALUE a_nth, b_nth; int a_jd, b_jd; m_canonicalize_jd(self, adat); m_canonicalize_jd(other, bdat); a_nth = m_nth(adat); b_nth = m_nth(bdat); a_jd = m_local_jd(adat); b_jd = m_local_jd(bdat); if (f_eqeq_p(a_nth, b_nth) && a_jd == b_jd) return Qtrue; return Qfalse; } } } /* :nodoc: */ static VALUE d_lite_eql_p(VALUE self, VALUE other) { if (!k_date_p(other)) return Qfalse; return f_zero_p(d_lite_cmp(self, other)); } /* :nodoc: */ static VALUE d_lite_hash(VALUE self) { st_index_t v, h[4]; get_d1(self); h[0] = m_nth(dat); h[1] = m_jd(dat); h[2] = m_df(dat); h[3] = m_sf(dat); v = rb_memhash(h, sizeof(h)); return LONG2FIX(v); } #include "date_tmx.h" static void set_tmx(VALUE, struct tmx *); static VALUE strftimev(const char *, VALUE, void (*)(VALUE, struct tmx *)); /* * call-seq: * d.to_s -> string * * Returns a string in an ISO 8601 format (This method doesn't use the * expanded representations). * * Date.new(2001,2,3).to_s #=> "2001-02-03" */ static VALUE d_lite_to_s(VALUE self) { return strftimev("%Y-%m-%d", self, set_tmx); } #ifndef NDEBUG static VALUE mk_inspect_raw(union DateData *x, VALUE klass) { char flags[5]; flags[0] = (x->flags & COMPLEX_DAT) ? 'C' : 'S'; flags[1] = (x->flags & HAVE_JD) ? 'j' : '-'; flags[2] = (x->flags & HAVE_DF) ? 'd' : '-'; flags[3] = (x->flags & HAVE_CIVIL) ? 'c' : '-'; flags[4] = (x->flags & HAVE_TIME) ? 't' : '-'; flags[5] = '\0'; if (simple_dat_p(x)) { return rb_enc_sprintf(rb_usascii_encoding(), "#<%"PRIsVALUE": " "(%+"PRIsVALUE"th,%dj),+0s,%.0fj; " "%dy%dm%dd; %s>", klass, x->s.nth, x->s.jd, x->s.sg, #ifndef USE_PACK x->s.year, x->s.mon, x->s.mday, #else x->s.year, EX_MON(x->s.pc), EX_MDAY(x->s.pc), #endif flags); } else { return rb_enc_sprintf(rb_usascii_encoding(), "#<%"PRIsVALUE": " "(%+"PRIsVALUE"th,%dj,%ds,%+"PRIsVALUE"n)," "%+ds,%.0fj; " "%dy%dm%dd %dh%dm%ds; %s>", klass, x->c.nth, x->c.jd, x->c.df, x->c.sf, x->c.of, x->c.sg, #ifndef USE_PACK x->c.year, x->c.mon, x->c.mday, x->c.hour, x->c.min, x->c.sec, #else x->c.year, EX_MON(x->c.pc), EX_MDAY(x->c.pc), EX_HOUR(x->c.pc), EX_MIN(x->c.pc), EX_SEC(x->c.pc), #endif flags); } } static VALUE d_lite_inspect_raw(VALUE self) { get_d1(self); return mk_inspect_raw(dat, rb_obj_class(self)); } #endif static VALUE mk_inspect(union DateData *x, VALUE klass, VALUE to_s) { return rb_enc_sprintf(rb_usascii_encoding(), "#<%"PRIsVALUE": %"PRIsVALUE" " "((%+"PRIsVALUE"j,%ds,%+"PRIsVALUE"n),%+ds,%.0fj)>", klass, to_s, m_real_jd(x), m_df(x), m_sf(x), m_of(x), m_sg(x)); } /* * call-seq: * d.inspect -> string * * Returns the value as a string for inspection. * * Date.new(2001,2,3).inspect * #=> "#" * DateTime.new(2001,2,3,4,5,6,'-7').inspect * #=> "#" */ static VALUE d_lite_inspect(VALUE self) { get_d1(self); return mk_inspect(dat, rb_obj_class(self), self); } #include #include "date_tmx.h" size_t date_strftime(char *s, size_t maxsize, const char *format, const struct tmx *tmx); #define SMALLBUF 100 static size_t date_strftime_alloc(char **buf, const char *format, struct tmx *tmx) { size_t size, len, flen; (*buf)[0] = '\0'; flen = strlen(format); if (flen == 0) { return 0; } errno = 0; len = date_strftime(*buf, SMALLBUF, format, tmx); if (len != 0 || (**buf == '\0' && errno != ERANGE)) return len; for (size=1024; ; size*=2) { *buf = xmalloc(size); (*buf)[0] = '\0'; len = date_strftime(*buf, size, format, tmx); /* * buflen can be zero EITHER because there's not enough * room in the string, or because the control command * goes to the empty string. Make a reasonable guess that * if the buffer is 1024 times bigger than the length of the * format string, it's not failing for lack of room. */ if (len > 0) break; xfree(*buf); if (size >= 1024 * flen) { rb_sys_fail(format); break; } } return len; } static VALUE tmx_m_secs(union DateData *x) { VALUE s; int df; s = day_to_sec(f_sub(m_real_jd(x), UNIX_EPOCH_IN_CJD)); if (simple_dat_p(x)) return s; df = m_df(x); if (df) s = f_add(s, INT2FIX(df)); return s; } #define MILLISECOND_IN_NANOSECONDS 1000000 static VALUE tmx_m_msecs(union DateData *x) { VALUE s, sf; s = sec_to_ms(tmx_m_secs(x)); if (simple_dat_p(x)) return s; sf = m_sf(x); if (f_nonzero_p(sf)) s = f_add(s, f_div(sf, INT2FIX(MILLISECOND_IN_NANOSECONDS))); return s; } static int tmx_m_of(union DateData *x) { return m_of(x); } static char * tmx_m_zone(union DateData *x) { return RSTRING_PTR(m_zone(x)); } static struct tmx_funcs tmx_funcs = { (VALUE (*)(void *))m_real_year, (int (*)(void *))m_yday, (int (*)(void *))m_mon, (int (*)(void *))m_mday, (VALUE (*)(void *))m_real_cwyear, (int (*)(void *))m_cweek, (int (*)(void *))m_cwday, (int (*)(void *))m_wnum0, (int (*)(void *))m_wnum1, (int (*)(void *))m_wday, (int (*)(void *))m_hour, (int (*)(void *))m_min, (int (*)(void *))m_sec, (VALUE (*)(void *))m_sf_in_sec, (VALUE (*)(void *))tmx_m_secs, (VALUE (*)(void *))tmx_m_msecs, (int (*)(void *))tmx_m_of, (char *(*)(void *))tmx_m_zone }; static void set_tmx(VALUE self, struct tmx *tmx) { get_d1(self); tmx->dat = (void *)dat; tmx->funcs = &tmx_funcs; } static VALUE date_strftime_internal(int argc, VALUE *argv, VALUE self, const char *default_fmt, void (*func)(VALUE, struct tmx *)) { VALUE vfmt; const char *fmt; long len; char buffer[SMALLBUF], *buf = buffer; struct tmx tmx; VALUE str; rb_scan_args(argc, argv, "01", &vfmt); if (argc < 1) vfmt = rb_usascii_str_new2(default_fmt); else { StringValue(vfmt); if (!rb_enc_str_asciicompat_p(vfmt)) { rb_raise(rb_eArgError, "format should have ASCII compatible encoding"); } } fmt = RSTRING_PTR(vfmt); len = RSTRING_LEN(vfmt); (*func)(self, &tmx); if (memchr(fmt, '\0', len)) { /* Ruby string may contain \0's. */ const char *p = fmt, *pe = fmt + len; str = rb_str_new(0, 0); while (p < pe) { len = date_strftime_alloc(&buf, p, &tmx); rb_str_cat(str, buf, len); p += strlen(p); if (buf != buffer) { xfree(buf); buf = buffer; } for (fmt = p; p < pe && !*p; ++p); if (p > fmt) rb_str_cat(str, fmt, p - fmt); } rb_enc_copy(str, vfmt); OBJ_INFECT(str, vfmt); return str; } else len = date_strftime_alloc(&buf, fmt, &tmx); str = rb_str_new(buf, len); if (buf != buffer) xfree(buf); rb_enc_copy(str, vfmt); OBJ_INFECT(str, vfmt); return str; } /* * call-seq: * d.strftime([format='%F']) -> string * * Formats date according to the directives in the given format * string. * The directives begins with a percent (%) character. * Any text not listed as a directive will be passed through to the * output string. * * The directive consists of a percent (%) character, * zero or more flags, optional minimum field width, * optional modifier and a conversion specifier * as follows. * * % * * Flags: * - don't pad a numerical output. * _ use spaces for padding. * 0 use zeros for padding. * ^ upcase the result string. * # change case. * * The minimum field width specifies the minimum width. * * The modifiers are "E", "O", ":", "::" and ":::". * "E" and "O" are ignored. No effect to result currently. * * Format directives: * * Date (Year, Month, Day): * %Y - Year with century (can be negative, 4 digits at least) * -0001, 0000, 1995, 2009, 14292, etc. * %C - year / 100 (round down. 20 in 2009) * %y - year % 100 (00..99) * * %m - Month of the year, zero-padded (01..12) * %_m blank-padded ( 1..12) * %-m no-padded (1..12) * %B - The full month name (``January'') * %^B uppercased (``JANUARY'') * %b - The abbreviated month name (``Jan'') * %^b uppercased (``JAN'') * %h - Equivalent to %b * * %d - Day of the month, zero-padded (01..31) * %-d no-padded (1..31) * %e - Day of the month, blank-padded ( 1..31) * * %j - Day of the year (001..366) * * Time (Hour, Minute, Second, Subsecond): * %H - Hour of the day, 24-hour clock, zero-padded (00..23) * %k - Hour of the day, 24-hour clock, blank-padded ( 0..23) * %I - Hour of the day, 12-hour clock, zero-padded (01..12) * %l - Hour of the day, 12-hour clock, blank-padded ( 1..12) * %P - Meridian indicator, lowercase (``am'' or ``pm'') * %p - Meridian indicator, uppercase (``AM'' or ``PM'') * * %M - Minute of the hour (00..59) * * %S - Second of the minute (00..59) * * %L - Millisecond of the second (000..999) * %N - Fractional seconds digits, default is 9 digits (nanosecond) * %3N millisecond (3 digits) %15N femtosecond (15 digits) * %6N microsecond (6 digits) %18N attosecond (18 digits) * %9N nanosecond (9 digits) %21N zeptosecond (21 digits) * %12N picosecond (12 digits) %24N yoctosecond (24 digits) * * Time zone: * %z - Time zone as hour and minute offset from UTC (e.g. +0900) * %:z - hour and minute offset from UTC with a colon (e.g. +09:00) * %::z - hour, minute and second offset from UTC (e.g. +09:00:00) * %:::z - hour, minute and second offset from UTC * (e.g. +09, +09:30, +09:30:30) * %Z - Time zone abbreviation name or something similar information. * * Weekday: * %A - The full weekday name (``Sunday'') * %^A uppercased (``SUNDAY'') * %a - The abbreviated name (``Sun'') * %^a uppercased (``SUN'') * %u - Day of the week (Monday is 1, 1..7) * %w - Day of the week (Sunday is 0, 0..6) * * ISO 8601 week-based year and week number: * The week 1 of YYYY starts with a Monday and includes YYYY-01-04. * The days in the year before the first week are in the last week of * the previous year. * %G - The week-based year * %g - The last 2 digits of the week-based year (00..99) * %V - Week number of the week-based year (01..53) * * Week number: * The week 1 of YYYY starts with a Sunday or Monday (according to %U * or %W). The days in the year before the first week are in week 0. * %U - Week number of the year. The week starts with Sunday. (00..53) * %W - Week number of the year. The week starts with Monday. (00..53) * * Seconds since the Unix Epoch: * %s - Number of seconds since 1970-01-01 00:00:00 UTC. * %Q - Number of milliseconds since 1970-01-01 00:00:00 UTC. * * Literal string: * %n - Newline character (\n) * %t - Tab character (\t) * %% - Literal ``%'' character * * Combination: * %c - date and time (%a %b %e %T %Y) * %D - Date (%m/%d/%y) * %F - The ISO 8601 date format (%Y-%m-%d) * %v - VMS date (%e-%b-%Y) * %x - Same as %D * %X - Same as %T * %r - 12-hour time (%I:%M:%S %p) * %R - 24-hour time (%H:%M) * %T - 24-hour time (%H:%M:%S) * %+ - date(1) (%a %b %e %H:%M:%S %Z %Y) * * This method is similar to strftime() function defined in ISO C and POSIX. * Several directives (%a, %A, %b, %B, %c, %p, %r, %x, %X, %E*, %O* and %Z) * are locale dependent in the function. * However this method is locale independent. * So, the result may differ even if a same format string is used in other * systems such as C. * It is good practice to avoid %x and %X because there are corresponding * locale independent representations, %D and %T. * * Examples: * * d = DateTime.new(2007,11,19,8,37,48,"-06:00") * #=> # * d.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007" * d.strftime("at %I:%M%p") #=> "at 08:37AM" * * Various ISO 8601 formats: * %Y%m%d => 20071119 Calendar date (basic) * %F => 2007-11-19 Calendar date (extended) * %Y-%m => 2007-11 Calendar date, reduced accuracy, specific month * %Y => 2007 Calendar date, reduced accuracy, specific year * %C => 20 Calendar date, reduced accuracy, specific century * %Y%j => 2007323 Ordinal date (basic) * %Y-%j => 2007-323 Ordinal date (extended) * %GW%V%u => 2007W471 Week date (basic) * %G-W%V-%u => 2007-W47-1 Week date (extended) * %GW%V => 2007W47 Week date, reduced accuracy, specific week (basic) * %G-W%V => 2007-W47 Week date, reduced accuracy, specific week (extended) * %H%M%S => 083748 Local time (basic) * %T => 08:37:48 Local time (extended) * %H%M => 0837 Local time, reduced accuracy, specific minute (basic) * %H:%M => 08:37 Local time, reduced accuracy, specific minute (extended) * %H => 08 Local time, reduced accuracy, specific hour * %H%M%S,%L => 083748,000 Local time with decimal fraction, comma as decimal sign (basic) * %T,%L => 08:37:48,000 Local time with decimal fraction, comma as decimal sign (extended) * %H%M%S.%L => 083748.000 Local time with decimal fraction, full stop as decimal sign (basic) * %T.%L => 08:37:48.000 Local time with decimal fraction, full stop as decimal sign (extended) * %H%M%S%z => 083748-0600 Local time and the difference from UTC (basic) * %T%:z => 08:37:48-06:00 Local time and the difference from UTC (extended) * %Y%m%dT%H%M%S%z => 20071119T083748-0600 Date and time of day for calendar date (basic) * %FT%T%:z => 2007-11-19T08:37:48-06:00 Date and time of day for calendar date (extended) * %Y%jT%H%M%S%z => 2007323T083748-0600 Date and time of day for ordinal date (basic) * %Y-%jT%T%:z => 2007-323T08:37:48-06:00 Date and time of day for ordinal date (extended) * %GW%V%uT%H%M%S%z => 2007W471T083748-0600 Date and time of day for week date (basic) * %G-W%V-%uT%T%:z => 2007-W47-1T08:37:48-06:00 Date and time of day for week date (extended) * %Y%m%dT%H%M => 20071119T0837 Calendar date and local time (basic) * %FT%R => 2007-11-19T08:37 Calendar date and local time (extended) * %Y%jT%H%MZ => 2007323T0837Z Ordinal date and UTC of day (basic) * %Y-%jT%RZ => 2007-323T08:37Z Ordinal date and UTC of day (extended) * %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic) * %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended) * * See also strftime(3) and strptime. */ static VALUE d_lite_strftime(int argc, VALUE *argv, VALUE self) { return date_strftime_internal(argc, argv, self, "%Y-%m-%d", 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); RB_GC_GUARD(self); str = rb_usascii_str_new(buf, len); if (buf != buffer) xfree(buf); return str; } /* * call-seq: * d.asctime -> string * d.ctime -> string * * Returns a string in asctime(3) format (but without "\n\0" at the * end). This method is equivalent to strftime('%c'). * * See also asctime(3) or ctime(3). */ static VALUE d_lite_asctime(VALUE self) { return strftimev("%a %b %e %H:%M:%S %Y", self, set_tmx); } /* * call-seq: * d.iso8601 -> string * d.xmlschema -> string * * This method is equivalent to strftime('%F'). */ static VALUE d_lite_iso8601(VALUE self) { return strftimev("%Y-%m-%d", self, set_tmx); } /* * call-seq: * d.rfc3339 -> string * * This method is equivalent to strftime('%FT%T%:z'). */ static VALUE d_lite_rfc3339(VALUE self) { return strftimev("%Y-%m-%dT%H:%M:%S%:z", self, set_tmx); } /* * call-seq: * d.rfc2822 -> string * d.rfc822 -> string * * This method is 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, set_tmx); } /* * call-seq: * d.httpdate -> string * * This method is equivalent to strftime('%a, %d %b %Y %T GMT'). * See also RFC 2616. */ static VALUE d_lite_httpdate(VALUE self) { volatile VALUE dup = dup_obj_with_new_offset(self, 0); return strftimev("%a, %d %b %Y %T GMT", dup, set_tmx); } enum { DECIMAL_SIZE_OF_LONG = DECIMAL_SIZE_OF_BITS(CHAR_BIT*sizeof(long)), JISX0301_DATE_SIZE = DECIMAL_SIZE_OF_LONG+8 }; static const char * jisx0301_date_format(char *fmt, size_t size, VALUE jd, VALUE y) { if (FIXNUM_P(jd)) { long d = FIX2INT(jd); long s; char c; if (d < 2405160) return "%Y-%m-%d"; if (d < 2419614) { c = 'M'; s = 1867; } else if (d < 2424875) { c = 'T'; s = 1911; } else if (d < 2447535) { c = 'S'; s = 1925; } else { c = 'H'; s = 1988; } snprintf(fmt, size, "%c%02ld" ".%%m.%%d", c, FIX2INT(y) - s); return fmt; } return "%Y-%m-%d"; } /* * call-seq: * d.jisx0301 -> string * * Returns a string in a JIS X 0301 format. * * Date.new(2001,2,3).jisx0301 #=> "H13.02.03" */ static VALUE d_lite_jisx0301(VALUE self) { char fmtbuf[JISX0301_DATE_SIZE]; const char *fmt; get_d1(self); fmt = jisx0301_date_format(fmtbuf, sizeof(fmtbuf), m_real_local_jd(dat), m_real_year(dat)); return strftimev(fmt, self, set_tmx); } #ifndef NDEBUG static VALUE d_lite_marshal_dump_old(VALUE self) { VALUE a; get_d1(self); a = rb_ary_new3(3, m_ajd(dat), m_of_in_day(dat), DBL2NUM(m_sg(dat))); if (FL_TEST(self, FL_EXIVAR)) { rb_copy_generic_ivar(a, self); FL_SET(a, FL_EXIVAR); } return a; } #endif /* :nodoc: */ static VALUE d_lite_marshal_dump(VALUE self) { VALUE a; get_d1(self); a = rb_ary_new3(6, m_nth(dat), INT2FIX(m_jd(dat)), INT2FIX(m_df(dat)), m_sf(dat), INT2FIX(m_of(dat)), DBL2NUM(m_sg(dat))); if (FL_TEST(self, FL_EXIVAR)) { rb_copy_generic_ivar(a, self); FL_SET(a, FL_EXIVAR); } return a; } /* :nodoc: */ static VALUE d_lite_marshal_load(VALUE self, VALUE a) { get_d1(self); rb_check_frozen(self); rb_check_trusted(self); if (!RB_TYPE_P(a, T_ARRAY)) rb_raise(rb_eTypeError, "expected an array"); switch (RARRAY_LEN(a)) { case 2: /* 1.6.x */ case 3: /* 1.8.x, 1.9.2 */ { VALUE ajd, of, sg, nth, sf; int jd, df, rof; double rsg; if (RARRAY_LEN(a) == 2) { ajd = f_sub(RARRAY_AREF(a, 0), half_days_in_day); of = INT2FIX(0); sg = RARRAY_AREF(a, 1); if (!k_numeric_p(sg)) sg = DBL2NUM(RTEST(sg) ? GREGORIAN : JULIAN); } else { ajd = RARRAY_AREF(a, 0); of = RARRAY_AREF(a, 1); sg = RARRAY_AREF(a, 2); } old_to_new(ajd, of, sg, &nth, &jd, &df, &sf, &rof, &rsg); if (!df && f_zero_p(sf) && !rof) { set_to_simple(self, &dat->s, nth, jd, rsg, 0, 0, 0, HAVE_JD); } else { if (!complex_dat_p(dat)) rb_raise(rb_eArgError, "cannot load complex into simple"); set_to_complex(self, &dat->c, nth, jd, df, sf, rof, rsg, 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF | COMPLEX_DAT); } } break; case 6: { VALUE nth, sf; int jd, df, of; double sg; nth = RARRAY_AREF(a, 0); jd = NUM2INT(RARRAY_AREF(a, 1)); df = NUM2INT(RARRAY_AREF(a, 2)); sf = RARRAY_AREF(a, 3); of = NUM2INT(RARRAY_AREF(a, 4)); sg = NUM2DBL(RARRAY_AREF(a, 5)); if (!df && f_zero_p(sf) && !of) { set_to_simple(self, &dat->s, nth, jd, sg, 0, 0, 0, HAVE_JD); } else { if (!complex_dat_p(dat)) rb_raise(rb_eArgError, "cannot load complex into simple"); set_to_complex(self, &dat->c, nth, jd, df, sf, of, sg, 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF | COMPLEX_DAT); } } break; default: rb_raise(rb_eTypeError, "invalid size"); break; } if (FL_TEST(a, FL_EXIVAR)) { rb_copy_generic_ivar(self, a); FL_SET(self, FL_EXIVAR); } return self; } /* :nodoc: */ static VALUE date_s__load(VALUE klass, VALUE s) { VALUE a, obj; a = rb_marshal_load(s); obj = d_lite_s_alloc(klass); return d_lite_marshal_load(obj, a); } /* datetime */ /* * call-seq: * DateTime.jd([jd=0[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]) -> datetime * * Creates a datetime object denoting the given chronological Julian * day number. * * DateTime.jd(2451944) #=> # * DateTime.jd(2451945) #=> # * DateTime.jd(Rational('0.5')) * #=> # */ static VALUE datetime_s_jd(int argc, VALUE *argv, VALUE klass) { VALUE vjd, vh, vmin, vs, vof, vsg, jd, fr, fr2, ret; int h, min, s, rof; double sg; rb_scan_args(argc, argv, "06", &vjd, &vh, &vmin, &vs, &vof, &vsg); jd = INT2FIX(0); h = min = s = 0; fr2 = INT2FIX(0); rof = 0; sg = DEFAULT_SG; switch (argc) { case 6: val2sg(vsg, sg); case 5: val2off(vof, rof); case 4: num2int_with_frac(s, positive_inf); case 3: num2int_with_frac(min, 3); case 2: num2int_with_frac(h, 2); case 1: num2num_with_frac(jd, 1); } { VALUE nth; int rh, rmin, rs, rjd, rjd2; if (!c_valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); canon24oc(); decode_jd(jd, &nth, &rjd); rjd2 = jd_local_to_utc(rjd, time_to_df(rh, rmin, rs), rof); ret = d_complex_new_internal(klass, nth, rjd2, 0, INT2FIX(0), rof, sg, 0, 0, 0, rh, rmin, rs, HAVE_JD | HAVE_TIME); } add_frac(); return ret; } /* * call-seq: * DateTime.ordinal([year=-4712[, yday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]) -> datetime * * Creates a date-time object denoting the given ordinal date. * * DateTime.ordinal(2001,34) #=> # * DateTime.ordinal(2001,34,4,5,6,'+7') * #=> # * DateTime.ordinal(2001,-332,-20,-55,-54,'+7') * #=> # */ static VALUE datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) { VALUE vy, vd, vh, vmin, vs, vof, vsg, y, fr, fr2, ret; int d, h, min, s, rof; double sg; rb_scan_args(argc, argv, "07", &vy, &vd, &vh, &vmin, &vs, &vof, &vsg); y = INT2FIX(-4712); d = 1; h = min = s = 0; fr2 = INT2FIX(0); rof = 0; sg = DEFAULT_SG; switch (argc) { case 7: val2sg(vsg, sg); case 6: val2off(vof, rof); case 5: num2int_with_frac(s, positive_inf); case 4: num2int_with_frac(min, 4); case 3: num2int_with_frac(h, 3); case 2: num2int_with_frac(d, 2); case 1: y = vy; } { VALUE nth; int ry, rd, rh, rmin, rs, rjd, rjd2, ns; if (!valid_ordinal_p(y, d, sg, &nth, &ry, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); if (!c_valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); canon24oc(); rjd2 = jd_local_to_utc(rjd, time_to_df(rh, rmin, rs), rof); ret = d_complex_new_internal(klass, nth, rjd2, 0, INT2FIX(0), rof, sg, 0, 0, 0, rh, rmin, rs, HAVE_JD | HAVE_TIME); } add_frac(); return ret; } /* * call-seq: * DateTime.civil([year=-4712[, month=1[, mday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]]) -> datetime * DateTime.new([year=-4712[, month=1[, mday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]]) -> datetime * * Creates a date-time object denoting the given calendar date. * * DateTime.new(2001,2,3) #=> # * DateTime.new(2001,2,3,4,5,6,'+7') * #=> # * DateTime.new(2001,-11,-26,-20,-55,-54,'+7') * #=> # */ static VALUE datetime_s_civil(int argc, VALUE *argv, VALUE klass) { VALUE vy, vm, vd, vh, vmin, vs, vof, vsg, y, fr, fr2, ret; int m, d, h, min, s, rof; double sg; rb_scan_args(argc, argv, "08", &vy, &vm, &vd, &vh, &vmin, &vs, &vof, &vsg); y = INT2FIX(-4712); m = 1; d = 1; h = min = s = 0; fr2 = INT2FIX(0); rof = 0; sg = DEFAULT_SG; switch (argc) { case 8: val2sg(vsg, sg); case 7: val2off(vof, rof); case 6: num2int_with_frac(s, positive_inf); case 5: num2int_with_frac(min, 5); case 4: num2int_with_frac(h, 4); case 3: num2int_with_frac(d, 3); case 2: m = NUM2INT(vm); case 1: y = vy; } if (guess_style(y, sg) < 0) { VALUE nth; int ry, rm, rd, rh, rmin, rs; if (!valid_gregorian_p(y, m, d, &nth, &ry, &rm, &rd)) rb_raise(rb_eArgError, "invalid date"); if (!c_valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); canon24oc(); ret = d_complex_new_internal(klass, nth, 0, 0, INT2FIX(0), rof, sg, ry, rm, rd, rh, rmin, rs, HAVE_CIVIL | HAVE_TIME); } else { VALUE nth; int ry, rm, rd, rh, rmin, rs, rjd, rjd2, ns; if (!valid_civil_p(y, m, d, sg, &nth, &ry, &rm, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); if (!c_valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); canon24oc(); rjd2 = jd_local_to_utc(rjd, time_to_df(rh, rmin, rs), rof); ret = d_complex_new_internal(klass, nth, rjd2, 0, INT2FIX(0), rof, sg, ry, rm, rd, rh, rmin, rs, HAVE_JD | HAVE_CIVIL | HAVE_TIME); } add_frac(); return ret; } /* * call-seq: * DateTime.commercial([cwyear=-4712[, cweek=1[, cwday=1[, hour=0[, minute=0[, second=0[, offset=0[, start=Date::ITALY]]]]]]]]) -> datetime * * Creates a date-time object denoting the given week date. * * DateTime.commercial(2001) #=> # * DateTime.commercial(2002) #=> # * DateTime.commercial(2001,5,6,4,5,6,'+7') * #=> # */ static VALUE datetime_s_commercial(int argc, VALUE *argv, VALUE klass) { VALUE vy, vw, vd, vh, vmin, vs, vof, vsg, y, fr, fr2, ret; int w, d, h, min, s, rof; double sg; rb_scan_args(argc, argv, "08", &vy, &vw, &vd, &vh, &vmin, &vs, &vof, &vsg); y = INT2FIX(-4712); w = 1; d = 1; h = min = s = 0; fr2 = INT2FIX(0); rof = 0; sg = DEFAULT_SG; switch (argc) { case 8: val2sg(vsg, sg); case 7: val2off(vof, rof); case 6: num2int_with_frac(s, positive_inf); case 5: num2int_with_frac(min, 5); case 4: num2int_with_frac(h, 4); case 3: num2int_with_frac(d, 3); case 2: w = NUM2INT(vw); case 1: y = vy; } { VALUE nth; int ry, rw, rd, rh, rmin, rs, rjd, rjd2, ns; if (!valid_commercial_p(y, w, d, sg, &nth, &ry, &rw, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); if (!c_valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); canon24oc(); rjd2 = jd_local_to_utc(rjd, time_to_df(rh, rmin, rs), rof); ret = d_complex_new_internal(klass, nth, rjd2, 0, INT2FIX(0), rof, sg, 0, 0, 0, rh, rmin, rs, HAVE_JD | HAVE_TIME); } add_frac(); return ret; } #ifndef NDEBUG static VALUE datetime_s_weeknum(int argc, VALUE *argv, VALUE klass) { VALUE vy, vw, vd, vf, vh, vmin, vs, vof, vsg, y, fr, fr2, ret; int w, d, f, h, min, s, rof; double sg; rb_scan_args(argc, argv, "09", &vy, &vw, &vd, &vf, &vh, &vmin, &vs, &vof, &vsg); y = INT2FIX(-4712); w = 0; d = 1; f = 0; h = min = s = 0; fr2 = INT2FIX(0); rof = 0; sg = DEFAULT_SG; switch (argc) { case 9: val2sg(vsg, sg); case 8: val2off(vof, rof); case 7: num2int_with_frac(s, positive_inf); case 6: num2int_with_frac(min, 6); case 5: num2int_with_frac(h, 5); case 4: f = NUM2INT(vf); case 3: num2int_with_frac(d, 4); case 2: w = NUM2INT(vw); case 1: y = vy; } { VALUE nth; int ry, rw, rd, rh, rmin, rs, rjd, rjd2, ns; if (!valid_weeknum_p(y, w, d, f, sg, &nth, &ry, &rw, &rd, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); if (!c_valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); canon24oc(); rjd2 = jd_local_to_utc(rjd, time_to_df(rh, rmin, rs), rof); ret = d_complex_new_internal(klass, nth, rjd2, 0, INT2FIX(0), rof, sg, 0, 0, 0, rh, rmin, rs, HAVE_JD | HAVE_TIME); } add_frac(); return ret; } static VALUE datetime_s_nth_kday(int argc, VALUE *argv, VALUE klass) { VALUE vy, vm, vn, vk, vh, vmin, vs, vof, vsg, y, fr, fr2, ret; int m, n, k, h, min, s, rof; double sg; rb_scan_args(argc, argv, "09", &vy, &vm, &vn, &vk, &vh, &vmin, &vs, &vof, &vsg); y = INT2FIX(-4712); m = 1; n = 1; k = 1; h = min = s = 0; fr2 = INT2FIX(0); rof = 0; sg = DEFAULT_SG; switch (argc) { case 9: val2sg(vsg, sg); case 8: val2off(vof, rof); case 7: num2int_with_frac(s, positive_inf); case 6: num2int_with_frac(min, 6); case 5: num2int_with_frac(h, 5); case 4: num2int_with_frac(k, 4); case 3: n = NUM2INT(vn); case 2: m = NUM2INT(vm); case 1: y = vy; } { VALUE nth; int ry, rm, rn, rk, rh, rmin, rs, rjd, rjd2, ns; if (!valid_nth_kday_p(y, m, n, k, sg, &nth, &ry, &rm, &rn, &rk, &rjd, &ns)) rb_raise(rb_eArgError, "invalid date"); if (!c_valid_time_p(h, min, s, &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); canon24oc(); rjd2 = jd_local_to_utc(rjd, time_to_df(rh, rmin, rs), rof); ret = d_complex_new_internal(klass, nth, rjd2, 0, INT2FIX(0), rof, sg, 0, 0, 0, rh, rmin, rs, HAVE_JD | HAVE_TIME); } add_frac(); return ret; } #endif /* * call-seq: * DateTime.now([start=Date::ITALY]) -> datetime * * Creates a date-time object denoting the present time. * * DateTime.now #=> # */ static VALUE datetime_s_now(int argc, VALUE *argv, VALUE klass) { VALUE vsg, nth, ret; double sg; #ifdef HAVE_CLOCK_GETTIME struct timespec ts; #else struct timeval tv; #endif time_t sec; struct tm tm; long sf, of; int y, ry, m, d, h, min, s; rb_scan_args(argc, argv, "01", &vsg); if (argc < 1) sg = DEFAULT_SG; else sg = NUM2DBL(vsg); #ifdef HAVE_CLOCK_GETTIME if (clock_gettime(CLOCK_REALTIME, &ts) == -1) rb_sys_fail("clock_gettime"); sec = ts.tv_sec; #else if (gettimeofday(&tv, NULL) == -1) rb_sys_fail("gettimeofday"); sec = tv.tv_sec; #endif tzset(); if (!localtime_r(&sec, &tm)) rb_sys_fail("localtime"); y = tm.tm_year + 1900; m = tm.tm_mon + 1; d = tm.tm_mday; h = tm.tm_hour; min = tm.tm_min; s = tm.tm_sec; if (s == 60) s = 59; #ifdef HAVE_STRUCT_TM_TM_GMTOFF of = tm.tm_gmtoff; #elif defined(HAVE_VAR_TIMEZONE) #ifdef HAVE_VAR_ALTZONE of = (long)-((tm.tm_isdst > 0) ? altzone : timezone); #else of = (long)-timezone; if (tm.tm_isdst) { time_t sec2; tm.tm_isdst = 0; sec2 = mktime(&tm); of += (long)difftime(sec2, sec); } #endif #elif defined(HAVE_TIMEGM) { time_t sec2; sec2 = timegm(&tm); of = (long)difftime(sec2, sec); } #else { struct tm tm2; time_t sec2; if (!gmtime_r(&sec, &tm2)) rb_sys_fail("gmtime"); tm2.tm_isdst = tm.tm_isdst; sec2 = mktime(&tm2); of = (long)difftime(sec, sec2); } #endif #ifdef HAVE_CLOCK_GETTIME sf = ts.tv_nsec; #else sf = tv.tv_usec * 1000; #endif if (of < -DAY_IN_SECONDS || of > DAY_IN_SECONDS) { of = 0; rb_warning("invalid offset is ignored"); } decode_year(INT2FIX(y), -1, &nth, &ry); ret = d_complex_new_internal(klass, nth, 0, 0, LONG2NUM(sf), (int)of, GREGORIAN, ry, m, d, h, min, s, HAVE_CIVIL | HAVE_TIME); { get_d1(ret); set_sg(dat, sg); } return ret; } static VALUE dt_new_by_frags(VALUE klass, VALUE hash, VALUE sg) { VALUE jd, sf, t; int df, of; if (!c_valid_start_p(NUM2DBL(sg))) { sg = INT2FIX(DEFAULT_SG); rb_warning("invalid start is ignored"); } if (NIL_P(hash)) rb_raise(rb_eArgError, "invalid date"); if (NIL_P(ref_hash("jd")) && NIL_P(ref_hash("yday")) && !NIL_P(ref_hash("year")) && !NIL_P(ref_hash("mon")) && !NIL_P(ref_hash("mday"))) { jd = rt__valid_civil_p(ref_hash("year"), ref_hash("mon"), ref_hash("mday"), sg); 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_eqeq_p(ref_hash("sec"), INT2FIX(60))) set_hash("sec", INT2FIX(59)); } else { 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"); { int rh, rmin, rs; if (!c_valid_time_p(NUM2INT(ref_hash("hour")), NUM2INT(ref_hash("min")), NUM2INT(ref_hash("sec")), &rh, &rmin, &rs)) rb_raise(rb_eArgError, "invalid date"); df = time_to_df(rh, rmin, rs); } t = ref_hash("sec_fraction"); if (NIL_P(t)) sf = INT2FIX(0); else sf = sec_to_ns(t); t = ref_hash("offset"); if (NIL_P(t)) of = 0; else { of = NUM2INT(t); if (of < -DAY_IN_SECONDS || of > DAY_IN_SECONDS) { of = 0; rb_warning("invalid offset is ignored"); } } { VALUE nth; int rjd, rjd2; decode_jd(jd, &nth, &rjd); rjd2 = jd_local_to_utc(rjd, df, of); df = df_local_to_utc(df, of); return d_complex_new_internal(klass, nth, rjd2, df, sf, of, NUM2DBL(sg), 0, 0, 0, 0, 0, 0, HAVE_JD | HAVE_DF); } } /* * call-seq: * DateTime._strptime(string[, format='%FT%T%z']) -> hash * * Parses the given representation of date and time with the given * template, and returns a hash of parsed elements. _strptime does * not support specification of flags and width unlike strftime. * * See also strptime(3) and strftime. */ static VALUE datetime_s__strptime(int argc, VALUE *argv, VALUE klass) { return date_s__strptime_internal(argc, argv, klass, "%FT%T%z"); } /* * call-seq: * DateTime.strptime([string='-4712-01-01T00:00:00+00:00'[, format='%FT%T%z'[ ,start=ITALY]]]) -> datetime * * Parses the given representation of date and time with the given * template, and creates a date object. strptime does not support * specification of flags and width unlike strftime. * * DateTime.strptime('2001-02-03T04:05:06+07:00', '%Y-%m-%dT%H:%M:%S%z') * #=> # * DateTime.strptime('03-02-2001 04:05:06 PM', '%d-%m-%Y %I:%M:%S %p') * #=> # * DateTime.strptime('2001-W05-6T04:05:06+07:00', '%G-W%V-%uT%H:%M:%S%z') * #=> # * DateTime.strptime('2001 04 6 04 05 06 +7', '%Y %U %w %H %M %S %z') * #=> # * DateTime.strptime('2001 05 6 04 05 06 +7', '%Y %W %u %H %M %S %z') * #=> # * DateTime.strptime('-1', '%s') * #=> # * DateTime.strptime('-1000', '%Q') * #=> # * DateTime.strptime('sat3feb014pm+7', '%a%d%b%y%H%p%z') * #=> # * * See also strptime(3) and strftime. */ 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(DEFAULT_SG); } { VALUE argv2[2], hash; argv2[0] = str; argv2[1] = fmt; hash = date_s__strptime(2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=ITALY]]) -> datetime * * Parses the given representation of date and time, and creates a * date object. This method does not function as a validator. * * If the optional second argument is true and the detected year is in * the range "00" to "99", makes it full. * * DateTime.parse('2001-02-03T04:05:06+07:00') * #=> # * DateTime.parse('20010203T040506+0700') * #=> # * DateTime.parse('3rd Feb 2001 04:05:06 PM') * #=> # */ 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(DEFAULT_SG); } { VALUE argv2[2], hash; argv2[0] = str; argv2[1] = comp; hash = date_s__parse(2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime * * Creates a new Date object by parsing from a string according to * some typical ISO 8601 formats. * * DateTime.iso8601('2001-02-03T04:05:06+07:00') * #=> # * DateTime.iso8601('20010203T040506+0700') * #=> # * DateTime.iso8601('2001-W05-6T04:05:06+07:00') * #=> # */ static VALUE datetime_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-01T00:00:00+00:00"); case 1: sg = INT2FIX(DEFAULT_SG); } { VALUE hash = date_s__iso8601(klass, str); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime * * Creates a new Date object by parsing from a string according to * some typical RFC 3339 formats. * * DateTime.rfc3339('2001-02-03T04:05:06+07:00') * #=> # */ static VALUE datetime_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(DEFAULT_SG); } { VALUE hash = date_s__rfc3339(klass, str); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime * * Creates a new Date object by parsing from a string according to * some typical XML Schema formats. * * DateTime.xmlschema('2001-02-03T04:05:06+07:00') * #=> # */ static VALUE datetime_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-01T00:00:00+00:00"); case 1: sg = INT2FIX(DEFAULT_SG); } { VALUE hash = date_s__xmlschema(klass, str); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=ITALY]) -> datetime * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=ITALY]) -> datetime * * Creates a new Date object by parsing from a string according to * some typical RFC 2822 formats. * * DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700') * #=> # */ static VALUE datetime_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(DEFAULT_SG); } { VALUE hash = date_s__rfc2822(klass, str); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * DateTime.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=ITALY]) -> datetime * * Creates a new Date object by parsing from a string according to * some RFC 2616 format. * * DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT') * #=> # */ static VALUE datetime_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(DEFAULT_SG); } { VALUE hash = date_s__httpdate(klass, str); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=ITALY]) -> datetime * * Creates a new Date object by parsing from a string according to * some typical JIS X 0301 formats. * * DateTime.jisx0301('H13.02.03T04:05:06+07:00') * #=> # */ static VALUE datetime_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-01T00:00:00+00:00"); case 1: sg = INT2FIX(DEFAULT_SG); } { VALUE hash = date_s__jisx0301(klass, str); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: * dt.to_s -> string * * Returns a string in an ISO 8601 format (This method doesn't use the * expanded representations). * * DateTime.new(2001,2,3,4,5,6,'-7').to_s * #=> "2001-02-03T04:05:06-07:00" */ static VALUE dt_lite_to_s(VALUE self) { return strftimev("%Y-%m-%dT%H:%M:%S%:z", self, set_tmx); } /* * call-seq: * dt.strftime([format='%FT%T%:z']) -> string * * Formats date according to the directives in the given format * string. * The directives begins with a percent (%) character. * Any text not listed as a directive will be passed through to the * output string. * * The directive consists of a percent (%) character, * zero or more flags, optional minimum field width, * optional modifier and a conversion specifier * as follows. * * % * * Flags: * - don't pad a numerical output. * _ use spaces for padding. * 0 use zeros for padding. * ^ upcase the result string. * # change case. * : use colons for %z. * * The minimum field width specifies the minimum width. * * The modifier is "E" and "O". * They are ignored. * * Format directives: * * Date (Year, Month, Day): * %Y - Year with century (can be negative, 4 digits at least) * -0001, 0000, 1995, 2009, 14292, etc. * %C - year / 100 (round down. 20 in 2009) * %y - year % 100 (00..99) * * %m - Month of the year, zero-padded (01..12) * %_m blank-padded ( 1..12) * %-m no-padded (1..12) * %B - The full month name (``January'') * %^B uppercased (``JANUARY'') * %b - The abbreviated month name (``Jan'') * %^b uppercased (``JAN'') * %h - Equivalent to %b * * %d - Day of the month, zero-padded (01..31) * %-d no-padded (1..31) * %e - Day of the month, blank-padded ( 1..31) * * %j - Day of the year (001..366) * * Time (Hour, Minute, Second, Subsecond): * %H - Hour of the day, 24-hour clock, zero-padded (00..23) * %k - Hour of the day, 24-hour clock, blank-padded ( 0..23) * %I - Hour of the day, 12-hour clock, zero-padded (01..12) * %l - Hour of the day, 12-hour clock, blank-padded ( 1..12) * %P - Meridian indicator, lowercase (``am'' or ``pm'') * %p - Meridian indicator, uppercase (``AM'' or ``PM'') * * %M - Minute of the hour (00..59) * * %S - Second of the minute (00..59) * * %L - Millisecond of the second (000..999) * %N - Fractional seconds digits, default is 9 digits (nanosecond) * %3N millisecond (3 digits) %15N femtosecond (15 digits) * %6N microsecond (6 digits) %18N attosecond (18 digits) * %9N nanosecond (9 digits) %21N zeptosecond (21 digits) * %12N picosecond (12 digits) %24N yoctosecond (24 digits) * * Time zone: * %z - Time zone as hour and minute offset from UTC (e.g. +0900) * %:z - hour and minute offset from UTC with a colon (e.g. +09:00) * %::z - hour, minute and second offset from UTC (e.g. +09:00:00) * %:::z - hour, minute and second offset from UTC * (e.g. +09, +09:30, +09:30:30) * %Z - Time zone abbreviation name or something similar information. * * Weekday: * %A - The full weekday name (``Sunday'') * %^A uppercased (``SUNDAY'') * %a - The abbreviated name (``Sun'') * %^a uppercased (``SUN'') * %u - Day of the week (Monday is 1, 1..7) * %w - Day of the week (Sunday is 0, 0..6) * * ISO 8601 week-based year and week number: * The week 1 of YYYY starts with a Monday and includes YYYY-01-04. * The days in the year before the first week are in the last week of * the previous year. * %G - The week-based year * %g - The last 2 digits of the week-based year (00..99) * %V - Week number of the week-based year (01..53) * * Week number: * The week 1 of YYYY starts with a Sunday or Monday (according to %U * or %W). The days in the year before the first week are in week 0. * %U - Week number of the year. The week starts with Sunday. (00..53) * %W - Week number of the year. The week starts with Monday. (00..53) * * Seconds since the Unix Epoch: * %s - Number of seconds since 1970-01-01 00:00:00 UTC. * %Q - Number of milliseconds since 1970-01-01 00:00:00 UTC. * * Literal string: * %n - Newline character (\n) * %t - Tab character (\t) * %% - Literal ``%'' character * * Combination: * %c - date and time (%a %b %e %T %Y) * %D - Date (%m/%d/%y) * %F - The ISO 8601 date format (%Y-%m-%d) * %v - VMS date (%e-%b-%Y) * %x - Same as %D * %X - Same as %T * %r - 12-hour time (%I:%M:%S %p) * %R - 24-hour time (%H:%M) * %T - 24-hour time (%H:%M:%S) * %+ - date(1) (%a %b %e %H:%M:%S %Z %Y) * * This method is similar to strftime() function defined in ISO C and POSIX. * Several directives (%a, %A, %b, %B, %c, %p, %r, %x, %X, %E*, %O* and %Z) * are locale dependent in the function. * However this method is locale independent. * So, the result may differ even if a same format string is used in other * systems such as C. * It is good practice to avoid %x and %X because there are corresponding * locale independent representations, %D and %T. * * Examples: * * d = DateTime.new(2007,11,19,8,37,48,"-06:00") * #=> # * d.strftime("Printed on %m/%d/%Y") #=> "Printed on 11/19/2007" * d.strftime("at %I:%M%p") #=> "at 08:37AM" * * Various ISO 8601 formats: * %Y%m%d => 20071119 Calendar date (basic) * %F => 2007-11-19 Calendar date (extended) * %Y-%m => 2007-11 Calendar date, reduced accuracy, specific month * %Y => 2007 Calendar date, reduced accuracy, specific year * %C => 20 Calendar date, reduced accuracy, specific century * %Y%j => 2007323 Ordinal date (basic) * %Y-%j => 2007-323 Ordinal date (extended) * %GW%V%u => 2007W471 Week date (basic) * %G-W%V-%u => 2007-W47-1 Week date (extended) * %GW%V => 2007W47 Week date, reduced accuracy, specific week (basic) * %G-W%V => 2007-W47 Week date, reduced accuracy, specific week (extended) * %H%M%S => 083748 Local time (basic) * %T => 08:37:48 Local time (extended) * %H%M => 0837 Local time, reduced accuracy, specific minute (basic) * %H:%M => 08:37 Local time, reduced accuracy, specific minute (extended) * %H => 08 Local time, reduced accuracy, specific hour * %H%M%S,%L => 083748,000 Local time with decimal fraction, comma as decimal sign (basic) * %T,%L => 08:37:48,000 Local time with decimal fraction, comma as decimal sign (extended) * %H%M%S.%L => 083748.000 Local time with decimal fraction, full stop as decimal sign (basic) * %T.%L => 08:37:48.000 Local time with decimal fraction, full stop as decimal sign (extended) * %H%M%S%z => 083748-0600 Local time and the difference from UTC (basic) * %T%:z => 08:37:48-06:00 Local time and the difference from UTC (extended) * %Y%m%dT%H%M%S%z => 20071119T083748-0600 Date and time of day for calendar date (basic) * %FT%T%:z => 2007-11-19T08:37:48-06:00 Date and time of day for calendar date (extended) * %Y%jT%H%M%S%z => 2007323T083748-0600 Date and time of day for ordinal date (basic) * %Y-%jT%T%:z => 2007-323T08:37:48-06:00 Date and time of day for ordinal date (extended) * %GW%V%uT%H%M%S%z => 2007W471T083748-0600 Date and time of day for week date (basic) * %G-W%V-%uT%T%:z => 2007-W47-1T08:37:48-06:00 Date and time of day for week date (extended) * %Y%m%dT%H%M => 20071119T0837 Calendar date and local time (basic) * %FT%R => 2007-11-19T08:37 Calendar date and local time (extended) * %Y%jT%H%MZ => 2007323T0837Z Ordinal date and UTC of day (basic) * %Y-%jT%RZ => 2007-323T08:37Z Ordinal date and UTC of day (extended) * %GW%V%uT%H%M%z => 2007W471T0837-0600 Week date and local time and difference from UTC (basic) * %G-W%V-%uT%R%:z => 2007-W47-1T08:37-06:00 Week date and local time and difference from UTC (extended) * * See also strftime(3) and strptime. */ static VALUE dt_lite_strftime(int argc, VALUE *argv, VALUE self) { return date_strftime_internal(argc, argv, self, "%Y-%m-%dT%H:%M:%S%:z", set_tmx); } static VALUE iso8601_timediv(VALUE self, long n) { static const char timefmt[] = "T%H:%M:%S"; static const char zone[] = "%:z"; char fmt[sizeof(timefmt) + sizeof(zone) + rb_strlen_lit(".%N") + DECIMAL_SIZE_OF_LONG]; char *p = fmt; memcpy(p, timefmt, sizeof(timefmt)-1); p += sizeof(timefmt)-1; if (n > 0) p += snprintf(p, fmt+sizeof(fmt)-p, ".%%%ldN", n); memcpy(p, zone, sizeof(zone)); return strftimev(fmt, self, set_tmx); } /* * call-seq: * dt.iso8601([n=0]) -> string * dt.xmlschema([n=0]) -> string * * This method is equivalent to strftime('%FT%T'). The optional * argument n is length of fractional seconds. * * DateTime.parse('2001-02-03T04:05:06.123456789+07:00').iso8601(9) * #=> "2001-02-03T04:05:06.123456789+07:00" */ static VALUE dt_lite_iso8601(int argc, VALUE *argv, VALUE self) { long n = 0; rb_check_arity(argc, 0, 1); if (argc >= 1) n = NUM2LONG(argv[0]); return rb_str_append(strftimev("%Y-%m-%d", self, set_tmx), iso8601_timediv(self, n)); } /* * call-seq: * dt.rfc3339([n=0]) -> string * * This method is equivalent to strftime('%FT%T'). The optional * argument n is length of fractional seconds. * * DateTime.parse('2001-02-03T04:05:06.123456789+07:00').rfc3339(9) * #=> "2001-02-03T04:05:06.123456789+07:00" */ static VALUE dt_lite_rfc3339(int argc, VALUE *argv, VALUE self) { return dt_lite_iso8601(argc, argv, self); } /* * call-seq: * dt.jisx0301([n=0]) -> string * * Returns a string in a JIS X 0301 format. The optional argument n * is length of fractional seconds. * * DateTime.parse('2001-02-03T04:05:06.123456789+07:00').jisx0301(9) * #=> "H13.02.03T04:05:06.123456789+07:00" */ static VALUE dt_lite_jisx0301(int argc, VALUE *argv, VALUE self) { long n = 0; rb_check_arity(argc, 0, 1); if (argc >= 1) n = NUM2LONG(argv[0]); return rb_str_append(d_lite_jisx0301(self), iso8601_timediv(self, n)); } /* 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) /* * call-seq: * t.to_time -> time * * Returns self. */ static VALUE time_to_time(VALUE self) { return self; } /* * call-seq: * t.to_date -> date * * Returns a Date object which denotes self. */ static VALUE time_to_date(VALUE self) { VALUE y, nth, ret; int ry, m, d; y = f_year(self); m = FIX2INT(f_mon(self)); d = FIX2INT(f_mday(self)); decode_year(y, -1, &nth, &ry); ret = d_simple_new_internal(cDate, nth, 0, GREGORIAN, ry, m, d, HAVE_CIVIL); { get_d1(ret); set_sg(dat, DEFAULT_SG); } return ret; } /* * call-seq: * t.to_datetime -> datetime * * Returns a DateTime object which denotes self. */ static VALUE time_to_datetime(VALUE self) { VALUE y, sf, nth, ret; int ry, m, d, h, min, s, of; y = f_year(self); m = FIX2INT(f_mon(self)); d = FIX2INT(f_mday(self)); h = FIX2INT(f_hour(self)); min = FIX2INT(f_min(self)); s = FIX2INT(f_sec(self)); if (s == 60) s = 59; sf = sec_to_ns(f_subsec(self)); of = FIX2INT(f_utc_offset(self)); decode_year(y, -1, &nth, &ry); ret = d_complex_new_internal(cDateTime, nth, 0, 0, sf, of, DEFAULT_SG, ry, m, d, h, min, s, HAVE_CIVIL | HAVE_TIME); { get_d1(ret); set_sg(dat, DEFAULT_SG); } return ret; } /* * call-seq: * d.to_time -> time * * Returns a Time object which denotes self. */ static VALUE date_to_time(VALUE self) { get_d1(self); return f_local3(rb_cTime, m_real_year(dat), INT2FIX(m_mon(dat)), INT2FIX(m_mday(dat))); } /* * call-seq: * d.to_date -> self * * Returns self; */ static VALUE date_to_date(VALUE self) { return self; } /* * call-seq: * d.to_datetime -> datetime * * Returns a DateTime object which denotes self. */ static VALUE date_to_datetime(VALUE self) { get_d1a(self); if (simple_dat_p(adat)) { VALUE new = d_lite_s_alloc_simple(cDateTime); { get_d1b(new); bdat->s = adat->s; return new; } } else { VALUE new = d_lite_s_alloc_complex(cDateTime); { get_d1b(new); bdat->c = adat->c; bdat->c.df = 0; RB_OBJ_WRITE(new, &bdat->c.sf, INT2FIX(0)); #ifndef USE_PACK bdat->c.hour = 0; bdat->c.min = 0; bdat->c.sec = 0; #else bdat->c.pc = PACK5(EX_MON(adat->c.pc), EX_MDAY(adat->c.pc), 0, 0, 0); bdat->c.flags |= HAVE_DF | HAVE_TIME; #endif return new; } } } /* * call-seq: * dt.to_time -> time * * Returns a Time object which denotes self. */ static VALUE datetime_to_time(VALUE self) { volatile VALUE dup = dup_obj(self); { VALUE t; get_d1(dup); t = rb_funcall(rb_cTime, rb_intern("new"), 7, m_real_year(dat), INT2FIX(m_mon(dat)), INT2FIX(m_mday(dat)), INT2FIX(m_hour(dat)), INT2FIX(m_min(dat)), f_add(INT2FIX(m_sec(dat)), m_sf_in_sec(dat)), INT2FIX(m_of(dat))); return t; } } /* * call-seq: * dt.to_date -> date * * Returns a Date object which denotes self. */ static VALUE datetime_to_date(VALUE self) { get_d1a(self); if (simple_dat_p(adat)) { VALUE new = d_lite_s_alloc_simple(cDate); { get_d1b(new); bdat->s = adat->s; bdat->s.jd = m_local_jd(adat); return new; } } else { VALUE new = d_lite_s_alloc_simple(cDate); { get_d1b(new); copy_complex_to_simple(new, &bdat->s, &adat->c) bdat->s.jd = m_local_jd(adat); bdat->s.flags &= ~(HAVE_DF | HAVE_TIME | COMPLEX_DAT); return new; } } } /* * call-seq: * dt.to_datetime -> self * * Returns self. */ static VALUE datetime_to_datetime(VALUE self) { return self; } #ifndef NDEBUG /* tests */ #define MIN_YEAR -4713 #define MAX_YEAR 1000000 #define MIN_JD -327 #define MAX_JD 366963925 static int test_civil(int from, int to, double sg) { int j; fprintf(stderr, "test_civil: %d...%d (%d) - %.0f\n", from, to, to - from, sg); for (j = from; j <= to; j++) { int y, m, d, rj, ns; c_jd_to_civil(j, sg, &y, &m, &d); c_civil_to_jd(y, m, d, sg, &rj, &ns); if (j != rj) { fprintf(stderr, "%d != %d\n", j, rj); return 0; } } return 1; } static VALUE date_s_test_civil(VALUE klass) { if (!test_civil(MIN_JD, MIN_JD + 366, GREGORIAN)) return Qfalse; if (!test_civil(2305814, 2598007, GREGORIAN)) return Qfalse; if (!test_civil(MAX_JD - 366, MAX_JD, GREGORIAN)) return Qfalse; if (!test_civil(MIN_JD, MIN_JD + 366, ITALY)) return Qfalse; if (!test_civil(2305814, 2598007, ITALY)) return Qfalse; if (!test_civil(MAX_JD - 366, MAX_JD, ITALY)) return Qfalse; return Qtrue; } static int test_ordinal(int from, int to, double sg) { int j; fprintf(stderr, "test_ordinal: %d...%d (%d) - %.0f\n", from, to, to - from, sg); for (j = from; j <= to; j++) { int y, d, rj, ns; c_jd_to_ordinal(j, sg, &y, &d); c_ordinal_to_jd(y, d, sg, &rj, &ns); if (j != rj) { fprintf(stderr, "%d != %d\n", j, rj); return 0; } } return 1; } static VALUE date_s_test_ordinal(VALUE klass) { if (!test_ordinal(MIN_JD, MIN_JD + 366, GREGORIAN)) return Qfalse; if (!test_ordinal(2305814, 2598007, GREGORIAN)) return Qfalse; if (!test_ordinal(MAX_JD - 366, MAX_JD, GREGORIAN)) return Qfalse; if (!test_ordinal(MIN_JD, MIN_JD + 366, ITALY)) return Qfalse; if (!test_ordinal(2305814, 2598007, ITALY)) return Qfalse; if (!test_ordinal(MAX_JD - 366, MAX_JD, ITALY)) return Qfalse; return Qtrue; } static int test_commercial(int from, int to, double sg) { int j; fprintf(stderr, "test_commercial: %d...%d (%d) - %.0f\n", from, to, to - from, sg); for (j = from; j <= to; j++) { int y, w, d, rj, ns; c_jd_to_commercial(j, sg, &y, &w, &d); c_commercial_to_jd(y, w, d, sg, &rj, &ns); if (j != rj) { fprintf(stderr, "%d != %d\n", j, rj); return 0; } } return 1; } static VALUE date_s_test_commercial(VALUE klass) { if (!test_commercial(MIN_JD, MIN_JD + 366, GREGORIAN)) return Qfalse; if (!test_commercial(2305814, 2598007, GREGORIAN)) return Qfalse; if (!test_commercial(MAX_JD - 366, MAX_JD, GREGORIAN)) return Qfalse; if (!test_commercial(MIN_JD, MIN_JD + 366, ITALY)) return Qfalse; if (!test_commercial(2305814, 2598007, ITALY)) return Qfalse; if (!test_commercial(MAX_JD - 366, MAX_JD, ITALY)) return Qfalse; return Qtrue; } static int test_weeknum(int from, int to, int f, double sg) { int j; fprintf(stderr, "test_weeknum: %d...%d (%d) - %.0f\n", from, to, to - from, sg); for (j = from; j <= to; j++) { int y, w, d, rj, ns; c_jd_to_weeknum(j, f, sg, &y, &w, &d); c_weeknum_to_jd(y, w, d, f, sg, &rj, &ns); if (j != rj) { fprintf(stderr, "%d != %d\n", j, rj); return 0; } } return 1; } static VALUE date_s_test_weeknum(VALUE klass) { int f; for (f = 0; f <= 1; f++) { if (!test_weeknum(MIN_JD, MIN_JD + 366, f, GREGORIAN)) return Qfalse; if (!test_weeknum(2305814, 2598007, f, GREGORIAN)) return Qfalse; if (!test_weeknum(MAX_JD - 366, MAX_JD, f, GREGORIAN)) return Qfalse; if (!test_weeknum(MIN_JD, MIN_JD + 366, f, ITALY)) return Qfalse; if (!test_weeknum(2305814, 2598007, f, ITALY)) return Qfalse; if (!test_weeknum(MAX_JD - 366, MAX_JD, f, ITALY)) return Qfalse; } return Qtrue; } static int test_nth_kday(int from, int to, double sg) { int j; fprintf(stderr, "test_nth_kday: %d...%d (%d) - %.0f\n", from, to, to - from, sg); for (j = from; j <= to; j++) { int y, m, n, k, rj, ns; c_jd_to_nth_kday(j, sg, &y, &m, &n, &k); c_nth_kday_to_jd(y, m, n, k, sg, &rj, &ns); if (j != rj) { fprintf(stderr, "%d != %d\n", j, rj); return 0; } } return 1; } static VALUE date_s_test_nth_kday(VALUE klass) { if (!test_nth_kday(MIN_JD, MIN_JD + 366, GREGORIAN)) return Qfalse; if (!test_nth_kday(2305814, 2598007, GREGORIAN)) return Qfalse; if (!test_nth_kday(MAX_JD - 366, MAX_JD, GREGORIAN)) return Qfalse; if (!test_nth_kday(MIN_JD, MIN_JD + 366, ITALY)) return Qfalse; if (!test_nth_kday(2305814, 2598007, ITALY)) return Qfalse; if (!test_nth_kday(MAX_JD - 366, MAX_JD, ITALY)) return Qfalse; return Qtrue; } static int test_unit_v2v(VALUE i, VALUE (* conv1)(VALUE), VALUE (* conv2)(VALUE)) { VALUE c, o; c = (*conv1)(i); o = (*conv2)(c); return f_eqeq_p(o, i); } static int test_unit_v2v_iter2(VALUE (* conv1)(VALUE), VALUE (* conv2)(VALUE)) { if (!test_unit_v2v(INT2FIX(0), conv1, conv2)) return 0; if (!test_unit_v2v(INT2FIX(1), conv1, conv2)) return 0; if (!test_unit_v2v(INT2FIX(2), conv1, conv2)) return 0; if (!test_unit_v2v(INT2FIX(3), conv1, conv2)) return 0; if (!test_unit_v2v(INT2FIX(11), conv1, conv2)) return 0; if (!test_unit_v2v(INT2FIX(65535), conv1, conv2)) return 0; if (!test_unit_v2v(INT2FIX(1073741823), conv1, conv2)) return 0; if (!test_unit_v2v(INT2NUM(1073741824), conv1, conv2)) return 0; if (!test_unit_v2v(rb_rational_new2(INT2FIX(0), INT2FIX(1)), conv1, conv2)) return 0; if (!test_unit_v2v(rb_rational_new2(INT2FIX(1), INT2FIX(1)), conv1, conv2)) return 0; if (!test_unit_v2v(rb_rational_new2(INT2FIX(1), INT2FIX(2)), conv1, conv2)) return 0; if (!test_unit_v2v(rb_rational_new2(INT2FIX(2), INT2FIX(3)), conv1, conv2)) return 0; return 1; } static int test_unit_v2v_iter(VALUE (* conv1)(VALUE), VALUE (* conv2)(VALUE)) { if (!test_unit_v2v_iter2(conv1, conv2)) return 0; if (!test_unit_v2v_iter2(conv2, conv1)) return 0; return 1; } static VALUE date_s_test_unit_conv(VALUE klass) { if (!test_unit_v2v_iter(sec_to_day, day_to_sec)) return Qfalse; if (!test_unit_v2v_iter(ms_to_sec, sec_to_ms)) return Qfalse; if (!test_unit_v2v_iter(ns_to_day, day_to_ns)) return Qfalse; if (!test_unit_v2v_iter(ns_to_sec, sec_to_ns)) return Qfalse; return Qtrue; } static VALUE date_s_test_all(VALUE klass) { if (date_s_test_civil(klass) == Qfalse) return Qfalse; if (date_s_test_ordinal(klass) == Qfalse) return Qfalse; if (date_s_test_commercial(klass) == Qfalse) return Qfalse; if (date_s_test_weeknum(klass) == Qfalse) return Qfalse; if (date_s_test_nth_kday(klass) == Qfalse) return Qfalse; if (date_s_test_unit_conv(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_usascii_str_new2(a[i]); rb_obj_freeze(e); } rb_ary_push(o, e); } rb_obj_freeze(o); return o; } void Init_date_core(void) { #undef rb_intern #define rb_intern(str) rb_intern_const(str) assert(fprintf(stderr, "assert() is now active\n")); id_cmp = rb_intern("<=>"); id_le_p = rb_intern("<="); id_ge_p = rb_intern(">="); id_eqeq_p = rb_intern("=="); half_days_in_day = rb_rational_new2(INT2FIX(1), INT2FIX(2)); #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(half_days_in_day); rb_gc_register_mark_object(day_in_nanoseconds); positive_inf = +INFINITY; negative_inf = -INFINITY; /* * date and datetime class - Tadayoshi Funaba 1998-2011 * * 'date' provides two classes Date and DateTime. * * == Terms and definitions * * Some terms and definitions are based on ISO 8601 and JIS X 0301. * * === calendar date * * The calendar date is a particular day of a calendar year, * identified by its ordinal number within a calendar month within * that year. * * In those classes, this is so-called "civil". * * === ordinal date * * The ordinal date is a particular day of a calendar year identified * by its ordinal number within the year. * * In those classes, this is so-called "ordinal". * * === week date * * The week date is a date identified by calendar week and day numbers. * * The calendar week is a seven day period within a calendar year, * starting on a Monday and identified by its ordinal number within * the year; the first calendar week of the year is the one that * includes the first Thursday of that year. In the Gregorian * calendar, this is equivalent to the week which includes January 4. * * In those classes, this so-called "commercial". * * === julian day number * * The Julian day number is in elapsed days since noon (Greenwich mean * time) on January 1, 4713 BCE (in the Julian calendar). * * In this document, the astronomical Julian day number is same as the * original Julian day number. And the chronological Julian day * number is a variation of the Julian day number. Its days begin at * midnight on local time. * * In this document, when the term "Julian day number" simply appears, * it just refers to "chronological Julian day number", not the * original. * * In those classes, those are so-called "ajd" and "jd". * * === modified julian day number * * The modified Julian day number is in elapsed days since midnight * (Coordinated universal time) on November 17, 1858 CE (in the * Gregorian calendar). * * In this document, the astronomical modified Julian day number is * same as the original modified Julian day number. And the * chronological modified Julian day number is a variation of the * modified Julian day number. Its days begin at midnight on local * time. * * In this document, when the term "modified Julian day number" simply * appears, it just refers to "chronological modified Julian day * number", not the original. * * In those classes, this is so-called "mjd". * * == Date * * A subclass of Object that includes Comparable module and easily handles * date. * * Date object is created with Date::new, Date::jd, Date::ordinal, * Date::commercial, Date::parse, Date::strptime, Date::today, * Time#to_date or etc. * * require 'date' * * Date.new(2001,2,3) * #=> # * Date.jd(2451944) * #=> # * Date.ordinal(2001,34) * #=> # * Date.commercial(2001,5,6) * #=> # * Date.parse('2001-02-03') * #=> # * Date.strptime('03-02-2001', '%d-%m-%Y') * #=> # * Time.new(2001,2,3).to_date * #=> # * * All date objects are immutable; hence cannot modify themselves. * * The concept of this date object can be represented as a tuple * of the day count, the offset and the day of calendar reform. * * The day count denotes the absolute position of a temporal * dimension. The offset is relative adjustment, which determines * decoded local time with the day count. The day of calendar * reform denotes the start day of the new style. The old style * of the West is the Julian calendar which was adopted by * Caesar. The new style is the Gregorian calendar, which is the * current civil calendar of many countries. * * The day count is virtually the astronomical Julian day number. * The offset in this class is usually zero, and cannot be * specified directly. * * An optional argument the day of calendar reform (start) as a * Julian day number, which should be 2298874 to 2426355 or -/+oo. * The default value is Date::ITALY (2299161=1582-10-15). See * also sample/cal.rb. * * $ ruby sample/cal.rb -c it 10 1582 * October 1582 * S M Tu W Th F S * 1 2 3 4 15 16 * 17 18 19 20 21 22 23 * 24 25 26 27 28 29 30 * 31 * * $ ruby sample/cal.rb -c gb 9 1752 * September 1752 * S M Tu W Th F S * 1 2 14 15 16 * 17 18 19 20 21 22 23 * 24 25 26 27 28 29 30 * * Date object has various methods. See each reference. * * d = Date.parse('3rd Feb 2001') * #=> # * d.year #=> 2001 * d.mon #=> 2 * d.mday #=> 3 * d.wday #=> 6 * d += 1 #=> # * d.strftime('%a %d %b %Y') #=> "Sun 04 Feb 2001" * */ cDate = rb_define_class("Date", rb_cObject); rb_include_module(cDate, rb_mComparable); /* An array of strings of full month names in English. The first * element is nil. */ rb_define_const(cDate, "MONTHNAMES", mk_ary_of_str(13, monthnames)); /* An array of strings of abbreviated month names in English. The * first element is nil. */ rb_define_const(cDate, "ABBR_MONTHNAMES", mk_ary_of_str(13, abbr_monthnames)); /* An array of strings of the full names of days of the week in English. * The first is "Sunday". */ rb_define_const(cDate, "DAYNAMES", mk_ary_of_str(7, daynames)); /* An array of strings of abbreviated day names in English. The * first is "Sun". */ 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 some 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)); /* The Julian day number of the day of calendar reform for the * proleptic Julian calendar */ rb_define_const(cDate, "JULIAN", DBL2NUM(JULIAN)); /* The Julian day number of the day of calendar reform for the * proleptic Gregorian calendar */ rb_define_const(cDate, "GREGORIAN", DBL2NUM(GREGORIAN)); rb_define_alloc_func(cDate, d_lite_s_alloc); #ifndef NDEBUG #define de_define_private_method rb_define_private_method de_define_private_method(CLASS_OF(cDate), "_valid_jd?", date_s__valid_jd_p, -1); de_define_private_method(CLASS_OF(cDate), "_valid_ordinal?", date_s__valid_ordinal_p, -1); de_define_private_method(CLASS_OF(cDate), "_valid_civil?", date_s__valid_civil_p, -1); de_define_private_method(CLASS_OF(cDate), "_valid_date?", date_s__valid_civil_p, -1); de_define_private_method(CLASS_OF(cDate), "_valid_commercial?", date_s__valid_commercial_p, -1); de_define_private_method(CLASS_OF(cDate), "_valid_weeknum?", date_s__valid_weeknum_p, -1); de_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); rb_define_singleton_method(cDate, "valid_ordinal?", date_s_valid_ordinal_p, -1); rb_define_singleton_method(cDate, "valid_civil?", date_s_valid_civil_p, -1); rb_define_singleton_method(cDate, "valid_date?", date_s_valid_civil_p, -1); rb_define_singleton_method(cDate, "valid_commercial?", date_s_valid_commercial_p, -1); #ifndef NDEBUG de_define_private_method(CLASS_OF(cDate), "valid_weeknum?", date_s_valid_weeknum_p, -1); de_define_private_method(CLASS_OF(cDate), "valid_nth_kday?", date_s_valid_nth_kday_p, -1); de_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); #ifndef NDEBUG #define de_define_singleton_method rb_define_singleton_method #define de_define_alias rb_define_alias de_define_singleton_method(cDate, "new!", date_s_new_bang, -1); de_define_alias(rb_singleton_class(cDate), "new_l!", "new"); #endif 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 de_define_singleton_method(cDate, "weeknum", date_s_weeknum, -1); de_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); #ifndef NDEBUG #define de_define_method rb_define_method de_define_method(cDate, "initialize", d_lite_initialize, -1); #endif rb_define_method(cDate, "initialize_copy", d_lite_initialize_copy, 1); #ifndef NDEBUG de_define_method(cDate, "fill", d_lite_fill, 0); #endif rb_define_method(cDate, "ajd", d_lite_ajd, 0); rb_define_method(cDate, "amjd", d_lite_amjd, 0); rb_define_method(cDate, "jd", d_lite_jd, 0); rb_define_method(cDate, "mjd", d_lite_mjd, 0); rb_define_method(cDate, "ld", d_lite_ld, 0); rb_define_method(cDate, "year", d_lite_year, 0); rb_define_method(cDate, "yday", d_lite_yday, 0); rb_define_method(cDate, "mon", d_lite_mon, 0); rb_define_method(cDate, "month", d_lite_mon, 0); rb_define_method(cDate, "mday", d_lite_mday, 0); rb_define_method(cDate, "day", d_lite_mday, 0); rb_define_method(cDate, "day_fraction", d_lite_day_fraction, 0); rb_define_method(cDate, "cwyear", d_lite_cwyear, 0); rb_define_method(cDate, "cweek", d_lite_cweek, 0); rb_define_method(cDate, "cwday", d_lite_cwday, 0); #ifndef NDEBUG de_define_private_method(cDate, "wnum0", d_lite_wnum0, 0); de_define_private_method(cDate, "wnum1", d_lite_wnum1, 0); #endif 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 de_define_method(cDate, "nth_kday?", d_lite_nth_kday_p, 2); #endif rb_define_private_method(cDate, "hour", d_lite_hour, 0); rb_define_private_method(cDate, "min", d_lite_min, 0); rb_define_private_method(cDate, "minute", d_lite_min, 0); rb_define_private_method(cDate, "sec", d_lite_sec, 0); rb_define_private_method(cDate, "second", d_lite_sec, 0); rb_define_private_method(cDate, "sec_fraction", d_lite_sec_fraction, 0); rb_define_private_method(cDate, "second_fraction", d_lite_sec_fraction, 0); rb_define_private_method(cDate, "offset", d_lite_offset, 0); rb_define_private_method(cDate, "zone", d_lite_zone, 0); 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", d_lite_step, -1); rb_define_method(cDate, "upto", d_lite_upto, 1); rb_define_method(cDate, "downto", d_lite_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); rb_define_method(cDate, "hash", d_lite_hash, 0); rb_define_method(cDate, "to_s", d_lite_to_s, 0); #ifndef NDEBUG de_define_method(cDate, "inspect_raw", d_lite_inspect_raw, 0); #endif 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); #ifndef NDEBUG de_define_method(cDate, "marshal_dump_old", d_lite_marshal_dump_old, 0); #endif rb_define_method(cDate, "marshal_dump", d_lite_marshal_dump, 0); rb_define_method(cDate, "marshal_load", d_lite_marshal_load, 1); rb_define_singleton_method(cDate, "_load", date_s__load, 1); /* :markup: Markdown ## DateTime A subclass of Date that easily handles date, hour, minute, second and offset. DateTime does not consider any leap seconds, does not track any summer time rules. DateTime object is created with DateTime::new, DateTime::jd, DateTime::ordinal, DateTime::commercial, DateTime::parse, DateTime::strptime, DateTime::now, Time#to_datetime or etc. require 'date' DateTime.new(2001,2,3,4,5,6) #=> # The last element of day, hour, minute or second can be fractional number. The fractional number's precision is assumed at most nanosecond. DateTime.new(2001,2,3.5) #=> # An optional argument the offset indicates the difference between the local time and UTC. For example, `Rational(3,24)` represents ahead of 3 hours of UTC, `Rational(-5,24)` represents behind of 5 hours of UTC. The offset should be -1 to +1, and its precision is assumed at most second. The default value is zero(equals to UTC). DateTime.new(2001,2,3,4,5,6,Rational(3,24)) #=> # also accepts string form. DateTime.new(2001,2,3,4,5,6,'+03:00') #=> # An optional argument the day of calendar reform (start) denotes a Julian day number, which should be 2298874 to 2426355 or -/+oo. The default value is `Date::ITALY` (2299161=1582-10-15). DateTime object has various methods. See each reference. d = DateTime.parse('3rd Feb 2001 04:05:06+03:30') #=> # d.hour #=> 4 d.min #=> 5 d.sec #=> 6 d.offset #=> (7/48) d.zone #=> "+03:30" d += Rational('1.5') #=> # d = d.new_offset('+09:00') #=> # d.strftime('%I:%M:%S %p') #=> "09:35:06 PM" d > DateTime.new(1999) #=> true ### When should you use DateTime and when should you use Time? It's a common misconception that [William Shakespeare][1] and [Miguel de Cervantes][2] died on the same day in history - so much so that UNESCO named April 23 as [World Book Day because of this fact][3]. However because England hadn't yet adopted [Gregorian Calendar Reform][4] (and wouldn't until [1752][5]) their deaths are actually 10 days apart. Since Ruby's `Time` class implements a [proleptic Gregorian calendar][6] and has no concept of calendar reform then there's no way to express this. This is where `DateTime` steps in: ``` irb >> shakespeare = DateTime.iso8601('1616-04-23', Date::ENGLAND) => Tue, 23 Apr 1616 00:00:00 +0000 >> cervantes = DateTime.iso8601('1616-04-23', Date::ITALY) => Sat, 23 Apr 1616 00:00:00 +0000 ``` Already you can see something's weird - the days of the week are different, taking this further: ``` irb >> cervantes == shakespeare => false >> (shakespeare - cervantes).to_i => 10 ``` This shows that in fact they died 10 days apart (in reality 11 days since Cervantes died a day earlier but was buried on the 23rd). We can see the actual date of Shakespeare's death by using the `gregorian` method to convert it: ``` irb >> shakespeare.gregorian => Tue, 03 May 1616 00:00:00 +0000 ``` So there's an argument that all the celebrations that take place on the 23rd April in Stratford-upon-Avon are actually the wrong date since England is now using the Gregorian calendar. You can see why when we transition across the reform date boundary: ``` irb # start off with the anniversary of Shakespeare's birth in 1751 >> shakespeare = DateTime.iso8601('1751-04-23', Date::ENGLAND) => Tue, 23 Apr 1751 00:00:00 +0000 # add 366 days since 1752 is a leap year and April 23 is after February 29 >> shakespeare + 366 => Thu, 23 Apr 1752 00:00:00 +0000 # add another 365 days to take us to the anniversary in 1753 >> shakespeare + 366 + 365 => Fri, 04 May 1753 00:00:00 +0000 ``` As you can see, if we're accurately tracking the number of [solar years][9] since Shakespeare's birthday then the correct anniversary date would be the 4th May and not the 23rd April. So when should you use `DateTime` in Ruby and when should you use `Time`? Almost certainly you'll want to use `Time` since your app is probably dealing with current dates and times. However, if you need to deal with dates and times in a historical context you'll want to use `DateTime` to avoid making the same mistakes as UNESCO. If you also have to deal with timezones then best of luck - just bear in mind that you'll probably be dealing with [local solar times][7], since it wasn't until the 19th century that the introduction of the railways necessitated the need for [Standard Time][8] and eventually timezones. [1]: http://en.wikipedia.org/wiki/William_Shakespeare [2]: http://en.wikipedia.org/wiki/Miguel_de_Cervantes [3]: http://en.wikipedia.org/wiki/World_Book_Day [4]: http://en.wikipedia.org/wiki/Gregorian_calendar#Gregorian_reform [5]: http://en.wikipedia.org/wiki/Calendar_(New_Style)_Act_1750 [6]: http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar [7]: http://en.wikipedia.org/wiki/Solar_time [8]: http://en.wikipedia.org/wiki/Standard_time#Great_Britain [9]: http://en.wikipedia.org/wiki/Tropical_year */ cDateTime = rb_define_class("DateTime", cDate); rb_define_singleton_method(cDateTime, "jd", datetime_s_jd, -1); rb_define_singleton_method(cDateTime, "ordinal", datetime_s_ordinal, -1); rb_define_singleton_method(cDateTime, "civil", datetime_s_civil, -1); rb_define_singleton_method(cDateTime, "new", datetime_s_civil, -1); rb_define_singleton_method(cDateTime, "commercial", datetime_s_commercial, -1); #ifndef NDEBUG de_define_singleton_method(cDateTime, "weeknum", datetime_s_weeknum, -1); de_define_singleton_method(cDateTime, "nth_kday", datetime_s_nth_kday, -1); #endif rb_undef_method(CLASS_OF(cDateTime), "today"); 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); #define f_public(m,s) rb_funcall(m, rb_intern("public"), 1,\ ID2SYM(rb_intern(s))) f_public(cDateTime, "hour"); f_public(cDateTime, "min"); f_public(cDateTime, "minute"); f_public(cDateTime, "sec"); f_public(cDateTime, "second"); f_public(cDateTime, "sec_fraction"); f_public(cDateTime, "second_fraction"); f_public(cDateTime, "offset"); f_public(cDateTime, "zone"); f_public(cDateTime, "new_offset"); rb_define_method(cDateTime, "to_s", dt_lite_to_s, 0); rb_define_method(cDateTime, "strftime", dt_lite_strftime, -1); 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, "jisx0301", dt_lite_jisx0301, -1); /* 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); 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 /* tests */ de_define_singleton_method(cDate, "test_civil", date_s_test_civil, 0); de_define_singleton_method(cDate, "test_ordinal", date_s_test_ordinal, 0); de_define_singleton_method(cDate, "test_commercial", date_s_test_commercial, 0); de_define_singleton_method(cDate, "test_weeknum", date_s_test_weeknum, 0); de_define_singleton_method(cDate, "test_nth_kday", date_s_test_nth_kday, 0); de_define_singleton_method(cDate, "test_unit_conv", date_s_test_unit_conv, 0); de_define_singleton_method(cDate, "test_all", date_s_test_all, 0); #endif } /* Local variables: c-file-style: "ruby" End: */