~ubuntu-branches/ubuntu/quantal/ruby1.9.1/quantal

« back to all changes in this revision

Viewing changes to ext/date/date_strptime.c

  • Committer: Bazaar Package Importer
  • Author(s): Lucas Nussbaum
  • Date: 2011-09-24 19:16:17 UTC
  • mfrom: (1.1.8 upstream) (13.1.7 experimental)
  • Revision ID: james.westby@ubuntu.com-20110924191617-o1qz4rcmqjot8zuy
Tags: 1.9.3~rc1-1
* New upstream release: 1.9.3 RC1.
  + Includes load.c fixes. Closes: #639959.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

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