Ruby: ext/date/date_strptime.c Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

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