date 3.5.1 → 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: ad88171312f53ac18149f50e57442c482c849d06c5f49ff4075373416bc0a8ed
4
- data.tar.gz: 29b2d8a37e4ccaeaf29e5d85e32c5c57850de8e5438b40e8f8f7a40eace57e6f
3
+ metadata.gz: e3e5e30902faa5fe0f946df02df28a7d52b64e6a78d39e9b16ec9f0d88377646
4
+ data.tar.gz: 5a0c7a8ae91bbfd3a0fe6a152465217070ccbd7f5b5892c9657a865f47d6d492
5
5
  SHA512:
6
- metadata.gz: 98cf48a5dd42f9ea06cd56c9533408d926e1fa571c862fc5f29325db07c90797bdb0a92336a6c584593f4a3afcf3040335c3c53956b6a95bbfb0b1db15c11ce2
7
- data.tar.gz: efae967466b4f84107635b4a50051e2d756bad3f5ee1dab8f5890cb4958c182898db1b3f7575ea9da8fc835e42d77f2034d4e0df0928df15dd8758d44e4706bc
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
  }
@@ -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
 
@@ -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
  /*
@@ -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
  *
@@ -7285,7 +7503,7 @@ strftimev(const char *fmt, VALUE self,
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
@@ -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);
@@ -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.1" # :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.1
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-12-10 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: