Ruby  2.5.0dev(2017-10-22revision60238)
date_strptime.c
Go to the documentation of this file.
1 /*
2  date_strptime.c: Coded by Tadayoshi Funaba 2011,2012
3 */
4 
5 #include "ruby.h"
6 #include "ruby/encoding.h"
7 #include "ruby/re.h"
8 #include <ctype.h>
9 
10 static const char *day_names[] = {
11  "Sunday", "Monday", "Tuesday", "Wednesday",
12  "Thursday", "Friday", "Saturday",
13  "Sun", "Mon", "Tue", "Wed",
14  "Thu", "Fri", "Sat"
15 };
16 
17 static const char *month_names[] = {
18  "January", "February", "March", "April",
19  "May", "June", "July", "August", "September",
20  "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 };
35 
36 #define sizeof_array(o) (sizeof o / sizeof o[0])
37 
38 #define f_negate(x) rb_funcall(x, rb_intern("-@"), 0)
39 #define f_add(x,y) rb_funcall(x, '+', 1, y)
40 #define f_sub(x,y) rb_funcall(x, '-', 1, y)
41 #define f_mul(x,y) rb_funcall(x, '*', 1, y)
42 #define f_div(x,y) rb_funcall(x, '/', 1, y)
43 #define f_idiv(x,y) rb_funcall(x, rb_intern("div"), 1, y)
44 #define f_mod(x,y) rb_funcall(x, '%', 1, y)
45 #define f_expt(x,y) rb_funcall(x, rb_intern("**"), 1, y)
46 
47 #define f_lt_p(x,y) rb_funcall(x, '<', 1, y)
48 #define f_gt_p(x,y) rb_funcall(x, '>', 1, y)
49 #define f_le_p(x,y) rb_funcall(x, rb_intern("<="), 1, y)
50 #define f_ge_p(x,y) rb_funcall(x, rb_intern(">="), 1, y)
51 
52 #define f_match(r,s) rb_funcall(r, rb_intern("match"), 1, s)
53 #define f_aref(o,i) rb_funcall(o, rb_intern("[]"), 1, i)
54 #define f_end(o,i) rb_funcall(o, rb_intern("end"), 1, i)
55 
56 #define issign(c) ((c) == '-' || (c) == '+')
57 
58 static int
59 num_pattern_p(const char *s)
60 {
61  if (isdigit((unsigned char)*s))
62  return 1;
63  if (*s == '%') {
64  s++;
65  if (*s == 'E' || *s == 'O')
66  s++;
67  if (*s &&
68  (strchr("CDdeFGgHIjkLlMmNQRrSsTUuVvWwXxYy", *s) ||
69  isdigit((unsigned char)*s)))
70  return 1;
71  }
72  return 0;
73 }
74 
75 #define NUM_PATTERN_P() num_pattern_p(&fmt[fi + 1])
76 
77 static long
78 read_digits(const char *s, VALUE *n, size_t width)
79 {
80  size_t l;
81 
82  l = strspn(s, "0123456789");
83 
84  if (l == 0)
85  return 0;
86 
87  if (width < l)
88  l = width;
89 
90  if ((4 * l * sizeof(char)) <= (sizeof(long)*CHAR_BIT)) {
91  const char *os = s;
92  long v;
93 
94  v = 0;
95  while ((size_t)(s - os) < l) {
96  v *= 10;
97  v += *s - '0';
98  s++;
99  }
100  if (os == s)
101  return 0;
102  *n = LONG2NUM(v);
103  return l;
104  }
105  else {
106  VALUE vbuf = 0;
107  char *s2 = ALLOCV_N(char, vbuf, l + 1);
108  memcpy(s2, s, l);
109  s2[l] = '\0';
110  *n = rb_cstr_to_inum(s2, 10, 0);
111  ALLOCV_END(vbuf);
112  return l;
113  }
114 }
115 
116 #define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k)), v)
117 #define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k)))
118 #define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k)))
119 
120 #define fail() \
121 { \
122  set_hash("_fail", Qtrue); \
123  return 0; \
124 }
125 
126 #define fail_p() (!NIL_P(ref_hash("_fail")))
127 
128 #define READ_DIGITS(n,w) \
129 { \
130  size_t l; \
131  l = read_digits(&str[si], &n, w); \
132  if (l == 0) \
133  fail(); \
134  si += l; \
135 }
136 
137 #define READ_DIGITS_MAX(n) READ_DIGITS(n, LONG_MAX)
138 
139 static int
140 valid_range_p(VALUE v, int a, int b)
141 {
142  if (FIXNUM_P(v)) {
143  int vi = FIX2INT(v);
144  return !(vi < a || vi > b);
145  }
146  return !(f_lt_p(v, INT2NUM(a)) || f_gt_p(v, INT2NUM(b)));
147 }
148 
149 #define recur(fmt) \
150 { \
151  size_t l; \
152  l = date__strptime_internal(&str[si], slen - si, \
153  fmt, sizeof fmt - 1, hash); \
154  if (fail_p()) \
155  return 0; \
156  si += l; \
157 }
158 
160 
161 static size_t
162 date__strptime_internal(const char *str, size_t slen,
163  const char *fmt, size_t flen, VALUE hash)
164 {
165  size_t si, fi;
166  int c;
167 
168  si = fi = 0;
169 
170  while (fi < flen) {
171 
172  switch (fmt[fi]) {
173  case '%':
174 
175  again:
176  fi++;
177  c = fmt[fi];
178 
179  switch (c) {
180  case 'E':
181  if (fmt[fi + 1] && strchr("cCxXyY", fmt[fi + 1]))
182  goto again;
183  fi--;
184  goto ordinal;
185  case 'O':
186  if (fmt[fi + 1] && strchr("deHImMSuUVwWy", fmt[fi + 1]))
187  goto again;
188  fi--;
189  goto ordinal;
190  case ':':
191  {
192  int i;
193 
194  for (i = 0; i < (int)sizeof_array(extz_pats); i++)
195  if (strncmp(extz_pats[i], &fmt[fi],
196  strlen(extz_pats[i])) == 0) {
197  fi += i;
198  goto again;
199  }
200  fail();
201  }
202 
203  case 'A':
204  case 'a':
205  {
206  int i;
207 
208  for (i = 0; i < (int)sizeof_array(day_names); i++) {
209  size_t l = strlen(day_names[i]);
210  if (strncasecmp(day_names[i], &str[si], l) == 0) {
211  si += l;
212  set_hash("wday", INT2FIX(i % 7));
213  goto matched;
214  }
215  }
216  fail();
217  }
218  case 'B':
219  case 'b':
220  case 'h':
221  {
222  int i;
223 
224  for (i = 0; i < (int)sizeof_array(month_names); i++) {
225  size_t l = strlen(month_names[i]);
226  if (strncasecmp(month_names[i], &str[si], l) == 0) {
227  si += l;
228  set_hash("mon", INT2FIX((i % 12) + 1));
229  goto matched;
230  }
231  }
232  fail();
233  }
234 
235  case 'C':
236  {
237  VALUE n;
238 
239  if (NUM_PATTERN_P())
240  READ_DIGITS(n, 2)
241  else
242  READ_DIGITS_MAX(n)
243  set_hash("_cent", n);
244  goto matched;
245  }
246 
247  case 'c':
248  recur("%a %b %e %H:%M:%S %Y");
249  goto matched;
250 
251  case 'D':
252  recur("%m/%d/%y");
253  goto matched;
254 
255  case 'd':
256  case 'e':
257  {
258  VALUE n;
259 
260  if (str[si] == ' ') {
261  si++;
262  READ_DIGITS(n, 1);
263  } else {
264  READ_DIGITS(n, 2);
265  }
266  if (!valid_range_p(n, 1, 31))
267  fail();
268  set_hash("mday", n);
269  goto matched;
270  }
271 
272  case 'F':
273  recur("%Y-%m-%d");
274  goto matched;
275 
276  case 'G':
277  {
278  VALUE n;
279 
280  if (NUM_PATTERN_P())
281  READ_DIGITS(n, 4)
282  else
283  READ_DIGITS_MAX(n)
284  set_hash("cwyear", n);
285  goto matched;
286  }
287 
288  case 'g':
289  {
290  VALUE n;
291 
292  READ_DIGITS(n, 2);
293  if (!valid_range_p(n, 0, 99))
294  fail();
295  set_hash("cwyear",n);
296  if (NIL_P(ref_hash("_cent")))
297  set_hash("_cent",
298  INT2FIX(f_ge_p(n, INT2FIX(69)) ? 19 : 20));
299  goto matched;
300  }
301 
302  case 'H':
303  case 'k':
304  {
305  VALUE n;
306 
307  if (str[si] == ' ') {
308  si++;
309  READ_DIGITS(n, 1);
310  } else {
311  READ_DIGITS(n, 2);
312  }
313  if (!valid_range_p(n, 0, 24))
314  fail();
315  set_hash("hour", n);
316  goto matched;
317  }
318 
319  case 'I':
320  case 'l':
321  {
322  VALUE n;
323 
324  if (str[si] == ' ') {
325  si++;
326  READ_DIGITS(n, 1);
327  } else {
328  READ_DIGITS(n, 2);
329  }
330  if (!valid_range_p(n, 1, 12))
331  fail();
332  set_hash("hour", n);
333  goto matched;
334  }
335 
336  case 'j':
337  {
338  VALUE n;
339 
340  READ_DIGITS(n, 3);
341  if (!valid_range_p(n, 1, 366))
342  fail();
343  set_hash("yday", n);
344  goto matched;
345  }
346 
347  case 'L':
348  case 'N':
349  {
350  VALUE n;
351  int sign = 1;
352  size_t osi;
353 
354  if (issign(str[si])) {
355  if (str[si] == '-')
356  sign = -1;
357  si++;
358  }
359  osi = si;
360  if (NUM_PATTERN_P())
361  READ_DIGITS(n, c == 'L' ? 3 : 9)
362  else
363  READ_DIGITS_MAX(n)
364  if (sign == -1)
365  n = f_negate(n);
366  set_hash("sec_fraction",
368  f_expt(INT2FIX(10),
369  ULONG2NUM(si - osi))));
370  goto matched;
371  }
372 
373  case 'M':
374  {
375  VALUE n;
376 
377  READ_DIGITS(n, 2);
378  if (!valid_range_p(n, 0, 59))
379  fail();
380  set_hash("min", n);
381  goto matched;
382  }
383 
384  case 'm':
385  {
386  VALUE n;
387 
388  READ_DIGITS(n, 2);
389  if (!valid_range_p(n, 1, 12))
390  fail();
391  set_hash("mon", n);
392  goto matched;
393  }
394 
395  case 'n':
396  case 't':
397  recur(" ");
398  goto matched;
399 
400  case 'P':
401  case 'p':
402  {
403  int i;
404 
405  for (i = 0; i < 4; i++) {
406  size_t l = strlen(merid_names[i]);
407  if (strncasecmp(merid_names[i], &str[si], l) == 0) {
408  si += l;
409  set_hash("_merid", INT2FIX((i % 2) == 0 ? 0 : 12));
410  goto matched;
411  }
412  }
413  fail();
414  }
415 
416  case 'Q':
417  {
418  VALUE n;
419  int sign = 1;
420 
421  if (str[si] == '-') {
422  sign = -1;
423  si++;
424  }
425  READ_DIGITS_MAX(n);
426  if (sign == -1)
427  n = f_negate(n);
428  set_hash("seconds",
430  f_expt(INT2FIX(10),
431  INT2FIX(3))));
432  goto matched;
433  }
434 
435  case 'R':
436  recur("%H:%M");
437  goto matched;
438 
439  case 'r':
440  recur("%I:%M:%S %p");
441  goto matched;
442 
443  case 'S':
444  {
445  VALUE n;
446 
447  READ_DIGITS(n, 2);
448  if (!valid_range_p(n, 0, 60))
449  fail();
450  set_hash("sec", n);
451  goto matched;
452  }
453 
454  case 's':
455  {
456  VALUE n;
457  int sign = 1;
458 
459  if (str[si] == '-') {
460  sign = -1;
461  si++;
462  }
463  READ_DIGITS_MAX(n);
464  if (sign == -1)
465  n = f_negate(n);
466  set_hash("seconds", n);
467  goto matched;
468  }
469 
470  case 'T':
471  recur("%H:%M:%S");
472  goto matched;
473 
474  case 'U':
475  case 'W':
476  {
477  VALUE n;
478 
479  READ_DIGITS(n, 2);
480  if (!valid_range_p(n, 0, 53))
481  fail();
482  set_hash(c == 'U' ? "wnum0" : "wnum1", n);
483  goto matched;
484  }
485 
486  case 'u':
487  {
488  VALUE n;
489 
490  READ_DIGITS(n, 1);
491  if (!valid_range_p(n, 1, 7))
492  fail();
493  set_hash("cwday", n);
494  goto matched;
495  }
496 
497  case 'V':
498  {
499  VALUE n;
500 
501  READ_DIGITS(n, 2);
502  if (!valid_range_p(n, 1, 53))
503  fail();
504  set_hash("cweek", n);
505  goto matched;
506  }
507 
508  case 'v':
509  recur("%e-%b-%Y");
510  goto matched;
511 
512  case 'w':
513  {
514  VALUE n;
515 
516  READ_DIGITS(n, 1);
517  if (!valid_range_p(n, 0, 6))
518  fail();
519  set_hash("wday", n);
520  goto matched;
521  }
522 
523  case 'X':
524  recur("%H:%M:%S");
525  goto matched;
526 
527  case 'x':
528  recur("%m/%d/%y");
529  goto matched;
530 
531  case 'Y':
532  {
533  VALUE n;
534  int sign = 1;
535 
536  if (issign(str[si])) {
537  if (str[si] == '-')
538  sign = -1;
539  si++;
540  }
541  if (NUM_PATTERN_P())
542  READ_DIGITS(n, 4)
543  else
544  READ_DIGITS_MAX(n)
545  if (sign == -1)
546  n = f_negate(n);
547  set_hash("year", n);
548  goto matched;
549  }
550 
551  case 'y':
552  {
553  VALUE n;
554  int sign = 1;
555 
556  READ_DIGITS(n, 2);
557  if (!valid_range_p(n, 0, 99))
558  fail();
559  if (sign == -1)
560  n = f_negate(n);
561  set_hash("year", n);
562  if (NIL_P(ref_hash("_cent")))
563  set_hash("_cent",
564  INT2FIX(f_ge_p(n, INT2FIX(69)) ? 19 : 20));
565  goto matched;
566  }
567 
568  case 'Z':
569  case 'z':
570  {
571  static const char pat_source[] =
572  "\\A("
573  "(?:gmt|utc?)?[-+]\\d+(?:[,.:]\\d+(?::\\d+)?)?"
574  "|(?-i:[[:alpha:].\\s]+)(?:standard|daylight)\\s+time\\b"
575  "|(?-i:[[:alpha:]]+)(?:\\s+dst)?\\b"
576  ")";
577  static VALUE pat = Qnil;
578  VALUE m, b;
579 
580  if (NIL_P(pat)) {
581  pat = rb_reg_new(pat_source, sizeof pat_source - 1,
584  }
585 
586  b = rb_backref_get();
587  rb_match_busy(b);
588  m = f_match(pat, rb_usascii_str_new2(&str[si]));
589 
590  if (!NIL_P(m)) {
591  VALUE s, l, o;
592 
593  s = rb_reg_nth_match(1, m);
594  l = f_end(m, INT2FIX(0));
595  o = date_zone_to_diff(s);
596  si += NUM2LONG(l);
597  set_hash("zone", s);
598  set_hash("offset", o);
599  rb_backref_set(b);
600  goto matched;
601  }
602  rb_backref_set(b);
603  fail();
604  }
605 
606  case '%':
607  if (str[si] != '%')
608  fail();
609  si++;
610  goto matched;
611 
612  case '+':
613  recur("%a %b %e %H:%M:%S %Z %Y");
614  goto matched;
615 
616  default:
617  if (str[si] != '%')
618  fail();
619  si++;
620  if (fi < flen)
621  if (str[si] != fmt[fi])
622  fail();
623  si++;
624  goto matched;
625  }
626  case ' ':
627  case '\t':
628  case '\n':
629  case '\v':
630  case '\f':
631  case '\r':
632  while (isspace((unsigned char)str[si]))
633  si++;
634  fi++;
635  break;
636  default:
637  ordinal:
638  if (str[si] != fmt[fi])
639  fail();
640  si++;
641  fi++;
642  break;
643  matched:
644  fi++;
645  break;
646  }
647  }
648 
649  return si;
650 }
651 
652 VALUE
653 date__strptime(const char *str, size_t slen,
654  const char *fmt, size_t flen, VALUE hash)
655 {
656  size_t si;
657  VALUE cent, merid;
658 
659  si = date__strptime_internal(str, slen, fmt, flen, hash);
660 
661  if (slen > si) {
662  VALUE s;
663 
664  s = rb_usascii_str_new(&str[si], slen - si);
665  set_hash("leftover", s);
666  }
667 
668  if (fail_p())
669  return Qnil;
670 
671  cent = ref_hash("_cent");
672  if (!NIL_P(cent)) {
673  VALUE year;
674 
675  year = ref_hash("cwyear");
676  if (!NIL_P(year))
677  set_hash("cwyear", f_add(year, f_mul(cent, INT2FIX(100))));
678  year = ref_hash("year");
679  if (!NIL_P(year))
680  set_hash("year", f_add(year, f_mul(cent, INT2FIX(100))));
681  del_hash("_cent");
682  }
683 
684  merid = ref_hash("_merid");
685  if (!NIL_P(merid)) {
686  VALUE hour;
687 
688  hour = ref_hash("hour");
689  if (!NIL_P(hour)) {
690  hour = f_mod(hour, INT2FIX(12));
691  set_hash("hour", f_add(hour, merid));
692  }
693  del_hash("_merid");
694  }
695 
696  return hash;
697 }
698 
699 /*
700 Local variables:
701 c-file-style: "ruby"
702 End:
703 */
#define rb_rational_new2(x, y)
Definition: intern.h:167
size_t strlen(const char *)
#define INT2NUM(x)
Definition: ruby.h:1538
void rb_backref_set(VALUE)
Definition: vm.c:1235
#define fail_p()
#define rb_usascii_str_new2
Definition: intern.h:841
#define f_expt(x, y)
Definition: date_strptime.c:45
#define ULONG2NUM(x)
Definition: ruby.h:1574
if(len<=MAX_WORD_LENGTH &&len >=MIN_WORD_LENGTH)
Definition: zonetab.h:883
VALUE rb_backref_get(void)
Definition: vm.c:1229
#define f_lt_p(x, y)
Definition: date_strptime.c:47
VALUE date__strptime(const char *str, size_t slen, const char *fmt, size_t flen, VALUE hash)
#define ONIG_OPTION_IGNORECASE
Definition: onigmo.h:451
#define S(s)
#define READ_DIGITS(n, w)
#define FIXNUM_P(f)
Definition: ruby.h:365
#define strncasecmp
Definition: win32.h:192
#define f_ge_p(x, y)
Definition: date_strptime.c:50
#define f_gt_p(x, y)
Definition: date_strptime.c:48
#define fail()
#define f_negate(x)
Definition: date_strptime.c:38
#define M(n)
#define NIL_P(v)
Definition: ruby.h:451
#define H(x, y, z)
#define ALLOCV_N(type, v, n)
Definition: ruby.h:1657
void rb_gc_register_mark_object(VALUE obj)
Definition: gc.c:6227
#define sizeof_array(o)
Definition: date_strptime.c:36
#define ALLOCV_END(v)
Definition: ruby.h:1658
VALUE rb_reg_new(const char *, long, int)
Definition: re.c:2913
#define del_hash(k)
#define Qnil
Definition: ruby.h:438
unsigned long VALUE
Definition: ruby.h:85
#define READ_DIGITS_MAX(n)
char * strchr(char *, char)
#define FIX2INT(x)
Definition: ruby.h:686
void rb_match_busy(VALUE)
Definition: re.c:1252
#define f_add(x, y)
Definition: date_strptime.c:39
#define CHAR_BIT
Definition: ruby.h:196
#define LONG2NUM(x)
Definition: ruby.h:1573
#define recur(fmt)
#define INT2FIX(i)
Definition: ruby.h:232
#define f_match(r, s)
Definition: date_strptime.c:52
#define f_mod(x, y)
Definition: date_strptime.c:44
#define issign(c)
Definition: date_strptime.c:56
#define NUM_PATTERN_P()
Definition: date_strptime.c:75
#define ref_hash(k)
VALUE rb_cstr_to_inum(const char *str, int base, int badcheck)
Definition: bignum.c:3992
VALUE rb_usascii_str_new(const char *, long)
Definition: string.c:743
#define f_mul(x, y)
Definition: date_strptime.c:41
#define f_end(o, i)
Definition: date_strptime.c:54
#define D
Definition: util.c:234
#define NUM2LONG(x)
Definition: ruby.h:648
VALUE date_zone_to_diff(VALUE)
Definition: date_parse.c:353
VALUE rb_reg_nth_match(int, VALUE)
Definition: re.c:1679
#define set_hash(k, v)