date 3.2.1 → 3.5.1

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.
@@ -7,6 +7,9 @@
7
7
  #include "ruby/re.h"
8
8
  #include <ctype.h>
9
9
 
10
+ #undef strncasecmp
11
+ #define strncasecmp STRNCASECMP
12
+
10
13
  RUBY_EXTERN VALUE rb_int_positive_pow(long x, unsigned long y);
11
14
  RUBY_EXTERN unsigned long ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow);
12
15
 
@@ -253,6 +256,8 @@ s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
253
256
  #define ABBR_DAYS "sun|mon|tue|wed|thu|fri|sat"
254
257
  #define ABBR_MONTHS "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec"
255
258
 
259
+ #define NUMBER "(?<!\\d)\\d"
260
+
256
261
  #ifdef TIGHT_PARSER
257
262
  #define VALID_DAYS "(?:" DAYS ")" "|(?:tues|wednes|thurs|thur|" ABBR_DAYS ")\\.?"
258
263
  #define VALID_MONTHS "(?:" MONTHS ")" "|(?:sept|" ABBR_MONTHS ")\\.?"
@@ -411,7 +416,6 @@ VALUE
411
416
  date_zone_to_diff(VALUE str)
412
417
  {
413
418
  VALUE offset = Qnil;
414
- VALUE vbuf = 0;
415
419
  long l = RSTRING_LEN(str);
416
420
  const char *s = RSTRING_PTR(str);
417
421
 
@@ -437,16 +441,26 @@ date_zone_to_diff(VALUE str)
437
441
  l -= w;
438
442
  dst = 1;
439
443
  }
444
+
440
445
  {
446
+ const char *zn = s;
441
447
  long sl = shrunk_size(s, l);
448
+ char shrunk_buff[MAX_WORD_LENGTH]; /* no terminator to be added */
449
+ const struct zone *z = 0;
450
+
451
+ if (sl <= 0) {
452
+ sl = l;
453
+ }
454
+ else if (sl <= MAX_WORD_LENGTH) {
455
+ char *d = shrunk_buff;
456
+ sl = shrink_space(d, s, l);
457
+ zn = d;
458
+ }
459
+
442
460
  if (sl > 0 && sl <= MAX_WORD_LENGTH) {
443
- char *d = ALLOCV_N(char, vbuf, sl);
444
- l = shrink_space(d, s, l);
445
- s = d;
461
+ z = zonetab(zn, (unsigned int)sl);
446
462
  }
447
- }
448
- if (l > 0 && l <= MAX_WORD_LENGTH) {
449
- const struct zone *z = zonetab(s, (unsigned int)l);
463
+
450
464
  if (z) {
451
465
  int d = z->offset;
452
466
  if (dst)
@@ -455,6 +469,7 @@ date_zone_to_diff(VALUE str)
455
469
  goto ok;
456
470
  }
457
471
  }
472
+
458
473
  {
459
474
  char *p;
460
475
  int sign = 0;
@@ -471,27 +486,53 @@ date_zone_to_diff(VALUE str)
471
486
  s++;
472
487
  l--;
473
488
 
489
+ #define out_of_range(v, min, max) ((v) < (min) || (max) < (v))
474
490
  hour = STRTOUL(s, &p, 10);
475
491
  if (*p == ':') {
492
+ if (out_of_range(hour, 0, 23)) return Qnil;
476
493
  s = ++p;
477
494
  min = STRTOUL(s, &p, 10);
495
+ if (out_of_range(min, 0, 59)) return Qnil;
478
496
  if (*p == ':') {
479
497
  s = ++p;
480
498
  sec = STRTOUL(s, &p, 10);
499
+ if (out_of_range(sec, 0, 59)) return Qnil;
481
500
  }
482
- goto num;
483
501
  }
484
- if (*p == ',' || *p == '.') {
485
- char *e = 0;
486
- p++;
487
- min = STRTOUL(p, &e, 10) * 3600;
502
+ else if (*p == ',' || *p == '.') {
503
+ /* fractional hour */
504
+ size_t n;
505
+ int ov;
506
+ /* no over precision for offset; 10**-7 hour = 0.36
507
+ * milliseconds should be enough. */
508
+ const size_t max_digits = 7; /* 36 * 10**7 < 32-bit FIXNUM_MAX */
509
+
510
+ if (out_of_range(hour, 0, 23)) return Qnil;
511
+
512
+ n = (s + l) - ++p;
513
+ if (n > max_digits) n = max_digits;
514
+ sec = ruby_scan_digits(p, n, 10, &n, &ov);
515
+ if ((p += n) < s + l && *p >= ('5' + !(sec & 1)) && *p <= '9') {
516
+ /* round half to even */
517
+ sec++;
518
+ }
519
+ sec *= 36;
488
520
  if (sign) {
489
521
  hour = -hour;
490
- min = -min;
522
+ sec = -sec;
523
+ }
524
+ if (n <= 2) {
525
+ /* HH.nn or HH.n */
526
+ if (n == 1) sec *= 10;
527
+ offset = INT2FIX(sec + hour * 3600);
528
+ }
529
+ else {
530
+ VALUE denom = rb_int_positive_pow(10, (int)(n - 2));
531
+ offset = f_add(rb_rational_new(INT2FIX(sec), denom), INT2FIX(hour * 3600));
532
+ if (rb_rational_den(offset) == INT2FIX(1)) {
533
+ offset = rb_rational_num(offset);
534
+ }
491
535
  }
492
- offset = rb_rational_new(INT2FIX(min),
493
- rb_int_positive_pow(10, (int)(e - p)));
494
- offset = f_add(INT2FIX(hour * 3600), offset);
495
536
  goto ok;
496
537
  }
497
538
  else if (l > 2) {
@@ -504,18 +545,16 @@ date_zone_to_diff(VALUE str)
504
545
  min = ruby_scan_digits(&s[2 - l % 2], 2, 10, &n, &ov);
505
546
  if (l >= 5)
506
547
  sec = ruby_scan_digits(&s[4 - l % 2], 2, 10, &n, &ov);
507
- goto num;
508
548
  }
509
- num:
510
549
  sec += min * 60 + hour * 3600;
511
550
  if (sign) sec = -sec;
512
551
  offset = INT2FIX(sec);
552
+ #undef out_of_range
513
553
  }
514
554
  }
515
555
  }
516
556
  RB_GC_GUARD(str);
517
557
  ok:
518
- ALLOCV_END(vbuf);
519
558
  return offset;
520
559
  }
521
560
 
@@ -652,24 +691,27 @@ parse_time(VALUE str, VALUE hash)
652
691
  {
653
692
  static const char pat_source[] =
654
693
  "("
694
+ "" NUMBER "+\\s*"
655
695
  "(?:"
656
- "\\d+\\s*:\\s*\\d+"
657
696
  "(?:"
697
+ ":\\s*\\d+"
698
+ "(?:"
658
699
  #ifndef TIGHT_PARSER
659
- "\\s*:\\s*\\d+(?:[,.]\\d*)?"
700
+ "\\s*:\\s*\\d+(?:[,.]\\d*)?"
660
701
  #else
661
- "\\s*:\\s*\\d+(?:[,.]\\d+)?"
702
+ "\\s*:\\s*\\d+(?:[,.]\\d+)?"
662
703
  #endif
704
+ ")?"
705
+ "|"
706
+ "h(?:\\s*\\d+m?(?:\\s*\\d+s?)?)?"
707
+ ")"
708
+ "(?:"
709
+ "\\s*"
710
+ "[ap](?:m\\b|\\.m\\.)"
663
711
  ")?"
664
712
  "|"
665
- "\\d+\\s*h(?:\\s*\\d+m?(?:\\s*\\d+s?)?)?"
666
- ")"
667
- "(?:"
668
- "\\s*"
669
713
  "[ap](?:m\\b|\\.m\\.)"
670
- ")?"
671
- "|"
672
- "\\d+\\s*[ap](?:m\\b|\\.m\\.)"
714
+ ")"
673
715
  ")"
674
716
  "(?:"
675
717
  "\\s*"
@@ -691,6 +733,9 @@ parse_time(VALUE str, VALUE hash)
691
733
  #endif
692
734
  }
693
735
 
736
+ #define BEGIN_ERA "\\b"
737
+ #define END_ERA "(?!(?<!\\.)[a-z])"
738
+
694
739
  #ifdef TIGHT_PARSER
695
740
  static int
696
741
  parse_era1_cb(VALUE m, VALUE hash)
@@ -702,7 +747,7 @@ static int
702
747
  parse_era1(VALUE str, VALUE hash)
703
748
  {
704
749
  static const char pat_source[] =
705
- "(a(?:d|\\.d\\.))";
750
+ BEGIN_ERA "(a(?:d\\b|\\.d\\.))" END_ERA;
706
751
  static VALUE pat = Qnil;
707
752
 
708
753
  REGCOMP_I(pat);
@@ -724,8 +769,9 @@ parse_era2_cb(VALUE m, VALUE hash)
724
769
  static int
725
770
  parse_era2(VALUE str, VALUE hash)
726
771
  {
727
- static const char pat_source[] =
728
- "(c(?:e|\\.e\\.)|b(?:ce|\\.c\\.e\\.)|b(?:c|\\.c\\.))";
772
+ static const char pat_source[] = BEGIN_ERA
773
+ "(c(?:e\\b|\\.e\\.)|b(?:ce\\b|\\.c\\.e\\.)|b(?:c\\b|\\.c\\.))"
774
+ END_ERA;
729
775
  static VALUE pat = Qnil;
730
776
 
731
777
  REGCOMP_I(pat);
@@ -829,7 +875,7 @@ parse_eu(VALUE str, VALUE hash)
829
875
  FPW_COM FPT_COM
830
876
  #endif
831
877
  #ifndef TIGHT_PARSER
832
- "('?\\d+)[^-\\d\\s]*"
878
+ "('?" NUMBER "+)[^-\\d\\s]*"
833
879
  #else
834
880
  "(\\d+)(?:(?:st|nd|rd|th)\\b)?"
835
881
  #endif
@@ -842,7 +888,11 @@ parse_eu(VALUE str, VALUE hash)
842
888
  "(?:"
843
889
  "\\s*"
844
890
  #ifndef TIGHT_PARSER
845
- "(c(?:e|\\.e\\.)|b(?:ce|\\.c\\.e\\.)|a(?:d|\\.d\\.)|b(?:c|\\.c\\.))?"
891
+ "(?:"
892
+ BEGIN_ERA
893
+ "(c(?:e|\\.e\\.)|b(?:ce|\\.c\\.e\\.)|a(?:d|\\.d\\.)|b(?:c|\\.c\\.))"
894
+ END_ERA
895
+ ")?"
846
896
  "\\s*"
847
897
  "('?-?\\d+(?:(?:st|nd|rd|th)\\b)?)"
848
898
  #else
@@ -919,8 +969,8 @@ parse_us(VALUE str, VALUE hash)
919
969
  COM_FPT
920
970
  #endif
921
971
  "(?:"
922
- "\\s*,?"
923
- "\\s*"
972
+ "\\s*+,?"
973
+ "\\s*+"
924
974
  #ifndef TIGHT_PARSER
925
975
  "(c(?:e|\\.e\\.)|b(?:ce|\\.c\\.e\\.)|a(?:d|\\.d\\.)|b(?:c|\\.c\\.))?"
926
976
  "\\s*"
@@ -967,7 +1017,7 @@ parse_iso(VALUE str, VALUE hash)
967
1017
  {
968
1018
  static const char pat_source[] =
969
1019
  #ifndef TIGHT_PARSER
970
- "('?[-+]?\\d+)-(\\d+)-('?-?\\d+)"
1020
+ "('?[-+]?" NUMBER "+)-(\\d+)-('?-?\\d+)"
971
1021
  #else
972
1022
  BOS
973
1023
  FPW_COM FPT_COM
@@ -1321,7 +1371,7 @@ parse_vms11(VALUE str, VALUE hash)
1321
1371
  {
1322
1372
  static const char pat_source[] =
1323
1373
  #ifndef TIGHT_PARSER
1324
- "('?-?\\d+)-(" ABBR_MONTHS ")[^-/.]*"
1374
+ "('?-?" NUMBER "+)-(" ABBR_MONTHS ")[^-/.]*"
1325
1375
  "-('?-?\\d+)"
1326
1376
  #else
1327
1377
  BOS
@@ -1416,7 +1466,7 @@ parse_sla(VALUE str, VALUE hash)
1416
1466
  {
1417
1467
  static const char pat_source[] =
1418
1468
  #ifndef TIGHT_PARSER
1419
- "('?-?\\d+)/\\s*('?\\d+)(?:\\D\\s*('?-?\\d+))?"
1469
+ "('?-?" NUMBER "+)/\\s*('?\\d+)(?:\\D\\s*('?-?\\d+))?"
1420
1470
  #else
1421
1471
  BOS
1422
1472
  FPW_COM FPT_COM
@@ -1524,7 +1574,7 @@ parse_dot(VALUE str, VALUE hash)
1524
1574
  {
1525
1575
  static const char pat_source[] =
1526
1576
  #ifndef TIGHT_PARSER
1527
- "('?-?\\d+)\\.\\s*('?\\d+)\\.\\s*('?-?\\d+)"
1577
+ "('?-?" NUMBER "+)\\.\\s*('?\\d+)\\.\\s*('?-?\\d+)"
1528
1578
  #else
1529
1579
  BOS
1530
1580
  FPW_COM FPT_COM
@@ -1684,7 +1734,7 @@ parse_mday(VALUE str, VALUE hash)
1684
1734
  {
1685
1735
  static const char pat_source[] =
1686
1736
  #ifndef TIGHT_PARSER
1687
- "(\\d+)(st|nd|rd|th)\\b"
1737
+ "(" NUMBER "+)(st|nd|rd|th)\\b"
1688
1738
  #else
1689
1739
  BOS
1690
1740
  FPW_COM FPT_COM
@@ -1922,7 +1972,7 @@ parse_ddd(VALUE str, VALUE hash)
1922
1972
  #ifdef TIGHT_PARSER
1923
1973
  BOS
1924
1974
  #endif
1925
- "([-+]?)(\\d{2,14})"
1975
+ "([-+]?)(" NUMBER "{2,14})"
1926
1976
  "(?:"
1927
1977
  "\\s*"
1928
1978
  "t?"
@@ -7,31 +7,21 @@
7
7
  #include "ruby/re.h"
8
8
  #include <ctype.h>
9
9
 
10
+ #undef strncasecmp
11
+ #define strncasecmp STRNCASECMP
12
+
10
13
  static const char *day_names[] = {
11
14
  "Sunday", "Monday", "Tuesday", "Wednesday",
12
15
  "Thursday", "Friday", "Saturday",
13
- "Sun", "Mon", "Tue", "Wed",
14
- "Thu", "Fri", "Sat"
15
16
  };
17
+ static const int ABBREVIATED_DAY_NAME_LENGTH = 3;
16
18
 
17
19
  static const char *month_names[] = {
18
20
  "January", "February", "March", "April",
19
21
  "May", "June", "July", "August", "September",
20
22
  "October", "November", "December",
21
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
22
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
23
- };
24
-
25
- static const char *merid_names[] = {
26
- "am", "pm",
27
- "a.m.", "p.m."
28
- };
29
-
30
- static const char *extz_pats[] = {
31
- ":z",
32
- "::z",
33
- ":::z"
34
23
  };
24
+ static const int ABBREVIATED_MONTH_NAME_LENGTH = 3;
35
25
 
36
26
  #define sizeof_array(o) (sizeof o / sizeof o[0])
37
27
 
@@ -75,7 +65,7 @@ num_pattern_p(const char *s)
75
65
  #define NUM_PATTERN_P() num_pattern_p(&fmt[fi + 1])
76
66
 
77
67
  static long
78
- read_digits(const char *s, VALUE *n, size_t width)
68
+ read_digits(const char *s, size_t slen, VALUE *n, size_t width)
79
69
  {
80
70
  size_t l;
81
71
 
@@ -83,7 +73,7 @@ read_digits(const char *s, VALUE *n, size_t width)
83
73
  return 0;
84
74
 
85
75
  l = 0;
86
- while (ISDIGIT(s[l])) {
76
+ while (l < slen && ISDIGIT(s[l])) {
87
77
  if (++l == width) break;
88
78
  }
89
79
 
@@ -131,9 +121,10 @@ do { \
131
121
  #define READ_DIGITS(n,w) \
132
122
  do { \
133
123
  size_t l; \
134
- l = read_digits(&str[si], &n, w); \
135
- if (l == 0) \
124
+ l = read_digits(&str[si], slen - si, &n, w); \
125
+ if (l == 0) { \
136
126
  fail(); \
127
+ } \
137
128
  si += l; \
138
129
  } while (0)
139
130
 
@@ -161,6 +152,12 @@ do { \
161
152
 
162
153
  VALUE date_zone_to_diff(VALUE);
163
154
 
155
+ static inline int
156
+ head_match_p(size_t len, const char *name, const char *str, size_t slen, size_t si)
157
+ {
158
+ return slen - si >= len && strncasecmp(name, &str[si], len) == 0;
159
+ }
160
+
164
161
  static size_t
165
162
  date__strptime_internal(const char *str, size_t slen,
166
163
  const char *fmt, size_t flen, VALUE hash)
@@ -168,9 +165,18 @@ date__strptime_internal(const char *str, size_t slen,
168
165
  size_t si, fi;
169
166
  int c;
170
167
 
168
+ #define HEAD_MATCH_P(len, name) head_match_p(len, name, str, slen, si)
171
169
  si = fi = 0;
172
170
 
173
171
  while (fi < flen) {
172
+ if (isspace((unsigned char)fmt[fi])) {
173
+ while (si < slen && isspace((unsigned char)str[si]))
174
+ si++;
175
+ while (++fi < flen && isspace((unsigned char)fmt[fi]));
176
+ continue;
177
+ }
178
+
179
+ if (si >= slen) fail();
174
180
 
175
181
  switch (fmt[fi]) {
176
182
  case '%':
@@ -194,12 +200,11 @@ date__strptime_internal(const char *str, size_t slen,
194
200
  {
195
201
  int i;
196
202
 
197
- for (i = 0; i < (int)sizeof_array(extz_pats); i++)
198
- if (strncmp(extz_pats[i], &fmt[fi],
199
- strlen(extz_pats[i])) == 0) {
200
- fi += i;
201
- goto again;
202
- }
203
+ for (i = 1; i < 3 && fi + i < flen && fmt[fi+i] == ':'; ++i);
204
+ if (fmt[fi+i] == 'z') {
205
+ fi += i - 1;
206
+ goto again;
207
+ }
203
208
  fail();
204
209
  }
205
210
 
@@ -209,10 +214,12 @@ date__strptime_internal(const char *str, size_t slen,
209
214
  int i;
210
215
 
211
216
  for (i = 0; i < (int)sizeof_array(day_names); i++) {
212
- size_t l = strlen(day_names[i]);
213
- if (strncasecmp(day_names[i], &str[si], l) == 0) {
217
+ const char *day_name = day_names[i];
218
+ size_t l = strlen(day_name);
219
+ if (HEAD_MATCH_P(l, day_name) ||
220
+ HEAD_MATCH_P(l = ABBREVIATED_DAY_NAME_LENGTH, day_name)) {
214
221
  si += l;
215
- set_hash("wday", INT2FIX(i % 7));
222
+ set_hash("wday", INT2FIX(i));
216
223
  goto matched;
217
224
  }
218
225
  }
@@ -225,10 +232,12 @@ date__strptime_internal(const char *str, size_t slen,
225
232
  int i;
226
233
 
227
234
  for (i = 0; i < (int)sizeof_array(month_names); i++) {
228
- size_t l = strlen(month_names[i]);
229
- if (strncasecmp(month_names[i], &str[si], l) == 0) {
235
+ const char *month_name = month_names[i];
236
+ size_t l = strlen(month_name);
237
+ if (HEAD_MATCH_P(l, month_name) ||
238
+ HEAD_MATCH_P(l = ABBREVIATED_MONTH_NAME_LENGTH, month_name)) {
230
239
  si += l;
231
- set_hash("mon", INT2FIX((i % 12) + 1));
240
+ set_hash("mon", INT2FIX(i + 1));
232
241
  goto matched;
233
242
  }
234
243
  }
@@ -402,18 +411,19 @@ date__strptime_internal(const char *str, size_t slen,
402
411
 
403
412
  case 'P':
404
413
  case 'p':
414
+ if (slen - si < 2) fail();
405
415
  {
406
- int i;
407
-
408
- for (i = 0; i < 4; i++) {
409
- size_t l = strlen(merid_names[i]);
410
- if (strncasecmp(merid_names[i], &str[si], l) == 0) {
411
- si += l;
412
- set_hash("_merid", INT2FIX((i % 2) == 0 ? 0 : 12));
413
- goto matched;
414
- }
416
+ char c = str[si];
417
+ const int hour = (c == 'P' || c == 'p') ? 12 : 0;
418
+ if (!hour && !(c == 'A' || c == 'a')) fail();
419
+ if ((c = str[si+1]) == '.') {
420
+ if (slen - si < 4 || str[si+3] != '.') fail();
421
+ c = str[si += 2];
415
422
  }
416
- fail();
423
+ if (!(c == 'M' || c == 'm')) fail();
424
+ si += 2;
425
+ set_hash("_merid", INT2FIX(hour));
426
+ goto matched;
417
427
  }
418
428
 
419
429
  case 'Q':
@@ -587,7 +597,7 @@ date__strptime_internal(const char *str, size_t slen,
587
597
 
588
598
  b = rb_backref_get();
589
599
  rb_match_busy(b);
590
- m = f_match(pat, rb_usascii_str_new2(&str[si]));
600
+ m = f_match(pat, rb_usascii_str_new(&str[si], slen - si));
591
601
 
592
602
  if (!NIL_P(m)) {
593
603
  VALUE s, l, o;
@@ -619,22 +629,13 @@ date__strptime_internal(const char *str, size_t slen,
619
629
  if (str[si] != '%')
620
630
  fail();
621
631
  si++;
622
- if (fi < flen)
623
- if (str[si] != fmt[fi])
632
+ if (fi < flen) {
633
+ if (si >= slen || str[si] != fmt[fi])
624
634
  fail();
625
- si++;
635
+ si++;
636
+ }
626
637
  goto matched;
627
638
  }
628
- case ' ':
629
- case '\t':
630
- case '\n':
631
- case '\v':
632
- case '\f':
633
- case '\r':
634
- while (isspace((unsigned char)str[si]))
635
- si++;
636
- fi++;
637
- break;
638
639
  default:
639
640
  ordinal:
640
641
  if (str[si] != fmt[fi])
data/ext/date/extconf.rb CHANGED
@@ -3,6 +3,8 @@ require 'mkmf'
3
3
 
4
4
  config_string("strict_warnflags") {|w| $warnflags += " #{w}"}
5
5
 
6
+ append_cflags("-Wno-compound-token-split-by-macro") if RUBY_VERSION < "2.7."
7
+ have_func("rb_category_warn")
6
8
  with_werror("", {:werror => true}) do |opt, |
7
9
  have_var("timezone", "time.h", opt)
8
10
  have_var("altzone", "time.h", opt)
data/ext/date/prereq.mk CHANGED
@@ -1,7 +1,7 @@
1
1
  .SUFFIXES: .list
2
2
 
3
3
  .list.h:
4
- gperf --ignore-case -C -c -P -p -j1 -i 1 -g -o -t -N $(*F) $< \
4
+ gperf --ignore-case -L ANSI-C -C -c -P -p -j1 -i 1 -g -o -t -N $(*F) $< \
5
5
  | sed -f $(top_srcdir)/tool/gperf.sed \
6
6
  > $(@F)
7
7