date 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4bfc8d13122968d3e8be9e00abdcac1308a4b86f699d5747e492ee80d97ed13
4
- data.tar.gz: 579ae53dacefba07eabb2e2a6b9cada3c377efd0348d3290e4a6b392059f9883
3
+ metadata.gz: e3e5e30902faa5fe0f946df02df28a7d52b64e6a78d39e9b16ec9f0d88377646
4
+ data.tar.gz: 5a0c7a8ae91bbfd3a0fe6a152465217070ccbd7f5b5892c9657a865f47d6d492
5
5
  SHA512:
6
- metadata.gz: f75049bebd6c5c9acd3ed8290be91514f20cd275348e4c6d1887175ccac2c88890f0b07e8236d24bb37cab3223d7d1038e02726f880ec7c186a1ce300c196fe5
7
- data.tar.gz: c1d74e9cb222d4eb2f3b5715d6aa68a411b6b92ca05cc47babcee05343e455ba218a15b9c12f25d925887ea7a7dac0a203011a2e5e783269a2f16de921143fba
6
+ metadata.gz: bb10bbaa46008e4af4c8ba566aafbf71c45e3fcecd80b783b2ae06a0334df66ba49d4435ae969fb24b79e6ab2f53e16c80e1ae6f7b54f46314f87f7747702e89
7
+ data.tar.gz: b130c6501519228151df6df25cedae7457be32ed3c6f118f331ade479f14ec9bd7f7148ef48575e199cf987c2398d1688ddb3e9f45d6b08a8d919294998bda11
data/ext/date/date_core.c CHANGED
@@ -27,7 +27,7 @@ static VALUE eDateError;
27
27
  static VALUE half_days_in_day, day_in_nanoseconds;
28
28
  static double positive_inf, negative_inf;
29
29
 
30
- // used by deconstruct_keys
30
+ /* used by deconstruct_keys */
31
31
  static VALUE sym_year, sym_month, sym_day, sym_yday, sym_wday;
32
32
  static VALUE sym_hour, sym_min, sym_sec, sym_sec_fraction, sym_zone;
33
33
 
@@ -452,11 +452,43 @@ do {\
452
452
  static int c_valid_civil_p(int, int, int, double,
453
453
  int *, int *, int *, int *);
454
454
 
455
+ /* Check if using pure Gregorian calendar (sg == -Infinity) */
456
+ #define c_gregorian_only_p(sg) (isinf(sg) && (sg) < 0)
457
+
458
+ /*
459
+ * Fast path macros for pure Gregorian calendar.
460
+ * Sets *rjd to the JD value, *ns to 1 (new style), and returns.
461
+ */
462
+ #define GREGORIAN_JD_FAST_PATH_RET(sg, jd_expr, rjd, ns) \
463
+ if (c_gregorian_only_p(sg)) { \
464
+ *(rjd) = (jd_expr); \
465
+ *(ns) = 1; \
466
+ return 1; \
467
+ }
468
+
469
+ #define GREGORIAN_JD_FAST_PATH(sg, jd_expr, rjd, ns) \
470
+ if (c_gregorian_only_p(sg)) { \
471
+ *(rjd) = (jd_expr); \
472
+ *(ns) = 1; \
473
+ return; \
474
+ }
475
+
476
+ /* Forward declarations for Neri-Schneider optimized functions */
477
+ static int c_gregorian_civil_to_jd(int y, int m, int d);
478
+ static void c_gregorian_jd_to_civil(int jd, int *ry, int *rm, int *rd);
479
+ static int c_gregorian_fdoy(int y);
480
+ static int c_gregorian_ldoy(int y);
481
+ static int c_gregorian_ldom_jd(int y, int m);
482
+ static int ns_jd_in_range(int jd);
483
+
455
484
  static int
456
485
  c_find_fdoy(int y, double sg, int *rjd, int *ns)
457
486
  {
458
487
  int d, rm, rd;
459
488
 
489
+ GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_fdoy(y), rjd, ns);
490
+
491
+ /* Keep existing loop for Julian/reform period */
460
492
  for (d = 1; d < 31; d++)
461
493
  if (c_valid_civil_p(y, 1, d, sg, &rm, &rd, rjd, ns))
462
494
  return 1;
@@ -468,6 +500,9 @@ c_find_ldoy(int y, double sg, int *rjd, int *ns)
468
500
  {
469
501
  int i, rm, rd;
470
502
 
503
+ GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_ldoy(y), rjd, ns);
504
+
505
+ /* Keep existing loop for Julian/reform period */
471
506
  for (i = 0; i < 30; i++)
472
507
  if (c_valid_civil_p(y, 12, 31 - i, sg, &rm, &rd, rjd, ns))
473
508
  return 1;
@@ -493,6 +528,9 @@ c_find_ldom(int y, int m, double sg, int *rjd, int *ns)
493
528
  {
494
529
  int i, rm, rd;
495
530
 
531
+ GREGORIAN_JD_FAST_PATH_RET(sg, c_gregorian_ldom_jd(y, m), rjd, ns);
532
+
533
+ /* Keep existing loop for Julian/reform period */
496
534
  for (i = 0; i < 30; i++)
497
535
  if (c_valid_civil_p(y, m, 31 - i, sg, &rm, &rd, rjd, ns))
498
536
  return 1;
@@ -502,55 +540,69 @@ c_find_ldom(int y, int m, double sg, int *rjd, int *ns)
502
540
  static void
503
541
  c_civil_to_jd(int y, int m, int d, double sg, int *rjd, int *ns)
504
542
  {
505
- double a, b, jd;
543
+ int jd;
544
+
545
+ GREGORIAN_JD_FAST_PATH(sg, c_gregorian_civil_to_jd(y, m, d), rjd, ns);
546
+
547
+ /* Calculate Gregorian JD using optimized algorithm */
548
+ jd = c_gregorian_civil_to_jd(y, m, d);
506
549
 
507
- if (m <= 2) {
508
- y -= 1;
509
- m += 12;
510
- }
511
- a = floor(y / 100.0);
512
- b = 2 - a + floor(a / 4.0);
513
- jd = floor(365.25 * (y + 4716)) +
514
- floor(30.6001 * (m + 1)) +
515
- d + b - 1524;
516
550
  if (jd < sg) {
517
- jd -= b;
551
+ /* Before Gregorian switchover - use Julian calendar */
552
+ int y2 = y, m2 = m;
553
+ if (m2 <= 2) {
554
+ y2 -= 1;
555
+ m2 += 12;
556
+ }
557
+ jd = (int)(floor(365.25 * (y2 + 4716)) +
558
+ floor(30.6001 * (m2 + 1)) +
559
+ d - 1524);
518
560
  *ns = 0;
519
561
  }
520
- else
562
+ else {
521
563
  *ns = 1;
564
+ }
522
565
 
523
- *rjd = (int)jd;
566
+ *rjd = jd;
524
567
  }
525
568
 
526
569
  static void
527
570
  c_jd_to_civil(int jd, double sg, int *ry, int *rm, int *rdom)
528
571
  {
529
- double x, a, b, c, d, e, y, m, dom;
530
-
531
- if (jd < sg)
532
- a = jd;
533
- else {
534
- x = floor((jd - 1867216.25) / 36524.25);
535
- a = jd + 1 + x - floor(x / 4.0);
536
- }
537
- b = a + 1524;
538
- c = floor((b - 122.1) / 365.25);
539
- d = floor(365.25 * c);
540
- e = floor((b - d) / 30.6001);
541
- dom = b - d - floor(30.6001 * e);
542
- if (e <= 13) {
543
- m = e - 1;
544
- y = c - 4716;
545
- }
546
- else {
547
- m = e - 13;
548
- y = c - 4715;
572
+ /* Fast path: pure Gregorian or date after switchover, within safe range */
573
+ if ((c_gregorian_only_p(sg) || jd >= sg) && ns_jd_in_range(jd)) {
574
+ c_gregorian_jd_to_civil(jd, ry, rm, rdom);
575
+ return;
549
576
  }
550
577
 
551
- *ry = (int)y;
552
- *rm = (int)m;
553
- *rdom = (int)dom;
578
+ /* Original algorithm for Julian calendar or extreme dates */
579
+ {
580
+ double x, a, b, c, d, e, y, m, dom;
581
+
582
+ if (jd < sg)
583
+ a = jd;
584
+ else {
585
+ x = floor((jd - 1867216.25) / 36524.25);
586
+ a = jd + 1 + x - floor(x / 4.0);
587
+ }
588
+ b = a + 1524;
589
+ c = floor((b - 122.1) / 365.25);
590
+ d = floor(365.25 * c);
591
+ e = floor((b - d) / 30.6001);
592
+ dom = b - d - floor(30.6001 * e);
593
+ if (e <= 13) {
594
+ m = e - 1;
595
+ y = c - 4716;
596
+ }
597
+ else {
598
+ m = e - 13;
599
+ y = c - 4715;
600
+ }
601
+
602
+ *ry = (int)y;
603
+ *rm = (int)m;
604
+ *rdom = (int)dom;
605
+ }
554
606
  }
555
607
 
556
608
  static void
@@ -725,6 +777,147 @@ c_gregorian_last_day_of_month(int y, int m)
725
777
  return monthtab[c_gregorian_leap_p(y) ? 1 : 0][m];
726
778
  }
727
779
 
780
+ /*
781
+ * Neri-Schneider algorithm for optimized Gregorian date conversion.
782
+ * Reference: Neri & Schneider, "Euclidean Affine Functions and Applications
783
+ * to Calendar Algorithms", Software: Practice and Experience, 2023.
784
+ * https://arxiv.org/abs/2102.06959
785
+ *
786
+ * This algorithm provides ~2-3x speedup over traditional floating-point
787
+ * implementations by using pure integer arithmetic with multiplication
788
+ * and bit-shifts instead of expensive division operations.
789
+ */
790
+
791
+ /* JDN of March 1, Year 0 in proleptic Gregorian calendar */
792
+ #define NS_EPOCH 1721120
793
+
794
+ /* Days in a 4-year cycle (3 normal years + 1 leap year) */
795
+ #define NS_DAYS_IN_4_YEARS 1461
796
+
797
+ /* Days in a 400-year Gregorian cycle (97 leap years in 400 years) */
798
+ #define NS_DAYS_IN_400_YEARS 146097
799
+
800
+ /* Years per century */
801
+ #define NS_YEARS_PER_CENTURY 100
802
+
803
+ /*
804
+ * Multiplier for extracting year within century using fixed-point arithmetic.
805
+ * This is ceil(2^32 / NS_DAYS_IN_4_YEARS) for the Euclidean affine function.
806
+ */
807
+ #define NS_YEAR_MULTIPLIER 2939745
808
+
809
+ /*
810
+ * Coefficients for month calculation from day-of-year.
811
+ * Maps day-of-year to month using: month = (NS_MONTH_COEFF * doy + NS_MONTH_OFFSET) >> 16
812
+ */
813
+ #define NS_MONTH_COEFF 2141
814
+ #define NS_MONTH_OFFSET 197913
815
+
816
+ /*
817
+ * Coefficients for civil date to JDN month contribution.
818
+ * Maps month to accumulated days: days = (NS_CIVIL_MONTH_COEFF * m - NS_CIVIL_MONTH_OFFSET) / 32
819
+ */
820
+ #define NS_CIVIL_MONTH_COEFF 979
821
+ #define NS_CIVIL_MONTH_OFFSET 2919
822
+ #define NS_CIVIL_MONTH_DIVISOR 32
823
+
824
+ /* Days from March 1 to December 31 (for Jan/Feb year adjustment) */
825
+ #define NS_DAYS_BEFORE_NEW_YEAR 306
826
+
827
+ /*
828
+ * Safe bounds for Neri-Schneider algorithm to avoid integer overflow.
829
+ * These correspond to approximately years -1,000,000 to +1,000,000.
830
+ */
831
+ #define NS_JD_MIN -364000000
832
+ #define NS_JD_MAX 538000000
833
+
834
+ inline static int
835
+ ns_jd_in_range(int jd)
836
+ {
837
+ return jd >= NS_JD_MIN && jd <= NS_JD_MAX;
838
+ }
839
+
840
+ /* Optimized: Gregorian date -> Julian Day Number */
841
+ static int
842
+ c_gregorian_civil_to_jd(int y, int m, int d)
843
+ {
844
+ /* Shift epoch to March 1 of year 0 (Jan/Feb belong to previous year) */
845
+ int j = (m < 3) ? 1 : 0;
846
+ int y0 = y - j;
847
+ int m0 = j ? m + 12 : m;
848
+ int d0 = d - 1;
849
+
850
+ /* Calculate year contribution with leap year correction */
851
+ int q1 = DIV(y0, NS_YEARS_PER_CENTURY);
852
+ int yc = DIV(NS_DAYS_IN_4_YEARS * y0, 4) - q1 + DIV(q1, 4);
853
+
854
+ /* Calculate month contribution using integer arithmetic */
855
+ int mc = (NS_CIVIL_MONTH_COEFF * m0 - NS_CIVIL_MONTH_OFFSET) / NS_CIVIL_MONTH_DIVISOR;
856
+
857
+ /* Combine and add epoch offset to get JDN */
858
+ return yc + mc + d0 + NS_EPOCH;
859
+ }
860
+
861
+ /* Optimized: Julian Day Number -> Gregorian date */
862
+ static void
863
+ c_gregorian_jd_to_civil(int jd, int *ry, int *rm, int *rd)
864
+ {
865
+ int r0, n1, q1, r1, n2, q2, r2, n3, q3, r3, y0, j;
866
+ uint64_t u2;
867
+
868
+ /* Convert JDN to rata die (March 1, Year 0 epoch) */
869
+ r0 = jd - NS_EPOCH;
870
+
871
+ /* Extract century and day within 400-year cycle */
872
+ /* Use Euclidean (floor) division for negative values */
873
+ n1 = 4 * r0 + 3;
874
+ q1 = DIV(n1, NS_DAYS_IN_400_YEARS);
875
+ r1 = MOD(n1, NS_DAYS_IN_400_YEARS) / 4;
876
+
877
+ /* Calculate year within century and day of year */
878
+ n2 = 4 * r1 + 3;
879
+ /* Use 64-bit arithmetic to avoid overflow */
880
+ u2 = (uint64_t)NS_YEAR_MULTIPLIER * (uint64_t)n2;
881
+ q2 = (int)(u2 >> 32);
882
+ r2 = (int)((uint32_t)u2 / NS_YEAR_MULTIPLIER / 4);
883
+
884
+ /* Calculate month and day using integer arithmetic */
885
+ n3 = NS_MONTH_COEFF * r2 + NS_MONTH_OFFSET;
886
+ q3 = n3 >> 16;
887
+ r3 = (n3 & 0xFFFF) / NS_MONTH_COEFF;
888
+
889
+ /* Combine century and year */
890
+ y0 = NS_YEARS_PER_CENTURY * q1 + q2;
891
+
892
+ /* Adjust for January/February (shift from fiscal year) */
893
+ j = (r2 >= NS_DAYS_BEFORE_NEW_YEAR) ? 1 : 0;
894
+
895
+ *ry = y0 + j;
896
+ *rm = j ? q3 - 12 : q3;
897
+ *rd = r3 + 1;
898
+ }
899
+
900
+ /* O(1) first day of year for Gregorian calendar */
901
+ inline static int
902
+ c_gregorian_fdoy(int y)
903
+ {
904
+ return c_gregorian_civil_to_jd(y, 1, 1);
905
+ }
906
+
907
+ /* O(1) last day of year for Gregorian calendar */
908
+ inline static int
909
+ c_gregorian_ldoy(int y)
910
+ {
911
+ return c_gregorian_civil_to_jd(y, 12, 31);
912
+ }
913
+
914
+ /* O(1) last day of month (JDN) for Gregorian calendar */
915
+ inline static int
916
+ c_gregorian_ldom_jd(int y, int m)
917
+ {
918
+ return c_gregorian_civil_to_jd(y, m, c_gregorian_last_day_of_month(y, m));
919
+ }
920
+
728
921
  static int
729
922
  c_valid_julian_p(int y, int m, int d, int *rm, int *rd)
730
923
  {
@@ -799,12 +992,16 @@ c_valid_commercial_p(int y, int w, int d, double sg,
799
992
  if (w < 0) {
800
993
  int rjd2;
801
994
 
995
+ if (w < -53) return 0;
802
996
  c_commercial_to_jd(y + 1, 1, 1, sg, &rjd2, &ns2);
803
997
  c_jd_to_commercial(rjd2 + w * 7, sg, &ry2, &rw2, &rd2);
804
998
  if (ry2 != y)
805
999
  return 0;
806
1000
  w = rw2;
807
1001
  }
1002
+ else {
1003
+ if (w < 1 || 53 < w) return 0;
1004
+ }
808
1005
  c_commercial_to_jd(y, w, d, sg, rjd, ns);
809
1006
  c_jd_to_commercial(*rjd, sg, &ry2, rw, rd);
810
1007
  if (y != ry2 || w != *rw || d != *rd)
@@ -2428,9 +2625,9 @@ offset_to_sec(VALUE vof, int *rof)
2428
2625
  if (!FIXNUM_P(vn))
2429
2626
  return 0;
2430
2627
  n = FIX2LONG(vn);
2431
- if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS)
2432
- return 0;
2433
2628
  }
2629
+ if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS)
2630
+ return 0;
2434
2631
  *rof = (int)n;
2435
2632
  return 1;
2436
2633
  }
@@ -2498,7 +2695,7 @@ date_s__valid_jd_p(int argc, VALUE *argv, VALUE klass)
2498
2695
  *
2499
2696
  * Date.valid_jd?(2451944) # => true
2500
2697
  *
2501
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
2698
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
2502
2699
  *
2503
2700
  * Related: Date.jd.
2504
2701
  */
@@ -2592,7 +2789,7 @@ date_s__valid_civil_p(int argc, VALUE *argv, VALUE klass)
2592
2789
  * Date.valid_date?(2001, 2, 29) # => false
2593
2790
  * Date.valid_date?(2001, 2, -1) # => true
2594
2791
  *
2595
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
2792
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
2596
2793
  *
2597
2794
  * Related: Date.jd, Date.new.
2598
2795
  */
@@ -2680,7 +2877,7 @@ date_s__valid_ordinal_p(int argc, VALUE *argv, VALUE klass)
2680
2877
  * Date.valid_ordinal?(2001, 34) # => true
2681
2878
  * Date.valid_ordinal?(2001, 366) # => false
2682
2879
  *
2683
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
2880
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
2684
2881
  *
2685
2882
  * Related: Date.jd, Date.ordinal.
2686
2883
  */
@@ -2770,7 +2967,7 @@ date_s__valid_commercial_p(int argc, VALUE *argv, VALUE klass)
2770
2967
  *
2771
2968
  * See Date.commercial.
2772
2969
  *
2773
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
2970
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
2774
2971
  *
2775
2972
  * Related: Date.jd, Date.commercial.
2776
2973
  */
@@ -3002,34 +3199,66 @@ date_s_gregorian_leap_p(VALUE klass, VALUE y)
3002
3199
  return f_boolcast(c_gregorian_leap_p(ry));
3003
3200
  }
3004
3201
 
3202
+ #ifndef HAVE_RB_GC_MARK_MOVABLE
3203
+ #define rb_gc_mark_movable rb_gc_mark
3204
+ #else
3005
3205
  static void
3006
- d_lite_gc_mark(void *ptr)
3206
+ d_lite_gc_compact(void *ptr)
3007
3207
  {
3008
3208
  union DateData *dat = ptr;
3009
3209
  if (simple_dat_p(dat))
3010
- rb_gc_mark(dat->s.nth);
3210
+ dat->s.nth = rb_gc_location(dat->s.nth);
3011
3211
  else {
3012
- rb_gc_mark(dat->c.nth);
3013
- rb_gc_mark(dat->c.sf);
3212
+ dat->c.nth = rb_gc_location(dat->c.nth);
3213
+ dat->c.sf = rb_gc_location(dat->c.sf);
3014
3214
  }
3015
3215
  }
3216
+ #endif
3016
3217
 
3017
- static size_t
3018
- d_lite_memsize(const void *ptr)
3218
+ static void
3219
+ d_lite_gc_mark(void *ptr)
3019
3220
  {
3020
- const union DateData *dat = ptr;
3021
- return complex_dat_p(dat) ? sizeof(struct ComplexDateData) : sizeof(struct SimpleDateData);
3221
+ union DateData *dat = ptr;
3222
+ if (simple_dat_p(dat))
3223
+ rb_gc_mark_movable(dat->s.nth);
3224
+ else {
3225
+ rb_gc_mark_movable(dat->c.nth);
3226
+ rb_gc_mark_movable(dat->c.sf);
3227
+ }
3022
3228
  }
3023
3229
 
3024
3230
  #ifndef HAVE_RB_EXT_RACTOR_SAFE
3025
3231
  # define RUBY_TYPED_FROZEN_SHAREABLE 0
3026
3232
  #endif
3027
3233
 
3234
+ #if defined(RUBY_TYPED_EMBEDDABLE) || defined(HAVE_CONST_RUBY_TYPED_EMBEDDABLE)
3235
+ # define HAVE_RUBY_TYPED_EMBEDDABLE 1
3236
+ #else
3237
+ # define RUBY_TYPED_EMBEDDABLE 0
3238
+ #endif
3239
+
3240
+ static size_t
3241
+ d_lite_memsize(const void *ptr)
3242
+ {
3243
+ #ifdef HAVE_RUBY_TYPED_EMBEDDABLE
3244
+ return 0;
3245
+ #else
3246
+ return sizeof(union DateData);
3247
+ #endif
3248
+ }
3249
+
3028
3250
  static const rb_data_type_t d_lite_type = {
3029
3251
  "Date",
3030
- {d_lite_gc_mark, RUBY_TYPED_DEFAULT_FREE, d_lite_memsize,},
3252
+ {
3253
+ d_lite_gc_mark,
3254
+ RUBY_TYPED_DEFAULT_FREE,
3255
+ d_lite_memsize,
3256
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
3257
+ d_lite_gc_compact,
3258
+ #endif
3259
+ },
3031
3260
  0, 0,
3032
- RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED|RUBY_TYPED_FROZEN_SHAREABLE,
3261
+ RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED|RUBY_TYPED_EMBEDDABLE|RUBY_TYPED_FROZEN_SHAREABLE,
3033
3262
  };
3034
3263
 
3035
3264
  inline static VALUE
@@ -3040,10 +3269,11 @@ d_simple_new_internal(VALUE klass,
3040
3269
  unsigned flags)
3041
3270
  {
3042
3271
  struct SimpleDateData *dat;
3272
+ union DateData *u_dat;
3043
3273
  VALUE obj;
3044
3274
 
3045
- obj = TypedData_Make_Struct(klass, struct SimpleDateData,
3046
- &d_lite_type, dat);
3275
+ obj = TypedData_Make_Struct(klass, union DateData, &d_lite_type, u_dat);
3276
+ dat = &u_dat->s;
3047
3277
  set_to_simple(obj, dat, nth, jd, sg, y, m, d, flags);
3048
3278
 
3049
3279
  assert(have_jd_p(dat) || have_civil_p(dat));
@@ -3061,10 +3291,11 @@ d_complex_new_internal(VALUE klass,
3061
3291
  unsigned flags)
3062
3292
  {
3063
3293
  struct ComplexDateData *dat;
3294
+ union DateData *u_dat;
3064
3295
  VALUE obj;
3065
3296
 
3066
- obj = TypedData_Make_Struct(klass, struct ComplexDateData,
3067
- &d_lite_type, dat);
3297
+ obj = TypedData_Make_Struct(klass, union DateData, &d_lite_type, u_dat);
3298
+ dat = &u_dat->c;
3068
3299
  set_to_complex(obj, dat, nth, jd, df, sf, of, sg,
3069
3300
  y, m, d, h, min, s, flags);
3070
3301
 
@@ -3350,7 +3581,7 @@ static VALUE d_lite_plus(VALUE, VALUE);
3350
3581
  *
3351
3582
  * Date.jd(Date::ITALY - 1).julian? # => true
3352
3583
  *
3353
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
3584
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
3354
3585
  *
3355
3586
  * Related: Date.new.
3356
3587
  */
@@ -3415,7 +3646,7 @@ date_s_jd(int argc, VALUE *argv, VALUE klass)
3415
3646
  *
3416
3647
  * Raises an exception if +yday+ is zero or out of range.
3417
3648
  *
3418
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
3649
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
3419
3650
  *
3420
3651
  * Related: Date.jd, Date.new.
3421
3652
  */
@@ -3492,7 +3723,7 @@ date_s_civil(int argc, VALUE *argv, VALUE klass)
3492
3723
  * where +n+ is the number of days in the month;
3493
3724
  * when the argument is negative, counts backward from the end of the month.
3494
3725
  *
3495
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
3726
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
3496
3727
  *
3497
3728
  * Related: Date.jd.
3498
3729
  */
@@ -3598,7 +3829,7 @@ date_initialize(int argc, VALUE *argv, VALUE self)
3598
3829
  * Date.commercial(2020, 1, 1).to_s # => "2019-12-30"
3599
3830
  Date.commercial(2020, 1, 7).to_s # => "2020-01-05"
3600
3831
  *
3601
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
3832
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
3602
3833
  *
3603
3834
  * Related: Date.jd, Date.new, Date.ordinal.
3604
3835
  */
@@ -3783,7 +4014,7 @@ static void set_sg(union DateData *, double);
3783
4014
  *
3784
4015
  * Date.today.to_s # => "2022-07-06"
3785
4016
  *
3786
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
4017
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
3787
4018
  *
3788
4019
  */
3789
4020
  static VALUE
@@ -4322,7 +4553,8 @@ d_new_by_frags(VALUE klass, VALUE hash, VALUE sg)
4322
4553
  }
4323
4554
 
4324
4555
  VALUE date__strptime(const char *str, size_t slen,
4325
- const char *fmt, size_t flen, VALUE hash);
4556
+ const char *fmt, size_t flen,
4557
+ VALUE hash, rb_encoding *enc);
4326
4558
 
4327
4559
  static VALUE
4328
4560
  date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
@@ -4331,10 +4563,12 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
4331
4563
  VALUE vstr, vfmt, hash;
4332
4564
  const char *str, *fmt;
4333
4565
  size_t slen, flen;
4566
+ rb_encoding *enc;
4334
4567
 
4335
4568
  rb_scan_args(argc, argv, "11", &vstr, &vfmt);
4336
4569
 
4337
4570
  StringValue(vstr);
4571
+ if (argc > 1) StringValue(vfmt);
4338
4572
  if (!rb_enc_str_asciicompat_p(vstr))
4339
4573
  rb_raise(rb_eArgError,
4340
4574
  "string should have ASCII compatible encoding");
@@ -4343,34 +4577,18 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
4343
4577
  if (argc < 2) {
4344
4578
  fmt = default_fmt;
4345
4579
  flen = strlen(default_fmt);
4580
+ enc = rb_enc_get(vstr);
4346
4581
  }
4347
4582
  else {
4348
- StringValue(vfmt);
4349
4583
  if (!rb_enc_str_asciicompat_p(vfmt))
4350
4584
  rb_raise(rb_eArgError,
4351
4585
  "format should have ASCII compatible encoding");
4586
+ enc = rb_enc_check(vstr, vfmt);
4352
4587
  fmt = RSTRING_PTR(vfmt);
4353
4588
  flen = RSTRING_LEN(vfmt);
4354
4589
  }
4355
4590
  hash = rb_hash_new();
4356
- if (NIL_P(date__strptime(str, slen, fmt, flen, hash)))
4357
- return Qnil;
4358
-
4359
- {
4360
- VALUE zone = ref_hash("zone");
4361
- VALUE left = ref_hash("leftover");
4362
-
4363
- if (!NIL_P(zone)) {
4364
- rb_enc_copy(zone, vstr);
4365
- set_hash("zone", zone);
4366
- }
4367
- if (!NIL_P(left)) {
4368
- rb_enc_copy(left, vstr);
4369
- set_hash("leftover", left);
4370
- }
4371
- }
4372
-
4373
- return hash;
4591
+ return date__strptime(str, slen, fmt, flen, hash, enc);
4374
4592
  }
4375
4593
 
4376
4594
  /*
@@ -4383,7 +4601,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
4383
4601
  * Date._strptime('2001-02-03', '%Y-%m-%d') # => {:year=>2001, :mon=>2, :mday=>3}
4384
4602
  *
4385
4603
  * For other formats, see
4386
- * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html].
4604
+ * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc].
4387
4605
  * (Unlike Date.strftime, does not support flags and width.)
4388
4606
  *
4389
4607
  * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html].
@@ -4412,10 +4630,10 @@ date_s__strptime(int argc, VALUE *argv, VALUE klass)
4412
4630
  * Date.strptime('sat3feb01', '%a%d%b%y') # => #<Date: 2001-02-03>
4413
4631
  *
4414
4632
  * For other formats, see
4415
- * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html].
4633
+ * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc].
4416
4634
  * (Unlike Date.strftime, does not support flags and width.)
4417
4635
  *
4418
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
4636
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4419
4637
  *
4420
4638
  * See also {strptime(3)}[https://man7.org/linux/man-pages/man3/strptime.3.html].
4421
4639
  *
@@ -4504,7 +4722,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
4504
4722
  * This method recognizes many forms in +string+,
4505
4723
  * but it is not a validator.
4506
4724
  * For formats, see
4507
- * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings]
4725
+ * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings]
4508
4726
  *
4509
4727
  * If +string+ does not specify a valid date,
4510
4728
  * the result is unpredictable;
@@ -4539,7 +4757,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
4539
4757
  * This method recognizes many forms in +string+,
4540
4758
  * but it is not a validator.
4541
4759
  * For formats, see
4542
- * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings]
4760
+ * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings]
4543
4761
  * If +string+ does not specify a valid date,
4544
4762
  * the result is unpredictable;
4545
4763
  * consider using Date._strptime instead.
@@ -4559,7 +4777,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
4559
4777
  *
4560
4778
  * See:
4561
4779
  *
4562
- * - Argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
4780
+ * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4563
4781
  * - Argument {limit}[rdoc-ref:Date@Argument+limit].
4564
4782
  *
4565
4783
  * Related: Date._parse (returns a hash).
@@ -4603,7 +4821,7 @@ VALUE date__jisx0301(VALUE);
4603
4821
  * Date._iso8601(string, limit: 128) -> hash
4604
4822
  *
4605
4823
  * Returns a hash of values parsed from +string+, which should contain
4606
- * an {ISO 8601 formatted date}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]:
4824
+ * an {ISO 8601 formatted date}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications]:
4607
4825
  *
4608
4826
  * d = Date.new(2001, 2, 3)
4609
4827
  * s = d.iso8601 # => "2001-02-03"
@@ -4630,7 +4848,7 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass)
4630
4848
  *
4631
4849
  * Returns a new \Date object with values parsed from +string+,
4632
4850
  * which should contain
4633
- * an {ISO 8601 formatted date}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]:
4851
+ * an {ISO 8601 formatted date}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications]:
4634
4852
  *
4635
4853
  * d = Date.new(2001, 2, 3)
4636
4854
  * s = d.iso8601 # => "2001-02-03"
@@ -4638,7 +4856,7 @@ date_s__iso8601(int argc, VALUE *argv, VALUE klass)
4638
4856
  *
4639
4857
  * See:
4640
4858
  *
4641
- * - Argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
4859
+ * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4642
4860
  * - Argument {limit}[rdoc-ref:Date@Argument+limit].
4643
4861
  *
4644
4862
  * Related: Date._iso8601 (returns a hash).
@@ -4672,7 +4890,7 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass)
4672
4890
  * Date._rfc3339(string, limit: 128) -> hash
4673
4891
  *
4674
4892
  * Returns a hash of values parsed from +string+, which should be a valid
4675
- * {RFC 3339 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+3339+Format]:
4893
+ * {RFC 3339 format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+3339+Format]:
4676
4894
  *
4677
4895
  * d = Date.new(2001, 2, 3)
4678
4896
  * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00"
@@ -4700,7 +4918,7 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
4700
4918
  *
4701
4919
  * Returns a new \Date object with values parsed from +string+,
4702
4920
  * which should be a valid
4703
- * {RFC 3339 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+3339+Format]:
4921
+ * {RFC 3339 format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+3339+Format]:
4704
4922
  *
4705
4923
  * d = Date.new(2001, 2, 3)
4706
4924
  * s = d.rfc3339 # => "2001-02-03T00:00:00+00:00"
@@ -4708,7 +4926,7 @@ date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
4708
4926
  *
4709
4927
  * See:
4710
4928
  *
4711
- * - Argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
4929
+ * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4712
4930
  * - Argument {limit}[rdoc-ref:Date@Argument+limit].
4713
4931
  *
4714
4932
  * Related: Date._rfc3339 (returns a hash).
@@ -4776,7 +4994,7 @@ date_s__xmlschema(int argc, VALUE *argv, VALUE klass)
4776
4994
  *
4777
4995
  * See:
4778
4996
  *
4779
- * - Argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
4997
+ * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4780
4998
  * - Argument {limit}[rdoc-ref:Date@Argument+limit].
4781
4999
  *
4782
5000
  * Related: Date._xmlschema (returns a hash).
@@ -4810,7 +5028,7 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
4810
5028
  * Date._rfc2822(string, limit: 128) -> hash
4811
5029
  *
4812
5030
  * Returns a hash of values parsed from +string+, which should be a valid
4813
- * {RFC 2822 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+2822+Format]:
5031
+ * {RFC 2822 date format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+2822+Format]:
4814
5032
  *
4815
5033
  * d = Date.new(2001, 2, 3)
4816
5034
  * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
@@ -4838,7 +5056,7 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
4838
5056
  *
4839
5057
  * Returns a new \Date object with values parsed from +string+,
4840
5058
  * which should be a valid
4841
- * {RFC 2822 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-RFC+2822+Format]:
5059
+ * {RFC 2822 date format}[rdoc-ref:language/strftime_formatting.rdoc@RFC+2822+Format]:
4842
5060
  *
4843
5061
  * d = Date.new(2001, 2, 3)
4844
5062
  * s = d.rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
@@ -4846,7 +5064,7 @@ date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
4846
5064
  *
4847
5065
  * See:
4848
5066
  *
4849
- * - Argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
5067
+ * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4850
5068
  * - Argument {limit}[rdoc-ref:Date@Argument+limit].
4851
5069
  *
4852
5070
  * Related: Date._rfc2822 (returns a hash).
@@ -4880,7 +5098,7 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
4880
5098
  * Date._httpdate(string, limit: 128) -> hash
4881
5099
  *
4882
5100
  * Returns a hash of values parsed from +string+, which should be a valid
4883
- * {HTTP date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-HTTP+Format]:
5101
+ * {HTTP date format}[rdoc-ref:language/strftime_formatting.rdoc@HTTP+Format]:
4884
5102
  *
4885
5103
  * d = Date.new(2001, 2, 3)
4886
5104
  * s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
@@ -4906,7 +5124,7 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass)
4906
5124
  *
4907
5125
  * Returns a new \Date object with values parsed from +string+,
4908
5126
  * which should be a valid
4909
- * {HTTP date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-HTTP+Format]:
5127
+ * {HTTP date format}[rdoc-ref:language/strftime_formatting.rdoc@HTTP+Format]:
4910
5128
  *
4911
5129
  * d = Date.new(2001, 2, 3)
4912
5130
  s = d.httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
@@ -4914,7 +5132,7 @@ date_s__httpdate(int argc, VALUE *argv, VALUE klass)
4914
5132
  *
4915
5133
  * See:
4916
5134
  *
4917
- * - Argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
5135
+ * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4918
5136
  * - Argument {limit}[rdoc-ref:Date@Argument+limit].
4919
5137
  *
4920
5138
  * Related: Date._httpdate (returns a hash).
@@ -4948,7 +5166,7 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass)
4948
5166
  * Date._jisx0301(string, limit: 128) -> hash
4949
5167
  *
4950
5168
  * Returns a hash of values parsed from +string+, which should be a valid
4951
- * {JIS X 0301 date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-JIS+X+0301+Format]:
5169
+ * {JIS X 0301 date format}[rdoc-ref:language/strftime_formatting.rdoc@JIS+X+0301+Format]:
4952
5170
  *
4953
5171
  * d = Date.new(2001, 2, 3)
4954
5172
  * s = d.jisx0301 # => "H13.02.03"
@@ -4974,7 +5192,7 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
4974
5192
  * Date.jisx0301(string = '-4712-01-01', start = Date::ITALY, limit: 128) -> date
4975
5193
  *
4976
5194
  * Returns a new \Date object with values parsed from +string+,
4977
- * which should be a valid {JIS X 0301 format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-JIS+X+0301+Format]:
5195
+ * which should be a valid {JIS X 0301 format}[rdoc-ref:language/strftime_formatting.rdoc@JIS+X+0301+Format]:
4978
5196
  *
4979
5197
  * d = Date.new(2001, 2, 3)
4980
5198
  * s = d.jisx0301 # => "H13.02.03"
@@ -4986,7 +5204,7 @@ date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
4986
5204
  *
4987
5205
  * See:
4988
5206
  *
4989
- * - Argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
5207
+ * - Argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
4990
5208
  * - Argument {limit}[rdoc-ref:Date@Argument+limit].
4991
5209
  *
4992
5210
  * Related: Date._jisx0301 (returns a hash).
@@ -5744,7 +5962,7 @@ d_lite_leap_p(VALUE self)
5744
5962
  * Date.new(2001, 2, 3, Date::GREGORIAN).start # => -Infinity
5745
5963
  * Date.new(2001, 2, 3, Date::JULIAN).start # => Infinity
5746
5964
  *
5747
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
5965
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
5748
5966
  *
5749
5967
  */
5750
5968
  static VALUE
@@ -5810,7 +6028,7 @@ dup_obj_with_new_start(VALUE obj, double sg)
5810
6028
 
5811
6029
  /*
5812
6030
  * call-seq:
5813
- * new_start(start = Date::ITALY]) -> new_date
6031
+ * new_start(start = Date::ITALY) -> new_date
5814
6032
  *
5815
6033
  * Returns a copy of +self+ with the given +start+ value:
5816
6034
  *
@@ -5819,7 +6037,7 @@ dup_obj_with_new_start(VALUE obj, double sg)
5819
6037
  * d1 = d0.new_start(Date::JULIAN)
5820
6038
  * d1.julian? # => true
5821
6039
  *
5822
- * See argument {start}[rdoc-ref:date/calendars.rdoc@Argument+start].
6040
+ * See argument {start}[rdoc-ref:language/calendars.rdoc@Argument+start].
5823
6041
  *
5824
6042
  */
5825
6043
  static VALUE
@@ -6967,7 +7185,7 @@ static VALUE strftimev(const char *, VALUE,
6967
7185
  * to_s -> string
6968
7186
  *
6969
7187
  * Returns a string representation of the date in +self+
6970
- * in {ISO 8601 extended date format}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-ISO+8601+Format+Specifications]
7188
+ * in {ISO 8601 extended date format}[rdoc-ref:language/strftime_formatting.rdoc@ISO+8601+Format+Specifications]
6971
7189
  * (<tt>'%Y-%m-%d'</tt>):
6972
7190
  *
6973
7191
  * Date.new(2001, 2, 3).to_s # => "2001-02-03"
@@ -7248,7 +7466,7 @@ date_strftime_internal(int argc, VALUE *argv, VALUE self,
7248
7466
  * Date.new(2001, 2, 3).strftime # => "2001-02-03"
7249
7467
  *
7250
7468
  * For other formats, see
7251
- * {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html].
7469
+ * {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc].
7252
7470
  *
7253
7471
  */
7254
7472
  static VALUE
@@ -7280,12 +7498,12 @@ strftimev(const char *fmt, VALUE self,
7280
7498
  * asctime -> string
7281
7499
  *
7282
7500
  * Equivalent to #strftime with argument <tt>'%a %b %e %T %Y'</tt>
7283
- * (or its {shorthand form}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Shorthand+Conversion+Specifiers]
7501
+ * (or its {shorthand form}[rdoc-ref:language/strftime_formatting.rdoc@Shorthand+Conversion+Specifiers]
7284
7502
  * <tt>'%c'</tt>):
7285
7503
  *
7286
7504
  * Date.new(2001, 2, 3).asctime # => "Sat Feb 3 00:00:00 2001"
7287
7505
  *
7288
- * See {asctime}[https://linux.die.net/man/3/asctime].
7506
+ * See {asctime}[https://man7.org/linux/man-pages/man3/asctime.3p.html].
7289
7507
  *
7290
7508
  */
7291
7509
  static VALUE
@@ -7299,7 +7517,7 @@ d_lite_asctime(VALUE self)
7299
7517
  * iso8601 -> string
7300
7518
  *
7301
7519
  * Equivalent to #strftime with argument <tt>'%Y-%m-%d'</tt>
7302
- * (or its {shorthand form}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Shorthand+Conversion+Specifiers]
7520
+ * (or its {shorthand form}[rdoc-ref:language/strftime_formatting.rdoc@Shorthand+Conversion+Specifiers]
7303
7521
  * <tt>'%F'</tt>);
7304
7522
  *
7305
7523
  * Date.new(2001, 2, 3).iso8601 # => "2001-02-03"
@@ -7316,7 +7534,7 @@ d_lite_iso8601(VALUE self)
7316
7534
  * rfc3339 -> string
7317
7535
  *
7318
7536
  * Equivalent to #strftime with argument <tt>'%FT%T%:z'</tt>;
7319
- * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]:
7537
+ * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]:
7320
7538
  *
7321
7539
  * Date.new(2001, 2, 3).rfc3339 # => "2001-02-03T00:00:00+00:00"
7322
7540
  *
@@ -7332,7 +7550,7 @@ d_lite_rfc3339(VALUE self)
7332
7550
  * rfc2822 -> string
7333
7551
  *
7334
7552
  * Equivalent to #strftime with argument <tt>'%a, %-d %b %Y %T %z'</tt>;
7335
- * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]:
7553
+ * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]:
7336
7554
  *
7337
7555
  * Date.new(2001, 2, 3).rfc2822 # => "Sat, 3 Feb 2001 00:00:00 +0000"
7338
7556
  *
@@ -7348,7 +7566,7 @@ d_lite_rfc2822(VALUE self)
7348
7566
  * httpdate -> string
7349
7567
  *
7350
7568
  * Equivalent to #strftime with argument <tt>'%a, %d %b %Y %T GMT'</tt>;
7351
- * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]:
7569
+ * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]:
7352
7570
  *
7353
7571
  * Date.new(2001, 2, 3).httpdate # => "Sat, 03 Feb 2001 00:00:00 GMT"
7354
7572
  *
@@ -7611,8 +7829,6 @@ d_lite_marshal_load(VALUE self, VALUE a)
7611
7829
  if (simple_dat_p(dat)) {
7612
7830
  if (df || !f_zero_p(sf) || of) {
7613
7831
  /* loading a fractional date; promote to complex */
7614
- dat = ruby_xrealloc(dat, sizeof(struct ComplexDateData));
7615
- RTYPEDDATA(self)->data = dat;
7616
7832
  goto complex_data;
7617
7833
  }
7618
7834
  set_to_simple(self, &dat->s, nth, jd, sg, 0, 0, 0, HAVE_JD);
@@ -8717,7 +8933,7 @@ dt_lite_to_s(VALUE self)
8717
8933
  * DateTime.now.strftime # => "2022-07-01T11:03:19-05:00"
8718
8934
  *
8719
8935
  * For other formats,
8720
- * see {Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html]:
8936
+ * see {Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc]:
8721
8937
  *
8722
8938
  */
8723
8939
  static VALUE
@@ -9496,6 +9712,7 @@ Init_date_core(void)
9496
9712
  sym_zone = ID2SYM(rb_intern_const("zone"));
9497
9713
 
9498
9714
  half_days_in_day = rb_rational_new2(INT2FIX(1), INT2FIX(2));
9715
+ rb_gc_register_mark_object(half_days_in_day);
9499
9716
 
9500
9717
  #if (LONG_MAX / DAY_IN_SECONDS) > SECOND_IN_NANOSECONDS
9501
9718
  day_in_nanoseconds = LONG2NUM((long)DAY_IN_SECONDS *
@@ -9507,8 +9724,6 @@ Init_date_core(void)
9507
9724
  day_in_nanoseconds = f_mul(INT2FIX(DAY_IN_SECONDS),
9508
9725
  INT2FIX(SECOND_IN_NANOSECONDS));
9509
9726
  #endif
9510
-
9511
- rb_gc_register_mark_object(half_days_in_day);
9512
9727
  rb_gc_register_mark_object(day_in_nanoseconds);
9513
9728
 
9514
9729
  positive_inf = +INFINITY;
@@ -9519,12 +9734,12 @@ Init_date_core(void)
9519
9734
  * calendar dates.
9520
9735
  *
9521
9736
  * Consider using
9522
- * {class Time}[https://docs.ruby-lang.org/en/master/Time.html]
9737
+ * {class Time}[rdoc-ref:Time]
9523
9738
  * instead of class \Date if:
9524
9739
  *
9525
9740
  * - You need both dates and times; \Date handles only dates.
9526
9741
  * - You need only Gregorian dates (and not Julian dates);
9527
- * see {Julian and Gregorian Calendars}[rdoc-ref:date/calendars.rdoc].
9742
+ * see {Julian and Gregorian Calendars}[rdoc-ref:language/calendars.rdoc].
9528
9743
  *
9529
9744
  * A \Date object, once created, is immutable, and cannot be modified.
9530
9745
  *
@@ -9571,7 +9786,7 @@ Init_date_core(void)
9571
9786
  * Date.strptime('fri31dec99', '%a%d%b%y') # => #<Date: 1999-12-31>
9572
9787
  *
9573
9788
  * See also the specialized methods in
9574
- * {"Specialized Format Strings" in Formats for Dates and Times}[https://docs.ruby-lang.org/en/master/strftime_formatting_rdoc.html#label-Specialized+Format+Strings]
9789
+ * {"Specialized Format Strings" in Formats for Dates and Times}[rdoc-ref:language/strftime_formatting.rdoc@Specialized+Format+Strings]
9575
9790
  *
9576
9791
  * == Argument +limit+
9577
9792
  *
@@ -2325,6 +2325,16 @@ sec_fraction(VALUE f)
2325
2325
 
2326
2326
  #define SNUM 14
2327
2327
 
2328
+ #ifndef rb_streql_lit
2329
+ static inline int
2330
+ rb_streql_cstr(VALUE str, const char *lit, size_t len)
2331
+ {
2332
+ if ((size_t)RSTRING_LEN(str) != len) return 0;
2333
+ return memcmp(RSTRING_PTR(str), lit, len) == 0;
2334
+ }
2335
+ #define rb_streql_lit(str, lit) rb_streql_cstr(str, lit, sizeof(lit "") - 1)
2336
+ #endif
2337
+
2328
2338
  static int
2329
2339
  iso8601_ext_datetime_cb(VALUE m, VALUE hash)
2330
2340
  {
@@ -2339,14 +2349,14 @@ iso8601_ext_datetime_cb(VALUE m, VALUE hash)
2339
2349
 
2340
2350
  if (!NIL_P(s[1])) {
2341
2351
  if (!NIL_P(s[3])) set_hash("mday", str2num(s[3]));
2342
- if (strcmp(RSTRING_PTR(s[1]), "-") != 0) {
2352
+ if (!rb_streql_lit(s[1], "-")) {
2343
2353
  y = str2num(s[1]);
2344
2354
  if (RSTRING_LEN(s[1]) < 4)
2345
2355
  y = comp_year69(y);
2346
2356
  set_hash("year", y);
2347
2357
  }
2348
2358
  if (NIL_P(s[2])) {
2349
- if (strcmp(RSTRING_PTR(s[1]), "-") != 0)
2359
+ if (!rb_streql_lit(s[1], "-"))
2350
2360
  return 0;
2351
2361
  }
2352
2362
  else
@@ -2425,14 +2435,14 @@ iso8601_bas_datetime_cb(VALUE m, VALUE hash)
2425
2435
 
2426
2436
  if (!NIL_P(s[3])) {
2427
2437
  set_hash("mday", str2num(s[3]));
2428
- if (strcmp(RSTRING_PTR(s[1]), "--") != 0) {
2438
+ if (!rb_streql_lit(s[1], "--")) {
2429
2439
  y = str2num(s[1]);
2430
2440
  if (RSTRING_LEN(s[1]) < 4)
2431
2441
  y = comp_year69(y);
2432
2442
  set_hash("year", y);
2433
2443
  }
2434
2444
  if (*RSTRING_PTR(s[2]) == '-') {
2435
- if (strcmp(RSTRING_PTR(s[1]), "--") != 0)
2445
+ if (!rb_streql_lit(s[1], "--"))
2436
2446
  return 0;
2437
2447
  }
2438
2448
  else
@@ -41,7 +41,6 @@ static const int ABBREVIATED_MONTH_NAME_LENGTH = 3;
41
41
 
42
42
  #define f_match(r,s) rb_funcall(r, rb_intern("match"), 1, s)
43
43
  #define f_aref(o,i) rb_funcall(o, rb_intern("[]"), 1, i)
44
- #define f_end(o,i) rb_funcall(o, rb_intern("end"), 1, i)
45
44
 
46
45
  #define issign(c) ((c) == '-' || (c) == '+')
47
46
 
@@ -144,7 +143,7 @@ valid_range_p(VALUE v, int a, int b)
144
143
  do { \
145
144
  size_t l; \
146
145
  l = date__strptime_internal(&str[si], slen - si, \
147
- fmt, sizeof fmt - 1, hash); \
146
+ fmt, sizeof fmt - 1, hash, enc); \
148
147
  if (fail_p()) \
149
148
  return 0; \
150
149
  si += l; \
@@ -160,7 +159,8 @@ head_match_p(size_t len, const char *name, const char *str, size_t slen, size_t
160
159
 
161
160
  static size_t
162
161
  date__strptime_internal(const char *str, size_t slen,
163
- const char *fmt, size_t flen, VALUE hash)
162
+ const char *fmt, size_t flen,
163
+ VALUE hash, rb_encoding *enc)
164
164
  {
165
165
  size_t si, fi;
166
166
  int c;
@@ -597,15 +597,14 @@ date__strptime_internal(const char *str, size_t slen,
597
597
 
598
598
  b = rb_backref_get();
599
599
  rb_match_busy(b);
600
- m = f_match(pat, rb_usascii_str_new(&str[si], slen - si));
600
+ m = f_match(pat, rb_enc_str_new(&str[si], slen - si, enc));
601
601
 
602
602
  if (!NIL_P(m)) {
603
- VALUE s, l, o;
603
+ VALUE s, o;
604
604
 
605
605
  s = rb_reg_nth_match(1, m);
606
- l = f_end(m, INT2FIX(0));
607
606
  o = date_zone_to_diff(s);
608
- si += NUM2LONG(l);
607
+ si += RSTRING_LEN(s);
609
608
  set_hash("zone", s);
610
609
  set_hash("offset", o);
611
610
  rb_backref_set(b);
@@ -654,23 +653,24 @@ date__strptime_internal(const char *str, size_t slen,
654
653
 
655
654
  VALUE
656
655
  date__strptime(const char *str, size_t slen,
657
- const char *fmt, size_t flen, VALUE hash)
656
+ const char *fmt, size_t flen,
657
+ VALUE hash, rb_encoding *enc)
658
658
  {
659
659
  size_t si;
660
660
  VALUE cent, merid;
661
661
 
662
- si = date__strptime_internal(str, slen, fmt, flen, hash);
662
+ si = date__strptime_internal(str, slen, fmt, flen, hash, enc);
663
+
664
+ if (fail_p())
665
+ return Qnil;
663
666
 
664
667
  if (slen > si) {
665
668
  VALUE s;
666
669
 
667
- s = rb_usascii_str_new(&str[si], slen - si);
670
+ s = rb_enc_str_new(&str[si], slen - si, enc);
668
671
  set_hash("leftover", s);
669
672
  }
670
673
 
671
- if (fail_p())
672
- return Qnil;
673
-
674
674
  cent = del_hash("_cent");
675
675
  if (!NIL_P(cent)) {
676
676
  VALUE year;
data/ext/date/extconf.rb CHANGED
@@ -3,6 +3,9 @@ require 'mkmf'
3
3
 
4
4
  config_string("strict_warnflags") {|w| $warnflags += " #{w}"}
5
5
 
6
+ unless try_compile("", "", werror: true)
7
+ append_cflags("-Wno-default-const-init-field-unsafe")
8
+ end
6
9
  append_cflags("-Wno-compound-token-split-by-macro") if RUBY_VERSION < "2.7."
7
10
  have_func("rb_category_warn")
8
11
  with_werror("", {:werror => true}) do |opt, |
@@ -10,4 +13,7 @@ with_werror("", {:werror => true}) do |opt, |
10
13
  have_var("altzone", "time.h", opt)
11
14
  end
12
15
 
16
+ have_func("rb_gc_mark_movable", "ruby.h") # RUBY_VERSION >= 2.7
17
+ have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
18
+
13
19
  create_makefile('date_core')
data/lib/date.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  require 'date_core'
5
5
 
6
6
  class Date
7
- VERSION = "3.5.0" # :nodoc:
7
+ VERSION = "3.6.0" # :nodoc:
8
8
 
9
9
  # call-seq:
10
10
  # infinite? -> false
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: date
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.0
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tadayoshi Funaba
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-29 00:00:00.000000000 Z
11
+ date: 2026-10-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: The official date library for Ruby.
14
14
  email: