diff options
Diffstat (limited to 'ext/date/date_core.c')
| -rw-r--r-- | ext/date/date_core.c | 2644 |
1 files changed, 1571 insertions, 1073 deletions
diff --git a/ext/date/date_core.c b/ext/date/date_core.c index f9567e23e8..72d697c8ea 100644 --- a/ext/date/date_core.c +++ b/ext/date/date_core.c @@ -27,6 +27,10 @@ static VALUE eDateError; static VALUE half_days_in_day, day_in_nanoseconds; static double positive_inf, negative_inf; +/* used by deconstruct_keys */ +static VALUE sym_year, sym_month, sym_day, sym_yday, sym_wday; +static VALUE sym_hour, sym_min, sym_sec, sym_sec_fraction, sym_zone; + #define f_boolcast(x) ((x) ? Qtrue : Qfalse) #define f_abs(x) rb_funcall(x, rb_intern("abs"), 0) @@ -53,12 +57,15 @@ static double positive_inf, negative_inf; #define f_add3(x,y,z) f_add(f_add(x, y), z) #define f_sub3(x,y,z) f_sub(f_sub(x, y), z) +#define f_frozen_ary(...) rb_ary_freeze(rb_ary_new3(__VA_ARGS__)) + static VALUE date_initialize(int argc, VALUE *argv, VALUE self); static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self); #define RETURN_FALSE_UNLESS_NUMERIC(obj) if(!RTEST(rb_obj_is_kind_of((obj), rb_cNumeric))) return Qfalse inline static void -check_numeric(VALUE obj, const char* field) { +check_numeric(VALUE obj, const char* field) +{ if(!RTEST(rb_obj_is_kind_of(obj, rb_cNumeric))) { rb_raise(rb_eTypeError, "invalid %s (not numeric)", field); } @@ -241,6 +248,11 @@ f_negative_p(VALUE x) #define date_sg_t double #endif +#define JULIAN_EPOCH_DATE "-4712-01-01" +#define JULIAN_EPOCH_DATETIME JULIAN_EPOCH_DATE "T00:00:00+00:00" +#define JULIAN_EPOCH_DATETIME_RFC3339 "Mon, 1 Jan -4712 00:00:00 +0000" +#define JULIAN_EPOCH_DATETIME_HTTPDATE "Mon, 01 Jan -4712 00:00:00 GMT" + /* 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. @@ -440,11 +452,43 @@ do {\ static int c_valid_civil_p(int, int, int, double, int *, int *, int *, int *); +/* Check if using pure Gregorian calendar (sg == -Infinity) */ +#define c_gregorian_only_p(sg) (isinf(sg) && (sg) < 0) + +/* + * Fast path macros for pure Gregorian calendar. + * Sets *rjd to the JD value, *ns to 1 (new style), and returns. + */ +#define GREGORIAN_JD_FAST_PATH_RET(sg, jd_expr, rjd, ns) \ + if (c_gregorian_only_p(sg)) { \ + *(rjd) = (jd_expr); \ + *(ns) = 1; \ + return 1; \ + } + +#define GREGORIAN_JD_FAST_PATH(sg, jd_expr, rjd, ns) \ + if (c_gregorian_only_p(sg)) { \ + *(rjd) = (jd_expr); \ + *(ns) = 1; \ + return; \ + } + +/* Forward declarations for Neri-Schneider optimized functions */ +static int c_gregorian_civil_to_jd(int y, int m, int d); +static void c_gregorian_jd_to_civil(int jd, int *ry, int *rm, int *rd); +static int c_gregorian_fdoy(int y); +static int c_gregorian_ldoy(int y); +static int c_gregorian_ldom_jd(int y, int m); +static int ns_jd_in_range(int jd); + static int c_find_fdoy(int y, double sg, int *rjd, int *ns) { int d, rm, rd; + GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_fdoy(y), rjd, ns); + + /* Keep existing loop for Julian/reform period */ for (d = 1; d < 31; d++) if (c_valid_civil_p(y, 1, d, sg, &rm, &rd, rjd, ns)) return 1; @@ -456,6 +500,9 @@ c_find_ldoy(int y, double sg, int *rjd, int *ns) { int i, rm, rd; + GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_ldoy(y), rjd, ns); + + /* Keep existing loop for Julian/reform period */ for (i = 0; i < 30; i++) if (c_valid_civil_p(y, 12, 31 - i, sg, &rm, &rd, rjd, ns)) return 1; @@ -463,6 +510,7 @@ c_find_ldoy(int y, double sg, int *rjd, int *ns) } #ifndef NDEBUG +/* :nodoc: */ static int c_find_fdom(int y, int m, double sg, int *rjd, int *ns) { @@ -480,6 +528,9 @@ c_find_ldom(int y, int m, double sg, int *rjd, int *ns) { int i, rm, rd; + GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_ldom_jd(y, m), rjd, ns); + + /* Keep existing loop for Julian/reform period */ for (i = 0; i < 30; i++) if (c_valid_civil_p(y, m, 31 - i, sg, &rm, &rd, rjd, ns)) return 1; @@ -489,55 +540,69 @@ c_find_ldom(int y, int m, double sg, int *rjd, int *ns) static void c_civil_to_jd(int y, int m, int d, double sg, int *rjd, int *ns) { - double a, b, jd; + int jd; + + GREGORIAN_JD_FAST_PATH(sg, c_gregorian_civil_to_jd(y, m, d), rjd, ns); + + /* Calculate Gregorian JD using optimized algorithm */ + jd = c_gregorian_civil_to_jd(y, m, d); - 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; + /* Before Gregorian switchover - use Julian calendar */ + int y2 = y, m2 = m; + if (m2 <= 2) { + y2 -= 1; + m2 += 12; + } + jd = (int)(floor(365.25 * (y2 + 4716)) + + floor(30.6001 * (m2 + 1)) + + d - 1524); *ns = 0; } - else + else { *ns = 1; + } - *rjd = (int)jd; + *rjd = 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; + /* Fast path: pure Gregorian or date after switchover, within safe range */ + if ((c_gregorian_only_p(sg) || jd >= sg) && ns_jd_in_range(jd)) { + c_gregorian_jd_to_civil(jd, ry, rm, rdom); + return; } - *ry = (int)y; - *rm = (int)m; - *rdom = (int)dom; + /* Original algorithm for Julian calendar or extreme dates */ + { + 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 @@ -619,6 +684,7 @@ c_jd_to_weeknum(int jd, int f, double sg, int *ry, int *rw, int *rd) } #ifndef NDEBUG +/* :nodoc: */ static void c_nth_kday_to_jd(int y, int m, int n, int k, double sg, int *rjd, int *ns) { @@ -644,6 +710,7 @@ c_jd_to_wday(int jd) } #ifndef NDEBUG +/* :nodoc: */ static void c_jd_to_nth_kday(int jd, double sg, int *ry, int *rm, int *rn, int *rk) { @@ -710,6 +777,147 @@ c_gregorian_last_day_of_month(int y, int m) return monthtab[c_gregorian_leap_p(y) ? 1 : 0][m]; } +/* + * Neri-Schneider algorithm for optimized Gregorian date conversion. + * Reference: Neri & Schneider, "Euclidean Affine Functions and Applications + * to Calendar Algorithms", Software: Practice and Experience, 2023. + * https://arxiv.org/abs/2102.06959 + * + * This algorithm provides ~2-3x speedup over traditional floating-point + * implementations by using pure integer arithmetic with multiplication + * and bit-shifts instead of expensive division operations. + */ + +/* JDN of March 1, Year 0 in proleptic Gregorian calendar */ +#define NS_EPOCH 1721120 + +/* Days in a 4-year cycle (3 normal years + 1 leap year) */ +#define NS_DAYS_IN_4_YEARS 1461 + +/* Days in a 400-year Gregorian cycle (97 leap years in 400 years) */ +#define NS_DAYS_IN_400_YEARS 146097 + +/* Years per century */ +#define NS_YEARS_PER_CENTURY 100 + +/* + * Multiplier for extracting year within century using fixed-point arithmetic. + * This is ceil(2^32 / NS_DAYS_IN_4_YEARS) for the Euclidean affine function. + */ +#define NS_YEAR_MULTIPLIER 2939745 + +/* + * Coefficients for month calculation from day-of-year. + * Maps day-of-year to month using: month = (NS_MONTH_COEFF * doy + NS_MONTH_OFFSET) >> 16 + */ +#define NS_MONTH_COEFF 2141 +#define NS_MONTH_OFFSET 197913 + +/* + * Coefficients for civil date to JDN month contribution. + * Maps month to accumulated days: days = (NS_CIVIL_MONTH_COEFF * m - NS_CIVIL_MONTH_OFFSET) / 32 + */ +#define NS_CIVIL_MONTH_COEFF 979 +#define NS_CIVIL_MONTH_OFFSET 2919 +#define NS_CIVIL_MONTH_DIVISOR 32 + +/* Days from March 1 to December 31 (for Jan/Feb year adjustment) */ +#define NS_DAYS_BEFORE_NEW_YEAR 306 + +/* + * Safe bounds for Neri-Schneider algorithm to avoid integer overflow. + * These correspond to approximately years -1,000,000 to +1,000,000. + */ +#define NS_JD_MIN -364000000 +#define NS_JD_MAX 538000000 + +inline static int +ns_jd_in_range(int jd) +{ + return jd >= NS_JD_MIN && jd <= NS_JD_MAX; +} + +/* Optimized: Gregorian date -> Julian Day Number */ +static int +c_gregorian_civil_to_jd(int y, int m, int d) +{ + /* Shift epoch to March 1 of year 0 (Jan/Feb belong to previous year) */ + int j = (m < 3) ? 1 : 0; + int y0 = y - j; + int m0 = j ? m + 12 : m; + int d0 = d - 1; + + /* Calculate year contribution with leap year correction */ + int q1 = DIV(y0, NS_YEARS_PER_CENTURY); + int yc = DIV(NS_DAYS_IN_4_YEARS * y0, 4) - q1 + DIV(q1, 4); + + /* Calculate month contribution using integer arithmetic */ + int mc = (NS_CIVIL_MONTH_COEFF * m0 - NS_CIVIL_MONTH_OFFSET) / NS_CIVIL_MONTH_DIVISOR; + + /* Combine and add epoch offset to get JDN */ + return yc + mc + d0 + NS_EPOCH; +} + +/* Optimized: Julian Day Number -> Gregorian date */ +static void +c_gregorian_jd_to_civil(int jd, int *ry, int *rm, int *rd) +{ + int r0, n1, q1, r1, n2, q2, r2, n3, q3, r3, y0, j; + uint64_t u2; + + /* Convert JDN to rata die (March 1, Year 0 epoch) */ + r0 = jd - NS_EPOCH; + + /* Extract century and day within 400-year cycle */ + /* Use Euclidean (floor) division for negative values */ + n1 = 4 * r0 + 3; + q1 = DIV(n1, NS_DAYS_IN_400_YEARS); + r1 = MOD(n1, NS_DAYS_IN_400_YEARS) / 4; + + /* Calculate year within century and day of year */ + n2 = 4 * r1 + 3; + /* Use 64-bit arithmetic to avoid overflow */ + u2 = (uint64_t)NS_YEAR_MULTIPLIER * (uint64_t)n2; + q2 = (int)(u2 >> 32); + r2 = (int)((uint32_t)u2 / NS_YEAR_MULTIPLIER / 4); + + /* Calculate month and day using integer arithmetic */ + n3 = NS_MONTH_COEFF * r2 + NS_MONTH_OFFSET; + q3 = n3 >> 16; + r3 = (n3 & 0xFFFF) / NS_MONTH_COEFF; + + /* Combine century and year */ + y0 = NS_YEARS_PER_CENTURY * q1 + q2; + + /* Adjust for January/February (shift from fiscal year) */ + j = (r2 >= NS_DAYS_BEFORE_NEW_YEAR) ? 1 : 0; + + *ry = y0 + j; + *rm = j ? q3 - 12 : q3; + *rd = r3 + 1; +} + +/* O(1) first day of year for Gregorian calendar */ +inline static int +c_gregorian_fdoy(int y) +{ + return c_gregorian_civil_to_jd(y, 1, 1); +} + +/* O(1) last day of year for Gregorian calendar */ +inline static int +c_gregorian_ldoy(int y) +{ + return c_gregorian_civil_to_jd(y, 12, 31); +} + +/* O(1) last day of month (JDN) for Gregorian calendar */ +inline static int +c_gregorian_ldom_jd(int y, int m) +{ + return c_gregorian_civil_to_jd(y, m, c_gregorian_last_day_of_month(y, m)); +} + static int c_valid_julian_p(int y, int m, int d, int *rm, int *rd) { @@ -756,6 +964,8 @@ c_valid_civil_p(int y, int m, int d, double sg, if (m < 0) m += 13; + if (m < 1 || m > 12) + return 0; if (d < 0) { if (!c_find_ldom(y, m, sg, rjd, ns)) return 0; @@ -820,6 +1030,7 @@ c_valid_weeknum_p(int y, int w, int d, int f, double sg, } #ifndef NDEBUG +/* :nodoc: */ 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) @@ -961,6 +1172,7 @@ ns_to_day(VALUE n) } #ifndef NDEBUG +/* :nodoc: */ static VALUE ms_to_sec(VALUE m) { @@ -979,6 +1191,7 @@ ns_to_sec(VALUE n) } #ifndef NDEBUG +/* :nodoc: */ inline static VALUE ins_to_day(int n) { @@ -1014,6 +1227,7 @@ day_to_sec(VALUE d) } #ifndef NDEBUG +/* :nodoc: */ static VALUE day_to_ns(VALUE d) { @@ -1038,6 +1252,7 @@ sec_to_ns(VALUE s) } #ifndef NDEBUG +/* :nodoc: */ static VALUE isec_to_ns(int s) { @@ -1064,6 +1279,7 @@ div_df(VALUE d, VALUE *f) } #ifndef NDEBUG +/* :nodoc: */ static VALUE div_sf(VALUE s, VALUE *f) { @@ -1498,6 +1714,7 @@ m_df(union DateData *x) } #ifndef NDEBUG +/* :nodoc: */ static VALUE m_df_in_day(union DateData *x) { @@ -1575,7 +1792,7 @@ m_ajd(union DateData *x) if (simple_dat_p(x)) { r = m_real_jd(x); - if (FIXNUM_P(r) && FIX2LONG(r) <= (FIXNUM_MAX / 2)) { + if (FIXNUM_P(r) && FIX2LONG(r) <= (FIXNUM_MAX / 2) && FIX2LONG(r) >= (FIXNUM_MIN + 1) / 2) { long ir = FIX2LONG(r); ir = ir * 2 - 1; return rb_rational_new2(LONG2FIX(ir), INT2FIX(2)); @@ -1995,6 +2212,7 @@ expect_numeric(VALUE x) } #ifndef NDEBUG +/* :nodoc: */ static void civil_to_jd(VALUE y, int m, int d, double sg, VALUE *nth, int *ry, @@ -2307,6 +2525,7 @@ valid_weeknum_p(VALUE y, int w, int d, int f, double sg, } #ifndef NDEBUG +/* :nodoc: */ static int valid_nth_kday_p(VALUE y, int m, int n, int k, double sg, VALUE *nth, int *ry, @@ -2444,6 +2663,7 @@ valid_jd_sub(int argc, VALUE *argv, VALUE klass, int need_jd) } #ifndef NDEBUG +/* :nodoc: */ static VALUE date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass) { @@ -2464,13 +2684,16 @@ date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date.valid_jd?(jd[, start=Date::ITALY]) -> bool + * Date.valid_jd?(jd, start = Date::ITALY) -> true * - * Just returns true. It's nonsense, but is for symmetry. + * Implemented for compatibility; + * returns +true+ unless +jd+ is invalid (i.e., not a Numeric). * - * Date.valid_jd?(2451944) #=> true + * Date.valid_jd?(2451944) # => true * - * See also ::jd. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * Related: Date.jd. */ static VALUE date_s_valid_jd_p(int argc, VALUE *argv, VALUE klass) @@ -2530,6 +2753,7 @@ valid_civil_sub(int argc, VALUE *argv, VALUE klass, int need_jd) } #ifndef NDEBUG +/* :nodoc: */ static VALUE date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass) { @@ -2552,18 +2776,18 @@ date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date.valid_civil?(year, month, mday[, start=Date::ITALY]) -> bool - * Date.valid_date?(year, month, mday[, start=Date::ITALY]) -> bool + * Date.valid_civil?(year, month, mday, start = Date::ITALY) -> true or false * - * Returns true if the given calendar date is valid, and false if not. - * Valid in this context is whether the arguments passed to this - * method would be accepted by ::new. + * Returns +true+ if the arguments define a valid ordinal date, + * +false+ otherwise: * - * Date.valid_date?(2001,2,3) #=> true - * Date.valid_date?(2001,2,29) #=> false - * Date.valid_date?(2001,2,-1) #=> true + * Date.valid_date?(2001, 2, 3) # => true + * Date.valid_date?(2001, 2, 29) # => false + * Date.valid_date?(2001, 2, -1) # => true * - * See also ::jd and ::civil. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * Related: Date.jd, Date.new. */ static VALUE date_s_valid_civil_p(int argc, VALUE *argv, VALUE klass) @@ -2619,6 +2843,7 @@ valid_ordinal_sub(int argc, VALUE *argv, VALUE klass, int need_jd) } #ifndef NDEBUG +/* :nodoc: */ static VALUE date_s__valid_ordinal_p(int argc, VALUE *argv, VALUE klass) { @@ -2640,14 +2865,17 @@ date_s__valid_ordinal_p(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date.valid_ordinal?(year, yday[, start=Date::ITALY]) -> bool + * Date.valid_ordinal?(year, yday, start = Date::ITALY) -> true or false * - * Returns true if the given ordinal date is valid, and false if not. + * Returns +true+ if the arguments define a valid ordinal date, + * +false+ otherwise: * - * Date.valid_ordinal?(2001,34) #=> true - * Date.valid_ordinal?(2001,366) #=> false + * Date.valid_ordinal?(2001, 34) # => true + * Date.valid_ordinal?(2001, 366) # => false * - * See also ::jd and ::ordinal. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * Related: Date.jd, Date.ordinal. */ static VALUE date_s_valid_ordinal_p(int argc, VALUE *argv, VALUE klass) @@ -2702,6 +2930,7 @@ valid_commercial_sub(int argc, VALUE *argv, VALUE klass, int need_jd) } #ifndef NDEBUG +/* :nodoc: */ static VALUE date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass) { @@ -2724,14 +2953,19 @@ date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date.valid_commercial?(cwyear, cweek, cwday[, start=Date::ITALY]) -> bool + * Date.valid_commercial?(cwyear, cweek, cwday, start = Date::ITALY) -> true or false + * + * Returns +true+ if the arguments define a valid commercial date, + * +false+ otherwise: + * + * Date.valid_commercial?(2001, 5, 6) # => true + * Date.valid_commercial?(2001, 5, 8) # => false * - * Returns true if the given week date is valid, and false if not. + * See Date.commercial. * - * Date.valid_commercial?(2001,5,6) #=> true - * Date.valid_commercial?(2001,5,8) #=> false + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - * See also ::jd and ::commercial. + * Related: Date.jd, Date.commercial. */ static VALUE date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass) @@ -2758,6 +2992,7 @@ date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass) } #ifndef NDEBUG +/* :nodoc: */ static VALUE valid_weeknum_sub(int argc, VALUE *argv, VALUE klass, int need_jd) { @@ -2789,6 +3024,7 @@ valid_weeknum_sub(int argc, VALUE *argv, VALUE klass, int need_jd) } } +/* :nodoc: */ static VALUE date_s__valid_weeknum_p(int argc, VALUE *argv, VALUE klass) { @@ -2809,6 +3045,7 @@ date_s__valid_weeknum_p(int argc, VALUE *argv, VALUE klass) return valid_weeknum_sub(5, argv2, klass, 1); } +/* :nodoc: */ static VALUE date_s_valid_weeknum_p(int argc, VALUE *argv, VALUE klass) { @@ -2860,6 +3097,7 @@ valid_nth_kday_sub(int argc, VALUE *argv, VALUE klass, int need_jd) } } +/* :nodoc: */ static VALUE date_s__valid_nth_kday_p(int argc, VALUE *argv, VALUE klass) { @@ -2880,6 +3118,7 @@ date_s__valid_nth_kday_p(int argc, VALUE *argv, VALUE klass) return valid_nth_kday_sub(5, argv2, klass, 1); } +/* :nodoc: */ static VALUE date_s_valid_nth_kday_p(int argc, VALUE *argv, VALUE klass) { @@ -2902,6 +3141,7 @@ date_s_valid_nth_kday_p(int argc, VALUE *argv, VALUE klass) return Qtrue; } +/* :nodoc: */ static VALUE date_s_zone_to_diff(VALUE klass, VALUE str) { @@ -2911,13 +3151,15 @@ date_s_zone_to_diff(VALUE klass, VALUE str) /* * call-seq: - * Date.julian_leap?(year) -> bool + * Date.julian_leap?(year) -> true or false + * + * Returns +true+ if the given year is a leap year + * in the {proleptic Julian calendar}[https://en.wikipedia.org/wiki/Proleptic_Julian_calendar], +false+ otherwise: * - * 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 * - * Date.julian_leap?(1900) #=> true - * Date.julian_leap?(1901) #=> false + * Related: Date.gregorian_leap?. */ static VALUE date_s_julian_leap_p(VALUE klass, VALUE y) @@ -2932,14 +3174,15 @@ date_s_julian_leap_p(VALUE klass, VALUE y) /* * call-seq: - * Date.gregorian_leap?(year) -> bool - * Date.leap?(year) -> bool + * Date.gregorian_leap?(year) -> true or false * - * Returns true if the given year is a leap year of the proleptic - * Gregorian calendar. + * Returns +true+ if the given year is a leap year + * in the {proleptic Gregorian calendar}[https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar], +false+ otherwise: * - * Date.gregorian_leap?(1900) #=> false - * Date.gregorian_leap?(2000) #=> true + * Date.gregorian_leap?(2000) # => true + * Date.gregorian_leap?(2001) # => false + * + * Related: Date.julian_leap?. */ static VALUE date_s_gregorian_leap_p(VALUE klass, VALUE y) @@ -2971,11 +3214,15 @@ d_lite_memsize(const void *ptr) return complex_dat_p(dat) ? sizeof(struct ComplexDateData) : sizeof(struct SimpleDateData); } +#ifndef HAVE_RB_EXT_RACTOR_SAFE +# define RUBY_TYPED_FROZEN_SHAREABLE 0 +#endif + 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, + RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED|RUBY_TYPED_FROZEN_SHAREABLE, }; inline static VALUE @@ -3088,6 +3335,7 @@ old_to_new(VALUE ajd, VALUE of, VALUE sg, } #ifndef NDEBUG +/* :nodoc: */ static VALUE date_s_new_bang(int argc, VALUE *argv, VALUE klass) { @@ -3275,16 +3523,29 @@ static VALUE d_lite_plus(VALUE, VALUE); /* * call-seq: - * Date.jd([jd=0[, start=Date::ITALY]]) -> date + * Date.jd(jd = 0, start = Date::ITALY) -> date + * + * Returns a new \Date object formed from the arguments: + * + * Date.jd(2451944).to_s # => "2001-02-03" + * Date.jd(2451945).to_s # => "2001-02-04" + * Date.jd(0).to_s # => "-4712-01-01" + * + * The returned date is: + * + * - Gregorian, if the argument is greater than or equal to +start+: * - * Creates a date object denoting the given chronological Julian day - * number. + * Date::ITALY # => 2299161 + * Date.jd(Date::ITALY).gregorian? # => true + * Date.jd(Date::ITALY + 1).gregorian? # => true * - * Date.jd(2451944) #=> #<Date: 2001-02-03 ...> - * Date.jd(2451945) #=> #<Date: 2001-02-04 ...> - * Date.jd(0) #=> #<Date: -4712-01-01 ...> + * - Julian, otherwise * - * See also ::new. + * Date.jd(Date::ITALY - 1).julian? # => true + * + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * Related: Date.new. */ static VALUE date_s_jd(int argc, VALUE *argv, VALUE klass) @@ -3323,19 +3584,33 @@ date_s_jd(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date.ordinal([year=-4712[, yday=1[, start=Date::ITALY]]]) -> date + * Date.ordinal(year = -4712, yday = 1, start = Date::ITALY) -> date + * + * Returns a new \Date object formed fom the arguments. + * + * With no arguments, returns the date for January 1, -4712: + * + * Date.ordinal.to_s # => "-4712-01-01" + * + * With argument +year+, returns the date for January 1 of that year: + * + * Date.ordinal(2001).to_s # => "2001-01-01" + * Date.ordinal(-2001).to_s # => "-2001-01-01" + * + * With positive argument +yday+ == +n+, + * returns the date for the +nth+ day of the given year: * - * Creates a date object denoting the given ordinal date. + * Date.ordinal(2001, 14).to_s # => "2001-01-14" * - * 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. + * With negative argument +yday+, counts backward from the end of the year: * - * Date.ordinal(2001) #=> #<Date: 2001-01-01 ...> - * Date.ordinal(2001,34) #=> #<Date: 2001-02-03 ...> - * Date.ordinal(2001,-1) #=> #<Date: 2001-12-31 ...> + * Date.ordinal(2001, -14).to_s # => "2001-12-18" * - * See also ::jd and ::new. + * Raises an exception if +yday+ is zero or out of range. + * + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * Related: Date.jd, Date.new. */ static VALUE date_s_ordinal(int argc, VALUE *argv, VALUE klass) @@ -3383,29 +3658,7 @@ date_s_ordinal(int argc, VALUE *argv, VALUE klass) } /* - * 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: 2001-01-01 ...> - * Date.new(2001,2,3) #=> #<Date: 2001-02-03 ...> - * Date.new(2001,2,-1) #=> #<Date: 2001-02-28 ...> - * - * See also ::jd. + * Same as Date.new. */ static VALUE date_s_civil(int argc, VALUE *argv, VALUE klass) @@ -3413,6 +3666,29 @@ date_s_civil(int argc, VALUE *argv, VALUE klass) return date_initialize(argc, argv, d_lite_s_alloc_simple(klass)); } +/* + * call-seq: + * Date.new(year = -4712, month = 1, mday = 1, start = Date::ITALY) -> date + * + * Returns a new \Date object constructed from the given arguments: + * + * Date.new(2022).to_s # => "2022-01-01" + * Date.new(2022, 2).to_s # => "2022-02-01" + * Date.new(2022, 2, 4).to_s # => "2022-02-04" + * + * Argument +month+ should be in range (1..12) or range (-12..-1); + * when the argument is negative, counts backward from the end of the year: + * + * Date.new(2022, -11, 4).to_s # => "2022-02-04" + * + * Argument +mday+ should be in range (1..n) or range (-n..-1) + * where +n+ is the number of days in the month; + * when the argument is negative, counts backward from the end of the month. + * + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * Related: Date.jd. + */ static VALUE date_initialize(int argc, VALUE *argv, VALUE self) { @@ -3477,19 +3753,47 @@ date_initialize(int argc, VALUE *argv, VALUE self) /* * call-seq: - * Date.commercial([cwyear=-4712[, cweek=1[, cwday=1[, start=Date::ITALY]]]]) -> date + * Date.commercial(cwyear = -4712, cweek = 1, cwday = 1, start = Date::ITALY) -> date + * + * Returns a new \Date object constructed from the arguments. + * + * Argument +cwyear+ gives the year, and should be an integer. + * + * Argument +cweek+ gives the index of the week within the year, + * and should be in range (1..53) or (-53..-1); + * in some years, 53 or -53 will be out-of-range; + * if negative, counts backward from the end of the year: + * + * Date.commercial(2022, 1, 1).to_s # => "2022-01-03" + * Date.commercial(2022, 52, 1).to_s # => "2022-12-26" + * + * Argument +cwday+ gives the indes of the weekday within the week, + * and should be in range (1..7) or (-7..-1); + * 1 or -7 is Monday; + * if negative, counts backward from the end of the week: * - * Creates a date object denoting the given week date. + * Date.commercial(2022, 1, 1).to_s # => "2022-01-03" + * Date.commercial(2022, 1, -7).to_s # => "2022-01-03" * - * 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. + * When +cweek+ is 1: * - * Date.commercial(2001) #=> #<Date: 2001-01-01 ...> - * Date.commercial(2002) #=> #<Date: 2001-12-31 ...> - * Date.commercial(2001,5,6) #=> #<Date: 2001-02-03 ...> + * - If January 1 is a Friday, Saturday, or Sunday, + * the first week begins in the week after: * - * See also ::jd and ::new. + * Date::ABBR_DAYNAMES[Date.new(2023, 1, 1).wday] # => "Sun" + * Date.commercial(2023, 1, 1).to_s # => "2023-01-02" + Date.commercial(2023, 1, 7).to_s # => "2023-01-08" + * + * - Otherwise, the first week is the week of January 1, + * which may mean some of the days fall on the year before: + * + * Date::ABBR_DAYNAMES[Date.new(2020, 1, 1).wday] # => "Wed" + * Date.commercial(2020, 1, 1).to_s # => "2019-12-30" + Date.commercial(2020, 1, 7).to_s # => "2020-01-05" + * + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * Related: Date.jd, Date.new, Date.ordinal. */ static VALUE date_s_commercial(int argc, VALUE *argv, VALUE klass) @@ -3541,6 +3845,7 @@ date_s_commercial(int argc, VALUE *argv, VALUE klass) } #ifndef NDEBUG +/* :nodoc: */ static VALUE date_s_weeknum(int argc, VALUE *argv, VALUE klass) { @@ -3590,6 +3895,7 @@ date_s_weeknum(int argc, VALUE *argv, VALUE klass) return ret; } +/* :nodoc: */ static VALUE date_s_nth_kday(int argc, VALUE *argv, VALUE klass) { @@ -3664,11 +3970,14 @@ static void set_sg(union DateData *, double); /* * call-seq: - * Date.today([start=Date::ITALY]) -> date + * Date.today(start = Date::ITALY) -> date + * + * Returns a new \Date object constructed from the present date: * - * Creates a date object denoting the present day. + * Date.today.to_s # => "2022-07-06" + * + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - * Date.today #=> #<Date: 2011-06-11 ...> */ static VALUE date_s_today(int argc, VALUE *argv, VALUE klass) @@ -3762,94 +4071,93 @@ 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, + tab = f_frozen_ary(11, + f_frozen_ary(2, sym("time"), - rb_ary_new3(3, + f_frozen_ary(3, sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, Qnil, - rb_ary_new3(1, + f_frozen_ary(1, sym("jd"))), - rb_ary_new3(2, + f_frozen_ary(2, sym("ordinal"), - rb_ary_new3(5, + f_frozen_ary(5, sym("year"), sym("yday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, sym("civil"), - rb_ary_new3(6, + f_frozen_ary(6, sym("year"), sym("mon"), sym("mday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, sym("commercial"), - rb_ary_new3(6, + f_frozen_ary(6, sym("cwyear"), sym("cweek"), sym("cwday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, sym("wday"), - rb_ary_new3(4, + f_frozen_ary(4, sym("wday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, sym("wnum0"), - rb_ary_new3(6, + f_frozen_ary(6, sym("year"), sym("wnum0"), sym("wday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, sym("wnum1"), - rb_ary_new3(6, + f_frozen_ary(6, sym("year"), sym("wnum1"), sym("wday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, Qnil, - rb_ary_new3(6, + f_frozen_ary(6, sym("cwyear"), sym("cweek"), sym("wday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, Qnil, - rb_ary_new3(6, + f_frozen_ary(6, sym("year"), sym("wnum0"), sym("cwday"), sym("hour"), sym("min"), sym("sec"))), - rb_ary_new3(2, + f_frozen_ary(2, Qnil, - rb_ary_new3(6, + f_frozen_ary(6, sym("year"), sym("wnum1"), sym("cwday"), @@ -3859,9 +4167,13 @@ rt_complete_frags(VALUE klass, VALUE hash) rb_gc_register_mark_object(tab); } + k = a = Qnil; + { - long i, eno = 0, idx = 0; + long i, eno = 0; + VALUE t = Qnil; + e = 0; for (i = 0; i < RARRAY_LEN(tab); i++) { VALUE x, a; @@ -3876,23 +4188,20 @@ rt_complete_frags(VALUE klass, VALUE hash) n++; if (n > eno) { eno = n; - idx = i; + t = x; } } } - 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; + if (eno > 0) { + k = RARRAY_AREF(t, 0); + a = RARRAY_AREF(t, 1); } + e = eno; } d = Qnil; - if (g && !NIL_P(k) && (RARRAY_LEN(a) - e)) { + if (!NIL_P(k) && (RARRAY_LEN(a) > e)) { if (k == sym("ordinal")) { if (NIL_P(ref_hash("year"))) { if (NIL_P(d)) @@ -3979,7 +4288,7 @@ rt_complete_frags(VALUE klass, VALUE hash) } } - if (g && k == sym("time")) { + if (k == sym("time")) { if (f_le_p(klass, cDateTime)) { if (NIL_P(d)) d = date_s_today(0, (VALUE *)0, cDate); @@ -4219,6 +4528,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, rb_scan_args(argc, argv, "11", &vstr, &vfmt); StringValue(vstr); + if (argc > 1) StringValue(vfmt); if (!rb_enc_str_asciicompat_p(vstr)) rb_raise(rb_eArgError, "string should have ASCII compatible encoding"); @@ -4229,7 +4539,6 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, flen = strlen(default_fmt); } else { - StringValue(vfmt); if (!rb_enc_str_asciicompat_p(vfmt)) rb_raise(rb_eArgError, "format should have ASCII compatible encoding"); @@ -4259,16 +4568,20 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, /* * call-seq: - * Date._strptime(string[, format='%F']) -> hash + * 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. + * Returns a hash of values parsed from +string+ + * according to the given +format+: * - * Date._strptime('2001-02-03', '%Y-%m-%d') - * #=> {:year=>2001, :mon=>2, :mday=>3} + * Date._strptime('2001-02-03', '%Y-%m-%d') # => {:year=>2001, :mon=>2, :mday=>3} * - * See also strptime(3) and #strftime. + * For other formats, see + * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]. + * (Unlike Date.strftime, does not support flags and width.) + * + * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html]. + * + * Related: Date.strptime (returns a \Date object). */ static VALUE date_s__strptime(int argc, VALUE *argv, VALUE klass) @@ -4278,21 +4591,28 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date.strptime([string='-4712-01-01'[, format='%F'[, start=Date::ITALY]]]) -> date + * Date.strptime(string = '-4712-01-01', format = '%F', start = Date::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. + * Returns a new \Date object with values parsed from +string+, + * according to the given +format+: * - * Date.strptime('2001-02-03', '%Y-%m-%d') #=> #<Date: 2001-02-03 ...> - * Date.strptime('03-02-2001', '%d-%m-%Y') #=> #<Date: 2001-02-03 ...> - * Date.strptime('2001-034', '%Y-%j') #=> #<Date: 2001-02-03 ...> - * Date.strptime('2001-W05-6', '%G-W%V-%u') #=> #<Date: 2001-02-03 ...> - * Date.strptime('2001 04 6', '%Y %U %w') #=> #<Date: 2001-02-03 ...> - * Date.strptime('2001 05 6', '%Y %W %u') #=> #<Date: 2001-02-03 ...> - * Date.strptime('sat3feb01', '%a%d%b%y') #=> #<Date: 2001-02-03 ...> + * Date.strptime('2001-02-03', '%Y-%m-%d') # => #<Date: 2001-02-03> + * Date.strptime('03-02-2001', '%d-%m-%Y') # => #<Date: 2001-02-03> + * Date.strptime('2001-034', '%Y-%j') # => #<Date: 2001-02-03> + * Date.strptime('2001-W05-6', '%G-W%V-%u') # => #<Date: 2001-02-03> + * Date.strptime('2001 04 6', '%Y %U %w') # => #<Date: 2001-02-03> + * Date.strptime('2001 05 6', '%Y %W %u') # => #<Date: 2001-02-03> + * Date.strptime('sat3feb01', '%a%d%b%y') # => #<Date: 2001-02-03> * - * See also strptime(3) and #strftime. + * For other formats, see + * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]. + * (Unlike Date.strftime, does not support flags and width.) + * + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * + * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html]. + * + * Related: Date._strptime (returns a hash). */ static VALUE date_s_strptime(int argc, VALUE *argv, VALUE klass) @@ -4303,7 +4623,7 @@ date_s_strptime(int argc, VALUE *argv, VALUE klass) switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: fmt = rb_str_new2("%F"); case 2: @@ -4322,13 +4642,42 @@ date_s_strptime(int argc, VALUE *argv, VALUE klass) VALUE date__parse(VALUE str, VALUE comp); +static size_t +get_limit(VALUE opt) +{ + if (!NIL_P(opt)) { + VALUE limit = rb_hash_aref(opt, ID2SYM(rb_intern("limit"))); + if (NIL_P(limit)) return SIZE_MAX; + return NUM2SIZET(limit); + } + return 128; +} + +#ifndef HAVE_RB_CATEGORY_WARN +#define rb_category_warn(category, fmt) rb_warn(fmt) +#endif + +static VALUE +check_limit(VALUE str, VALUE opt) +{ + size_t slen, limit; + StringValue(str); + slen = RSTRING_LEN(str); + limit = get_limit(opt); + if (slen > limit) { + rb_raise(rb_eArgError, + "string length (%"PRI_SIZE_PREFIX"u) exceeds the limit %"PRI_SIZE_PREFIX"u", slen, limit); + } + return str; +} + static VALUE date_s__parse_internal(int argc, VALUE *argv, VALUE klass) { - VALUE vstr, vcomp, hash; + VALUE vstr, vcomp, hash, opt; - rb_scan_args(argc, argv, "11", &vstr, &vcomp); - StringValue(vstr); + argc = rb_scan_args(argc, argv, "11:", &vstr, &vcomp, &opt); + vstr = check_limit(vstr, opt); if (!rb_enc_str_asciicompat_p(vstr)) rb_raise(rb_eArgError, "string should have ASCII compatible encoding"); @@ -4342,21 +4691,32 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date._parse(string[, comp=true]) -> hash + * Date._parse(string, comp = true, limit: 128) -> hash * - * Parses the given representation of date and time, and returns a - * hash of parsed elements. + * <b>Note</b>: + * This method recognizes many forms in +string+, + * but it is not a validator. + * For formats, see + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings] * - * This method **does not** function as a validator. If the input - * string does not match valid formats strictly, you may get a cryptic - * result. Should consider to use `Date._strptime` or - * `DateTime._strptime` instead of this method as possible. + * If +string+ does not specify a valid date, + * the result is unpredictable; + * consider using Date._strptime instead. * - * 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. + * Returns a hash of values parsed from +string+: + * + * Date._parse('2001-02-03') # => {:year=>2001, :mon=>2, :mday=>3} * - * Date._parse('2001-02-03') #=> {:year=>2001, :mon=>2, :mday=>3} + * If +comp+ is +true+ and the given year is in the range <tt>(0..99)</tt>, + * the current century is supplied; + * otherwise, the year is taken as given: + * + * Date._parse('01-02-03', true) # => {:year=>2001, :mon=>2, :mday=>3} + * Date._parse('01-02-03', false) # => {:year=>1, :mon=>2, :mday=>3} + * + * See argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date.parse(returns a \Date object). */ static VALUE date_s__parse(int argc, VALUE *argv, VALUE klass) @@ -4366,34 +4726,47 @@ date_s__parse(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * Date.parse(string='-4712-01-01'[, comp=true[, start=Date::ITALY]]) -> date + * Date.parse(string = '-4712-01-01', comp = true, start = Date::ITALY, limit: 128) -> date * - * Parses the given representation of date and time, and creates a - * date object. + * <b>Note</b>: + * This method recognizes many forms in +string+, + * but it is not a validator. + * For formats, see + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings] + * If +string+ does not specify a valid date, + * the result is unpredictable; + * consider using Date._strptime instead. * - * This method **does not** function as a validator. If the input - * string does not match valid formats strictly, you may get a cryptic - * result. Should consider to use `Date.strptime` instead of this - * method as possible. + * Returns a new \Date object with values parsed from +string+: * - * 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: 2001-02-03> + * Date.parse('20010203') # => #<Date: 2001-02-03> + * Date.parse('3rd Feb 2001') # => #<Date: 2001-02-03> + * + * If +comp+ is +true+ and the given year is in the range <tt>(0..99)</tt>, + * the current century is supplied; + * otherwise, the year is taken as given: + * + * Date.parse('01-02-03', true) # => #<Date: 2001-02-03> + * Date.parse('01-02-03', false) # => #<Date: 0001-02-03> + * + * See: + * + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * - * Date.parse('2001-02-03') #=> #<Date: 2001-02-03 ...> - * Date.parse('20010203') #=> #<Date: 2001-02-03 ...> - * Date.parse('3rd Feb 2001') #=> #<Date: 2001-02-03 ...> + * Related: Date._parse (returns a hash). */ static VALUE date_s_parse(int argc, VALUE *argv, VALUE klass) { - VALUE str, comp, sg; + VALUE str, comp, sg, opt; - rb_scan_args(argc, argv, "03", &str, &comp, &sg); + argc = rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: comp = Qtrue; case 2: @@ -4401,11 +4774,12 @@ date_s_parse(int argc, VALUE *argv, VALUE klass) } { - VALUE argv2[2], hash; - - argv2[0] = str; - argv2[1] = comp; - hash = date_s__parse(2, argv2, klass); + int argc2 = 2; + VALUE argv2[3], hash; + argv2[0] = str; + argv2[1] = comp; + if (!NIL_P(opt)) argv2[argc2++] = opt; + hash = date_s__parse(argc2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } @@ -4419,256 +4793,417 @@ VALUE date__jisx0301(VALUE); /* * call-seq: - * Date._iso8601(string) -> hash + * Date._iso8601(string, limit: 128) -> hash + * + * Returns a hash of values parsed from +string+, which should contain + * an {ISO 8601 formatted date}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications]: * - * Returns a hash of parsed elements. + * d = Date.new(2001, 2, 3) + * s = d.iso8601 # => "2001-02-03" + * Date._iso8601(s) # => {:mday=>3, :year=>2001, :mon=>2} + * + * See argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date.iso8601 (returns a \Date object). */ static VALUE -date_s__iso8601(VALUE klass, VALUE str) +date_s__iso8601(int argc, VALUE *argv, VALUE klass) { + VALUE str, opt; + + rb_scan_args(argc, argv, "1:", &str, &opt); + if (!NIL_P(str)) str = check_limit(str, opt); + return date__iso8601(str); } /* * call-seq: - * Date.iso8601(string='-4712-01-01'[, start=Date::ITALY]) -> date + * Date.iso8601(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date * - * Creates a new Date object by parsing from a string according to - * some typical ISO 8601 formats. + * Returns a new \Date object with values parsed from +string+, + * which should contain + * an {ISO 8601 formatted date}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications]: + * + * d = Date.new(2001, 2, 3) + * s = d.iso8601 # => "2001-02-03" + * Date.iso8601(s) # => #<Date: 2001-02-03> + * + * See: + * + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * - * Date.iso8601('2001-02-03') #=> #<Date: 2001-02-03 ...> - * Date.iso8601('20010203') #=> #<Date: 2001-02-03 ...> - * Date.iso8601('2001-W05-6') #=> #<Date: 2001-02-03 ...> + * Related: Date._iso8601 (returns a hash). */ static VALUE date_s_iso8601(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__iso8601(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + if (!NIL_P(opt)) argv2[argc2++] = opt; + hash = date_s__iso8601(argc2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: - * Date._rfc3339(string) -> hash + * Date._rfc3339(string, limit: 128) -> hash + * + * Returns a hash of values parsed from +string+, which should be a valid + * {RFC 3339 format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+3339+Format]: + * + * d = Date.new(2001, 2, 3) + * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00" + * Date._rfc3339(s) + * # => {:year=>2001, :mon=>2, :mday=>3, :hour=>0, :min=>0, :sec=>0, :zone=>"+00:00", :offset=>0} + * + * See argument {limit}[rdoc-ref:Date@Argument+limit]. * - * Returns a hash of parsed elements. + * Related: Date.rfc3339 (returns a \Date object). */ static VALUE -date_s__rfc3339(VALUE klass, VALUE str) +date_s__rfc3339(int argc, VALUE *argv, VALUE klass) { + VALUE str, opt; + + rb_scan_args(argc, argv, "1:", &str, &opt); + if (!NIL_P(str)) str = check_limit(str, opt); + return date__rfc3339(str); } /* * call-seq: - * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> date + * Date.rfc3339(string = '-4712-01-01T00:00:00+00:00', start = Date::ITALY, limit: 128) -> date * - * Creates a new Date object by parsing from a string according to - * some typical RFC 3339 formats. + * Returns a new \Date object with values parsed from +string+, + * which should be a valid + * {RFC 3339 format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+3339+Format]: + * + * d = Date.new(2001, 2, 3) + * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00" + * Date.rfc3339(s) # => #<Date: 2001-02-03> * - * Date.rfc3339('2001-02-03T04:05:06+07:00') #=> #<Date: 2001-02-03 ...> + * See: + * + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * - Argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date._rfc3339 (returns a hash). */ static VALUE date_s_rfc3339(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__rfc3339(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + if (!NIL_P(opt)) argv2[argc2++] = opt; + hash = date_s__rfc3339(argc2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: - * Date._xmlschema(string) -> hash + * Date._xmlschema(string, limit: 128) -> hash + * + * Returns a hash of values parsed from +string+, which should be a valid + * XML date format: + * + * d = Date.new(2001, 2, 3) + * s = d.xmlschema # => "2001-02-03" + * Date._xmlschema(s) # => {:year=>2001, :mon=>2, :mday=>3} * - * Returns a hash of parsed elements. + * See argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date.xmlschema (returns a \Date object). */ static VALUE -date_s__xmlschema(VALUE klass, VALUE str) +date_s__xmlschema(int argc, VALUE *argv, VALUE klass) { + VALUE str, opt; + + rb_scan_args(argc, argv, "1:", &str, &opt); + if (!NIL_P(str)) str = check_limit(str, opt); + return date__xmlschema(str); } /* * call-seq: - * Date.xmlschema(string='-4712-01-01'[, start=Date::ITALY]) -> date + * Date.xmlschema(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date * - * Creates a new Date object by parsing from a string according to - * some typical XML Schema formats. + * Returns a new \Date object with values parsed from +string+, + * which should be a valid XML date format: + * + * d = Date.new(2001, 2, 3) + * s = d.xmlschema # => "2001-02-03" + * Date.xmlschema(s) # => #<Date: 2001-02-03> + * + * See: + * + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * - Argument {limit}[rdoc-ref:Date@Argument+limit]. * - * Date.xmlschema('2001-02-03') #=> #<Date: 2001-02-03 ...> + * Related: Date._xmlschema (returns a hash). */ static VALUE date_s_xmlschema(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__xmlschema(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + if (!NIL_P(opt)) argv2[argc2++] = opt; + hash = date_s__xmlschema(argc2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: - * Date._rfc2822(string) -> hash - * Date._rfc822(string) -> hash + * Date._rfc2822(string, limit: 128) -> hash * - * Returns a hash of parsed elements. + * Returns a hash of values parsed from +string+, which should be a valid + * {RFC 2822 date format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+2822+Format]: + * + * d = Date.new(2001, 2, 3) + * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" + * Date._rfc2822(s) + * # => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"+0000", :offset=>0} + * + * See argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date.rfc2822 (returns a \Date object). */ static VALUE -date_s__rfc2822(VALUE klass, VALUE str) +date_s__rfc2822(int argc, VALUE *argv, VALUE klass) { + VALUE str, opt; + + rb_scan_args(argc, argv, "1:", &str, &opt); + if (!NIL_P(str)) str = check_limit(str, opt); + return date__rfc2822(str); } /* * call-seq: - * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date - * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date + * Date.rfc2822(string = 'Mon, 1 Jan -4712 00:00:00 +0000', start = Date::ITALY, limit: 128) -> date * - * Creates a new Date object by parsing from a string according to - * some typical RFC 2822 formats. + * Returns a new \Date object with values parsed from +string+, + * which should be a valid + * {RFC 2822 date format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+2822+Format]: + * + * d = Date.new(2001, 2, 3) + * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" + * Date.rfc2822(s) # => #<Date: 2001-02-03> + * + * See: * - * Date.rfc2822('Sat, 3 Feb 2001 00:00:00 +0000') - * #=> #<Date: 2001-02-03 ...> + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * - Argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date._rfc2822 (returns a hash). */ static VALUE date_s_rfc2822(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 1 Jan -4712 00:00:00 +0000"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_RFC3339); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__rfc2822(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + if (!NIL_P(opt)) argv2[argc2++] = opt; + hash = date_s__rfc2822(argc2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: - * Date._httpdate(string) -> hash + * Date._httpdate(string, limit: 128) -> hash + * + * Returns a hash of values parsed from +string+, which should be a valid + * {HTTP date format}[rdoc-ref:language/strftime_formatting.rdoc@HTTP+Format]: * - * Returns a hash of parsed elements. + * d = Date.new(2001, 2, 3) + * s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" + * Date._httpdate(s) + * # => {:wday=>6, :mday=>3, :mon=>2, :year=>2001, :hour=>0, :min=>0, :sec=>0, :zone=>"GMT", :offset=>0} + * + * Related: Date.httpdate (returns a \Date object). */ static VALUE -date_s__httpdate(VALUE klass, VALUE str) +date_s__httpdate(int argc, VALUE *argv, VALUE klass) { + VALUE str, opt; + + rb_scan_args(argc, argv, "1:", &str, &opt); + if (!NIL_P(str)) str = check_limit(str, opt); + return date__httpdate(str); } /* * call-seq: - * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY]) -> date + * Date.httpdate(string = 'Mon, 01 Jan -4712 00:00:00 GMT', start = Date::ITALY, limit: 128) -> date * - * Creates a new Date object by parsing from a string according to - * some RFC 2616 format. + * Returns a new \Date object with values parsed from +string+, + * which should be a valid + * {HTTP date format}[rdoc-ref:language/strftime_formatting.rdoc@HTTP+Format]: + * + * d = Date.new(2001, 2, 3) + s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" + Date.httpdate(s) # => #<Date: 2001-02-03> + * + * See: * - * Date.httpdate('Sat, 03 Feb 2001 00:00:00 GMT') - * #=> #<Date: 2001-02-03 ...> + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * - Argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date._httpdate (returns a hash). */ static VALUE date_s_httpdate(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 01 Jan -4712 00:00:00 GMT"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_HTTPDATE); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__httpdate(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + if (!NIL_P(opt)) argv2[argc2++] = opt; + hash = date_s__httpdate(argc2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } /* * call-seq: - * Date._jisx0301(string) -> hash + * Date._jisx0301(string, limit: 128) -> hash + * + * Returns a hash of values parsed from +string+, which should be a valid + * {JIS X 0301 date format}[rdoc-ref:language/strftime_formatting.rdoc@JIS+X+0301+Format]: + * + * d = Date.new(2001, 2, 3) + * s = d.jisx0301 # => "H13.02.03" + * Date._jisx0301(s) # => {:year=>2001, :mon=>2, :mday=>3} + * + * See argument {limit}[rdoc-ref:Date@Argument+limit]. * - * Returns a hash of parsed elements. + * Related: Date.jisx0301 (returns a \Date object). */ static VALUE -date_s__jisx0301(VALUE klass, VALUE str) +date_s__jisx0301(int argc, VALUE *argv, VALUE klass) { + VALUE str, opt; + + rb_scan_args(argc, argv, "1:", &str, &opt); + if (!NIL_P(str)) str = check_limit(str, opt); + return date__jisx0301(str); } /* * call-seq: - * Date.jisx0301(string='-4712-01-01'[, start=Date::ITALY]) -> date + * Date.jisx0301(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date * - * Creates a new Date object by parsing from a string according to - * some typical JIS X 0301 formats. + * Returns a new \Date object with values parsed from +string+, + * which should be a valid {JIS X 0301 format}[rdoc-ref:language/strftime_formatting.rdoc@JIS+X+0301+Format]: * - * Date.jisx0301('H13.02.03') #=> #<Date: 2001-02-03 ...> + * d = Date.new(2001, 2, 3) + * s = d.jisx0301 # => "H13.02.03" + * Date.jisx0301(s) # => #<Date: 2001-02-03> * * For no-era year, legacy format, Heisei is assumed. * - * Date.jisx0301('13.02.03') #=> #<Date: 2001-02-03 ...> + * Date.jisx0301('13.02.03') # => #<Date: 2001-02-03> + * + * See: + * + * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. + * - Argument {limit}[rdoc-ref:Date@Argument+limit]. + * + * Related: Date._jisx0301 (returns a hash). */ static VALUE date_s_jisx0301(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01"); + str = rb_str_new2(JULIAN_EPOCH_DATE); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__jisx0301(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + if (!NIL_P(opt)) argv2[argc2++] = opt; + hash = date_s__jisx0301(argc2, argv2, klass); return d_new_by_frags(klass, hash, sg); } } @@ -4838,6 +5373,7 @@ d_lite_initialize_copy(VALUE copy, VALUE date) } #ifndef NDEBUG +/* :nodoc: */ static VALUE d_lite_fill(VALUE self) { @@ -4927,12 +5463,15 @@ d_lite_mjd(VALUE self) /* * call-seq: - * d.ld -> integer + * ld -> integer * - * Returns the Lilian day number. This is a whole number, which is - * adjusted by the offset as the local time. + * Returns the + * {Lilian day number}[https://en.wikipedia.org/wiki/Lilian_date], + * which is the number of days since the beginning of the Gregorian + * calendar, October 15, 1582. + * + * Date.new(2001, 2, 3).ld # => 152784 * - * Date.new(2001,2,3).ld #=> 152784 */ static VALUE d_lite_ld(VALUE self) @@ -4943,12 +5482,13 @@ d_lite_ld(VALUE self) /* * call-seq: - * d.year -> integer + * year -> integer + * + * Returns the year: * - * Returns the year. + * Date.new(2001, 2, 3).year # => 2001 + * (Date.new(1, 1, 1) - 1).year # => 0 * - * Date.new(2001,2,3).year #=> 2001 - * (Date.new(1,1,1) - 1).year #=> 0 */ static VALUE d_lite_year(VALUE self) @@ -4959,11 +5499,12 @@ d_lite_year(VALUE self) /* * call-seq: - * d.yday -> fixnum + * yday -> integer + * + * Returns the day of the year, in range (1..366): * - * Returns the day of the year (1-366). + * Date.new(2001, 2, 3).yday # => 34 * - * Date.new(2001,2,3).yday #=> 34 */ static VALUE d_lite_yday(VALUE self) @@ -4974,12 +5515,12 @@ d_lite_yday(VALUE self) /* * call-seq: - * d.mon -> fixnum - * d.month -> fixnum + * mon -> integer * - * Returns the month (1-12). + * Returns the month in range (1..12): + * + * Date.new(2001, 2, 3).mon # => 2 * - * Date.new(2001,2,3).mon #=> 2 */ static VALUE d_lite_mon(VALUE self) @@ -4990,12 +5531,12 @@ d_lite_mon(VALUE self) /* * call-seq: - * d.mday -> fixnum - * d.day -> fixnum + * mday -> integer + * + * Returns the day of the month in range (1..31): * - * Returns the day of the month (1-31). + * Date.new(2001, 2, 3).mday # => 3 * - * Date.new(2001,2,3).mday #=> 3 */ static VALUE d_lite_mday(VALUE self) @@ -5006,11 +5547,12 @@ d_lite_mday(VALUE self) /* * call-seq: - * d.day_fraction -> rational + * day_fraction -> rational * - * Returns the fractional part of the day. + * Returns the fractional part of the day in range (Rational(0, 1)...Rational(1, 1)): + * + * DateTime.new(2001,2,3,12).day_fraction # => (1/2) * - * DateTime.new(2001,2,3,12).day_fraction #=> (1/2) */ static VALUE d_lite_day_fraction(VALUE self) @@ -5023,12 +5565,14 @@ d_lite_day_fraction(VALUE self) /* * call-seq: - * d.cwyear -> integer + * cwyear -> integer + * + * Returns commercial-date year for +self+ + * (see Date.commercial): * - * Returns the calendar week based year. + * Date.new(2001, 2, 3).cwyear # => 2001 + * Date.new(2000, 1, 1).cwyear # => 1999 * - * Date.new(2001,2,3).cwyear #=> 2001 - * Date.new(2000,1,1).cwyear #=> 1999 */ static VALUE d_lite_cwyear(VALUE self) @@ -5039,11 +5583,13 @@ d_lite_cwyear(VALUE self) /* * call-seq: - * d.cweek -> fixnum + * cweek -> integer * - * Returns the calendar week number (1-53). + * Returns commercial-date week index for +self+ + * (see Date.commercial): + * + * Date.new(2001, 2, 3).cweek # => 5 * - * Date.new(2001,2,3).cweek #=> 5 */ static VALUE d_lite_cweek(VALUE self) @@ -5054,11 +5600,14 @@ d_lite_cweek(VALUE self) /* * call-seq: - * d.cwday -> fixnum + * cwday -> integer + * + * Returns the commercial-date weekday index for +self+ + * (see Date.commercial); + * 1 is Monday: * - * Returns the day of calendar week (1-7, Monday is 1). + * Date.new(2001, 2, 3).cwday # => 6 * - * Date.new(2001,2,3).cwday #=> 6 */ static VALUE d_lite_cwday(VALUE self) @@ -5068,6 +5617,7 @@ d_lite_cwday(VALUE self) } #ifndef NDEBUG +/* :nodoc: */ static VALUE d_lite_wnum0(VALUE self) { @@ -5075,6 +5625,7 @@ d_lite_wnum0(VALUE self) return INT2FIX(m_wnum0(dat)); } +/* :nodoc: */ static VALUE d_lite_wnum1(VALUE self) { @@ -5085,11 +5636,12 @@ d_lite_wnum1(VALUE self) /* * call-seq: - * d.wday -> fixnum + * wday -> integer * - * Returns the day of week (0-6, Sunday is zero). + * Returns the day of week in range (0..6); Sunday is 0: + * + * Date.new(2001, 2, 3).wday # => 6 * - * Date.new(2001,2,3).wday #=> 6 */ static VALUE d_lite_wday(VALUE self) @@ -5100,9 +5652,9 @@ d_lite_wday(VALUE self) /* * call-seq: - * d.sunday? -> bool + * sunday? -> true or false * - * Returns true if the date is Sunday. + * Returns +true+ if +self+ is a Sunday, +false+ otherwise. */ static VALUE d_lite_sunday_p(VALUE self) @@ -5113,9 +5665,9 @@ d_lite_sunday_p(VALUE self) /* * call-seq: - * d.monday? -> bool + * monday? -> true or false * - * Returns true if the date is Monday. + * Returns +true+ if +self+ is a Monday, +false+ otherwise. */ static VALUE d_lite_monday_p(VALUE self) @@ -5126,9 +5678,9 @@ d_lite_monday_p(VALUE self) /* * call-seq: - * d.tuesday? -> bool + * tuesday? -> true or false * - * Returns true if the date is Tuesday. + * Returns +true+ if +self+ is a Tuesday, +false+ otherwise. */ static VALUE d_lite_tuesday_p(VALUE self) @@ -5139,9 +5691,9 @@ d_lite_tuesday_p(VALUE self) /* * call-seq: - * d.wednesday? -> bool + * wednesday? -> true or false * - * Returns true if the date is Wednesday. + * Returns +true+ if +self+ is a Wednesday, +false+ otherwise. */ static VALUE d_lite_wednesday_p(VALUE self) @@ -5152,9 +5704,9 @@ d_lite_wednesday_p(VALUE self) /* * call-seq: - * d.thursday? -> bool + * thursday? -> true or false * - * Returns true if the date is Thursday. + * Returns +true+ if +self+ is a Thursday, +false+ otherwise. */ static VALUE d_lite_thursday_p(VALUE self) @@ -5165,9 +5717,9 @@ d_lite_thursday_p(VALUE self) /* * call-seq: - * d.friday? -> bool + * friday? -> true or false * - * Returns true if the date is Friday. + * Returns +true+ if +self+ is a Friday, +false+ otherwise. */ static VALUE d_lite_friday_p(VALUE self) @@ -5178,9 +5730,9 @@ d_lite_friday_p(VALUE self) /* * call-seq: - * d.saturday? -> bool + * saturday? -> true or false * - * Returns true if the date is Saturday. + * Returns +true+ if +self+ is a Saturday, +false+ otherwise. */ static VALUE d_lite_saturday_p(VALUE self) @@ -5190,6 +5742,7 @@ d_lite_saturday_p(VALUE self) } #ifndef NDEBUG +/* :nodoc: */ static VALUE d_lite_nth_kday_p(VALUE self, VALUE n, VALUE k) { @@ -5211,11 +5764,12 @@ d_lite_nth_kday_p(VALUE self, VALUE n, VALUE k) /* * call-seq: - * d.hour -> fixnum + * hour -> integer * - * Returns the hour (0-23). + * Returns the hour in range (0..23): + * + * DateTime.new(2001, 2, 3, 4, 5, 6).hour # => 4 * - * DateTime.new(2001,2,3,4,5,6).hour #=> 4 */ static VALUE d_lite_hour(VALUE self) @@ -5226,12 +5780,12 @@ d_lite_hour(VALUE self) /* * call-seq: - * d.min -> fixnum - * d.minute -> fixnum + * min -> integer + * + * Returns the minute in range (0..59): * - * Returns the minute (0-59). + * DateTime.new(2001, 2, 3, 4, 5, 6).min # => 5 * - * DateTime.new(2001,2,3,4,5,6).min #=> 5 */ static VALUE d_lite_min(VALUE self) @@ -5242,12 +5796,12 @@ d_lite_min(VALUE self) /* * call-seq: - * d.sec -> fixnum - * d.second -> fixnum + * sec -> integer * - * Returns the second (0-59). + * Returns the second in range (0..59): + * + * DateTime.new(2001, 2, 3, 4, 5, 6).sec # => 6 * - * DateTime.new(2001,2,3,4,5,6).sec #=> 6 */ static VALUE d_lite_sec(VALUE self) @@ -5258,12 +5812,13 @@ d_lite_sec(VALUE self) /* * call-seq: - * d.sec_fraction -> rational - * d.second_fraction -> rational + * sec_fraction -> rational + * + * Returns the fractional part of the second in range + * (Rational(0, 1)...Rational(1, 1)): * - * Returns the fractional part of the second. + * DateTime.new(2001, 2, 3, 4, 5, 6.5).sec_fraction # => (1/2) * - * DateTime.new(2001,2,3,4,5,6.5).sec_fraction #=> (1/2) */ static VALUE d_lite_sec_fraction(VALUE self) @@ -5304,12 +5859,14 @@ d_lite_zone(VALUE self) /* * call-seq: - * d.julian? -> bool + * d.julian? -> true or false + * + * Returns +true+ if the date is before the date of calendar reform, + * +false+ otherwise: * - * Returns true if the date is before the day of calendar reform. + * (Date.new(1582, 10, 15) - 1).julian? # => true + * Date.new(1582, 10, 15).julian? # => false * - * Date.new(1582,10,15).julian? #=> false - * (Date.new(1582,10,15) - 1).julian? #=> true */ static VALUE d_lite_julian_p(VALUE self) @@ -5320,12 +5877,14 @@ d_lite_julian_p(VALUE self) /* * call-seq: - * d.gregorian? -> bool + * gregorian? -> true or false * - * Returns true if the date is on or after the day of calendar reform. + * Returns +true+ if the date is on or after + * the date of calendar reform, +false+ otherwise: + * + * Date.new(1582, 10, 15).gregorian? # => true + * (Date.new(1582, 10, 15) - 1).gregorian? # => false * - * Date.new(1582,10,15).gregorian? #=> true - * (Date.new(1582,10,15) - 1).gregorian? #=> false */ static VALUE d_lite_gregorian_p(VALUE self) @@ -5336,12 +5895,13 @@ d_lite_gregorian_p(VALUE self) /* * call-seq: - * d.leap? -> bool + * leap? -> true or false + * + * Returns +true+ if the year is a leap year, +false+ otherwise: * - * Returns true if the year is a leap year. + * Date.new(2000).leap? # => true + * Date.new(2001).leap? # => false * - * Date.new(2000).leap? #=> true - * Date.new(2001).leap? #=> false */ static VALUE d_lite_leap_p(VALUE self) @@ -5360,12 +5920,25 @@ d_lite_leap_p(VALUE self) /* * call-seq: - * d.start -> float + * start -> float + * + * Returns the Julian start date for calendar reform; + * if not an infinity, the returned value is suitable + * for passing to Date#jd: + * + * d = Date.new(2001, 2, 3, Date::ITALY) + * s = d.start # => 2299161.0 + * Date.jd(s).to_s # => "1582-10-15" * - * Returns the Julian day number denoting the day of calendar reform. + * d = Date.new(2001, 2, 3, Date::ENGLAND) + * s = d.start # => 2361222.0 + * Date.jd(s).to_s # => "1752-09-14" + * + * Date.new(2001, 2, 3, Date::GREGORIAN).start # => -Infinity + * Date.new(2001, 2, 3, Date::JULIAN).start # => Infinity + * + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - * Date.new(2001,2,3).start #=> 2299161.0 - * Date.new(2001,2,3,Date::GREGORIAN).start #=> -Infinity */ static VALUE d_lite_start(VALUE self) @@ -5430,12 +6003,17 @@ dup_obj_with_new_start(VALUE obj, double sg) /* * call-seq: - * d.new_start([start=Date::ITALY]) -> date + * new_start(start = Date::ITALY) -> new_date + * + * Returns a copy of +self+ with the given +start+ value: + * + * d0 = Date.new(2000, 2, 3) + * d0.julian? # => false + * d1 = d0.new_start(Date::JULIAN) + * d1.julian? # => true * - * Duplicates self and resets its day of calendar reform. + * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start]. * - * d = Date.new(1582,10,15) - * d.new_start(Date::JULIAN) #=> #<Date: 1582-10-05 ...> */ static VALUE d_lite_new_start(int argc, VALUE *argv, VALUE self) @@ -5454,9 +6032,10 @@ d_lite_new_start(int argc, VALUE *argv, VALUE self) /* * call-seq: - * d.italy -> date + * italy -> new_date + * + * Equivalent to Date#new_start with argument Date::ITALY. * - * This method is equivalent to new_start(Date::ITALY). */ static VALUE d_lite_italy(VALUE self) @@ -5466,9 +6045,9 @@ d_lite_italy(VALUE self) /* * call-seq: - * d.england -> date + * england -> new_date * - * This method is equivalent to new_start(Date::ENGLAND). + * Equivalent to Date#new_start with argument Date::ENGLAND. */ static VALUE d_lite_england(VALUE self) @@ -5478,9 +6057,9 @@ d_lite_england(VALUE self) /* * call-seq: - * d.julian -> date + * julian -> new_date * - * This method is equivalent to new_start(Date::JULIAN). + * Equivalent to Date#new_start with argument Date::JULIAN. */ static VALUE d_lite_julian(VALUE self) @@ -5490,9 +6069,9 @@ d_lite_julian(VALUE self) /* * call-seq: - * d.gregorian -> date + * gregorian -> new_date * - * This method is equivalent to new_start(Date::GREGORIAN). + * Equivalent to Date#new_start with argument Date::GREGORIAN. */ static VALUE d_lite_gregorian(VALUE self) @@ -5938,9 +6517,11 @@ minus_dd(VALUE self, VALUE other) * 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 a fractional number, + * If the other is a date object, returns a Rational + * whose value is the difference between the two dates in days. + * If the other is a numeric value, returns a date object + * pointing +other+ days before self. + * If the other is a fractional number, * assumes its precision is at most nanosecond. * * Date.new(2001,2,3) - 1 #=> #<Date: 2001-02-02 ...> @@ -5973,9 +6554,9 @@ d_lite_minus(VALUE self, VALUE other) /* * call-seq: - * d.next_day([n=1]) -> date + * next_day(n = 1) -> new_date * - * This method is equivalent to d + n. + * Equivalent to Date#+ with argument +n+. */ static VALUE d_lite_next_day(int argc, VALUE *argv, VALUE self) @@ -5990,9 +6571,9 @@ d_lite_next_day(int argc, VALUE *argv, VALUE self) /* * call-seq: - * d.prev_day([n=1]) -> date + * prev_day(n = 1) -> new_date * - * This method is equivalent to d - n. + * Equivalent to Date#- with argument +n+. */ static VALUE d_lite_prev_day(int argc, VALUE *argv, VALUE self) @@ -6007,10 +6588,14 @@ d_lite_prev_day(int argc, VALUE *argv, VALUE self) /* * call-seq: - * d.succ -> date - * d.next -> date + * d.next -> new_date + * + * Returns a new \Date object representing the following day: + * + * d = Date.new(2001, 2, 3) + * d.to_s # => "2001-02-03" + * d.next.to_s # => "2001-02-04" * - * Returns a date object denoting the following day. */ static VALUE d_lite_next(VALUE self) @@ -6020,26 +6605,30 @@ d_lite_next(VALUE self) /* * call-seq: - * d >> n -> date + * d >> n -> new_date + * + * Returns a new \Date object representing the date + * +n+ months later; +n+ should be a numeric: * - * Returns a date object pointing +n+ months after self. - * The argument +n+ should be a numeric value. + * (Date.new(2001, 2, 3) >> 1).to_s # => "2001-03-03" + * (Date.new(2001, 2, 3) >> -2).to_s # => "2000-12-03" * - * Date.new(2001,2,3) >> 1 #=> #<Date: 2001-03-03 ...> - * Date.new(2001,2,3) >> -2 #=> #<Date: 2000-12-03 ...> + * When the same day does not exist for the new month, + * the last day of that month is used instead: * - * When the same day does not exist for the corresponding month, - * the last day of the month is used instead: + * (Date.new(2001, 1, 31) >> 1).to_s # => "2001-02-28" + * (Date.new(2001, 1, 31) >> -4).to_s # => "2000-09-30" * - * Date.new(2001,1,28) >> 1 #=> #<Date: 2001-02-28 ...> - * Date.new(2001,1,31) >> 1 #=> #<Date: 2001-02-28 ...> + * This results in the following, possibly unexpected, behaviors: * - * This also results in the following, possibly unexpected, behavior: + * d0 = Date.new(2001, 1, 31) + * d1 = d0 >> 1 # => #<Date: 2001-02-28> + * d2 = d1 >> 1 # => #<Date: 2001-03-28> * - * Date.new(2001,1,31) >> 2 #=> #<Date: 2001-03-31 ...> - * Date.new(2001,1,31) >> 1 >> 1 #=> #<Date: 2001-03-28 ...> + * d0 = Date.new(2001, 1, 31) + * d1 = d0 >> 1 # => #<Date: 2001-02-28> + * d2 = d1 >> -1 # => #<Date: 2001-01-28> * - * Date.new(2001,1,31) >> 1 >> -1 #=> #<Date: 2001-01-28 ...> */ static VALUE d_lite_rshift(VALUE self, VALUE other) @@ -6084,24 +6673,28 @@ d_lite_rshift(VALUE self, VALUE other) * call-seq: * d << n -> date * - * Returns a date object pointing +n+ months before self. - * The argument +n+ should be a numeric value. + * Returns a new \Date object representing the date + * +n+ months earlier; +n+ should be a numeric: * - * Date.new(2001,2,3) << 1 #=> #<Date: 2001-01-03 ...> - * Date.new(2001,2,3) << -2 #=> #<Date: 2001-04-03 ...> + * (Date.new(2001, 2, 3) << 1).to_s # => "2001-01-03" + * (Date.new(2001, 2, 3) << -2).to_s # => "2001-04-03" * - * When the same day does not exist for the corresponding month, - * the last day of the month is used instead: + * When the same day does not exist for the new month, + * the last day of that month is used instead: * - * Date.new(2001,3,28) << 1 #=> #<Date: 2001-02-28 ...> - * Date.new(2001,3,31) << 1 #=> #<Date: 2001-02-28 ...> + * (Date.new(2001, 3, 31) << 1).to_s # => "2001-02-28" + * (Date.new(2001, 3, 31) << -6).to_s # => "2001-09-30" * - * This also results in the following, possibly unexpected, behavior: + * This results in the following, possibly unexpected, behaviors: * - * Date.new(2001,3,31) << 2 #=> #<Date: 2001-01-31 ...> - * Date.new(2001,3,31) << 1 << 1 #=> #<Date: 2001-01-28 ...> + * d0 = Date.new(2001, 3, 31) + * d0 << 2 # => #<Date: 2001-01-31> + * d0 << 1 << 1 # => #<Date: 2001-01-28> + * + * d0 = Date.new(2001, 3, 31) + * d1 = d0 << 1 # => #<Date: 2001-02-28> + * d2 = d1 << -1 # => #<Date: 2001-03-28> * - * Date.new(2001,3,31) << 1 << -1 #=> #<Date: 2001-03-28 ...> */ static VALUE d_lite_lshift(VALUE self, VALUE other) @@ -6112,11 +6705,9 @@ d_lite_lshift(VALUE self, VALUE other) /* * call-seq: - * d.next_month([n=1]) -> date - * - * This method is equivalent to d >> n. + * next_month(n = 1) -> new_date * - * See Date#>> for examples. + * Equivalent to #>> with argument +n+. */ static VALUE d_lite_next_month(int argc, VALUE *argv, VALUE self) @@ -6131,11 +6722,9 @@ d_lite_next_month(int argc, VALUE *argv, VALUE self) /* * call-seq: - * d.prev_month([n=1]) -> date - * - * This method is equivalent to d << n. + * prev_month(n = 1) -> new_date * - * See Date#<< for examples. + * Equivalent to #<< with argument +n+. */ static VALUE d_lite_prev_month(int argc, VALUE *argv, VALUE self) @@ -6150,15 +6739,9 @@ d_lite_prev_month(int argc, VALUE *argv, VALUE self) /* * call-seq: - * d.next_year([n=1]) -> date + * next_year(n = 1) -> new_date * - * This method is equivalent to d >> (n * 12). - * - * Date.new(2001,2,3).next_year #=> #<Date: 2002-02-03 ...> - * Date.new(2008,2,29).next_year #=> #<Date: 2009-02-28 ...> - * Date.new(2008,2,29).next_year(4) #=> #<Date: 2012-02-29 ...> - * - * See also Date#>>. + * Equivalent to #>> with argument <tt>n * 12</tt>. */ static VALUE d_lite_next_year(int argc, VALUE *argv, VALUE self) @@ -6173,15 +6756,9 @@ d_lite_next_year(int argc, VALUE *argv, VALUE self) /* * call-seq: - * d.prev_year([n=1]) -> date - * - * This method is equivalent to d << (n * 12). - * - * Date.new(2001,2,3).prev_year #=> #<Date: 2000-02-03 ...> - * Date.new(2008,2,29).prev_year #=> #<Date: 2007-02-28 ...> - * Date.new(2008,2,29).prev_year(4) #=> #<Date: 2004-02-29 ...> + * prev_year(n = 1) -> new_date * - * See also Date#<<. + * Equivalent to #<< with argument <tt>n * 12</tt>. */ static VALUE d_lite_prev_year(int argc, VALUE *argv, VALUE self) @@ -6198,14 +6775,33 @@ static VALUE d_lite_cmp(VALUE, VALUE); /* * call-seq: - * d.step(limit[, step=1]) -> enumerator - * d.step(limit[, step=1]){|date| ...} -> self + * step(limit, step = 1){|date| ... } -> self + * + * Calls the block with specified dates; + * returns +self+. + * + * - The first +date+ is +self+. + * - Each successive +date+ is <tt>date + step</tt>, + * where +step+ is the numeric step size in days. + * - The last date is the last one that is before or equal to +limit+, + * which should be a \Date object. + * + * Example: + * + * limit = Date.new(2001, 12, 31) + * Date.new(2001).step(limit){|date| p date.to_s if date.mday == 31 } * - * Iterates evaluation of the given block, which takes a date object. - * The limit should be a date object. + * Output: * - * Date.new(2001).step(Date.new(2001,-1,-1)).select{|d| d.sunday?}.size - * #=> 52 + * "2001-01-31" + * "2001-03-31" + * "2001-05-31" + * "2001-07-31" + * "2001-08-31" + * "2001-10-31" + * "2001-12-31" + * + * Returns an Enumerator if no block is given. */ static VALUE d_lite_step(int argc, VALUE *argv, VALUE self) @@ -6248,10 +6844,9 @@ d_lite_step(int argc, VALUE *argv, VALUE self) /* * call-seq: - * d.upto(max) -> enumerator - * d.upto(max){|date| ...} -> self + * upto(max){|date| ... } -> self * - * This method is equivalent to step(max, 1){|date| ...}. + * Equivalent to #step with arguments +max+ and +1+. */ static VALUE d_lite_upto(VALUE self, VALUE max) @@ -6270,10 +6865,9 @@ d_lite_upto(VALUE self, VALUE max) /* * call-seq: - * d.downto(min) -> enumerator - * d.downto(min){|date| ...} -> self + * downto(min){|date| ... } -> self * - * This method is equivalent to step(min, -1){|date| ...}. + * Equivalent to #step with arguments +min+ and <tt>-1</tt>. */ static VALUE d_lite_downto(VALUE self, VALUE min) @@ -6361,19 +6955,43 @@ cmp_dd(VALUE self, VALUE other) /* * call-seq: - * d <=> other -> -1, 0, +1 or nil + * self <=> other -> -1, 0, 1 or nil + * + * Compares +self+ and +other+, returning: + * + * - <tt>-1</tt> if +other+ is larger. + * - <tt>0</tt> if the two are equal. + * - <tt>1</tt> if +other+ is smaller. + * - +nil+ if the two are incomparable. + * + * Argument +other+ may be: + * + * - Another \Date object: + * + * d = Date.new(2022, 7, 27) # => #<Date: 2022-07-27 ((2459788j,0s,0n),+0s,2299161j)> + * prev_date = d.prev_day # => #<Date: 2022-07-26 ((2459787j,0s,0n),+0s,2299161j)> + * next_date = d.next_day # => #<Date: 2022-07-28 ((2459789j,0s,0n),+0s,2299161j)> + * d <=> next_date # => -1 + * d <=> d # => 0 + * d <=> prev_date # => 1 + * + * - A DateTime object: * - * 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. + * d <=> DateTime.new(2022, 7, 26) # => 1 + * d <=> DateTime.new(2022, 7, 27) # => 0 + * d <=> DateTime.new(2022, 7, 28) # => -1 * - * 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 + * - A numeric (compares <tt>self.ajd</tt> to +other+): + * + * d <=> 2459788 # => -1 + * d <=> 2459787 # => 1 + * d <=> 2459786 # => 1 + * d <=> d.ajd # => 0 + * + * - Any other object: + * + * d <=> Object.new # => nil * - * See also Comparable. */ static VALUE d_lite_cmp(VALUE self, VALUE other) @@ -6433,20 +7051,39 @@ equal_gen(VALUE self, VALUE other) /* * 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 + * self === other -> true, false, or nil. + * + * Returns +true+ if +self+ and +other+ represent the same date, + * +false+ if not, +nil+ if the two are not comparable. + * + * Argument +other+ may be: + * + * - Another \Date object: + * + * d = Date.new(2022, 7, 27) # => #<Date: 2022-07-27 ((2459788j,0s,0n),+0s,2299161j)> + * prev_date = d.prev_day # => #<Date: 2022-07-26 ((2459787j,0s,0n),+0s,2299161j)> + * next_date = d.next_day # => #<Date: 2022-07-28 ((2459789j,0s,0n),+0s,2299161j)> + * d === prev_date # => false + * d === d # => true + * d === next_date # => false + * + * - A DateTime object: + * + * d === DateTime.new(2022, 7, 26) # => false + * d === DateTime.new(2022, 7, 27) # => true + * d === DateTime.new(2022, 7, 28) # => false + * + * - A numeric (compares <tt>self.jd</tt> to +other+): + * + * d === 2459788 # => true + * d === 2459787 # => false + * d === 2459786 # => false + * d === d.jd # => true + * + * - An object not comparable: + * + * d === Object.new # => nil + * */ static VALUE d_lite_equal(VALUE self, VALUE other) @@ -6491,13 +7128,24 @@ d_lite_eql_p(VALUE self, VALUE other) static VALUE d_lite_hash(VALUE self) { - st_index_t v, h[4]; + st_index_t v, h[5]; + VALUE nth; get_d1(self); - h[0] = m_nth(dat); - h[1] = m_jd(dat); - h[2] = m_df(dat); - h[3] = m_sf(dat); + nth = m_nth(dat); + + if (FIXNUM_P(nth)) { + h[0] = 0; + h[1] = (st_index_t)nth; + } else { + h[0] = 1; + h[1] = (st_index_t)FIX2LONG(rb_hash(nth)); + } + + h[2] = m_jd(dat); + h[3] = m_df(dat); + h[4] = m_sf(dat); + v = rb_memhash(h, sizeof(h)); return ST2FIX(v); } @@ -6509,12 +7157,14 @@ static VALUE strftimev(const char *, VALUE, /* * call-seq: - * d.to_s -> string + * to_s -> string * - * Returns a string in an ISO 8601 format. (This method doesn't use the - * expanded representations.) + * Returns a string representation of the date in +self+ + * in {ISO 8601 extended date format}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications] + * (<tt>'%Y-%m-%d'</tt>): + * + * Date.new(2001, 2, 3).to_s # => "2001-02-03" * - * Date.new(2001,2,3).to_s #=> "2001-02-03" */ static VALUE d_lite_to_s(VALUE self) @@ -6523,6 +7173,7 @@ d_lite_to_s(VALUE self) } #ifndef NDEBUG +/* :nodoc: */ static VALUE mk_inspect_raw(union DateData *x, VALUE klass) { @@ -6572,6 +7223,7 @@ mk_inspect_raw(union DateData *x, VALUE klass) } } +/* :nodoc: */ static VALUE d_lite_inspect_raw(VALUE self) { @@ -6593,14 +7245,13 @@ mk_inspect(union DateData *x, VALUE klass, VALUE to_s) /* * call-seq: - * d.inspect -> string + * inspect -> string * - * Returns the value as a string for inspection. + * Returns a string representation of +self+: + * + * Date.new(2001, 2, 3).inspect + * # => "#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>" * - * Date.new(2001,2,3).inspect - * #=> "#<Date: 2001-02-03>" - * DateTime.new(2001,2,3,4,5,6,'-7').inspect - * #=> "#<DateTime: 2001-02-03T04:05:06-07:00>" */ static VALUE d_lite_inspect(VALUE self) @@ -6782,180 +7433,16 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self, /* * call-seq: - * d.strftime([format='%F']) -> string - * - * Formats date according to the directives in the given format - * string. - * The directives begin with a percent (%) character. - * Any text not listed as a directive will be passed through to the - * output string. - * - * A directive consists of a percent (%) character, - * zero or more flags, an optional minimum field width, - * an optional modifier, and a conversion specifier - * as follows. - * - * %<flags><width><modifier><conversion> - * - * 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..60) - * - * %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 - Equivalent to %:z (e.g. +09:00) - * - * 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 the 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 the 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") - * #=> #<DateTime: 2007-11-19T08:37:48-0600 ...> - * 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. + * strftime(format = '%F') -> string + * + * Returns a string representation of the date in +self+, + * formatted according the given +format+: + * + * Date.new(2001, 2, 3).strftime # => "2001-02-03" + * + * For other formats, see + * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]. + * */ static VALUE d_lite_strftime(int argc, VALUE *argv, VALUE self) @@ -6983,13 +7470,16 @@ strftimev(const char *fmt, VALUE self, /* * call-seq: - * d.asctime -> string - * d.ctime -> string + * asctime -> string * - * Returns a string in asctime(3) format (but without "\n\0" at the - * end). This method is equivalent to strftime('%c'). + * Equivalent to #strftime with argument <tt>'%a %b %e %T %Y'</tt> + * (or its {shorthand form}[rdoc-ref:language/strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] + * <tt>'%c'</tt>): + * + * Date.new(2001, 2, 3).asctime # => "Sat Feb 3 00:00:00 2001" + * + * See {asctime}[https://man7.org/linux/man-pages/man3/asctime.3p.html]. * - * See also asctime(3) or ctime(3). */ static VALUE d_lite_asctime(VALUE self) @@ -6999,10 +7489,14 @@ d_lite_asctime(VALUE self) /* * call-seq: - * d.iso8601 -> string - * d.xmlschema -> string + * iso8601 -> string + * + * Equivalent to #strftime with argument <tt>'%Y-%m-%d'</tt> + * (or its {shorthand form}[rdoc-ref:language/strftime_formatting.rdoc@Shorthand+Conversion+Specifiers] + * <tt>'%F'</tt>); + * + * Date.new(2001, 2, 3).iso8601 # => "2001-02-03" * - * This method is equivalent to strftime('%F'). */ static VALUE d_lite_iso8601(VALUE self) @@ -7012,9 +7506,13 @@ d_lite_iso8601(VALUE self) /* * call-seq: - * d.rfc3339 -> string + * rfc3339 -> string + * + * Equivalent to #strftime with argument <tt>'%FT%T%:z'</tt>; + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: + * + * Date.new(2001, 2, 3).rfc3339 # => "2001-02-03T00:00:00+00:00" * - * This method is equivalent to strftime('%FT%T%:z'). */ static VALUE d_lite_rfc3339(VALUE self) @@ -7024,10 +7522,13 @@ d_lite_rfc3339(VALUE self) /* * call-seq: - * d.rfc2822 -> string - * d.rfc822 -> string + * rfc2822 -> string + * + * Equivalent to #strftime with argument <tt>'%a, %-d %b %Y %T %z'</tt>; + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: + * + * Date.new(2001, 2, 3).rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000" * - * This method is equivalent to strftime('%a, %-d %b %Y %T %z'). */ static VALUE d_lite_rfc2822(VALUE self) @@ -7037,10 +7538,13 @@ d_lite_rfc2822(VALUE self) /* * call-seq: - * d.httpdate -> string + * httpdate -> string + * + * Equivalent to #strftime with argument <tt>'%a, %d %b %Y %T GMT'</tt>; + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: + * + * Date.new(2001, 2, 3).httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT" * - * This method is equivalent to strftime('%a, %d %b %Y %T GMT'). - * See also RFC 2616. */ static VALUE d_lite_httpdate(VALUE self) @@ -7091,11 +7595,13 @@ jisx0301_date_format(char *fmt, size_t size, VALUE jd, VALUE y) /* * call-seq: - * d.jisx0301 -> string + * jisx0301 -> string * - * Returns a string in a JIS X 0301 format. + * Returns a string representation of the date in +self+ + * in JIS X 0301 format. + * + * Date.new(2001, 2, 3).jisx0301 # => "H13.02.03" * - * Date.new(2001,2,3).jisx0301 #=> "H13.02.03" */ static VALUE d_lite_jisx0301(VALUE self) @@ -7110,7 +7616,98 @@ d_lite_jisx0301(VALUE self) return strftimev(fmt, self, set_tmx); } +static VALUE +deconstruct_keys(VALUE self, VALUE keys, int is_datetime) +{ + VALUE h = rb_hash_new(); + long i; + + get_d1(self); + + if (NIL_P(keys)) { + rb_hash_aset(h, sym_year, m_real_year(dat)); + rb_hash_aset(h, sym_month, INT2FIX(m_mon(dat))); + rb_hash_aset(h, sym_day, INT2FIX(m_mday(dat))); + rb_hash_aset(h, sym_yday, INT2FIX(m_yday(dat))); + rb_hash_aset(h, sym_wday, INT2FIX(m_wday(dat))); + if (is_datetime) { + rb_hash_aset(h, sym_hour, INT2FIX(m_hour(dat))); + rb_hash_aset(h, sym_min, INT2FIX(m_min(dat))); + rb_hash_aset(h, sym_sec, INT2FIX(m_sec(dat))); + rb_hash_aset(h, sym_sec_fraction, m_sf_in_sec(dat)); + rb_hash_aset(h, sym_zone, m_zone(dat)); + } + + return h; + } + if (!RB_TYPE_P(keys, T_ARRAY)) { + rb_raise(rb_eTypeError, + "wrong argument type %"PRIsVALUE" (expected Array or nil)", + rb_obj_class(keys)); + + } + + for (i=0; i<RARRAY_LEN(keys); i++) { + VALUE key = RARRAY_AREF(keys, i); + + if (sym_year == key) rb_hash_aset(h, key, m_real_year(dat)); + if (sym_month == key) rb_hash_aset(h, key, INT2FIX(m_mon(dat))); + if (sym_day == key) rb_hash_aset(h, key, INT2FIX(m_mday(dat))); + if (sym_yday == key) rb_hash_aset(h, key, INT2FIX(m_yday(dat))); + if (sym_wday == key) rb_hash_aset(h, key, INT2FIX(m_wday(dat))); + if (is_datetime) { + if (sym_hour == key) rb_hash_aset(h, key, INT2FIX(m_hour(dat))); + if (sym_min == key) rb_hash_aset(h, key, INT2FIX(m_min(dat))); + if (sym_sec == key) rb_hash_aset(h, key, INT2FIX(m_sec(dat))); + if (sym_sec_fraction == key) rb_hash_aset(h, key, m_sf_in_sec(dat)); + if (sym_zone == key) rb_hash_aset(h, key, m_zone(dat)); + } + } + return h; +} + +/* + * call-seq: + * deconstruct_keys(array_of_names_or_nil) -> hash + * + * Returns a hash of the name/value pairs, to use in pattern matching. + * Possible keys are: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, + * <tt>:wday</tt>, <tt>:yday</tt>. + * + * Possible usages: + * + * d = Date.new(2022, 10, 5) + * + * if d in wday: 3, day: ..7 # uses deconstruct_keys underneath + * puts "first Wednesday of the month" + * end + * #=> prints "first Wednesday of the month" + * + * case d + * in year: ...2022 + * puts "too old" + * in month: ..9 + * puts "quarter 1-3" + * in wday: 1..5, month: + * puts "working day in month #{month}" + * end + * #=> prints "working day in month 10" + * + * Note that deconstruction by pattern can also be combined with class check: + * + * if d in Date(wday: 3, day: ..7) + * puts "first Wednesday of the month" + * end + * + */ +static VALUE +d_lite_deconstruct_keys(VALUE self, VALUE keys) +{ + return deconstruct_keys(self, keys, /* is_datetime=false */ 0); +} + #ifndef NDEBUG +/* :nodoc: */ static VALUE d_lite_marshal_dump_old(VALUE self) { @@ -7123,10 +7720,7 @@ d_lite_marshal_dump_old(VALUE self) 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); - } + rb_copy_generic_ivar(a, self); return a; } @@ -7148,10 +7742,8 @@ d_lite_marshal_dump(VALUE self) 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); - } + + rb_copy_generic_ivar(a, self); return a; } @@ -7224,10 +7816,7 @@ d_lite_marshal_load(VALUE self, VALUE a) HAVE_JD | HAVE_DF); } - if (FL_TEST(a, FL_EXIVAR)) { - rb_copy_generic_ivar(self, a); - FL_SET(self, FL_EXIVAR); - } + rb_copy_generic_ivar(self, a); return self; } @@ -7398,17 +7987,7 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) } /* - * 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 DateTime object denoting the given calendar date. - * - * DateTime.new(2001,2,3) #=> #<DateTime: 2001-02-03T00:00:00+00:00 ...> - * DateTime.new(2001,2,3,4,5,6,'+7') - * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> - * DateTime.new(2001,-11,-26,-20,-55,-54,'+7') - * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> + * Same as DateTime.new. */ static VALUE datetime_s_civil(int argc, VALUE *argv, VALUE klass) @@ -7598,6 +8177,7 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass) } #ifndef NDEBUG +/* :nodoc: */ static VALUE datetime_s_weeknum(int argc, VALUE *argv, VALUE klass) { @@ -7667,6 +8247,7 @@ datetime_s_weeknum(int argc, VALUE *argv, VALUE klass) return ret; } +/* :nodoc: */ static VALUE datetime_s_nth_kday(int argc, VALUE *argv, VALUE klass) { @@ -7791,7 +8372,7 @@ datetime_s_now(int argc, VALUE *argv, VALUE klass) #ifdef HAVE_STRUCT_TM_TM_GMTOFF of = tm.tm_gmtoff; #elif defined(HAVE_TIMEZONE) -#ifdef HAVE_ALTZONE +#if defined(HAVE_ALTZONE) && !defined(_AIX) of = (long)-((tm.tm_isdst > 0) ? altzone : timezone); #else of = (long)-timezone; @@ -7988,7 +8569,7 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass) switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: fmt = rb_str_new2("%FT%T%z"); case 2: @@ -8007,14 +8588,14 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]]) -> datetime + * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]], limit: 128) -> datetime * * Parses the given representation of date and time, and creates a * DateTime object. * - * This method **does not** function as a validator. If the input + * This method *does* *not* function as a validator. If the input * string does not match valid formats strictly, you may get a cryptic - * result. Should consider to use `DateTime.strptime` instead of this + * result. Should consider to use DateTime.strptime instead of this * method as possible. * * If the optional second argument is true and the detected year is in @@ -8026,17 +8607,21 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass) * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> * DateTime.parse('3rd Feb 2001 04:05:06 PM') * #=> #<DateTime: 2001-02-03T16:05:06+00:00 ...> + * + * Raise an ArgumentError when the string length is longer than _limit_. + * You can stop this check by passing <code>limit: nil</code>, but note + * that it may take a long time to parse. */ static VALUE datetime_s_parse(int argc, VALUE *argv, VALUE klass) { - VALUE str, comp, sg; + VALUE str, comp, sg, opt; - rb_scan_args(argc, argv, "03", &str, &comp, &sg); + argc = rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: comp = Qtrue; case 2: @@ -8044,18 +8629,20 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass) } { - VALUE argv2[2], hash; - - argv2[0] = str; - argv2[1] = comp; - hash = date_s__parse(2, argv2, klass); + int argc2 = 2; + VALUE argv2[3], hash; + argv2[0] = str; + argv2[1] = comp; + argv2[2] = opt; + if (!NIL_P(opt)) argc2++; + hash = date_s__parse(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: - * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime + * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime * * Creates a new DateTime object by parsing from a string according to * some typical ISO 8601 formats. @@ -8066,114 +8653,150 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass) * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> * DateTime.iso8601('2001-W05-6T04:05:06+07:00') * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> + * + * Raise an ArgumentError when the string length is longer than _limit_. + * You can stop this check by passing <code>limit: nil</code>, but note + * that it may take a long time to parse. */ static VALUE datetime_s_iso8601(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__iso8601(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + argv2[1] = opt; + if (!NIL_P(opt)) argc2++; + hash = date_s__iso8601(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: - * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime + * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime * * Creates a new DateTime object by parsing from a string according to * some typical RFC 3339 formats. * * DateTime.rfc3339('2001-02-03T04:05:06+07:00') * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> + * + * Raise an ArgumentError when the string length is longer than _limit_. + * You can stop this check by passing <code>limit: nil</code>, but note + * that it may take a long time to parse. */ static VALUE datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__rfc3339(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + argv2[1] = opt; + if (!NIL_P(opt)) argc2++; + hash = date_s__rfc3339(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: - * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime + * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime * * Creates a new DateTime object by parsing from a string according to * some typical XML Schema formats. * * DateTime.xmlschema('2001-02-03T04:05:06+07:00') * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> + * + * Raise an ArgumentError when the string length is longer than _limit_. + * You can stop this check by passing <code>limit: nil</code>, but note + * that it may take a long time to parse. */ static VALUE datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__xmlschema(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + argv2[1] = opt; + if (!NIL_P(opt)) argc2++; + hash = date_s__xmlschema(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: - * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime - * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime + * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime + * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime * * Creates a new DateTime object by parsing from a string according to * some typical RFC 2822 formats. * * DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700') * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> + * + * Raise an ArgumentError when the string length is longer than _limit_. + * You can stop this check by passing <code>limit: nil</code>, but note + * that it may take a long time to parse. */ static VALUE datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 1 Jan -4712 00:00:00 +0000"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_RFC3339); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__rfc2822(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + argv2[1] = opt; + if (!NIL_P(opt)) argc2++; + hash = date_s__rfc2822(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } @@ -8187,30 +8810,39 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass) * * DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT') * #=> #<DateTime: 2001-02-03T04:05:06+00:00 ...> + * + * Raise an ArgumentError when the string length is longer than _limit_. + * You can stop this check by passing <code>limit: nil</code>, but note + * that it may take a long time to parse. */ static VALUE datetime_s_httpdate(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("Mon, 01 Jan -4712 00:00:00 GMT"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME_HTTPDATE); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__httpdate(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + argv2[1] = opt; + if (!NIL_P(opt)) argc2++; + hash = date_s__httpdate(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } /* * call-seq: - * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime + * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime * * Creates a new DateTime object by parsing from a string according to * some typical JIS X 0301 formats. @@ -8222,23 +8854,32 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass) * * DateTime.jisx0301('13.02.03T04:05:06+07:00') * #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...> + * + * Raise an ArgumentError when the string length is longer than _limit_. + * You can stop this check by passing <code>limit: nil</code>, but note + * that it may take a long time to parse. */ static VALUE datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass) { - VALUE str, sg; + VALUE str, sg, opt; - rb_scan_args(argc, argv, "02", &str, &sg); + argc = rb_scan_args(argc, argv, "02:", &str, &sg, &opt); switch (argc) { case 0: - str = rb_str_new2("-4712-01-01T00:00:00+00:00"); + str = rb_str_new2(JULIAN_EPOCH_DATETIME); case 1: sg = INT2FIX(DEFAULT_SG); } { - VALUE hash = date_s__jisx0301(klass, str); + int argc2 = 1; + VALUE argv2[2], hash; + argv2[0] = str; + argv2[1] = opt; + if (!NIL_P(opt)) argc2++; + hash = date_s__jisx0301(argc2, argv2, klass); return dt_new_by_frags(klass, hash, sg); } } @@ -8261,181 +8902,16 @@ dt_lite_to_s(VALUE self) /* * call-seq: - * dt.strftime([format='%FT%T%:z']) -> string - * - * Formats date according to the directives in the given format - * string. - * The directives begin with a percent (%) character. - * Any text not listed as a directive will be passed through to the - * output string. - * - * A directive consists of a percent (%) character, - * zero or more flags, an optional minimum field width, - * an optional modifier, and a conversion specifier - * as follows. - * - * %<flags><width><modifier><conversion> - * - * 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 modifiers are "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..60) - * - * %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 - Equivalent to %:z (e.g. +09:00) - * - * 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 the 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 the 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") - * #=> #<DateTime: 2007-11-19T08:37:48-0600 ...> - * 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. + * strftime(format = '%FT%T%:z') -> string + * + * Returns a string representation of +self+, + * formatted according the given +format: + * + * DateTime.now.strftime # => "2022-07-01T11:03:19-05:00" + * + * For other formats, + * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]: + * */ static VALUE dt_lite_strftime(int argc, VALUE *argv, VALUE self) @@ -8523,6 +8999,47 @@ dt_lite_jisx0301(int argc, VALUE *argv, VALUE self) iso8601_timediv(self, n)); } +/* + * call-seq: + * deconstruct_keys(array_of_names_or_nil) -> hash + * + * Returns a hash of the name/value pairs, to use in pattern matching. + * Possible keys are: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, + * <tt>:wday</tt>, <tt>:yday</tt>, <tt>:hour</tt>, <tt>:min</tt>, + * <tt>:sec</tt>, <tt>:sec_fraction</tt>, <tt>:zone</tt>. + * + * Possible usages: + * + * dt = DateTime.new(2022, 10, 5, 13, 30) + * + * if d in wday: 1..5, hour: 10..18 # uses deconstruct_keys underneath + * puts "Working time" + * end + * #=> prints "Working time" + * + * case dt + * in year: ...2022 + * puts "too old" + * in month: ..9 + * puts "quarter 1-3" + * in wday: 1..5, month: + * puts "working day in month #{month}" + * end + * #=> prints "working day in month 10" + * + * Note that deconstruction by pattern can also be combined with class check: + * + * if d in DateTime(wday: 1..5, hour: 10..18, day: ..7) + * puts "Working time, first week of the month" + * end + * + */ +static VALUE +dt_lite_deconstruct_keys(VALUE self, VALUE keys) +{ + return deconstruct_keys(self, keys, /* is_datetime=true */ 1); +} + /* conversions */ #define f_subsec(x) rb_funcall(x, rb_intern("subsec"), 0) @@ -8601,7 +9118,7 @@ time_to_datetime(VALUE self) ret = d_complex_new_internal(cDateTime, nth, 0, 0, sf, - of, DEFAULT_SG, + of, GREGORIAN, ry, m, d, h, min, s, HAVE_CIVIL | HAVE_TIME); @@ -8614,33 +9131,43 @@ time_to_datetime(VALUE self) /* * call-seq: - * d.to_time -> time + * to_time -> time + * + * Returns a new Time object with the same value as +self+; + * if +self+ is a Julian date, derives its Gregorian date + * for conversion to the \Time object: + * + * Date.new(2001, 2, 3).to_time # => 2001-02-03 00:00:00 -0600 + * Date.new(2001, 2, 3, Date::JULIAN).to_time # => 2001-02-16 00:00:00 -0600 * - * Returns a Time object which denotes self. If self is a julian date, - * convert it to a gregorian date before converting it to Time. */ static VALUE date_to_time(VALUE self) { + VALUE t; + get_d1a(self); if (m_julian_p(adat)) { - VALUE tmp = d_lite_gregorian(self); - get_d1b(tmp); + VALUE g = d_lite_gregorian(self); + get_d1b(g); adat = bdat; + self = g; } - return f_local3(rb_cTime, + t = f_local3(rb_cTime, m_real_year(adat), INT2FIX(m_mon(adat)), INT2FIX(m_mday(adat))); + RB_GC_GUARD(self); /* may be the converted gregorian */ + return t; } /* * call-seq: - * d.to_date -> self + * to_date -> self * - * Returns self. + * Returns +self+. */ static VALUE date_to_date(VALUE self) @@ -8652,7 +9179,10 @@ date_to_date(VALUE self) * call-seq: * d.to_datetime -> datetime * - * Returns a DateTime object which denotes self. + * Returns a DateTime whose value is the same as +self+: + * + * Date.new(2001, 2, 3).to_datetime # => #<DateTime: 2001-02-03T00:00:00+00:00> + * */ static VALUE date_to_datetime(VALUE self) @@ -8697,12 +9227,18 @@ date_to_datetime(VALUE self) static VALUE datetime_to_time(VALUE self) { - volatile VALUE dup = dup_obj(self); + get_d1(self); + + if (m_julian_p(dat)) { + VALUE g = d_lite_gregorian(self); + get_d1a(g); + dat = adat; + self = g; + } + { VALUE t; - get_d1(dup); - t = rb_funcall(rb_cTime, rb_intern("new"), 7, @@ -8714,6 +9250,7 @@ datetime_to_time(VALUE self) f_add(INT2FIX(m_sec(dat)), m_sf_in_sec(dat)), INT2FIX(m_of(dat))); + RB_GC_GUARD(self); /* may be the converted gregorian */ return t; } } @@ -8770,6 +9307,7 @@ datetime_to_datetime(VALUE self) #define MIN_JD -327 #define MAX_JD 366963925 +/* :nodoc: */ static int test_civil(int from, int to, double sg) { @@ -8790,6 +9328,7 @@ test_civil(int from, int to, double sg) return 1; } +/* :nodoc: */ static VALUE date_s_test_civil(VALUE klass) { @@ -8810,6 +9349,7 @@ date_s_test_civil(VALUE klass) return Qtrue; } +/* :nodoc: */ static int test_ordinal(int from, int to, double sg) { @@ -8830,6 +9370,7 @@ test_ordinal(int from, int to, double sg) return 1; } +/* :nodoc: */ static VALUE date_s_test_ordinal(VALUE klass) { @@ -8850,6 +9391,7 @@ date_s_test_ordinal(VALUE klass) return Qtrue; } +/* :nodoc: */ static int test_commercial(int from, int to, double sg) { @@ -8870,6 +9412,7 @@ test_commercial(int from, int to, double sg) return 1; } +/* :nodoc: */ static VALUE date_s_test_commercial(VALUE klass) { @@ -8890,6 +9433,7 @@ date_s_test_commercial(VALUE klass) return Qtrue; } +/* :nodoc: */ static int test_weeknum(int from, int to, int f, double sg) { @@ -8910,6 +9454,7 @@ test_weeknum(int from, int to, int f, double sg) return 1; } +/* :nodoc: */ static VALUE date_s_test_weeknum(VALUE klass) { @@ -8934,6 +9479,7 @@ date_s_test_weeknum(VALUE klass) return Qtrue; } +/* :nodoc: */ static int test_nth_kday(int from, int to, double sg) { @@ -8954,6 +9500,7 @@ test_nth_kday(int from, int to, double sg) return 1; } +/* :nodoc: */ static VALUE date_s_test_nth_kday(VALUE klass) { @@ -8974,6 +9521,7 @@ date_s_test_nth_kday(VALUE klass) return Qtrue; } +/* :nodoc: */ static int test_unit_v2v(VALUE i, VALUE (* conv1)(VALUE), @@ -8985,6 +9533,7 @@ test_unit_v2v(VALUE i, return f_eqeq_p(o, i); } +/* :nodoc: */ static int test_unit_v2v_iter2(VALUE (* conv1)(VALUE), VALUE (* conv2)(VALUE)) @@ -9016,6 +9565,7 @@ test_unit_v2v_iter2(VALUE (* conv1)(VALUE), return 1; } +/* :nodoc: */ static int test_unit_v2v_iter(VALUE (* conv1)(VALUE), VALUE (* conv2)(VALUE)) @@ -9027,6 +9577,7 @@ test_unit_v2v_iter(VALUE (* conv1)(VALUE), return 1; } +/* :nodoc: */ static VALUE date_s_test_unit_conv(VALUE klass) { @@ -9041,6 +9592,7 @@ date_s_test_unit_conv(VALUE klass) return Qtrue; } +/* :nodoc: */ static VALUE date_s_test_all(VALUE klass) { @@ -9103,10 +9655,11 @@ mk_ary_of_str(long len, const char *a[]) } rb_ary_push(o, e); } - rb_obj_freeze(o); + rb_ary_freeze(o); return o; } +/* :nodoc: */ static VALUE d_lite_zero(VALUE x) { @@ -9116,15 +9669,27 @@ d_lite_zero(VALUE x) void Init_date_core(void) { -#undef rb_intern -#define rb_intern(str) rb_intern_const(str) - - id_cmp = rb_intern("<=>"); - id_le_p = rb_intern("<="); - id_ge_p = rb_intern(">="); - id_eqeq_p = rb_intern("=="); + #ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(true); + #endif + id_cmp = rb_intern_const("<=>"); + id_le_p = rb_intern_const("<="); + id_ge_p = rb_intern_const(">="); + id_eqeq_p = rb_intern_const("=="); + + sym_year = ID2SYM(rb_intern_const("year")); + sym_month = ID2SYM(rb_intern_const("month")); + sym_yday = ID2SYM(rb_intern_const("yday")); + sym_wday = ID2SYM(rb_intern_const("wday")); + sym_day = ID2SYM(rb_intern_const("day")); + sym_hour = ID2SYM(rb_intern_const("hour")); + sym_min = ID2SYM(rb_intern_const("min")); + sym_sec = ID2SYM(rb_intern_const("sec")); + sym_sec_fraction = ID2SYM(rb_intern_const("sec_fraction")); + sym_zone = ID2SYM(rb_intern_const("zone")); half_days_in_day = rb_rational_new2(INT2FIX(1), INT2FIX(2)); + rb_gc_register_mark_object(half_days_in_day); #if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS day_in_nanoseconds = LONG2NUM((long)DAY_IN_SECONDS * @@ -9136,160 +9701,87 @@ Init_date_core(void) 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 + * \Class \Date provides methods for storing and manipulating + * calendar dates. * - * The calendar date is a particular day of a calendar year, - * identified by its ordinal number within a calendar month within - * that year. + * Consider using + * {class Time}[rdoc-ref:Time] + * instead of class \Date if: * - * In those classes, this is so-called "civil". + * - You need both dates and times; \Date handles only dates. + * - You need only Gregorian dates (and not Julian dates); + * see {Julian and Gregorian Calendars}[rdoc-ref:language/calendars.rdoc]. * - * === Ordinal Date + * A \Date object, once created, is immutable, and cannot be modified. * - * The ordinal date is a particular day of a calendar year identified - * by its ordinal number within the year. + * == Creating a \Date * - * In those classes, this is so-called "ordinal". + * You can create a date for the current date, using Date.today: * - * === Week Date + * Date.today # => #<Date: 1999-12-31> * - * The week date is a date identified by calendar week and day numbers. + * You can create a specific date from various combinations of arguments: * - * 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. + * - Date.new takes integer year, month, and day-of-month: * - * In those classes, this is so-called "commercial". + * Date.new(1999, 12, 31) # => #<Date: 1999-12-31> * - * === Julian Day Number + * - Date.ordinal takes integer year and day-of-year: * - * The Julian day number is in elapsed days since noon (Greenwich Mean - * Time) on January 1, 4713 BCE (in the Julian calendar). + * Date.ordinal(1999, 365) # => #<Date: 1999-12-31> * - * In this document, the astronomical Julian day number is the 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. + * - Date.jd takes integer Julian day: * - * In this document, when the term "Julian day number" simply appears, - * it just refers to "chronological Julian day number", not the - * original. + * Date.jd(2451544) # => #<Date: 1999-12-31> * - * In those classes, those are so-called "ajd" and "jd". + * - Date.commercial takes integer commercial data (year, week, day-of-week): * - * === Modified Julian Day Number + * Date.commercial(1999, 52, 5) # => #<Date: 1999-12-31> * - * The modified Julian day number is in elapsed days since midnight - * (Coordinated Universal Time) on November 17, 1858 CE (in the - * Gregorian calendar). + * - Date.parse takes a string, which it parses heuristically: * - * In this document, the astronomical modified Julian day number is - * the 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. + * Date.parse('1999-12-31') # => #<Date: 1999-12-31> + * Date.parse('31-12-1999') # => #<Date: 1999-12-31> + * Date.parse('1999-365') # => #<Date: 1999-12-31> + * Date.parse('1999-W52-5') # => #<Date: 1999-12-31> * - * In this document, when the term "modified Julian day number" simply - * appears, it just refers to "chronological modified Julian day - * number", not the original. + * - Date.strptime takes a date string and a format string, + * then parses the date string according to the format string: * - * In those classes, those are so-called "amjd" and "mjd". + * Date.strptime('1999-12-31', '%Y-%m-%d') # => #<Date: 1999-12-31> + * Date.strptime('31-12-1999', '%d-%m-%Y') # => #<Date: 1999-12-31> + * Date.strptime('1999-365', '%Y-%j') # => #<Date: 1999-12-31> + * Date.strptime('1999-W52-5', '%G-W%V-%u') # => #<Date: 1999-12-31> + * Date.strptime('1999 52 5', '%Y %U %w') # => #<Date: 1999-12-31> + * Date.strptime('1999 52 5', '%Y %W %u') # => #<Date: 1999-12-31> + * Date.strptime('fri31dec99', '%a%d%b%y') # => #<Date: 1999-12-31> * - * == Date + * See also the specialized methods in + * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings] * - * A subclass of Object that includes the Comparable module and - * easily handles date. + * == Argument +limit+ * - * A Date object is created with Date::new, Date::jd, Date::ordinal, - * Date::commercial, Date::parse, Date::strptime, Date::today, - * Time#to_date, etc. + * Certain singleton methods in \Date that parse string arguments + * also take optional keyword argument +limit+, + * which can limit the length of the string argument. * - * require 'date' - * - * Date.new(2001,2,3) - * #=> #<Date: 2001-02-03 ...> - * Date.jd(2451944) - * #=> #<Date: 2001-02-03 ...> - * Date.ordinal(2001,34) - * #=> #<Date: 2001-02-03 ...> - * Date.commercial(2001,5,6) - * #=> #<Date: 2001-02-03 ...> - * Date.parse('2001-02-03') - * #=> #<Date: 2001-02-03 ...> - * Date.strptime('03-02-2001', '%d-%m-%Y') - * #=> #<Date: 2001-02-03 ...> - * Time.new(2001,2,3).to_date - * #=> #<Date: 2001-02-03 ...> - * - * All date objects are immutable; hence cannot modify themselves. - * - * The concept of a 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. - * - * A Date object can be created with an optional argument, - * the day of calendar reform as a Julian day number, which - * should be 2298874 to 2426355 or negative/positive infinity. - * The default value is +Date::ITALY+ (2299161=1582-10-15). - * See also sample/cal.rb. + * When +limit+ is: * - * $ 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 - * - * A Date object has various methods. See each reference. - * - * d = Date.parse('3rd Feb 2001') - * #=> #<Date: 2001-02-03 ...> - * d.year #=> 2001 - * d.mon #=> 2 - * d.mday #=> 3 - * d.wday #=> 6 - * d += 1 #=> #<Date: 2001-02-04 ...> - * d.strftime('%a %d %b %Y') #=> "Sun 04 Feb 2001" + * - Non-negative: + * raises ArgumentError if the string length is greater than _limit_. + * - Other numeric or +nil+: ignores +limit+. + * - Other non-numeric: raises TypeError. * */ cDate = rb_define_class("Date", rb_cObject); + + /* Exception for invalid date/time */ eDateError = rb_define_class_under(cDate, "Error", rb_eArgError); rb_include_module(cDate, rb_mComparable); @@ -9397,19 +9889,19 @@ Init_date_core(void) 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, "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, "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, "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, "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, "httpdate", date_s_httpdate, -1); - rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, 1); + rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, -1); rb_define_singleton_method(cDate, "jisx0301", date_s_jisx0301, -1); rb_define_method(cDate, "initialize", date_initialize, -1); @@ -9516,6 +10008,8 @@ Init_date_core(void) rb_define_method(cDate, "httpdate", d_lite_httpdate, 0); rb_define_method(cDate, "jisx0301", d_lite_jisx0301, 0); + rb_define_method(cDate, "deconstruct_keys", d_lite_deconstruct_keys, 1); + #ifndef NDEBUG rb_define_method(cDate, "marshal_dump_old", d_lite_marshal_dump_old, 0); #endif @@ -9529,6 +10023,8 @@ Init_date_core(void) * A subclass of Date that easily handles date, hour, minute, second, * and offset. * + * DateTime class is considered deprecated. Use Time class. + * * DateTime does not consider any leap seconds, does not track * any summer time rules. * @@ -9724,6 +10220,8 @@ Init_date_core(void) rb_define_method(cDateTime, "rfc3339", dt_lite_rfc3339, -1); rb_define_method(cDateTime, "jisx0301", dt_lite_jisx0301, -1); + rb_define_method(cDateTime, "deconstruct_keys", dt_lite_deconstruct_keys, 1); + /* conversions */ rb_define_method(rb_cTime, "to_time", time_to_time, 0); |
