Ruby  2.0.0p247(2013-06-27revision41674)
ext/date/date_parse.c
Go to the documentation of this file.
00001 /*
00002   date_parse.c: Coded by Tadayoshi Funaba 2011,2012
00003 */
00004 
00005 #include "ruby.h"
00006 #include "ruby/encoding.h"
00007 #include "ruby/re.h"
00008 #include <ctype.h>
00009 
00010 /* #define TIGHT_PARSER */
00011 
00012 #define sizeof_array(o) (sizeof o / sizeof o[0])
00013 
00014 #define f_negate(x) rb_funcall(x, rb_intern("-@"), 0)
00015 #define f_add(x,y) rb_funcall(x, '+', 1, y)
00016 #define f_sub(x,y) rb_funcall(x, '-', 1, y)
00017 #define f_mul(x,y) rb_funcall(x, '*', 1, y)
00018 #define f_div(x,y) rb_funcall(x, '/', 1, y)
00019 #define f_idiv(x,y) rb_funcall(x, rb_intern("div"), 1, y)
00020 #define f_mod(x,y) rb_funcall(x, '%', 1, y)
00021 #define f_expt(x,y) rb_funcall(x, rb_intern("**"), 1, y)
00022 
00023 #define f_lt_p(x,y) rb_funcall(x, '<', 1, y)
00024 #define f_gt_p(x,y) rb_funcall(x, '>', 1, y)
00025 #define f_le_p(x,y) rb_funcall(x, rb_intern("<="), 1, y)
00026 #define f_ge_p(x,y) rb_funcall(x, rb_intern(">="), 1, y)
00027 
00028 #define f_to_s(x) rb_funcall(x, rb_intern("to_s"), 0)
00029 
00030 #define f_match(r,s) rb_funcall(r, rb_intern("match"), 1, s)
00031 #define f_aref(o,i) rb_funcall(o, rb_intern("[]"), 1, i)
00032 #define f_aref2(o,i,j) rb_funcall(o, rb_intern("[]"), 2, i, j)
00033 #define f_begin(o,i) rb_funcall(o, rb_intern("begin"), 1, i)
00034 #define f_end(o,i) rb_funcall(o, rb_intern("end"), 1, i)
00035 #define f_aset(o,i,v) rb_funcall(o, rb_intern("[]="), 2, i, v)
00036 #define f_aset2(o,i,j,v) rb_funcall(o, rb_intern("[]="), 3, i, j, v)
00037 #define f_sub_bang(s,r,x) rb_funcall(s, rb_intern("sub!"), 2, r, x)
00038 #define f_gsub_bang(s,r,x) rb_funcall(s, rb_intern("gsub!"), 2, r, x)
00039 
00040 #define set_hash(k,v) rb_hash_aset(hash, ID2SYM(rb_intern(k)), v)
00041 #define ref_hash(k) rb_hash_aref(hash, ID2SYM(rb_intern(k)))
00042 #define del_hash(k) rb_hash_delete(hash, ID2SYM(rb_intern(k)))
00043 
00044 #define cstr2num(s) rb_cstr_to_inum(s, 10, 0)
00045 #define str2num(s) rb_str_to_inum(s, 10, 0)
00046 
00047 static const char *abbr_days[] = {
00048     "sun", "mon", "tue", "wed",
00049     "thu", "fri", "sat"
00050 };
00051 
00052 static const char *abbr_months[] = {
00053     "jan", "feb", "mar", "apr", "may", "jun",
00054     "jul", "aug", "sep", "oct", "nov", "dec"
00055 };
00056 
00057 #define issign(c) ((c) == '-' || (c) == '+')
00058 #define asp_string() rb_str_new(" ", 1)
00059 #ifdef TIGHT_PARSER
00060 #define asuba_string() rb_str_new("\001", 1)
00061 #define asubb_string() rb_str_new("\002", 1)
00062 #define asubw_string() rb_str_new("\027", 1)
00063 #define asubt_string() rb_str_new("\024", 1)
00064 #endif
00065 
00066 #define DECDIGIT "0123456789"
00067 
00068 static void
00069 s3e(VALUE hash, VALUE y, VALUE m, VALUE d, int bc)
00070 {
00071     VALUE c = Qnil;
00072 
00073     if (TYPE(m) != T_STRING)
00074         m = f_to_s(m);
00075 
00076     if (!NIL_P(y) && !NIL_P(m) && NIL_P(d)) {
00077         VALUE oy = y;
00078         VALUE om = m;
00079         VALUE od = d;
00080 
00081         y = od;
00082         m = oy;
00083         d = om;
00084     }
00085 
00086     if (NIL_P(y)) {
00087         if (!NIL_P(d) && RSTRING_LEN(d) > 2) {
00088             y = d;
00089             d = Qnil;
00090         }
00091         if (!NIL_P(d) && *RSTRING_PTR(d) == '\'') {
00092             y = d;
00093             d = Qnil;
00094         }
00095     }
00096 
00097     if (!NIL_P(y)) {
00098         const char *s, *bp, *ep;
00099         size_t l;
00100 
00101         s = RSTRING_PTR(y);
00102         while (!issign((unsigned char)*s) && !isdigit((unsigned char)*s))
00103             s++;
00104         bp = s;
00105         if (issign((unsigned char)*s))
00106             s++;
00107         l = strspn(s, DECDIGIT);
00108         ep = s + l;
00109         if (*ep) {
00110             y = d;
00111             d = rb_str_new(bp, ep - bp);
00112         }
00113     }
00114 
00115     if (!NIL_P(m)) {
00116         const char *s;
00117 
00118         s = RSTRING_PTR(m);
00119         if (*s == '\'' || RSTRING_LEN(m) > 2) {
00120             /* us -> be */
00121             VALUE oy = y;
00122             VALUE om = m;
00123             VALUE od = d;
00124 
00125             y = om;
00126             m = od;
00127             d = oy;
00128         }
00129     }
00130 
00131     if (!NIL_P(d)) {
00132         const char *s;
00133 
00134         s = RSTRING_PTR(d);
00135         if (*s == '\'' || RSTRING_LEN(d) > 2) {
00136             VALUE oy = y;
00137             VALUE od = d;
00138 
00139             y = od;
00140             d = oy;
00141         }
00142     }
00143 
00144     if (!NIL_P(y)) {
00145         const char *s, *bp, *ep;
00146         int sign = 0;
00147         size_t l;
00148         VALUE iy;
00149 
00150         s = RSTRING_PTR(y);
00151         while (!issign((unsigned char)*s) && !isdigit((unsigned char)*s))
00152             s++;
00153         bp = s;
00154         if (issign(*s)) {
00155             s++;
00156             sign = 1;
00157         }
00158         if (sign)
00159             c = Qfalse;
00160         l = strspn(s, DECDIGIT);
00161         ep = s + l;
00162         if (l > 2)
00163             c = Qfalse;
00164         {
00165             char *buf;
00166 
00167             buf = ALLOCA_N(char, ep - bp + 1);
00168             memcpy(buf, bp, ep - bp);
00169             buf[ep - bp] = '\0';
00170             iy = cstr2num(buf);
00171         }
00172         set_hash("year", iy);
00173     }
00174 
00175     if (bc)
00176         set_hash("_bc", Qtrue);
00177 
00178     if (!NIL_P(m)) {
00179         const char *s, *bp, *ep;
00180         size_t l;
00181         VALUE im;
00182 
00183         s = RSTRING_PTR(m);
00184         while (!isdigit((unsigned char)*s))
00185             s++;
00186         bp = s;
00187         l = strspn(s, DECDIGIT);
00188         ep = s + l;
00189         {
00190             char *buf;
00191 
00192             buf = ALLOCA_N(char, ep - bp + 1);
00193             memcpy(buf, bp, ep - bp);
00194             buf[ep - bp] = '\0';
00195             im = cstr2num(buf);
00196         }
00197         set_hash("mon", im);
00198     }
00199 
00200     if (!NIL_P(d)) {
00201         const char *s, *bp, *ep;
00202         size_t l;
00203         VALUE id;
00204 
00205         s = RSTRING_PTR(d);
00206         while (!isdigit((unsigned char)*s))
00207             s++;
00208         bp = s;
00209         l = strspn(s, DECDIGIT);
00210         ep = s + l;
00211         {
00212             char *buf;
00213 
00214             buf = ALLOCA_N(char, ep - bp + 1);
00215             memcpy(buf, bp, ep - bp);
00216             buf[ep - bp] = '\0';
00217             id = cstr2num(buf);
00218         }
00219         set_hash("mday", id);
00220     }
00221 
00222     if (!NIL_P(c))
00223         set_hash("_comp", c);
00224 }
00225 
00226 #define DAYS "sunday|monday|tuesday|wednesday|thursday|friday|saturday"
00227 #define MONTHS "january|february|march|april|may|june|july|august|september|october|november|december"
00228 #define ABBR_DAYS "sun|mon|tue|wed|thu|fri|sat"
00229 #define ABBR_MONTHS "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec"
00230 
00231 #ifdef TIGHT_PARSER
00232 #define VALID_DAYS "(?:" DAYS ")" "|(?:tues|wednes|thurs|thur|" ABBR_DAYS ")\\.?"
00233 #define VALID_MONTHS "(?:" MONTHS ")" "|(?:sept|" ABBR_MONTHS ")\\.?"
00234 #define DOTLESS_VALID_MONTHS "(?:" MONTHS ")" "|(?:sept|" ABBR_MONTHS ")"
00235 #define BOS "\\A\\s*"
00236 #define FPA "\\001"
00237 #define FPB "\\002"
00238 #define FPW "\\027"
00239 #define FPT "\\024"
00240 #define FPW_COM "\\s*(?:" FPW "\\s*,?)?\\s*"
00241 #define FPT_COM "\\s*(?:" FPT "\\s*,?)?\\s*"
00242 #define COM_FPW "\\s*(?:,?\\s*" FPW ")?\\s*"
00243 #define COM_FPT "\\s*(?:,?\\s*(?:@|\\b[aA][tT]\\b)?\\s*" FPT ")?\\s*"
00244 #define TEE_FPT "\\s*(?:[tT]?" FPT ")?"
00245 #define EOS "\\s*\\z"
00246 #endif
00247 
00248 static VALUE
00249 regcomp(const char *source, long len, int opt)
00250 {
00251     VALUE pat;
00252 
00253     pat = rb_reg_new(source, len, opt);
00254     rb_gc_register_mark_object(pat);
00255     return pat;
00256 }
00257 
00258 #define REGCOMP(pat,opt) \
00259 { \
00260     if (NIL_P(pat)) \
00261         pat = regcomp(pat##_source, sizeof pat##_source - 1, opt); \
00262 }
00263 
00264 #define REGCOMP_0(pat) REGCOMP(pat, 0)
00265 #define REGCOMP_I(pat) REGCOMP(pat, ONIG_OPTION_IGNORECASE)
00266 
00267 #define MATCH(s,p,c) \
00268 { \
00269     return match(s, p, hash, c); \
00270 }
00271 
00272 static int
00273 match(VALUE str, VALUE pat, VALUE hash, int (*cb)(VALUE, VALUE))
00274 {
00275     VALUE m;
00276 
00277     m = f_match(pat, str);
00278 
00279     if (NIL_P(m))
00280         return 0;
00281 
00282     (*cb)(m, hash);
00283 
00284     return 1;
00285 }
00286 
00287 static int
00288 subx(VALUE str, VALUE rep, VALUE pat, VALUE hash, int (*cb)(VALUE, VALUE))
00289 {
00290     VALUE m;
00291 
00292     m = f_match(pat, str);
00293 
00294     if (NIL_P(m))
00295         return 0;
00296 
00297     {
00298         VALUE be, en;
00299 
00300         be = f_begin(m, INT2FIX(0));
00301         en = f_end(m, INT2FIX(0));
00302         f_aset2(str, be, LONG2NUM(NUM2LONG(en) - NUM2LONG(be)), rep);
00303         (*cb)(m, hash);
00304     }
00305 
00306     return 1;
00307 }
00308 
00309 #define SUBS(s,p,c) \
00310 { \
00311     return subx(s, asp_string(), p, hash, c); \
00312 }
00313 
00314 #ifdef TIGHT_PARSER
00315 #define SUBA(s,p,c) \
00316 { \
00317     return subx(s, asuba_string(), p, hash, c); \
00318 }
00319 
00320 #define SUBB(s,p,c) \
00321 { \
00322     return subx(s, asubb_string(), p, hash, c); \
00323 }
00324 
00325 #define SUBW(s,p,c) \
00326 { \
00327     return subx(s, asubw_string(), p, hash, c); \
00328 }
00329 
00330 #define SUBT(s,p,c) \
00331 { \
00332     return subx(s, asubt_string(), p, hash, c); \
00333 }
00334 #endif
00335 
00336 struct zone {
00337     const char *name;
00338     int offset;
00339 };
00340 
00341 static struct zone zones_source[] = {
00342     {"ut",   0*3600}, {"gmt",  0*3600}, {"est", -5*3600}, {"edt", -4*3600},
00343     {"cst", -6*3600}, {"cdt", -5*3600}, {"mst", -7*3600}, {"mdt", -6*3600},
00344     {"pst", -8*3600}, {"pdt", -7*3600},
00345     {"a",    1*3600}, {"b",    2*3600}, {"c",    3*3600}, {"d",    4*3600},
00346     {"e",    5*3600}, {"f",    6*3600}, {"g",    7*3600}, {"h",    8*3600},
00347     {"i",    9*3600}, {"k",   10*3600}, {"l",   11*3600}, {"m",   12*3600},
00348     {"n",   -1*3600}, {"o",   -2*3600}, {"p",   -3*3600}, {"q",   -4*3600},
00349     {"r",   -5*3600}, {"s",   -6*3600}, {"t",   -7*3600}, {"u",   -8*3600},
00350     {"v",   -9*3600}, {"w",  -10*3600}, {"x",  -11*3600}, {"y",  -12*3600},
00351     {"z",    0*3600},
00352 
00353     {"utc",  0*3600}, {"wet",  0*3600},
00354     {"at",  -2*3600}, {"brst",-2*3600}, {"ndt", -(2*3600+1800)},
00355     {"art", -3*3600}, {"adt", -3*3600}, {"brt", -3*3600}, {"clst",-3*3600},
00356     {"nst", -(3*3600+1800)},
00357     {"ast", -4*3600}, {"clt", -4*3600},
00358     {"akdt",-8*3600}, {"ydt", -8*3600},
00359     {"akst",-9*3600}, {"hadt",-9*3600}, {"hdt", -9*3600}, {"yst", -9*3600},
00360     {"ahst",-10*3600},{"cat",-10*3600}, {"hast",-10*3600},{"hst",-10*3600},
00361     {"nt",  -11*3600},
00362     {"idlw",-12*3600},
00363     {"bst",  1*3600}, {"cet",  1*3600}, {"fwt",  1*3600}, {"met",  1*3600},
00364     {"mewt", 1*3600}, {"mez",  1*3600}, {"swt",  1*3600}, {"wat",  1*3600},
00365     {"west", 1*3600},
00366     {"cest", 2*3600}, {"eet",  2*3600}, {"fst",  2*3600}, {"mest", 2*3600},
00367     {"mesz", 2*3600}, {"sast", 2*3600}, {"sst",  2*3600},
00368     {"bt",   3*3600}, {"eat",  3*3600}, {"eest", 3*3600}, {"msk",  3*3600},
00369     {"msd",  4*3600}, {"zp4",  4*3600},
00370     {"zp5",  5*3600}, {"ist",  (5*3600+1800)},
00371     {"zp6",  6*3600},
00372     {"wast", 7*3600},
00373     {"cct",  8*3600}, {"sgt",  8*3600}, {"wadt", 8*3600},
00374     {"jst",  9*3600}, {"kst",  9*3600},
00375     {"east",10*3600}, {"gst", 10*3600},
00376     {"eadt",11*3600},
00377     {"idle",12*3600}, {"nzst",12*3600}, {"nzt", 12*3600},
00378     {"nzdt",13*3600},
00379 
00380     {"afghanistan",             16200}, {"alaskan",                -32400},
00381     {"arab",                    10800}, {"arabian",                 14400},
00382     {"arabic",                  10800}, {"atlantic",               -14400},
00383     {"aus central",             34200}, {"aus eastern",             36000},
00384     {"azores",                  -3600}, {"canada central",         -21600},
00385     {"cape verde",              -3600}, {"caucasus",                14400},
00386     {"cen. australia",          34200}, {"central america",        -21600},
00387     {"central asia",            21600}, {"central europe",           3600},
00388     {"central european",         3600}, {"central pacific",         39600},
00389     {"central",                -21600}, {"china",                   28800},
00390     {"dateline",               -43200}, {"e. africa",               10800},
00391     {"e. australia",            36000}, {"e. europe",                7200},
00392     {"e. south america",       -10800}, {"eastern",                -18000},
00393     {"egypt",                    7200}, {"ekaterinburg",            18000},
00394     {"fiji",                    43200}, {"fle",                      7200},
00395     {"greenland",              -10800}, {"greenwich",                   0},
00396     {"gtb",                      7200}, {"hawaiian",               -36000},
00397     {"india",                   19800}, {"iran",                    12600},
00398     {"jerusalem",                7200}, {"korea",                   32400},
00399     {"mexico",                 -21600}, {"mid-atlantic",            -7200},
00400     {"mountain",               -25200}, {"myanmar",                 23400},
00401     {"n. central asia",         21600}, {"nepal",                   20700},
00402     {"new zealand",             43200}, {"newfoundland",           -12600},
00403     {"north asia east",         28800}, {"north asia",              25200},
00404     {"pacific sa",             -14400}, {"pacific",                -28800},
00405     {"romance",                  3600}, {"russian",                 10800},
00406     {"sa eastern",             -10800}, {"sa pacific",             -18000},
00407     {"sa western",             -14400}, {"samoa",                  -39600},
00408     {"se asia",                 25200}, {"malay peninsula",         28800},
00409     {"south africa",             7200}, {"sri lanka",               21600},
00410     {"taipei",                  28800}, {"tasmania",                36000},
00411     {"tokyo",                   32400}, {"tonga",                   46800},
00412     {"us eastern",             -18000}, {"us mountain",            -25200},
00413     {"vladivostok",             36000}, {"w. australia",            28800},
00414     {"w. central africa",        3600}, {"w. europe",                3600},
00415     {"west asia",               18000}, {"west pacific",            36000},
00416     {"yakutsk",                 32400}
00417 };
00418 
00419 VALUE
00420 date_zone_to_diff(VALUE str)
00421 {
00422     VALUE offset = Qnil;
00423 
00424     long l, i;
00425     char *s, *dest, *d;
00426     int sp = 1;
00427 
00428     l = RSTRING_LEN(str);
00429     s = RSTRING_PTR(str);
00430 
00431     dest = d = ALLOCA_N(char, l + 1);
00432 
00433     for (i = 0; i < l; i++) {
00434         if (isspace((unsigned char)s[i]) || s[i] == '\0') {
00435             if (!sp)
00436                 *d++ = ' ';
00437             sp = 1;
00438         }
00439         else {
00440             if (isalpha((unsigned char)s[i]))
00441                 *d++ = tolower((unsigned char)s[i]);
00442             else
00443                 *d++ = s[i];
00444             sp = 0;
00445         }
00446     }
00447     if (d > dest) {
00448         if (*(d - 1) == ' ')
00449             --d;
00450         *d = '\0';
00451     }
00452     str = rb_str_new2(dest);
00453     {
00454 #define STD " standard time"
00455 #define DST " daylight time"
00456         char *ss, *ds;
00457         long sl, dl;
00458         int dst = 0;
00459 
00460         sl = RSTRING_LEN(str) - (sizeof STD - 1);
00461         ss = RSTRING_PTR(str) + sl;
00462         dl = RSTRING_LEN(str) - (sizeof DST - 1);
00463         ds = RSTRING_PTR(str) + dl;
00464 
00465         if (sl >= 0 && strcmp(ss, STD) == 0) {
00466             str = rb_str_new(RSTRING_PTR(str), sl);
00467         }
00468         else if (dl >= 0 && strcmp(ds, DST) == 0) {
00469             str = rb_str_new(RSTRING_PTR(str), dl);
00470             dst = 1;
00471         }
00472 #undef STD
00473 #undef DST
00474         else {
00475 #define DST " dst"
00476             char *ds;
00477             long dl;
00478 
00479             dl = RSTRING_LEN(str) - (sizeof DST - 1);
00480             ds = RSTRING_PTR(str) + dl;
00481 
00482             if (dl >= 0 && strcmp(ds, DST) == 0) {
00483                 str = rb_str_new(RSTRING_PTR(str), dl);
00484                 dst = 1;
00485             }
00486 #undef DST
00487         }
00488         {
00489             static VALUE zones = Qnil;
00490 
00491             if (NIL_P(zones)) {
00492                 int i;
00493 
00494                 zones = rb_hash_new();
00495                 rb_gc_register_mark_object(zones);
00496                 for (i = 0; i < (int)sizeof_array(zones_source); i++) {
00497                     VALUE name = rb_str_new2(zones_source[i].name);
00498                     VALUE offset = INT2FIX(zones_source[i].offset);
00499                     rb_hash_aset(zones, name, offset);
00500                 }
00501             }
00502 
00503             offset = f_aref(zones, str);
00504             if (!NIL_P(offset)) {
00505                 if (dst)
00506                     offset = f_add(offset, INT2FIX(3600));
00507                 goto ok;
00508             }
00509         }
00510         {
00511             char *s, *p;
00512             VALUE sign;
00513             VALUE hour = Qnil, min = Qnil, sec = Qnil;
00514             VALUE str_orig;
00515 
00516             s = RSTRING_PTR(str);
00517             str_orig = str;
00518 
00519             if (strncmp(s, "gmt", 3) == 0 ||
00520                 strncmp(s, "utc", 3) == 0)
00521                 s += 3;
00522             if (issign(*s)) {
00523                 sign = rb_str_new(s, 1);
00524                 s++;
00525 
00526                 str = rb_str_new2(s);
00527 
00528                 if (p = strchr(s, ':')) {
00529                     hour = rb_str_new(s, p - s);
00530                     s = ++p;
00531                     if (p = strchr(s, ':')) {
00532                         min = rb_str_new(s, p - s);
00533                         s = ++p;
00534                         if (p = strchr(s, ':')) {
00535                             sec = rb_str_new(s, p - s);
00536                         }
00537                         else
00538                             sec = rb_str_new2(s);
00539                     }
00540                     else
00541                         min = rb_str_new2(s);
00542                     RB_GC_GUARD(str_orig);
00543                     goto num;
00544                 }
00545                 if (strpbrk(RSTRING_PTR(str), ",.")) {
00546                     char *a, *b;
00547 
00548                     a = ALLOCA_N(char, RSTRING_LEN(str) + 1);
00549                     strcpy(a, RSTRING_PTR(str));
00550                     b = strpbrk(a, ",.");
00551                     *b = '\0';
00552                     b++;
00553 
00554                     hour = cstr2num(a);
00555                     min = f_mul(rb_rational_new2
00556                                 (cstr2num(b),
00557                                  f_expt(INT2FIX(10),
00558                                         LONG2NUM((long)strlen(b)))),
00559                                 INT2FIX(60));
00560                     goto num;
00561                 }
00562                 {
00563                     const char *cs = RSTRING_PTR(str);
00564                     long cl = RSTRING_LEN(str);
00565 
00566                     if (cl % 2) {
00567                         if (cl >= 1)
00568                             hour = rb_str_new(&cs[0], 1);
00569                         if (cl >= 3)
00570                             min  = rb_str_new(&cs[1], 2);
00571                         if (cl >= 5)
00572                             min  = rb_str_new(&cs[3], 2);
00573                     }
00574                     else {
00575                         if (cl >= 2)
00576                             hour = rb_str_new(&cs[0], 2);
00577                         if (cl >= 4)
00578                             min  = rb_str_new(&cs[2], 2);
00579                         if (cl >= 6)
00580                             sec  = rb_str_new(&cs[4], 2);
00581                     }
00582                     goto num;
00583                 }
00584               num:
00585                 if (NIL_P(hour))
00586                     offset = INT2FIX(0);
00587                 else {
00588                     if (TYPE(hour) == T_STRING)
00589                         hour = str2num(hour);
00590                     offset = f_mul(hour, INT2FIX(3600));
00591                 }
00592                 if (!NIL_P(min)) {
00593                     if (TYPE(min) == T_STRING)
00594                         min = str2num(min);
00595                     offset = f_add(offset, f_mul(min, INT2FIX(60)));
00596                 }
00597                 if (!NIL_P(sec))
00598                     offset = f_add(offset, str2num(sec));
00599                 if (!NIL_P(sign) &&
00600                     RSTRING_LEN(sign) == 1 &&
00601                     *RSTRING_PTR(sign) == '-')
00602                     offset = f_negate(offset);
00603             }
00604         }
00605     }
00606     RB_GC_GUARD(str);
00607   ok:
00608     return offset;
00609 }
00610 
00611 static int
00612 day_num(VALUE s)
00613 {
00614     int i;
00615 
00616     for (i = 0; i < (int)sizeof_array(abbr_days); i++)
00617         if (strncasecmp(abbr_days[i], RSTRING_PTR(s), 3) == 0)
00618             break;
00619     return i;
00620 }
00621 
00622 static int
00623 mon_num(VALUE s)
00624 {
00625     int i;
00626 
00627     for (i = 0; i < (int)sizeof_array(abbr_months); i++)
00628         if (strncasecmp(abbr_months[i], RSTRING_PTR(s), 3) == 0)
00629             break;
00630     return i + 1;
00631 }
00632 
00633 static int
00634 parse_day_cb(VALUE m, VALUE hash)
00635 {
00636     VALUE s;
00637 
00638     s = rb_reg_nth_match(1, m);
00639     set_hash("wday", INT2FIX(day_num(s)));
00640     return 1;
00641 }
00642 
00643 static int
00644 parse_day(VALUE str, VALUE hash)
00645 {
00646     static const char pat_source[] =
00647 #ifndef TIGHT_PARSER
00648         "\\b(" ABBR_DAYS ")[^-/\\d\\s]*"
00649 #else
00650         "(" VALID_DAYS ")"
00651 #endif
00652         ;
00653     static VALUE pat = Qnil;
00654 
00655     REGCOMP_I(pat);
00656 #ifndef TIGHT_PARSER
00657     SUBS(str, pat, parse_day_cb);
00658 #else
00659     SUBW(str, pat, parse_day_cb);
00660 #endif
00661 }
00662 
00663 static int
00664 parse_time2_cb(VALUE m, VALUE hash)
00665 {
00666     VALUE h, min, s, f, p;
00667 
00668     h = rb_reg_nth_match(1, m);
00669     h = str2num(h);
00670 
00671     min = rb_reg_nth_match(2, m);
00672     if (!NIL_P(min))
00673         min = str2num(min);
00674 
00675     s = rb_reg_nth_match(3, m);
00676     if (!NIL_P(s))
00677         s = str2num(s);
00678 
00679     f = rb_reg_nth_match(4, m);
00680 
00681     if (!NIL_P(f))
00682         f = rb_rational_new2(str2num(f),
00683                              f_expt(INT2FIX(10), LONG2NUM(RSTRING_LEN(f))));
00684 
00685     p = rb_reg_nth_match(5, m);
00686 
00687     if (!NIL_P(p)) {
00688         int ih = NUM2INT(h);
00689         ih %= 12;
00690         if (*RSTRING_PTR(p) == 'P' || *RSTRING_PTR(p) == 'p')
00691             ih += 12;
00692         h = INT2FIX(ih);
00693     }
00694 
00695     set_hash("hour", h);
00696     if (!NIL_P(min))
00697         set_hash("min", min);
00698     if (!NIL_P(s))
00699         set_hash("sec", s);
00700     if (!NIL_P(f))
00701         set_hash("sec_fraction", f);
00702 
00703     return 1;
00704 }
00705 
00706 static int
00707 parse_time_cb(VALUE m, VALUE hash)
00708 {
00709     static const char pat_source[] =
00710             "\\A(\\d+)h?"
00711               "(?:\\s*:?\\s*(\\d+)m?"
00712                 "(?:"
00713                   "\\s*:?\\s*(\\d+)(?:[,.](\\d+))?s?"
00714                 ")?"
00715               ")?"
00716             "(?:\\s*([ap])(?:m\\b|\\.m\\.))?";
00717     static VALUE pat = Qnil;
00718     VALUE s1, s2;
00719 
00720     s1 = rb_reg_nth_match(1, m);
00721     s2 = rb_reg_nth_match(2, m);
00722 
00723     if (!NIL_P(s2))
00724         set_hash("zone", s2);
00725 
00726     REGCOMP_I(pat);
00727 
00728     {
00729         VALUE m = f_match(pat, s1);
00730 
00731         if (NIL_P(m))
00732             return 0;
00733         parse_time2_cb(m, hash);
00734     }
00735 
00736     return 1;
00737 }
00738 
00739 static int
00740 parse_time(VALUE str, VALUE hash)
00741 {
00742     static const char pat_source[] =
00743                 "("
00744                    "(?:"
00745                      "\\d+\\s*:\\s*\\d+"
00746                      "(?:"
00747 #ifndef TIGHT_PARSER
00748                        "\\s*:\\s*\\d+(?:[,.]\\d*)?"
00749 #else
00750                        "\\s*:\\s*\\d+(?:[,.]\\d+)?"
00751 #endif
00752                      ")?"
00753                    "|"
00754                      "\\d+\\s*h(?:\\s*\\d+m?(?:\\s*\\d+s?)?)?"
00755                    ")"
00756                    "(?:"
00757                      "\\s*"
00758                      "[ap](?:m\\b|\\.m\\.)"
00759                    ")?"
00760                  "|"
00761                    "\\d+\\s*[ap](?:m\\b|\\.m\\.)"
00762                  ")"
00763                  "(?:"
00764                    "\\s*"
00765                    "("
00766                      "(?:gmt|utc?)?[-+]\\d+(?:[,.:]\\d+(?::\\d+)?)?"
00767                    "|"
00768                      "[[:alpha:].\\s]+(?:standard|daylight)\\stime\\b"
00769                    "|"
00770                      "[[:alpha:]]+(?:\\sdst)?\\b"
00771                    ")"
00772                 ")?";
00773     static VALUE pat = Qnil;
00774 
00775     REGCOMP_I(pat);
00776 #ifndef TIGHT_PARSER
00777     SUBS(str, pat, parse_time_cb);
00778 #else
00779     SUBT(str, pat, parse_time_cb);
00780 #endif
00781 }
00782 
00783 #ifdef TIGHT_PARSER
00784 static int
00785 parse_era1_cb(VALUE m, VALUE hash)
00786 {
00787     return 1;
00788 }
00789 
00790 static int
00791 parse_era1(VALUE str, VALUE hash)
00792 {
00793     static const char pat_source[] =
00794         "(a(?:d|\\.d\\.))";
00795     static VALUE pat = Qnil;
00796 
00797     REGCOMP_I(pat);
00798     SUBA(str, pat, parse_era1_cb);
00799 }
00800 
00801 static int
00802 parse_era2_cb(VALUE m, VALUE hash)
00803 {
00804     VALUE b;
00805 
00806     b = rb_reg_nth_match(1, m);
00807     if (*RSTRING_PTR(b) == 'B' ||
00808         *RSTRING_PTR(b) == 'b')
00809         set_hash("_bc", Qtrue);
00810     return 1;
00811 }
00812 
00813 static int
00814 parse_era2(VALUE str, VALUE hash)
00815 {
00816     static const char pat_source[] =
00817         "(c(?:e|\\.e\\.)|b(?:ce|\\.c\\.e\\.)|b(?:c|\\.c\\.))";
00818     static VALUE pat = Qnil;
00819 
00820     REGCOMP_I(pat);
00821     SUBB(str, pat, parse_era2_cb);
00822 }
00823 
00824 static int
00825 parse_era(VALUE str, VALUE hash)
00826 {
00827     if (parse_era1(str, hash)) /* pre */
00828         goto ok;
00829     if (parse_era2(str, hash)) /* post */
00830         goto ok;
00831     return 0;
00832   ok:
00833     return 1;
00834 }
00835 #endif
00836 
00837 #ifdef TIGHT_PARSER
00838 static int
00839 check_year_width(VALUE y)
00840 {
00841     char *s;
00842     size_t l;
00843 
00844     s = RSTRING_PTR(y);
00845     l = strcspn(s, DECDIGIT);
00846     s += l;
00847     l = strspn(s, DECDIGIT);
00848     if (l != 2)
00849         return 0;
00850     return 1;
00851 }
00852 
00853 static int
00854 check_apost(VALUE a, VALUE b, VALUE c)
00855 {
00856     int f = 0;
00857 
00858     if (!NIL_P(a) && *RSTRING_PTR(a) == '\'') {
00859         if (!check_year_width(a))
00860             return 0;
00861         f++;
00862     }
00863     if (!NIL_P(b) && *RSTRING_PTR(b) == '\'') {
00864         if (!check_year_width(b))
00865             return 0;
00866         if (!NIL_P(c))
00867             return 0;
00868         f++;
00869     }
00870     if (!NIL_P(c) && *RSTRING_PTR(c) == '\'') {
00871         if (!check_year_width(c))
00872             return 0;
00873         f++;
00874     }
00875     if (f > 1)
00876         return 0;
00877     return 1;
00878 }
00879 #endif
00880 
00881 static int
00882 parse_eu_cb(VALUE m, VALUE hash)
00883 {
00884 #ifndef TIGHT_PARSER
00885     VALUE y, mon, d, b;
00886 
00887     d = rb_reg_nth_match(1, m);
00888     mon = rb_reg_nth_match(2, m);
00889     b = rb_reg_nth_match(3, m);
00890     y = rb_reg_nth_match(4, m);
00891 
00892     mon = INT2FIX(mon_num(mon));
00893 
00894     s3e(hash, y, mon, d, !NIL_P(b) &&
00895         (*RSTRING_PTR(b) == 'B' ||
00896          *RSTRING_PTR(b) == 'b'));
00897 #else
00898     VALUE y, mon, d;
00899 
00900     d = rb_reg_nth_match(1, m);
00901     mon = rb_reg_nth_match(2, m);
00902     y = rb_reg_nth_match(3, m);
00903 
00904     if (!check_apost(d, mon, y))
00905         return 0;
00906 
00907     mon = INT2FIX(mon_num(mon));
00908 
00909     s3e(hash, y, mon, d, 0);
00910 #endif
00911     return 1;
00912 }
00913 
00914 static int
00915 parse_eu(VALUE str, VALUE hash)
00916 {
00917     static const char pat_source[] =
00918 #ifdef TIGHT_PARSER
00919                 BOS
00920                 FPW_COM FPT_COM
00921 #endif
00922 #ifndef TIGHT_PARSER
00923                 "('?\\d+)[^-\\d\\s]*"
00924 #else
00925                 "(\\d+)(?:(?:st|nd|rd|th)\\b)?"
00926 #endif
00927                  "\\s*"
00928 #ifndef TIGHT_PARSER
00929                  "(" ABBR_MONTHS ")[^-\\d\\s']*"
00930 #else
00931                  "(" VALID_MONTHS ")"
00932 #endif
00933                  "(?:"
00934                    "\\s*"
00935 #ifndef TIGHT_PARSER
00936                    "(c(?:e|\\.e\\.)|b(?:ce|\\.c\\.e\\.)|a(?:d|\\.d\\.)|b(?:c|\\.c\\.))?"
00937                    "\\s*"
00938                    "('?-?\\d+(?:(?:st|nd|rd|th)\\b)?)"
00939 #else
00940                    "(?:" FPA ")?"
00941                    "\\s*"
00942                    "([-']?\\d+)"
00943                    "\\s*"
00944                    "(?:" FPA "|" FPB ")?"
00945 #endif
00946                 ")?"
00947 #ifdef TIGHT_PARSER
00948                 COM_FPT COM_FPW
00949                 EOS
00950 #endif
00951                 ;
00952     static VALUE pat = Qnil;
00953 
00954     REGCOMP_I(pat);
00955     SUBS(str, pat, parse_eu_cb);
00956 }
00957 
00958 static int
00959 parse_us_cb(VALUE m, VALUE hash)
00960 {
00961 #ifndef TIGHT_PARSER
00962     VALUE y, mon, d, b;
00963 
00964     mon = rb_reg_nth_match(1, m);
00965     d = rb_reg_nth_match(2, m);
00966 
00967     b = rb_reg_nth_match(3, m);
00968     y = rb_reg_nth_match(4, m);
00969 
00970     mon = INT2FIX(mon_num(mon));
00971 
00972     s3e(hash, y, mon, d, !NIL_P(b) &&
00973         (*RSTRING_PTR(b) == 'B' ||
00974          *RSTRING_PTR(b) == 'b'));
00975 #else
00976     VALUE y, mon, d;
00977 
00978     mon = rb_reg_nth_match(1, m);
00979     d = rb_reg_nth_match(2, m);
00980     y = rb_reg_nth_match(3, m);
00981 
00982     if (!check_apost(mon, d, y))
00983         return 0;
00984 
00985     mon = INT2FIX(mon_num(mon));
00986 
00987     s3e(hash, y, mon, d, 0);
00988 #endif
00989     return 1;
00990 }
00991 
00992 static int
00993 parse_us(VALUE str, VALUE hash)
00994 {
00995     static const char pat_source[] =
00996 #ifdef TIGHT_PARSER
00997                 BOS
00998                 FPW_COM FPT_COM
00999 #endif
01000 #ifndef TIGHT_PARSER
01001                 "\\b(" ABBR_MONTHS ")[^-\\d\\s']*"
01002 #else
01003                 "\\b(" VALID_MONTHS ")"
01004 #endif
01005                  "\\s*"
01006 #ifndef TIGHT_PARSER
01007                  "('?\\d+)[^-\\d\\s']*"
01008 #else
01009                  "('?\\d+)(?:(?:st|nd|rd|th)\\b)?"
01010                 COM_FPT
01011 #endif
01012                  "(?:"
01013                    "\\s*,?"
01014                    "\\s*"
01015 #ifndef TIGHT_PARSER
01016                    "(c(?:e|\\.e\\.)|b(?:ce|\\.c\\.e\\.)|a(?:d|\\.d\\.)|b(?:c|\\.c\\.))?"
01017                    "\\s*"
01018                    "('?-?\\d+)"
01019 #else
01020                    "(?:" FPA ")?"
01021                    "\\s*"
01022                    "([-']?\\d+)"
01023                    "\\s*"
01024                    "(?:" FPA "|" FPB ")?"
01025 #endif
01026                 ")?"
01027 #ifdef TIGHT_PARSER
01028                 COM_FPT COM_FPW
01029                 EOS
01030 #endif
01031                 ;
01032     static VALUE pat = Qnil;
01033 
01034     REGCOMP_I(pat);
01035     SUBS(str, pat, parse_us_cb);
01036 }
01037 
01038 static int
01039 parse_iso_cb(VALUE m, VALUE hash)
01040 {
01041     VALUE y, mon, d;
01042 
01043     y = rb_reg_nth_match(1, m);
01044     mon = rb_reg_nth_match(2, m);
01045     d = rb_reg_nth_match(3, m);
01046 
01047 #ifdef TIGHT_PARSER
01048     if (!check_apost(y, mon, d))
01049         return 0;
01050 #endif
01051 
01052     s3e(hash, y, mon, d, 0);
01053     return 1;
01054 }
01055 
01056 static int
01057 parse_iso(VALUE str, VALUE hash)
01058 {
01059     static const char pat_source[] =
01060 #ifndef TIGHT_PARSER
01061         "('?[-+]?\\d+)-(\\d+)-('?-?\\d+)"
01062 #else
01063         BOS
01064         FPW_COM FPT_COM
01065         "([-+']?\\d+)-(\\d+)-([-']?\\d+)"
01066         TEE_FPT COM_FPW
01067         EOS
01068 #endif
01069         ;
01070     static VALUE pat = Qnil;
01071 
01072     REGCOMP_0(pat);
01073     SUBS(str, pat, parse_iso_cb);
01074 }
01075 
01076 static int
01077 parse_iso21_cb(VALUE m, VALUE hash)
01078 {
01079     VALUE y, w, d;
01080 
01081     y = rb_reg_nth_match(1, m);
01082     w = rb_reg_nth_match(2, m);
01083     d = rb_reg_nth_match(3, m);
01084 
01085     if (!NIL_P(y))
01086         set_hash("cwyear", str2num(y));
01087     set_hash("cweek", str2num(w));
01088     if (!NIL_P(d))
01089         set_hash("cwday", str2num(d));
01090 
01091     return 1;
01092 }
01093 
01094 static int
01095 parse_iso21(VALUE str, VALUE hash)
01096 {
01097     static const char pat_source[] =
01098 #ifndef TIGHT_PARSER
01099         "\\b(\\d{2}|\\d{4})?-?w(\\d{2})(?:-?(\\d))?\\b"
01100 #else
01101         BOS
01102         FPW_COM FPT_COM
01103         "(\\d{2}|\\d{4})?-?w(\\d{2})(?:-?(\\d))?"
01104         TEE_FPT COM_FPW
01105         EOS
01106 #endif
01107         ;
01108     static VALUE pat = Qnil;
01109 
01110     REGCOMP_I(pat);
01111     SUBS(str, pat, parse_iso21_cb);
01112 }
01113 
01114 static int
01115 parse_iso22_cb(VALUE m, VALUE hash)
01116 {
01117     VALUE d;
01118 
01119     d = rb_reg_nth_match(1, m);
01120     set_hash("cwday", str2num(d));
01121     return 1;
01122 }
01123 
01124 static int
01125 parse_iso22(VALUE str, VALUE hash)
01126 {
01127     static const char pat_source[] =
01128 #ifndef TIGHT_PARSER
01129         "-w-(\\d)\\b"
01130 #else
01131         BOS
01132         FPW_COM FPT_COM
01133         "-w-(\\d)"
01134         TEE_FPT COM_FPW
01135         EOS
01136 #endif
01137         ;
01138     static VALUE pat = Qnil;
01139 
01140     REGCOMP_I(pat);
01141     SUBS(str, pat, parse_iso22_cb);
01142 }
01143 
01144 static int
01145 parse_iso23_cb(VALUE m, VALUE hash)
01146 {
01147     VALUE mon, d;
01148 
01149     mon = rb_reg_nth_match(1, m);
01150     d = rb_reg_nth_match(2, m);
01151 
01152     if (!NIL_P(mon))
01153         set_hash("mon", str2num(mon));
01154     set_hash("mday", str2num(d));
01155 
01156     return 1;
01157 }
01158 
01159 static int
01160 parse_iso23(VALUE str, VALUE hash)
01161 {
01162     static const char pat_source[] =
01163 #ifndef TIGHT_PARSER
01164         "--(\\d{2})?-(\\d{2})\\b"
01165 #else
01166         BOS
01167         FPW_COM FPT_COM
01168         "--(\\d{2})?-(\\d{2})"
01169         TEE_FPT COM_FPW
01170         EOS
01171 #endif
01172         ;
01173     static VALUE pat = Qnil;
01174 
01175     REGCOMP_0(pat);
01176     SUBS(str, pat, parse_iso23_cb);
01177 }
01178 
01179 static int
01180 parse_iso24_cb(VALUE m, VALUE hash)
01181 {
01182     VALUE mon, d;
01183 
01184     mon = rb_reg_nth_match(1, m);
01185     d = rb_reg_nth_match(2, m);
01186 
01187     set_hash("mon", str2num(mon));
01188     if (!NIL_P(d))
01189         set_hash("mday", str2num(d));
01190 
01191     return 1;
01192 }
01193 
01194 static int
01195 parse_iso24(VALUE str, VALUE hash)
01196 {
01197     static const char pat_source[] =
01198 #ifndef TIGHT_PARSER
01199         "--(\\d{2})(\\d{2})?\\b"
01200 #else
01201         BOS
01202         FPW_COM FPT_COM
01203         "--(\\d{2})(\\d{2})?"
01204         TEE_FPT COM_FPW
01205         EOS
01206 #endif
01207         ;
01208     static VALUE pat = Qnil;
01209 
01210     REGCOMP_0(pat);
01211     SUBS(str, pat, parse_iso24_cb);
01212 }
01213 
01214 static int
01215 parse_iso25_cb(VALUE m, VALUE hash)
01216 {
01217     VALUE y, d;
01218 
01219     y = rb_reg_nth_match(1, m);
01220     d = rb_reg_nth_match(2, m);
01221 
01222     set_hash("year", str2num(y));
01223     set_hash("yday", str2num(d));
01224 
01225     return 1;
01226 }
01227 
01228 static int
01229 parse_iso25(VALUE str, VALUE hash)
01230 {
01231     static const char pat0_source[] =
01232 #ifndef TIGHT_PARSER
01233         "[,.](\\d{2}|\\d{4})-\\d{3}\\b"
01234 #else
01235         BOS
01236         FPW_COM FPT_COM
01237         "[,.](\\d{2}|\\d{4})-\\d{3}"
01238         TEE_FPT COM_FPW
01239         EOS
01240 #endif
01241         ;
01242     static VALUE pat0 = Qnil;
01243     static const char pat_source[] =
01244 #ifndef TIGHT_PARSER
01245         "\\b(\\d{2}|\\d{4})-(\\d{3})\\b"
01246 #else
01247         BOS
01248         FPW_COM FPT_COM
01249         "(\\d{2}|\\d{4})-(\\d{3})"
01250         TEE_FPT COM_FPW
01251         EOS
01252 #endif
01253         ;
01254     static VALUE pat = Qnil;
01255 
01256     REGCOMP_0(pat0);
01257     REGCOMP_0(pat);
01258 
01259     if (!NIL_P(f_match(pat0, str)))
01260         return 0;
01261     SUBS(str, pat, parse_iso25_cb);
01262 }
01263 
01264 static int
01265 parse_iso26_cb(VALUE m, VALUE hash)
01266 {
01267     VALUE d;
01268 
01269     d = rb_reg_nth_match(1, m);
01270     set_hash("yday", str2num(d));
01271 
01272     return 1;
01273 }
01274 static int
01275 parse_iso26(VALUE str, VALUE hash)
01276 {
01277     static const char pat0_source[] =
01278 #ifndef TIGHT_PARSER
01279         "\\d-\\d{3}\\b"
01280 #else
01281         BOS
01282         FPW_COM FPT_COM
01283         "\\d-\\d{3}"
01284         TEE_FPT COM_FPW
01285         EOS
01286 #endif
01287         ;
01288     static VALUE pat0 = Qnil;
01289     static const char pat_source[] =
01290 #ifndef TIGHT_PARSER
01291         "\\b-(\\d{3})\\b"
01292 #else
01293         BOS
01294         FPW_COM FPT_COM
01295         "-(\\d{3})"
01296         TEE_FPT COM_FPW
01297         EOS
01298 #endif
01299         ;
01300     static VALUE pat = Qnil;
01301 
01302     REGCOMP_0(pat0);
01303     REGCOMP_0(pat);
01304 
01305     if (!NIL_P(f_match(pat0, str)))
01306         return 0;
01307     SUBS(str, pat, parse_iso26_cb);
01308 }
01309 
01310 static int
01311 parse_iso2(VALUE str, VALUE hash)
01312 {
01313     if (parse_iso21(str, hash))
01314         goto ok;
01315     if (parse_iso22(str, hash))
01316         goto ok;
01317     if (parse_iso23(str, hash))
01318         goto ok;
01319     if (parse_iso24(str, hash))
01320         goto ok;
01321     if (parse_iso25(str, hash))
01322         goto ok;
01323     if (parse_iso26(str, hash))
01324         goto ok;
01325     return 0;
01326 
01327   ok:
01328     return 1;
01329 }
01330 
01331 static int
01332 gengo(int c)
01333 {
01334     int e;
01335 
01336     switch (c) {
01337       case 'M': case 'm': e = 1867; break;
01338       case 'T': case 't': e = 1911; break;
01339       case 'S': case 's': e = 1925; break;
01340       case 'H': case 'h': e = 1988; break;
01341       default:  e = 0; break;
01342     }
01343     return e;
01344 }
01345 
01346 static int
01347 parse_jis_cb(VALUE m, VALUE hash)
01348 {
01349     VALUE e, y, mon, d;
01350     int ep;
01351 
01352     e = rb_reg_nth_match(1, m);
01353     y = rb_reg_nth_match(2, m);
01354     mon = rb_reg_nth_match(3, m);
01355     d = rb_reg_nth_match(4, m);
01356 
01357     ep = gengo(*RSTRING_PTR(e));
01358 
01359     set_hash("year", f_add(str2num(y), INT2FIX(ep)));
01360     set_hash("mon", str2num(mon));
01361     set_hash("mday", str2num(d));
01362 
01363     return 1;
01364 }
01365 
01366 static int
01367 parse_jis(VALUE str, VALUE hash)
01368 {
01369     static const char pat_source[] =
01370 #ifndef TIGHT_PARSER
01371         "\\b([mtsh])(\\d+)\\.(\\d+)\\.(\\d+)"
01372 #else
01373         BOS
01374         FPW_COM FPT_COM
01375         "([mtsh])(\\d+)\\.(\\d+)\\.(\\d+)"
01376         TEE_FPT COM_FPW
01377         EOS
01378 #endif
01379         ;
01380     static VALUE pat = Qnil;
01381 
01382     REGCOMP_I(pat);
01383     SUBS(str, pat, parse_jis_cb);
01384 }
01385 
01386 static int
01387 parse_vms11_cb(VALUE m, VALUE hash)
01388 {
01389     VALUE y, mon, d;
01390 
01391     d = rb_reg_nth_match(1, m);
01392     mon = rb_reg_nth_match(2, m);
01393     y = rb_reg_nth_match(3, m);
01394 
01395 #ifdef TIGHT_PARSER
01396     if (!check_apost(d, mon, y))
01397         return 0;
01398 #endif
01399 
01400     mon = INT2FIX(mon_num(mon));
01401 
01402     s3e(hash, y, mon, d, 0);
01403     return 1;
01404 }
01405 
01406 static int
01407 parse_vms11(VALUE str, VALUE hash)
01408 {
01409     static const char pat_source[] =
01410 #ifndef TIGHT_PARSER
01411         "('?-?\\d+)-(" ABBR_MONTHS ")[^-/.]*"
01412         "-('?-?\\d+)"
01413 #else
01414         BOS
01415         FPW_COM FPT_COM
01416         "([-']?\\d+)-(" DOTLESS_VALID_MONTHS ")"
01417         "-([-']?\\d+)"
01418         COM_FPT COM_FPW
01419         EOS
01420 #endif
01421         ;
01422     static VALUE pat = Qnil;
01423 
01424     REGCOMP_I(pat);
01425     SUBS(str, pat, parse_vms11_cb);
01426 }
01427 
01428 static int
01429 parse_vms12_cb(VALUE m, VALUE hash)
01430 {
01431     VALUE y, mon, d;
01432 
01433     mon = rb_reg_nth_match(1, m);
01434     d = rb_reg_nth_match(2, m);
01435     y = rb_reg_nth_match(3, m);
01436 
01437 #ifdef TIGHT_PARSER
01438     if (!check_apost(mon, d, y))
01439         return 0;
01440 #endif
01441 
01442     mon = INT2FIX(mon_num(mon));
01443 
01444     s3e(hash, y, mon, d, 0);
01445     return 1;
01446 }
01447 
01448 static int
01449 parse_vms12(VALUE str, VALUE hash)
01450 {
01451     static const char pat_source[] =
01452 #ifndef TIGHT_PARSER
01453         "\\b(" ABBR_MONTHS ")[^-/.]*"
01454         "-('?-?\\d+)(?:-('?-?\\d+))?"
01455 #else
01456         BOS
01457         FPW_COM FPT_COM
01458         "(" DOTLESS_VALID_MONTHS ")"
01459         "-([-']?\\d+)(?:-([-']?\\d+))?"
01460         COM_FPT COM_FPW
01461         EOS
01462 #endif
01463         ;
01464     static VALUE pat = Qnil;
01465 
01466     REGCOMP_I(pat);
01467     SUBS(str, pat, parse_vms12_cb);
01468 }
01469 
01470 static int
01471 parse_vms(VALUE str, VALUE hash)
01472 {
01473     if (parse_vms11(str, hash))
01474         goto ok;
01475     if (parse_vms12(str, hash))
01476         goto ok;
01477     return 0;
01478 
01479   ok:
01480     return 1;
01481 }
01482 
01483 static int
01484 parse_sla_cb(VALUE m, VALUE hash)
01485 {
01486     VALUE y, mon, d;
01487 
01488     y = rb_reg_nth_match(1, m);
01489     mon = rb_reg_nth_match(2, m);
01490     d = rb_reg_nth_match(3, m);
01491 
01492 #ifdef TIGHT_PARSER
01493     if (!check_apost(y, mon, d))
01494         return 0;
01495 #endif
01496 
01497     s3e(hash, y, mon, d, 0);
01498     return 1;
01499 }
01500 
01501 static int
01502 parse_sla(VALUE str, VALUE hash)
01503 {
01504     static const char pat_source[] =
01505 #ifndef TIGHT_PARSER
01506         "('?-?\\d+)/\\s*('?\\d+)(?:\\D\\s*('?-?\\d+))?"
01507 #else
01508         BOS
01509         FPW_COM FPT_COM
01510         "([-']?\\d+)/\\s*('?\\d+)(?:(?:[-/]|\\s+)\\s*([-']?\\d+))?"
01511         COM_FPT COM_FPW
01512         EOS
01513 #endif
01514         ;
01515     static VALUE pat = Qnil;
01516 
01517     REGCOMP_I(pat);
01518     SUBS(str, pat, parse_sla_cb);
01519 }
01520 
01521 #ifdef TIGHT_PARSER
01522 static int
01523 parse_sla2_cb(VALUE m, VALUE hash)
01524 {
01525     VALUE y, mon, d;
01526 
01527     d = rb_reg_nth_match(1, m);
01528     mon = rb_reg_nth_match(2, m);
01529     y = rb_reg_nth_match(3, m);
01530 
01531     if (!check_apost(d, mon, y))
01532         return 0;
01533 
01534     mon = INT2FIX(mon_num(mon));
01535 
01536     s3e(hash, y, mon, d, 0);
01537     return 1;
01538 }
01539 
01540 static int
01541 parse_sla2(VALUE str, VALUE hash)
01542 {
01543     static const char pat_source[] =
01544         BOS
01545         FPW_COM FPT_COM
01546         "([-']?\\d+)/\\s*(" DOTLESS_VALID_MONTHS ")(?:(?:[-/]|\\s+)\\s*([-']?\\d+))?"
01547         COM_FPT COM_FPW
01548         EOS
01549         ;
01550     static VALUE pat = Qnil;
01551 
01552     REGCOMP_I(pat);
01553     SUBS(str, pat, parse_sla2_cb);
01554 }
01555 
01556 static int
01557 parse_sla3_cb(VALUE m, VALUE hash)
01558 {
01559     VALUE y, mon, d;
01560 
01561     mon = rb_reg_nth_match(1, m);
01562     d = rb_reg_nth_match(2, m);
01563     y = rb_reg_nth_match(3, m);
01564 
01565     if (!check_apost(mon, d, y))
01566         return 0;
01567 
01568     mon = INT2FIX(mon_num(mon));
01569 
01570     s3e(hash, y, mon, d, 0);
01571     return 1;
01572 }
01573 
01574 static int
01575 parse_sla3(VALUE str, VALUE hash)
01576 {
01577     static const char pat_source[] =
01578         BOS
01579         FPW_COM FPT_COM
01580         "(" DOTLESS_VALID_MONTHS ")/\\s*([-']?\\d+)(?:(?:[-/]|\\s+)\\s*([-']?\\d+))?"
01581         COM_FPT COM_FPW
01582         EOS
01583         ;
01584     static VALUE pat = Qnil;
01585 
01586     REGCOMP_I(pat);
01587     SUBS(str, pat, parse_sla3_cb);
01588 }
01589 #endif
01590 
01591 static int
01592 parse_dot_cb(VALUE m, VALUE hash)
01593 {
01594     VALUE y, mon, d;
01595 
01596     y = rb_reg_nth_match(1, m);
01597     mon = rb_reg_nth_match(2, m);
01598     d = rb_reg_nth_match(3, m);
01599 
01600 #ifdef TIGHT_PARSER
01601     if (!check_apost(y, mon, d))
01602         return 0;
01603 #endif
01604 
01605     s3e(hash, y, mon, d, 0);
01606     return 1;
01607 }
01608 
01609 static int
01610 parse_dot(VALUE str, VALUE hash)
01611 {
01612     static const char pat_source[] =
01613 #ifndef TIGHT_PARSER
01614         "('?-?\\d+)\\.\\s*('?\\d+)\\.\\s*('?-?\\d+)"
01615 #else
01616         BOS
01617         FPW_COM FPT_COM
01618         "([-']?\\d+)\\.\\s*(\\d+)\\.\\s*([-']?\\d+)"
01619         COM_FPT COM_FPW
01620         EOS
01621 #endif
01622         ;
01623     static VALUE pat = Qnil;
01624 
01625     REGCOMP_I(pat);
01626     SUBS(str, pat, parse_dot_cb);
01627 }
01628 
01629 #ifdef TIGHT_PARSER
01630 static int
01631 parse_dot2_cb(VALUE m, VALUE hash)
01632 {
01633     VALUE y, mon, d;
01634 
01635     d = rb_reg_nth_match(1, m);
01636     mon = rb_reg_nth_match(2, m);
01637     y = rb_reg_nth_match(3, m);
01638 
01639     if (!check_apost(d, mon, y))
01640         return 0;
01641 
01642     mon = INT2FIX(mon_num(mon));
01643 
01644     s3e(hash, y, mon, d, 0);
01645     return 1;
01646 }
01647 
01648 static int
01649 parse_dot2(VALUE str, VALUE hash)
01650 {
01651     static const char pat_source[] =
01652         BOS
01653         FPW_COM FPT_COM
01654         "([-']?\\d+)\\.\\s*(" DOTLESS_VALID_MONTHS ")(?:(?:[./])\\s*([-']?\\d+))?"
01655         COM_FPT COM_FPW
01656         EOS
01657         ;
01658     static VALUE pat = Qnil;
01659 
01660     REGCOMP_I(pat);
01661     SUBS(str, pat, parse_dot2_cb);
01662 }
01663 
01664 static int
01665 parse_dot3_cb(VALUE m, VALUE hash)
01666 {
01667     VALUE y, mon, d;
01668 
01669     mon = rb_reg_nth_match(1, m);
01670     d = rb_reg_nth_match(2, m);
01671     y = rb_reg_nth_match(3, m);
01672 
01673     if (!check_apost(mon, d, y))
01674         return 0;
01675 
01676     mon = INT2FIX(mon_num(mon));
01677 
01678     s3e(hash, y, mon, d, 0);
01679     return 1;
01680 }
01681 
01682 static int
01683 parse_dot3(VALUE str, VALUE hash)
01684 {
01685     static const char pat_source[] =
01686         BOS
01687         FPW_COM FPT_COM
01688         "(" DOTLESS_VALID_MONTHS ")\\.\\s*([-']?\\d+)(?:(?:[./])\\s*([-']?\\d+))?"
01689         COM_FPT COM_FPW
01690         EOS
01691         ;
01692     static VALUE pat = Qnil;
01693 
01694     REGCOMP_I(pat);
01695     SUBS(str, pat, parse_dot3_cb);
01696 }
01697 #endif
01698 
01699 static int
01700 parse_year_cb(VALUE m, VALUE hash)
01701 {
01702     VALUE y;
01703 
01704     y = rb_reg_nth_match(1, m);
01705     set_hash("year", str2num(y));
01706     return 1;
01707 }
01708 
01709 static int
01710 parse_year(VALUE str, VALUE hash)
01711 {
01712     static const char pat_source[] =
01713 #ifndef TIGHT_PARSER
01714         "'(\\d+)\\b"
01715 #else
01716         BOS
01717         FPW_COM FPT_COM
01718         "'(\\d+)"
01719         COM_FPT COM_FPW
01720         EOS
01721 #endif
01722         ;
01723     static VALUE pat = Qnil;
01724 
01725     REGCOMP_0(pat);
01726     SUBS(str, pat, parse_year_cb);
01727 }
01728 
01729 static int
01730 parse_mon_cb(VALUE m, VALUE hash)
01731 {
01732     VALUE mon;
01733 
01734     mon = rb_reg_nth_match(1, m);
01735     set_hash("mon", INT2FIX(mon_num(mon)));
01736     return 1;
01737 }
01738 
01739 static int
01740 parse_mon(VALUE str, VALUE hash)
01741 {
01742     static const char pat_source[] =
01743 #ifndef TIGHT_PARSER
01744         "\\b(" ABBR_MONTHS ")\\S*"
01745 #else
01746         BOS
01747         FPW_COM FPT_COM
01748         "(" VALID_MONTHS ")"
01749         COM_FPT COM_FPW
01750         EOS
01751 #endif
01752         ;
01753     static VALUE pat = Qnil;
01754 
01755     REGCOMP_I(pat);
01756     SUBS(str, pat, parse_mon_cb);
01757 }
01758 
01759 static int
01760 parse_mday_cb(VALUE m, VALUE hash)
01761 {
01762     VALUE d;
01763 
01764     d = rb_reg_nth_match(1, m);
01765     set_hash("mday", str2num(d));
01766     return 1;
01767 }
01768 
01769 static int
01770 parse_mday(VALUE str, VALUE hash)
01771 {
01772     static const char pat_source[] =
01773 #ifndef TIGHT_PARSER
01774         "(\\d+)(st|nd|rd|th)\\b"
01775 #else
01776         BOS
01777         FPW_COM FPT_COM
01778         "(\\d+)(st|nd|rd|th)"
01779         COM_FPT COM_FPW
01780         EOS
01781 #endif
01782         ;
01783     static VALUE pat = Qnil;
01784 
01785     REGCOMP_I(pat);
01786     SUBS(str, pat, parse_mday_cb);
01787 }
01788 
01789 static int
01790 n2i(const char *s, long f, long w)
01791 {
01792     long e, i;
01793     int v;
01794 
01795     e = f + w;
01796     v = 0;
01797     for (i = f; i < e; i++) {
01798         v *= 10;
01799         v += s[i] - '0';
01800     }
01801     return v;
01802 }
01803 
01804 static int
01805 parse_ddd_cb(VALUE m, VALUE hash)
01806 {
01807     VALUE s1, s2, s3, s4, s5;
01808     const char *cs2, *cs3, *cs5;
01809     long l2, l3, l4, l5;
01810 
01811     s1 = rb_reg_nth_match(1, m);
01812     s2 = rb_reg_nth_match(2, m);
01813     s3 = rb_reg_nth_match(3, m);
01814     s4 = rb_reg_nth_match(4, m);
01815     s5 = rb_reg_nth_match(5, m);
01816 
01817     cs2 = RSTRING_PTR(s2);
01818     l2 = RSTRING_LEN(s2);
01819 
01820     switch (l2) {
01821       case 2:
01822         if (NIL_P(s3) && !NIL_P(s4))
01823             set_hash("sec",  INT2FIX(n2i(cs2, l2-2, 2)));
01824         else
01825             set_hash("mday", INT2FIX(n2i(cs2,    0, 2)));
01826         break;
01827       case 4:
01828         if (NIL_P(s3) && !NIL_P(s4)) {
01829             set_hash("sec",  INT2FIX(n2i(cs2, l2-2, 2)));
01830             set_hash("min",  INT2FIX(n2i(cs2, l2-4, 2)));
01831         }
01832         else {
01833             set_hash("mon",  INT2FIX(n2i(cs2,    0, 2)));
01834             set_hash("mday", INT2FIX(n2i(cs2,    2, 2)));
01835         }
01836         break;
01837       case 6:
01838         if (NIL_P(s3) && !NIL_P(s4)) {
01839             set_hash("sec",  INT2FIX(n2i(cs2, l2-2, 2)));
01840             set_hash("min",  INT2FIX(n2i(cs2, l2-4, 2)));
01841             set_hash("hour", INT2FIX(n2i(cs2, l2-6, 2)));
01842         }
01843         else {
01844             int                  y = n2i(cs2,    0, 2);
01845             if (!NIL_P(s1) && *RSTRING_PTR(s1) == '-')
01846                 y = -y;
01847             set_hash("year", INT2FIX(y));
01848             set_hash("mon",  INT2FIX(n2i(cs2,    2, 2)));
01849             set_hash("mday", INT2FIX(n2i(cs2,    4, 2)));
01850         }
01851         break;
01852       case 8:
01853       case 10:
01854       case 12:
01855       case 14:
01856         if (NIL_P(s3) && !NIL_P(s4)) {
01857             set_hash("sec",  INT2FIX(n2i(cs2, l2-2, 2)));
01858             set_hash("min",  INT2FIX(n2i(cs2, l2-4, 2)));
01859             set_hash("hour", INT2FIX(n2i(cs2, l2-6, 2)));
01860             set_hash("mday", INT2FIX(n2i(cs2, l2-8, 2)));
01861             if (l2 >= 10)
01862                 set_hash("mon", INT2FIX(n2i(cs2, l2-10, 2)));
01863             if (l2 == 12) {
01864                 int y = n2i(cs2, l2-12, 2);
01865                 if (!NIL_P(s1) && *RSTRING_PTR(s1) == '-')
01866                     y = -y;
01867                 set_hash("year", INT2FIX(y));
01868             }
01869             if (l2 == 14) {
01870                 int y = n2i(cs2, l2-14, 4);
01871                 if (!NIL_P(s1) && *RSTRING_PTR(s1) == '-')
01872                     y = -y;
01873                 set_hash("year", INT2FIX(y));
01874                 set_hash("_comp", Qfalse);
01875             }
01876         }
01877         else {
01878             int                  y = n2i(cs2,    0, 4);
01879             if (!NIL_P(s1) && *RSTRING_PTR(s1) == '-')
01880                 y = -y;
01881             set_hash("year", INT2FIX(y));
01882             set_hash("mon",  INT2FIX(n2i(cs2,    4, 2)));
01883             set_hash("mday", INT2FIX(n2i(cs2,    6, 2)));
01884             if (l2 >= 10)
01885                 set_hash("hour", INT2FIX(n2i(cs2,    8, 2)));
01886             if (l2 >= 12)
01887                 set_hash("min",  INT2FIX(n2i(cs2,   10, 2)));
01888             if (l2 >= 14)
01889                 set_hash("sec",  INT2FIX(n2i(cs2,   12, 2)));
01890             set_hash("_comp", Qfalse);
01891         }
01892         break;
01893       case 3:
01894         if (NIL_P(s3) && !NIL_P(s4)) {
01895             set_hash("sec",  INT2FIX(n2i(cs2, l2-2, 2)));
01896             set_hash("min",  INT2FIX(n2i(cs2, l2-3, 1)));
01897         }
01898         else
01899             set_hash("yday", INT2FIX(n2i(cs2,    0, 3)));
01900         break;
01901       case 5:
01902         if (NIL_P(s3) && !NIL_P(s4)) {
01903             set_hash("sec",  INT2FIX(n2i(cs2, l2-2, 2)));
01904             set_hash("min",  INT2FIX(n2i(cs2, l2-4, 2)));
01905             set_hash("hour", INT2FIX(n2i(cs2, l2-5, 1)));
01906         }
01907         else {
01908             int                  y = n2i(cs2,    0, 2);
01909             if (!NIL_P(s1) && *RSTRING_PTR(s1) == '-')
01910                 y = -y;
01911             set_hash("year", INT2FIX(y));
01912             set_hash("yday", INT2FIX(n2i(cs2,    2, 3)));
01913         }
01914         break;
01915       case 7:
01916         if (NIL_P(s3) && !NIL_P(s4)) {
01917             set_hash("sec",  INT2FIX(n2i(cs2, l2-2, 2)));
01918             set_hash("min",  INT2FIX(n2i(cs2, l2-4, 2)));
01919             set_hash("hour", INT2FIX(n2i(cs2, l2-6, 2)));
01920             set_hash("mday", INT2FIX(n2i(cs2, l2-7, 1)));
01921         }
01922         else {
01923             int                  y = n2i(cs2,    0, 4);
01924             if (!NIL_P(s1) && *RSTRING_PTR(s1) == '-')
01925                 y = -y;
01926             set_hash("year", INT2FIX(y));
01927             set_hash("yday", INT2FIX(n2i(cs2,    4, 3)));
01928         }
01929         break;
01930     }
01931     RB_GC_GUARD(s2);
01932     if (!NIL_P(s3)) {
01933         cs3 = RSTRING_PTR(s3);
01934         l3 = RSTRING_LEN(s3);
01935 
01936         if (!NIL_P(s4)) {
01937             switch (l3) {
01938               case 2:
01939               case 4:
01940               case 6:
01941                 set_hash("sec", INT2FIX(n2i(cs3, l3-2, 2)));
01942                 if (l3 >= 4)
01943                     set_hash("min", INT2FIX(n2i(cs3, l3-4, 2)));
01944                 if (l3 >= 6)
01945                     set_hash("hour", INT2FIX(n2i(cs3, l3-6, 2)));
01946                 break;
01947             }
01948         }
01949         else {
01950             switch (l3) {
01951               case 2:
01952               case 4:
01953               case 6:
01954                 set_hash("hour", INT2FIX(n2i(cs3, 0, 2)));
01955                 if (l3 >= 4)
01956                     set_hash("min", INT2FIX(n2i(cs3, 2, 2)));
01957                 if (l3 >= 6)
01958                     set_hash("sec", INT2FIX(n2i(cs3, 4, 2)));
01959                 break;
01960             }
01961         }
01962         RB_GC_GUARD(s3);
01963     }
01964     if (!NIL_P(s4)) {
01965         l4 = RSTRING_LEN(s4);
01966 
01967         set_hash("sec_fraction",
01968                  rb_rational_new2(str2num(s4),
01969                                   f_expt(INT2FIX(10), LONG2NUM(l4))));
01970     }
01971     if (!NIL_P(s5)) {
01972         cs5 = RSTRING_PTR(s5);
01973         l5 = RSTRING_LEN(s5);
01974 
01975         set_hash("zone", s5);
01976 
01977         if (*cs5 == '[') {
01978             char *buf = ALLOCA_N(char, l5 + 1);
01979             char *s1, *s2, *s3;
01980             VALUE zone;
01981 
01982             memcpy(buf, cs5, l5);
01983             buf[l5 - 1] = '\0';
01984 
01985             s1 = buf + 1;
01986             s2 = strchr(buf, ':');
01987             if (s2) {
01988                 *s2 = '\0';
01989                 s2++;
01990             }
01991             if (s2)
01992                 s3 = s2;
01993             else
01994                 s3 = s1;
01995             zone = rb_str_new2(s3);
01996             set_hash("zone", zone);
01997             if (isdigit((unsigned char)*s1))
01998                 *--s1 = '+';
01999             set_hash("offset", date_zone_to_diff(rb_str_new2(s1)));
02000         }
02001         RB_GC_GUARD(s5);
02002     }
02003 
02004     return 1;
02005 }
02006 
02007 static int
02008 parse_ddd(VALUE str, VALUE hash)
02009 {
02010     static const char pat_source[] =
02011 #ifdef TIGHT_PARSER
02012                 BOS
02013 #endif
02014                 "([-+]?)(\\d{2,14})"
02015                   "(?:"
02016                     "\\s*"
02017                     "t?"
02018                     "\\s*"
02019                     "(\\d{2,6})?(?:[,.](\\d*))?"
02020                   ")?"
02021                   "(?:"
02022                     "\\s*"
02023                     "("
02024                       "z\\b"
02025                     "|"
02026                       "[-+]\\d{1,4}\\b"
02027                     "|"
02028                       "\\[[-+]?\\d[^\\]]*\\]"
02029                     ")"
02030                 ")?"
02031 #ifdef TIGHT_PARSER
02032                 EOS
02033 #endif
02034                 ;
02035     static VALUE pat = Qnil;
02036 
02037     REGCOMP_I(pat);
02038     SUBS(str, pat, parse_ddd_cb);
02039 }
02040 
02041 #ifndef TIGHT_PARSER
02042 static int
02043 parse_bc_cb(VALUE m, VALUE hash)
02044 {
02045     set_hash("_bc", Qtrue);
02046     return 1;
02047 }
02048 
02049 static int
02050 parse_bc(VALUE str, VALUE hash)
02051 {
02052     static const char pat_source[] =
02053         "\\b(bc\\b|bce\\b|b\\.c\\.|b\\.c\\.e\\.)";
02054     static VALUE pat = Qnil;
02055 
02056     REGCOMP_I(pat);
02057     SUBS(str, pat, parse_bc_cb);
02058 }
02059 
02060 static int
02061 parse_frag_cb(VALUE m, VALUE hash)
02062 {
02063     VALUE s, n;
02064 
02065     s = rb_reg_nth_match(1, m);
02066 
02067     if (!NIL_P(ref_hash("hour")) && NIL_P(ref_hash("mday"))) {
02068         n = str2num(s);
02069         if (f_ge_p(n, INT2FIX(1)) &&
02070             f_le_p(n, INT2FIX(31)))
02071             set_hash("mday", n);
02072     }
02073     if (!NIL_P(ref_hash("mday")) && NIL_P(ref_hash("hour"))) {
02074         n = str2num(s);
02075         if (f_ge_p(n, INT2FIX(0)) &&
02076             f_le_p(n, INT2FIX(24)))
02077             set_hash("hour", n);
02078     }
02079 
02080     return 1;
02081 }
02082 
02083 static int
02084 parse_frag(VALUE str, VALUE hash)
02085 {
02086     static const char pat_source[] = "\\A\\s*(\\d{1,2})\\s*\\z";
02087     static VALUE pat = Qnil;
02088 
02089     REGCOMP_I(pat);
02090     SUBS(str, pat, parse_frag_cb);
02091 }
02092 #endif
02093 
02094 #ifdef TIGHT_PARSER
02095 static int
02096 parse_dummy_cb(VALUE m, VALUE hash)
02097 {
02098     return 1;
02099 }
02100 
02101 static int
02102 parse_wday_only(VALUE str, VALUE hash)
02103 {
02104     static const char pat_source[] = "\\A\\s*" FPW "\\s*\\z";
02105     static VALUE pat = Qnil;
02106 
02107     REGCOMP_0(pat);
02108     SUBS(str, pat, parse_dummy_cb);
02109 }
02110 
02111 static int
02112 parse_time_only(VALUE str, VALUE hash)
02113 {
02114     static const char pat_source[] = "\\A\\s*" FPT "\\s*\\z";
02115     static VALUE pat = Qnil;
02116 
02117     REGCOMP_0(pat);
02118     SUBS(str, pat, parse_dummy_cb);
02119 }
02120 
02121 static int
02122 parse_wday_and_time(VALUE str, VALUE hash)
02123 {
02124     static const char pat_source[] = "\\A\\s*(" FPW "\\s+" FPT "|" FPT "\\s+" FPW ")\\s*\\z";
02125     static VALUE pat = Qnil;
02126 
02127     REGCOMP_0(pat);
02128     SUBS(str, pat, parse_dummy_cb);
02129 }
02130 
02131 static unsigned
02132 have_invalid_char_p(VALUE s)
02133 {
02134     long i;
02135 
02136     for (i = 0; i < RSTRING_LEN(s); i++)
02137         if (iscntrl((unsigned char)RSTRING_PTR(s)[i]) &&
02138             !isspace((unsigned char)RSTRING_PTR(s)[i]))
02139             return 1;
02140     return 0;
02141 }
02142 #endif
02143 
02144 #define HAVE_ALPHA (1<<0)
02145 #define HAVE_DIGIT (1<<1)
02146 #define HAVE_DASH (1<<2)
02147 #define HAVE_DOT (1<<3)
02148 #define HAVE_SLASH (1<<4)
02149 
02150 static unsigned
02151 check_class(VALUE s)
02152 {
02153     unsigned flags;
02154     long i;
02155 
02156     flags = 0;
02157     for (i = 0; i < RSTRING_LEN(s); i++) {
02158         if (isalpha((unsigned char)RSTRING_PTR(s)[i]))
02159             flags |= HAVE_ALPHA;
02160         if (isdigit((unsigned char)RSTRING_PTR(s)[i]))
02161             flags |= HAVE_DIGIT;
02162         if (RSTRING_PTR(s)[i] == '-')
02163             flags |= HAVE_DASH;
02164         if (RSTRING_PTR(s)[i] == '.')
02165             flags |= HAVE_DOT;
02166         if (RSTRING_PTR(s)[i] == '/')
02167             flags |= HAVE_SLASH;
02168     }
02169     return flags;
02170 }
02171 
02172 #define HAVE_ELEM_P(x) ((check_class(str) & (x)) == (x))
02173 
02174 #ifdef TIGHT_PARSER
02175 #define PARSER_ERROR return rb_hash_new()
02176 #endif
02177 
02178 VALUE
02179 date__parse(VALUE str, VALUE comp)
02180 {
02181     VALUE backref, hash;
02182 
02183 #ifdef TIGHT_PARSER
02184     if (have_invalid_char_p(str))
02185         PARSER_ERROR;
02186 #endif
02187 
02188     backref = rb_backref_get();
02189     rb_match_busy(backref);
02190 
02191     {
02192         static const char pat_source[] =
02193 #ifndef TIGHT_PARSER
02194             "[^-+',./:@[:alnum:]\\[\\]]+"
02195 #else
02196             "[^[:graph:]]+"
02197 #endif
02198             ;
02199         static VALUE pat = Qnil;
02200 
02201         REGCOMP_0(pat);
02202         str = rb_str_dup(str);
02203         f_gsub_bang(str, pat, asp_string());
02204     }
02205 
02206     hash = rb_hash_new();
02207     set_hash("_comp", comp);
02208 
02209     if (HAVE_ELEM_P(HAVE_ALPHA))
02210         parse_day(str, hash);
02211     if (HAVE_ELEM_P(HAVE_DIGIT))
02212         parse_time(str, hash);
02213 
02214 #ifdef TIGHT_PARSER
02215     if (HAVE_ELEM_P(HAVE_ALPHA))
02216         parse_era(str, hash);
02217 #endif
02218 
02219     if (HAVE_ELEM_P(HAVE_ALPHA|HAVE_DIGIT)) {
02220         if (parse_eu(str, hash))
02221             goto ok;
02222         if (parse_us(str, hash))
02223             goto ok;
02224     }
02225     if (HAVE_ELEM_P(HAVE_DIGIT|HAVE_DASH))
02226         if (parse_iso(str, hash))
02227             goto ok;
02228     if (HAVE_ELEM_P(HAVE_DIGIT|HAVE_DOT))
02229         if (parse_jis(str, hash))
02230             goto ok;
02231     if (HAVE_ELEM_P(HAVE_ALPHA|HAVE_DIGIT|HAVE_DASH))
02232         if (parse_vms(str, hash))
02233             goto ok;
02234     if (HAVE_ELEM_P(HAVE_DIGIT|HAVE_SLASH))
02235         if (parse_sla(str, hash))
02236             goto ok;
02237 #ifdef TIGHT_PARSER
02238     if (HAVE_ELEM_P(HAVE_ALPHA|HAVE_DIGIT|HAVE_SLASH)) {
02239         if (parse_sla2(str, hash))
02240             goto ok;
02241         if (parse_sla3(str, hash))
02242             goto ok;
02243     }
02244 #endif
02245     if (HAVE_ELEM_P(HAVE_DIGIT|HAVE_DOT))
02246         if (parse_dot(str, hash))
02247             goto ok;
02248 #ifdef TIGHT_PARSER
02249     if (HAVE_ELEM_P(HAVE_ALPHA|HAVE_DIGIT|HAVE_DOT)) {
02250         if (parse_dot2(str, hash))
02251             goto ok;
02252         if (parse_dot3(str, hash))
02253             goto ok;
02254     }
02255 #endif
02256     if (HAVE_ELEM_P(HAVE_DIGIT))
02257         if (parse_iso2(str, hash))
02258             goto ok;
02259     if (HAVE_ELEM_P(HAVE_DIGIT))
02260         if (parse_year(str, hash))
02261             goto ok;
02262     if (HAVE_ELEM_P(HAVE_ALPHA))
02263         if (parse_mon(str, hash))
02264             goto ok;
02265     if (HAVE_ELEM_P(HAVE_DIGIT))
02266         if (parse_mday(str, hash))
02267             goto ok;
02268     if (HAVE_ELEM_P(HAVE_DIGIT))
02269         if (parse_ddd(str, hash))
02270             goto ok;
02271 
02272 #ifdef TIGHT_PARSER
02273     if (parse_wday_only(str, hash))
02274         goto ok;
02275     if (parse_time_only(str, hash))
02276             goto ok;
02277     if (parse_wday_and_time(str, hash))
02278         goto ok;
02279 
02280     PARSER_ERROR; /* not found */
02281 #endif
02282 
02283   ok:
02284 #ifndef TIGHT_PARSER
02285     if (HAVE_ELEM_P(HAVE_ALPHA))
02286         parse_bc(str, hash);
02287     if (HAVE_ELEM_P(HAVE_DIGIT))
02288         parse_frag(str, hash);
02289 #endif
02290 
02291     {
02292         if (RTEST(ref_hash("_bc"))) {
02293             VALUE y;
02294 
02295             y = ref_hash("cwyear");
02296             if (!NIL_P(y)) {
02297                 y = f_add(f_negate(y), INT2FIX(1));
02298                 set_hash("cwyear", y);
02299             }
02300             y = ref_hash("year");
02301             if (!NIL_P(y)) {
02302                 y = f_add(f_negate(y), INT2FIX(1));
02303                 set_hash("year", y);
02304             }
02305         }
02306 
02307         if (RTEST(ref_hash("_comp"))) {
02308             VALUE y;
02309 
02310             y = ref_hash("cwyear");
02311             if (!NIL_P(y))
02312                 if (f_ge_p(y, INT2FIX(0)) && f_le_p(y, INT2FIX(99))) {
02313                     if (f_ge_p(y, INT2FIX(69)))
02314                         set_hash("cwyear", f_add(y, INT2FIX(1900)));
02315                     else
02316                         set_hash("cwyear", f_add(y, INT2FIX(2000)));
02317                 }
02318             y = ref_hash("year");
02319             if (!NIL_P(y))
02320                 if (f_ge_p(y, INT2FIX(0)) && f_le_p(y, INT2FIX(99))) {
02321                     if (f_ge_p(y, INT2FIX(69)))
02322                         set_hash("year", f_add(y, INT2FIX(1900)));
02323                     else
02324                         set_hash("year", f_add(y, INT2FIX(2000)));
02325                 }
02326         }
02327 
02328     }
02329 
02330     del_hash("_bc");
02331     del_hash("_comp");
02332 
02333     {
02334         VALUE zone = ref_hash("zone");
02335         if (!NIL_P(zone) && NIL_P(ref_hash("offset")))
02336             set_hash("offset", date_zone_to_diff(zone));
02337     }
02338 
02339     rb_backref_set(backref);
02340 
02341     return hash;
02342 }
02343 
02344 static VALUE
02345 comp_year69(VALUE y)
02346 {
02347     if (f_ge_p(y, INT2FIX(69)))
02348         return f_add(y, INT2FIX(1900));
02349     return f_add(y, INT2FIX(2000));
02350 }
02351 
02352 static VALUE
02353 comp_year50(VALUE y)
02354 {
02355     if (f_ge_p(y, INT2FIX(50)))
02356         return f_add(y, INT2FIX(1900));
02357     return f_add(y, INT2FIX(2000));
02358 }
02359 
02360 static VALUE
02361 sec_fraction(VALUE f)
02362 {
02363     return rb_rational_new2(str2num(f),
02364                             f_expt(INT2FIX(10),
02365                                    LONG2NUM(RSTRING_LEN(f))));
02366 }
02367 
02368 #define SNUM 14
02369 
02370 static int
02371 iso8601_ext_datetime_cb(VALUE m, VALUE hash)
02372 {
02373     VALUE s[SNUM + 1], y;
02374 
02375     {
02376         int i;
02377         s[0] = Qnil;
02378         for (i = 1; i <= SNUM; i++)
02379             s[i] = rb_reg_nth_match(i, m);
02380     }
02381 
02382     if (!NIL_P(s[3])) {
02383         set_hash("mday", str2num(s[3]));
02384         if (strcmp(RSTRING_PTR(s[1]), "-") != 0) {
02385             y = str2num(s[1]);
02386             if (RSTRING_LEN(s[1]) < 4)
02387                 y = comp_year69(y);
02388             set_hash("year", y);
02389         }
02390         if (NIL_P(s[2])) {
02391             if (strcmp(RSTRING_PTR(s[1]), "-") != 0)
02392                 return 0;
02393         }
02394         else
02395             set_hash("mon", str2num(s[2]));
02396     }
02397     else if (!NIL_P(s[5])) {
02398         set_hash("yday", str2num(s[5]));
02399         if (!NIL_P(s[4])) {
02400             y = str2num(s[4]);
02401             if (RSTRING_LEN(s[4]) < 4)
02402                 y = comp_year69(y);
02403             set_hash("year", y);
02404         }
02405     }
02406     else if (!NIL_P(s[8])) {
02407         set_hash("cweek", str2num(s[7]));
02408         set_hash("cwday", str2num(s[8]));
02409         if (!NIL_P(s[6])) {
02410             y = str2num(s[6]);
02411             if (RSTRING_LEN(s[6]) < 4)
02412                 y = comp_year69(y);
02413             set_hash("cwyear", y);
02414         }
02415     }
02416     else if (!NIL_P(s[9])) {
02417         set_hash("cwday", str2num(s[9]));
02418     }
02419     if (!NIL_P(s[10])) {
02420         set_hash("hour", str2num(s[10]));
02421         set_hash("min", str2num(s[11]));
02422         if (!NIL_P(s[12]))
02423             set_hash("sec", str2num(s[12]));
02424     }
02425     if (!NIL_P(s[13])) {
02426         set_hash("sec_fraction", sec_fraction(s[13]));
02427     }
02428     if (!NIL_P(s[14])) {
02429         set_hash("zone", s[14]);
02430         set_hash("offset", date_zone_to_diff(s[14]));
02431     }
02432 
02433     return 1;
02434 }
02435 
02436 static int
02437 iso8601_ext_datetime(VALUE str, VALUE hash)
02438 {
02439     static const char pat_source[] =
02440         "\\A\\s*(?:([-+]?\\d{2,}|-)-(\\d{2})?-(\\d{2})|"
02441                 "([-+]?\\d{2,})?-(\\d{3})|"
02442                 "(\\d{4}|\\d{2})?-w(\\d{2})-(\\d)|"
02443                 "-w-(\\d))"
02444         "(?:t"
02445         "(\\d{2}):(\\d{2})(?::(\\d{2})(?:[,.](\\d+))?)?"
02446         "(z|[-+]\\d{2}(?::?\\d{2})?)?)?\\s*\\z";
02447     static VALUE pat = Qnil;
02448 
02449     REGCOMP_I(pat);
02450     MATCH(str, pat, iso8601_ext_datetime_cb);
02451 }
02452 
02453 #undef SNUM
02454 #define SNUM 17
02455 
02456 static int
02457 iso8601_bas_datetime_cb(VALUE m, VALUE hash)
02458 {
02459     VALUE s[SNUM + 1], y;
02460 
02461     {
02462         int i;
02463         s[0] = Qnil;
02464         for (i = 1; i <= SNUM; i++)
02465             s[i] = rb_reg_nth_match(i, m);
02466     }
02467 
02468     if (!NIL_P(s[3])) {
02469         set_hash("mday", str2num(s[3]));
02470         if (strcmp(RSTRING_PTR(s[1]), "--") != 0) {
02471             y = str2num(s[1]);
02472             if (RSTRING_LEN(s[1]) < 4)
02473                 y = comp_year69(y);
02474             set_hash("year", y);
02475         }
02476         if (*RSTRING_PTR(s[2]) == '-') {
02477             if (strcmp(RSTRING_PTR(s[1]), "--") != 0)
02478                 return 0;
02479         }
02480         else
02481             set_hash("mon", str2num(s[2]));
02482     }
02483     else if (!NIL_P(s[5])) {
02484         set_hash("yday", str2num(s[5]));
02485         y = str2num(s[4]);
02486         if (RSTRING_LEN(s[4]) < 4)
02487             y = comp_year69(y);
02488         set_hash("year", y);
02489     }
02490     else if (!NIL_P(s[6])) {
02491         set_hash("yday", str2num(s[6]));
02492     }
02493     else if (!NIL_P(s[9])) {
02494         set_hash("cweek", str2num(s[8]));
02495         set_hash("cwday", str2num(s[9]));
02496         y = str2num(s[7]);
02497         if (RSTRING_LEN(s[7]) < 4)
02498             y = comp_year69(y);
02499         set_hash("cwyear", y);
02500     }
02501     else if (!NIL_P(s[11])) {
02502         set_hash("cweek", str2num(s[10]));
02503         set_hash("cwday", str2num(s[11]));
02504     }
02505     else if (!NIL_P(s[12])) {
02506         set_hash("cwday", str2num(s[12]));
02507     }
02508     if (!NIL_P(s[13])) {
02509         set_hash("hour", str2num(s[13]));
02510         set_hash("min", str2num(s[14]));
02511         if (!NIL_P(s[15]))
02512             set_hash("sec", str2num(s[15]));
02513     }
02514     if (!NIL_P(s[16])) {
02515         set_hash("sec_fraction", sec_fraction(s[16]));
02516     }
02517     if (!NIL_P(s[17])) {
02518         set_hash("zone", s[17]);
02519         set_hash("offset", date_zone_to_diff(s[17]));
02520     }
02521 
02522     return 1;
02523 }
02524 
02525 static int
02526 iso8601_bas_datetime(VALUE str, VALUE hash)
02527 {
02528     static const char pat_source[] =
02529         "\\A\\s*(?:([-+]?(?:\\d{4}|\\d{2})|--)(\\d{2}|-)(\\d{2})|"
02530                    "([-+]?(?:\\d{4}|\\d{2}))(\\d{3})|"
02531                    "-(\\d{3})|"
02532                    "(\\d{4}|\\d{2})w(\\d{2})(\\d)|"
02533                    "-w(\\d{2})(\\d)|"
02534                    "-w-(\\d))"
02535         "(?:t?"
02536         "(\\d{2})(\\d{2})(?:(\\d{2})(?:[,.](\\d+))?)?"
02537         "(z|[-+]\\d{2}(?:\\d{2})?)?)?\\s*\\z";
02538     static VALUE pat = Qnil;
02539 
02540     REGCOMP_I(pat);
02541     MATCH(str, pat, iso8601_bas_datetime_cb);
02542 }
02543 
02544 #undef SNUM
02545 #define SNUM 5
02546 
02547 static int
02548 iso8601_ext_time_cb(VALUE m, VALUE hash)
02549 {
02550     VALUE s[SNUM + 1];
02551 
02552     {
02553         int i;
02554         s[0] = Qnil;
02555         for (i = 1; i <= SNUM; i++)
02556             s[i] = rb_reg_nth_match(i, m);
02557     }
02558 
02559     set_hash("hour", str2num(s[1]));
02560     set_hash("min", str2num(s[2]));
02561     if (!NIL_P(s[3]))
02562         set_hash("sec", str2num(s[3]));
02563     if (!NIL_P(s[4]))
02564         set_hash("sec_fraction", sec_fraction(s[4]));
02565     if (!NIL_P(s[5])) {
02566         set_hash("zone", s[5]);
02567         set_hash("offset", date_zone_to_diff(s[5]));
02568     }
02569 
02570     return 1;
02571 }
02572 
02573 #define iso8601_bas_time_cb iso8601_ext_time_cb
02574 
02575 static int
02576 iso8601_ext_time(VALUE str, VALUE hash)
02577 {
02578     static const char pat_source[] =
02579         "\\A\\s*(\\d{2}):(\\d{2})(?::(\\d{2})(?:[,.](\\d+))?"
02580         "(z|[-+]\\d{2}(:?\\d{2})?)?)?\\s*\\z";
02581     static VALUE pat = Qnil;
02582 
02583     REGCOMP_I(pat);
02584     MATCH(str, pat, iso8601_ext_time_cb);
02585 }
02586 
02587 static int
02588 iso8601_bas_time(VALUE str, VALUE hash)
02589 {
02590     static const char pat_source[] =
02591         "\\A\\s*(\\d{2})(\\d{2})(?:(\\d{2})(?:[,.](\\d+))?"
02592         "(z|[-+]\\d{2}(\\d{2})?)?)?\\s*\\z";
02593     static VALUE pat = Qnil;
02594 
02595     REGCOMP_I(pat);
02596     MATCH(str, pat, iso8601_bas_time_cb);
02597 }
02598 
02599 VALUE
02600 date__iso8601(VALUE str)
02601 {
02602     VALUE backref, hash;
02603 
02604     backref = rb_backref_get();
02605     rb_match_busy(backref);
02606 
02607     hash = rb_hash_new();
02608 
02609     if (iso8601_ext_datetime(str, hash))
02610         goto ok;
02611     if (iso8601_bas_datetime(str, hash))
02612         goto ok;
02613     if (iso8601_ext_time(str, hash))
02614         goto ok;
02615     if (iso8601_bas_time(str, hash))
02616         goto ok;
02617 
02618   ok:
02619     rb_backref_set(backref);
02620 
02621     return hash;
02622 }
02623 
02624 #undef SNUM
02625 #define SNUM 8
02626 
02627 static int
02628 rfc3339_cb(VALUE m, VALUE hash)
02629 {
02630     VALUE s[SNUM + 1];
02631 
02632     {
02633         int i;
02634         s[0] = Qnil;
02635         for (i = 1; i <= SNUM; i++)
02636             s[i] = rb_reg_nth_match(i, m);
02637     }
02638 
02639     set_hash("year", str2num(s[1]));
02640     set_hash("mon", str2num(s[2]));
02641     set_hash("mday", str2num(s[3]));
02642     set_hash("hour", str2num(s[4]));
02643     set_hash("min", str2num(s[5]));
02644     set_hash("sec", str2num(s[6]));
02645     set_hash("zone", s[8]);
02646     set_hash("offset", date_zone_to_diff(s[8]));
02647     if (!NIL_P(s[7]))
02648         set_hash("sec_fraction", sec_fraction(s[7]));
02649 
02650     return 1;
02651 }
02652 
02653 static int
02654 rfc3339(VALUE str, VALUE hash)
02655 {
02656     static const char pat_source[] =
02657         "\\A\\s*(-?\\d{4})-(\\d{2})-(\\d{2})"
02658         "(?:t|\\s)"
02659         "(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?"
02660         "(z|[-+]\\d{2}:\\d{2})\\s*\\z";
02661     static VALUE pat = Qnil;
02662 
02663     REGCOMP_I(pat);
02664     MATCH(str, pat, rfc3339_cb);
02665 }
02666 
02667 VALUE
02668 date__rfc3339(VALUE str)
02669 {
02670     VALUE backref, hash;
02671 
02672     backref = rb_backref_get();
02673     rb_match_busy(backref);
02674 
02675     hash = rb_hash_new();
02676     rfc3339(str, hash);
02677     rb_backref_set(backref);
02678     return hash;
02679 }
02680 
02681 #undef SNUM
02682 #define SNUM 8
02683 
02684 static int
02685 xmlschema_datetime_cb(VALUE m, VALUE hash)
02686 {
02687     VALUE s[SNUM + 1];
02688 
02689     {
02690         int i;
02691         s[0] = Qnil;
02692         for (i = 1; i <= SNUM; i++)
02693             s[i] = rb_reg_nth_match(i, m);
02694     }
02695 
02696     set_hash("year", str2num(s[1]));
02697     if (!NIL_P(s[2]))
02698         set_hash("mon", str2num(s[2]));
02699     if (!NIL_P(s[3]))
02700         set_hash("mday", str2num(s[3]));
02701     if (!NIL_P(s[4]))
02702         set_hash("hour", str2num(s[4]));
02703     if (!NIL_P(s[5]))
02704         set_hash("min", str2num(s[5]));
02705     if (!NIL_P(s[6]))
02706         set_hash("sec", str2num(s[6]));
02707     if (!NIL_P(s[7]))
02708         set_hash("sec_fraction", sec_fraction(s[7]));
02709     if (!NIL_P(s[8])) {
02710         set_hash("zone", s[8]);
02711         set_hash("offset", date_zone_to_diff(s[8]));
02712     }
02713 
02714     return 1;
02715 }
02716 
02717 static int
02718 xmlschema_datetime(VALUE str, VALUE hash)
02719 {
02720     static const char pat_source[] =
02721         "\\A\\s*(-?\\d{4,})(?:-(\\d{2})(?:-(\\d{2}))?)?"
02722         "(?:t"
02723           "(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?)?"
02724         "(z|[-+]\\d{2}:\\d{2})?\\s*\\z";
02725     static VALUE pat = Qnil;
02726 
02727     REGCOMP_I(pat);
02728     MATCH(str, pat, xmlschema_datetime_cb);
02729 }
02730 
02731 #undef SNUM
02732 #define SNUM 5
02733 
02734 static int
02735 xmlschema_time_cb(VALUE m, VALUE hash)
02736 {
02737     VALUE s[SNUM + 1];
02738 
02739     {
02740         int i;
02741         s[0] = Qnil;
02742         for (i = 1; i <= SNUM; i++)
02743             s[i] = rb_reg_nth_match(i, m);
02744     }
02745 
02746     set_hash("hour", str2num(s[1]));
02747     set_hash("min", str2num(s[2]));
02748     if (!NIL_P(s[3]))
02749         set_hash("sec", str2num(s[3]));
02750     if (!NIL_P(s[4]))
02751         set_hash("sec_fraction", sec_fraction(s[4]));
02752     if (!NIL_P(s[5])) {
02753         set_hash("zone", s[5]);
02754         set_hash("offset", date_zone_to_diff(s[5]));
02755     }
02756 
02757     return 1;
02758 }
02759 
02760 static int
02761 xmlschema_time(VALUE str, VALUE hash)
02762 {
02763     static const char pat_source[] =
02764         "\\A\\s*(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?"
02765         "(z|[-+]\\d{2}:\\d{2})?\\s*\\z";
02766     static VALUE pat = Qnil;
02767 
02768     REGCOMP_I(pat);
02769     MATCH(str, pat, xmlschema_time_cb);
02770 }
02771 
02772 #undef SNUM
02773 #define SNUM 4
02774 
02775 static int
02776 xmlschema_trunc_cb(VALUE m, VALUE hash)
02777 {
02778     VALUE s[SNUM + 1];
02779 
02780     {
02781         int i;
02782         s[0] = Qnil;
02783         for (i = 1; i <= SNUM; i++)
02784             s[i] = rb_reg_nth_match(i, m);
02785     }
02786 
02787     if (!NIL_P(s[1]))
02788         set_hash("mon", str2num(s[1]));
02789     if (!NIL_P(s[2]))
02790         set_hash("mday", str2num(s[2]));
02791     if (!NIL_P(s[3]))
02792         set_hash("mday", str2num(s[3]));
02793     if (!NIL_P(s[4])) {
02794         set_hash("zone", s[4]);
02795         set_hash("offset", date_zone_to_diff(s[4]));
02796     }
02797 
02798     return 1;
02799 }
02800 
02801 static int
02802 xmlschema_trunc(VALUE str, VALUE hash)
02803 {
02804     static const char pat_source[] =
02805         "\\A\\s*(?:--(\\d{2})(?:-(\\d{2}))?|---(\\d{2}))"
02806         "(z|[-+]\\d{2}:\\d{2})?\\s*\\z";
02807     static VALUE pat = Qnil;
02808 
02809     REGCOMP_I(pat);
02810     MATCH(str, pat, xmlschema_trunc_cb);
02811 }
02812 
02813 VALUE
02814 date__xmlschema(VALUE str)
02815 {
02816     VALUE backref, hash;
02817 
02818     backref = rb_backref_get();
02819     rb_match_busy(backref);
02820 
02821     hash = rb_hash_new();
02822 
02823     if (xmlschema_datetime(str, hash))
02824         goto ok;
02825     if (xmlschema_time(str, hash))
02826         goto ok;
02827     if (xmlschema_trunc(str, hash))
02828         goto ok;
02829 
02830   ok:
02831     rb_backref_set(backref);
02832 
02833     return hash;
02834 }
02835 
02836 #undef SNUM
02837 #define SNUM 8
02838 
02839 static int
02840 rfc2822_cb(VALUE m, VALUE hash)
02841 {
02842     VALUE s[SNUM + 1], y;
02843 
02844     {
02845         int i;
02846         s[0] = Qnil;
02847         for (i = 1; i <= SNUM; i++)
02848             s[i] = rb_reg_nth_match(i, m);
02849     }
02850 
02851     set_hash("wday", INT2FIX(day_num(s[1])));
02852     set_hash("mday", str2num(s[2]));
02853     set_hash("mon", INT2FIX(mon_num(s[3])));
02854     y = str2num(s[4]);
02855     if (RSTRING_LEN(s[4]) < 4)
02856         y = comp_year50(y);
02857     set_hash("year", y);
02858     set_hash("hour", str2num(s[5]));
02859     set_hash("min", str2num(s[6]));
02860     if (!NIL_P(s[7]))
02861         set_hash("sec", str2num(s[7]));
02862     set_hash("zone", s[8]);
02863     set_hash("offset", date_zone_to_diff(s[8]));
02864 
02865     return 1;
02866 }
02867 
02868 static int
02869 rfc2822(VALUE str, VALUE hash)
02870 {
02871     static const char pat_source[] =
02872         "\\A\\s*(?:(" ABBR_DAYS ")\\s*,\\s+)?"
02873         "(\\d{1,2})\\s+"
02874         "(" ABBR_MONTHS ")\\s+"
02875         "(-?\\d{2,})\\s+"
02876         "(\\d{2}):(\\d{2})(?::(\\d{2}))?\\s*"
02877         "([-+]\\d{4}|ut|gmt|e[sd]t|c[sd]t|m[sd]t|p[sd]t|[a-ik-z])\\s*\\z";
02878     static VALUE pat = Qnil;
02879 
02880     REGCOMP_I(pat);
02881     MATCH(str, pat, rfc2822_cb);
02882 }
02883 
02884 VALUE
02885 date__rfc2822(VALUE str)
02886 {
02887     VALUE backref, hash;
02888 
02889     backref = rb_backref_get();
02890     rb_match_busy(backref);
02891 
02892     hash = rb_hash_new();
02893     rfc2822(str, hash);
02894     rb_backref_set(backref);
02895     return hash;
02896 }
02897 
02898 #undef SNUM
02899 #define SNUM 8
02900 
02901 static int
02902 httpdate_type1_cb(VALUE m, VALUE hash)
02903 {
02904     VALUE s[SNUM + 1];
02905 
02906     {
02907         int i;
02908         s[0] = Qnil;
02909         for (i = 1; i <= SNUM; i++)
02910             s[i] = rb_reg_nth_match(i, m);
02911     }
02912 
02913     set_hash("wday", INT2FIX(day_num(s[1])));
02914     set_hash("mday", str2num(s[2]));
02915     set_hash("mon", INT2FIX(mon_num(s[3])));
02916     set_hash("year", str2num(s[4]));
02917     set_hash("hour", str2num(s[5]));
02918     set_hash("min", str2num(s[6]));
02919     set_hash("sec", str2num(s[7]));
02920     set_hash("zone", s[8]);
02921     set_hash("offset", INT2FIX(0));
02922 
02923     return 1;
02924 }
02925 
02926 static int
02927 httpdate_type1(VALUE str, VALUE hash)
02928 {
02929     static const char pat_source[] =
02930         "\\A\\s*(" ABBR_DAYS ")\\s*,\\s+"
02931         "(\\d{2})\\s+"
02932         "(" ABBR_MONTHS ")\\s+"
02933         "(-?\\d{4})\\s+"
02934         "(\\d{2}):(\\d{2}):(\\d{2})\\s+"
02935         "(gmt)\\s*\\z";
02936     static VALUE pat = Qnil;
02937 
02938     REGCOMP_I(pat);
02939     MATCH(str, pat, httpdate_type1_cb);
02940 }
02941 
02942 #undef SNUM
02943 #define SNUM 8
02944 
02945 static int
02946 httpdate_type2_cb(VALUE m, VALUE hash)
02947 {
02948     VALUE s[SNUM + 1], y;
02949 
02950     {
02951         int i;
02952         s[0] = Qnil;
02953         for (i = 1; i <= SNUM; i++)
02954             s[i] = rb_reg_nth_match(i, m);
02955     }
02956 
02957     set_hash("wday", INT2FIX(day_num(s[1])));
02958     set_hash("mday", str2num(s[2]));
02959     set_hash("mon", INT2FIX(mon_num(s[3])));
02960     y = str2num(s[4]);
02961     if (f_ge_p(y, INT2FIX(0)) && f_le_p(y, INT2FIX(99)))
02962         y = comp_year69(y);
02963     set_hash("year", y);
02964     set_hash("hour", str2num(s[5]));
02965     set_hash("min", str2num(s[6]));
02966     set_hash("sec", str2num(s[7]));
02967     set_hash("zone", s[8]);
02968     set_hash("offset", INT2FIX(0));
02969 
02970     return 1;
02971 }
02972 
02973 static int
02974 httpdate_type2(VALUE str, VALUE hash)
02975 {
02976     static const char pat_source[] =
02977         "\\A\\s*(" DAYS ")\\s*,\\s+"
02978         "(\\d{2})\\s*-\\s*"
02979         "(" ABBR_MONTHS ")\\s*-\\s*"
02980         "(\\d{2})\\s+"
02981         "(\\d{2}):(\\d{2}):(\\d{2})\\s+"
02982         "(gmt)\\s*\\z";
02983     static VALUE pat = Qnil;
02984 
02985     REGCOMP_I(pat);
02986     MATCH(str, pat, httpdate_type2_cb);
02987 }
02988 
02989 #undef SNUM
02990 #define SNUM 7
02991 
02992 static int
02993 httpdate_type3_cb(VALUE m, VALUE hash)
02994 {
02995     VALUE s[SNUM + 1];
02996 
02997     {
02998         int i;
02999         s[0] = Qnil;
03000         for (i = 1; i <= SNUM; i++)
03001             s[i] = rb_reg_nth_match(i, m);
03002     }
03003 
03004     set_hash("wday", INT2FIX(day_num(s[1])));
03005     set_hash("mon", INT2FIX(mon_num(s[2])));
03006     set_hash("mday", str2num(s[3]));
03007     set_hash("hour", str2num(s[4]));
03008     set_hash("min", str2num(s[5]));
03009     set_hash("sec", str2num(s[6]));
03010     set_hash("year", str2num(s[7]));
03011 
03012     return 1;
03013 }
03014 
03015 static int
03016 httpdate_type3(VALUE str, VALUE hash)
03017 {
03018     static const char pat_source[] =
03019         "\\A\\s*(" ABBR_DAYS ")\\s+"
03020         "(" ABBR_MONTHS ")\\s+"
03021         "(\\d{1,2})\\s+"
03022         "(\\d{2}):(\\d{2}):(\\d{2})\\s+"
03023         "(\\d{4})\\s*\\z";
03024     static VALUE pat = Qnil;
03025 
03026     REGCOMP_I(pat);
03027     MATCH(str, pat, httpdate_type3_cb);
03028 }
03029 
03030 VALUE
03031 date__httpdate(VALUE str)
03032 {
03033     VALUE backref, hash;
03034 
03035     backref = rb_backref_get();
03036     rb_match_busy(backref);
03037 
03038     hash = rb_hash_new();
03039 
03040     if (httpdate_type1(str, hash))
03041         goto ok;
03042     if (httpdate_type2(str, hash))
03043         goto ok;
03044     if (httpdate_type3(str, hash))
03045         goto ok;
03046 
03047   ok:
03048     rb_backref_set(backref);
03049 
03050     return hash;
03051 }
03052 
03053 #undef SNUM
03054 #define SNUM 9
03055 
03056 static int
03057 jisx0301_cb(VALUE m, VALUE hash)
03058 {
03059     VALUE s[SNUM + 1];
03060     int ep;
03061 
03062     {
03063         int i;
03064         s[0] = Qnil;
03065         for (i = 1; i <= SNUM; i++)
03066             s[i] = rb_reg_nth_match(i, m);
03067     }
03068 
03069     ep = gengo(NIL_P(s[1]) ? 'h' : *RSTRING_PTR(s[1]));
03070     set_hash("year", f_add(str2num(s[2]), INT2FIX(ep)));
03071     set_hash("mon", str2num(s[3]));
03072     set_hash("mday", str2num(s[4]));
03073     if (!NIL_P(s[5])) {
03074         set_hash("hour", str2num(s[5]));
03075         if (!NIL_P(s[6]))
03076             set_hash("min", str2num(s[6]));
03077         if (!NIL_P(s[7]))
03078             set_hash("sec", str2num(s[7]));
03079     }
03080     if (!NIL_P(s[8]))
03081         set_hash("sec_fraction", sec_fraction(s[8]));
03082     if (!NIL_P(s[9])) {
03083         set_hash("zone", s[9]);
03084         set_hash("offset", date_zone_to_diff(s[9]));
03085     }
03086 
03087     return 1;
03088 }
03089 
03090 static int
03091 jisx0301(VALUE str, VALUE hash)
03092 {
03093     static const char pat_source[] =
03094         "\\A\\s*([mtsh])?(\\d{2})\\.(\\d{2})\\.(\\d{2})"
03095         "(?:t"
03096         "(?:(\\d{2}):(\\d{2})(?::(\\d{2})(?:[,.](\\d*))?)?"
03097         "(z|[-+]\\d{2}(?::?\\d{2})?)?)?)?\\s*\\z";
03098     static VALUE pat = Qnil;
03099 
03100     REGCOMP_I(pat);
03101     MATCH(str, pat, jisx0301_cb);
03102 }
03103 
03104 VALUE
03105 date__jisx0301(VALUE str)
03106 {
03107     VALUE backref, hash;
03108 
03109     backref = rb_backref_get();
03110     rb_match_busy(backref);
03111 
03112     hash = rb_hash_new();
03113     if (jisx0301(str, hash))
03114         goto ok;
03115     hash = date__iso8601(str);
03116 
03117   ok:
03118     rb_backref_set(backref);
03119     return hash;
03120 }
03121 
03122 /*
03123 Local variables:
03124 c-file-style: "ruby"
03125 End:
03126 */
03127