home_run 0.9.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/CHANGELOG +3 -0
  2. data/LICENSE +19 -0
  3. data/README.rdoc +314 -0
  4. data/Rakefile +136 -0
  5. data/bench/cpu_bench.rb +279 -0
  6. data/bench/dt_garbage_bench.rb +11 -0
  7. data/bench/dt_mem_bench.rb +14 -0
  8. data/bench/garbage_bench.rb +11 -0
  9. data/bench/mem_bench.rb +14 -0
  10. data/bin/home_run +91 -0
  11. data/default.mspec +12 -0
  12. data/ext/1.8/date_ext.so +0 -0
  13. data/ext/1.9/date_ext.so +0 -0
  14. data/ext/date.rb +7 -0
  15. data/ext/date/format.rb +842 -0
  16. data/ext/date_ext.c +4548 -0
  17. data/ext/date_parser.c +367 -0
  18. data/ext/date_parser.rl +134 -0
  19. data/ext/datetime.c +2804 -0
  20. data/ext/extconf.rb +6 -0
  21. data/spec/date/accessor_spec.rb +176 -0
  22. data/spec/date/add_month_spec.rb +26 -0
  23. data/spec/date/add_spec.rb +23 -0
  24. data/spec/date/boat_spec.rb +38 -0
  25. data/spec/date/civil_spec.rb +147 -0
  26. data/spec/date/commercial_spec.rb +153 -0
  27. data/spec/date/constants_spec.rb +44 -0
  28. data/spec/date/conversions_spec.rb +246 -0
  29. data/spec/date/day_spec.rb +73 -0
  30. data/spec/date/downto_spec.rb +17 -0
  31. data/spec/date/eql_spec.rb +16 -0
  32. data/spec/date/format_spec.rb +52 -0
  33. data/spec/date/gregorian_spec.rb +52 -0
  34. data/spec/date/hash_spec.rb +11 -0
  35. data/spec/date/julian_spec.rb +129 -0
  36. data/spec/date/leap_spec.rb +19 -0
  37. data/spec/date/minus_month_spec.rb +25 -0
  38. data/spec/date/minus_spec.rb +51 -0
  39. data/spec/date/next_prev_spec.rb +108 -0
  40. data/spec/date/ordinal_spec.rb +83 -0
  41. data/spec/date/parse_spec.rb +442 -0
  42. data/spec/date/parsing_spec.rb +77 -0
  43. data/spec/date/relationship_spec.rb +28 -0
  44. data/spec/date/step_spec.rb +109 -0
  45. data/spec/date/strftime_spec.rb +223 -0
  46. data/spec/date/strptime_spec.rb +201 -0
  47. data/spec/date/succ_spec.rb +20 -0
  48. data/spec/date/today_spec.rb +15 -0
  49. data/spec/date/upto_spec.rb +17 -0
  50. data/spec/datetime/accessor_spec.rb +218 -0
  51. data/spec/datetime/add_month_spec.rb +26 -0
  52. data/spec/datetime/add_spec.rb +36 -0
  53. data/spec/datetime/boat_spec.rb +43 -0
  54. data/spec/datetime/constructor_spec.rb +142 -0
  55. data/spec/datetime/conversions_spec.rb +54 -0
  56. data/spec/datetime/day_spec.rb +73 -0
  57. data/spec/datetime/downto_spec.rb +39 -0
  58. data/spec/datetime/eql_spec.rb +17 -0
  59. data/spec/datetime/format_spec.rb +59 -0
  60. data/spec/datetime/hash_spec.rb +11 -0
  61. data/spec/datetime/leap_spec.rb +19 -0
  62. data/spec/datetime/minus_month_spec.rb +25 -0
  63. data/spec/datetime/minus_spec.rb +77 -0
  64. data/spec/datetime/next_prev_spec.rb +138 -0
  65. data/spec/datetime/now_spec.rb +18 -0
  66. data/spec/datetime/parse_spec.rb +390 -0
  67. data/spec/datetime/parsing_spec.rb +77 -0
  68. data/spec/datetime/relationship_spec.rb +28 -0
  69. data/spec/datetime/step_spec.rb +155 -0
  70. data/spec/datetime/strftime_spec.rb +118 -0
  71. data/spec/datetime/strptime_spec.rb +117 -0
  72. data/spec/datetime/succ_spec.rb +24 -0
  73. data/spec/datetime/upto_spec.rb +39 -0
  74. data/spec/spec_helper.rb +59 -0
  75. metadata +154 -0
@@ -0,0 +1,842 @@
1
+ # format.rb: Written by Tadayoshi Funaba 1999-2009
2
+ # $Id: format.rb,v 2.43 2008-01-17 20:16:31+09 tadf Exp $
3
+
4
+ require 'date' unless defined?(Date)
5
+
6
+ class Date
7
+ # Holds some constants used by the pure ruby parsing code.
8
+ #
9
+ # The STYLE constant (a hash) allows the user to modify the parsing
10
+ # of DD/DD/DD and DD.DD.DD dates. For DD/DD/DD dates, you
11
+ # can set the :slash entry to :mdy (month/day/year,
12
+ # :dmy (day/month/year), or :ymd (year/month/day). The same
13
+ # can be done for DD.DD.DD dates using the :dot entry. Example:
14
+ #
15
+ # Date::Format::STYLE[:slash] = :mdy
16
+ # Date::Format::STYLE[:dot] = :dmy
17
+ module Format
18
+ if RUBY_VERSION < '1.8.7'
19
+ # On Ruby 1.8.6 and earlier, DD/DD/DD and DD.DD.DD dates
20
+ # are interpreted by default as month, day, year.
21
+ STYLE = {:dot=>:mdy, :slash=>:mdy}
22
+ elsif RUBY_VERSION >= '1.9.0'
23
+ # On Ruby 1.9.0 and later , DD/DD/DD and DD.DD.DD dates
24
+ # are interpreted by default as year, month, and day.
25
+ STYLE = {:dot=>:ymd, :slash=>:ymd}
26
+ else
27
+ # On Ruby 1.8.7, DD/DD/DD is interpreted by default as
28
+ # month, day, year, and DD.DD.DD is interpreted
29
+ # by default as year, month, day.
30
+ STYLE = {:dot=>:ymd, :slash=>:mdy}
31
+ end
32
+
33
+ # Hash mapping lowercase month names to month numbers (e.g. MONTHS['january'] => 1)
34
+ MONTHS = {
35
+ 'january' => 1, 'february' => 2, 'march' => 3, 'april' => 4,
36
+ 'may' => 5, 'june' => 6, 'july' => 7, 'august' => 8,
37
+ 'september'=> 9, 'october' =>10, 'november' =>11, 'december' =>12
38
+ }
39
+
40
+ # Hash mapping lowercase day names to day numbers (e.g. DAYS['sunday'] => 0)
41
+ DAYS = {
42
+ 'sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday'=> 3,
43
+ 'thursday' => 4, 'friday' => 5, 'saturday' => 6
44
+ }
45
+
46
+ # Hash mapping abbreviated lowercase month names to month numbers (e.g. ABBR_MONTHS['jan'] => 1)
47
+ ABBR_MONTHS = {
48
+ 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4,
49
+ 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8,
50
+ 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12
51
+ }
52
+
53
+ # Hash mapping abbreviated lowercase day names to day numbers (e.g. ABBR_DAYS['sun'] => 0)
54
+ ABBR_DAYS = {
55
+ 'sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3,
56
+ 'thu' => 4, 'fri' => 5, 'sat' => 6
57
+ }
58
+
59
+ # Hash mapping lowercase time zone names to offsets in seconds (e.g. ZONES['pst'] => -28800)
60
+ ZONES = Date::ZONES
61
+
62
+ [MONTHS, DAYS, ABBR_MONTHS, ABBR_DAYS, ZONES].each do |x|
63
+ x.freeze
64
+ end
65
+ end
66
+
67
+ # Does various guesses to see which of the middle
68
+ # three arguments is likely to be the day, the month,
69
+ # and the year. Complex code that doesn't even
70
+ # guess correctly in many cases, but difficult to
71
+ # modify without breaking backwards compatibility.
72
+ def self.s3e(e, y, m, d, bc=false) # :nodoc:
73
+ unless String === m
74
+ m = m.to_s
75
+ end
76
+
77
+ if y && m && !d
78
+ y, m, d = d, y, m
79
+ end
80
+
81
+ if y == nil
82
+ if d && d.size > 2
83
+ y = d
84
+ d = nil
85
+ end
86
+ if d && d[0,1] == "'"
87
+ y = d
88
+ d = nil
89
+ end
90
+ end
91
+
92
+ if y
93
+ y.scan(/(\d+)(.+)?/)
94
+ if $2
95
+ y, d = d, $1
96
+ end
97
+ end
98
+
99
+ if m
100
+ if m[0,1] == "'" || m.size > 2
101
+ y, m, d = m, d, y # us -> be
102
+ end
103
+ end
104
+
105
+ if d
106
+ if d[0,1] == "'" || d.size > 2
107
+ y, d = d, y
108
+ end
109
+ end
110
+
111
+ if y
112
+ y =~ /([-+])?(\d+)/
113
+ if $1 || $2.size > 2
114
+ c = false
115
+ end
116
+ iy = $&.to_i
117
+ if bc
118
+ iy = -iy + 1
119
+ end
120
+ e[:year] = iy
121
+ end
122
+
123
+ if m
124
+ m =~ /\d+/
125
+ e[:mon] = $&.to_i
126
+ end
127
+
128
+ if d
129
+ d =~ /\d+/
130
+ e[:mday] = $&.to_i
131
+ end
132
+
133
+ if c != nil
134
+ e[:_][:comp] = c
135
+ end
136
+
137
+ end
138
+
139
+ private_class_method :s3e
140
+
141
+ # Try to parse the abbreviated day name out of the string. Examples of formats handled:
142
+ #
143
+ # sun
144
+ # MON
145
+ def self._parse_day(str, e) # :nodoc:
146
+ if str.sub!(/\b(#{Format::ABBR_DAYS.keys.join('|')})[^-\d\s]*/io, ' ')
147
+ e[:wday] = Format::ABBR_DAYS[$1.downcase]
148
+ true
149
+ end
150
+ end
151
+
152
+ # Try to parse the time including time zone out of the string. Examples of formats handled:
153
+ #
154
+ # 10:20
155
+ # 10:20:30 a.m.
156
+ # 10:20:30.345 pm +10:00
157
+ # 10h 20m pm PDT
158
+ # 10h 20m 30s
159
+ # 10am
160
+ def self._parse_time(str, e) # :nodoc:
161
+ if str.sub!(
162
+ /(
163
+ (?:
164
+ \d+\s*:\s*\d+
165
+ (?:
166
+ \s*:\s*\d+(?:[,.]\d*)?
167
+ )?
168
+ |
169
+ \d+\s*h(?:\s*\d+m?(?:\s*\d+s?)?)?
170
+ )
171
+ (?:
172
+ \s*
173
+ [ap](?:m\b|\.m\.)
174
+ )?
175
+ |
176
+ \d+\s*[ap](?:m\b|\.m\.)
177
+ )
178
+ (?:
179
+ \s*
180
+ (
181
+ (?:gmt|utc?)?[-+]\d+(?:[,.:]\d+(?::\d+)?)?
182
+ |
183
+ [[:alpha:].\s]+(?:standard|daylight)\stime\b
184
+ |
185
+ [[:alpha:]]+(?:\sdst)?\b
186
+ )
187
+ )?
188
+ /ix,
189
+ ' ')
190
+
191
+ t = $1
192
+ e[:zone] = $2 if $2
193
+
194
+ t =~ /\A(\d+)h?
195
+ (?:\s*:?\s*(\d+)m?
196
+ (?:
197
+ \s*:?\s*(\d+)(?:[,.](\d+))?s?
198
+ )?
199
+ )?
200
+ (?:\s*([ap])(?:m\b|\.m\.))?/ix
201
+
202
+ e[:hour] = $1.to_i
203
+ e[:min] = $2.to_i if $2
204
+ e[:sec] = $3.to_i if $3
205
+ e[:sec_fraction] = $4.to_i/10.0**$4.size if $4
206
+
207
+ if $5
208
+ e[:hour] %= 12
209
+ if $5.downcase == 'p'
210
+ e[:hour] += 12
211
+ end
212
+ end
213
+ true
214
+ end
215
+ end
216
+
217
+ # Parse a European date format. Examples of formats handled:
218
+ #
219
+ # 12 Jan 2009
220
+ # 12 Jan bce 2009
221
+ # 2009 Jan 12rd
222
+ def self._parse_eu(str, e) # :nodoc:
223
+ if str.sub!(
224
+ /'?(\d+)[^-\d\s]*
225
+ \s*
226
+ (#{Format::ABBR_MONTHS.keys.join('|')})[^-\d\s']*
227
+ (?:
228
+ \s*
229
+ (c(?:e|\.e\.)|b(?:ce|\.c\.e\.)|a(?:d|\.d\.)|b(?:c|\.c\.))?
230
+ \s*
231
+ ('?-?\d+(?:(?:st|nd|rd|th)\b)?)
232
+ )?
233
+ /iox,
234
+ ' ') # '
235
+ s3e(e, $4, Format::ABBR_MONTHS[$2.downcase], $1,
236
+ $3 && $3[0,1].downcase == 'b')
237
+ true
238
+ end
239
+ end
240
+
241
+ # Parse a US date format. Examples of formats handled:
242
+ #
243
+ # Jan 2009 12
244
+ # Jan 12 2009
245
+ # Jan 12 bce 2009
246
+ def self._parse_us(str, e) # :nodoc:
247
+ if str.sub!(
248
+ /\b(#{Format::ABBR_MONTHS.keys.join('|')})[^-\d\s']*
249
+ \s*
250
+ ('?\d+)[^-\d\s']*
251
+ (?:
252
+ \s*
253
+ (c(?:e|\.e\.)|b(?:ce|\.c\.e\.)|a(?:d|\.d\.)|b(?:c|\.c\.))?
254
+ \s*
255
+ ('?-?\d+)
256
+ )?
257
+ /iox,
258
+ ' ') # '
259
+ s3e(e, $4, Format::ABBR_MONTHS[$1.downcase], $2,
260
+ $3 && $3[0,1].downcase == 'b')
261
+ true
262
+ end
263
+ end
264
+
265
+ # Parse an ISO 8601 date format. Examples of formats handled:
266
+ #
267
+ # 2009-01-12
268
+ def self._parse_iso(str, e) # :nodoc:
269
+ if str.sub!(/([-+]?\d+)-(\d+)-(\d+)/, ' ')
270
+ s3e(e, $1, $2, $3, false)
271
+ true
272
+ end
273
+ end
274
+
275
+ # Parse some lesser used ISO 8601 date formats (including the commercial
276
+ # week format). Examples of formats handled:
277
+ #
278
+ # # Commercial week
279
+ # 2009-w01-12
280
+ # w12-3
281
+ # # Civil without year
282
+ # --12-03
283
+ # --1203
284
+ # # Ordinal
285
+ # 2009-034
286
+ # x-034
287
+ def self._parse_iso2(str, e) # :nodoc:
288
+ if str.sub!(/\b(\d{2}|\d{4})?-?w(\d{2})(?:-?(\d))?\b/i, ' ')
289
+ e[:cwyear] = $1.to_i if $1
290
+ e[:cweek] = $2.to_i
291
+ e[:cwday] = $3.to_i if $3
292
+ true
293
+ elsif str.sub!(/-w-(\d)\b/i, ' ')
294
+ e[:cwday] = $1.to_i
295
+ true
296
+ elsif str.sub!(/--(\d{2})?-(\d{2})\b/, ' ')
297
+ e[:mon] = $1.to_i if $1
298
+ e[:mday] = $2.to_i
299
+ true
300
+ elsif str.sub!(/--(\d{2})(\d{2})?\b/, ' ')
301
+ e[:mon] = $1.to_i
302
+ e[:mday] = $2.to_i if $2
303
+ true
304
+ elsif /[,.](\d{2}|\d{4})-\d{3}\b/ !~ str &&
305
+ str.sub!(/\b(\d{2}|\d{4})-(\d{3})\b/, ' ')
306
+ e[:year] = $1.to_i
307
+ e[:yday] = $2.to_i
308
+ true
309
+ elsif /\d-\d{3}\b/ !~ str &&
310
+ str.sub!(/\b-(\d{3})\b/, ' ')
311
+ e[:yday] = $1.to_i
312
+ true
313
+ end
314
+ end
315
+
316
+ # Parse the JIS X 0301 date format. Examples of formats handled:
317
+ #
318
+ # H22.01.12
319
+ def self._parse_jis(str, e) # :nodoc:
320
+ if str.sub!(/\b([mtsh])(\d+)\.(\d+)\.(\d+)/i, ' ')
321
+ era = { 'm'=>1867,
322
+ 't'=>1911,
323
+ 's'=>1925,
324
+ 'h'=>1988
325
+ }[$1.downcase]
326
+ e[:year] = $2.to_i + era
327
+ e[:mon] = $3.to_i
328
+ e[:mday] = $4.to_i
329
+ true
330
+ end
331
+ end
332
+
333
+ # Parse some VMS date formats. Examples of formats handled:
334
+ #
335
+ # 2009-jan-12
336
+ # jan-12-2009
337
+ def self._parse_vms(str, e) # :nodoc:
338
+ if str.sub!(/('?-?\d+)-(#{Format::ABBR_MONTHS.keys.join('|')})[^-]*
339
+ -('?-?\d+)/iox, ' ')
340
+ s3e(e, $3, Format::ABBR_MONTHS[$2.downcase], $1)
341
+ true
342
+ elsif str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})[^-]*
343
+ -('?-?\d+)(?:-('?-?\d+))?/iox, ' ')
344
+ s3e(e, $3, Format::ABBR_MONTHS[$1.downcase], $2)
345
+ true
346
+ end
347
+ end
348
+
349
+ # Parse a slash separated date. Examples of formats handled:
350
+ #
351
+ # 1/2/2009
352
+ # 12/3/07
353
+ def self._parse_sla(str, e) # :nodoc:
354
+ if str.sub!(%r|('?-?\d+)/\s*('?\d+)(?:\D\s*('?-?\d+))?|, ' ') # '
355
+ case Format::STYLE[:slash]
356
+ when :mdy
357
+ s3e(e, $3, $1, $2)
358
+ when :dmy
359
+ s3e(e, $3, $2, $1)
360
+ else
361
+ s3e(e, $1, $2, $3)
362
+ end
363
+ true
364
+ end
365
+ end
366
+
367
+ # Parse a period separated date. Examples of formats handled:
368
+ #
369
+ # 1.2.2009
370
+ # 12.3.07
371
+ def self._parse_dot(str, e) # :nodoc:
372
+ if str.sub!(%r|('?-?\d+)\.\s*('?\d+)\.\s*('?-?\d+)|, ' ') # '
373
+ case Format::STYLE[:dot]
374
+ when :mdy
375
+ s3e(e, $3, $1, $2)
376
+ when :dmy
377
+ s3e(e, $3, $2, $1)
378
+ else
379
+ s3e(e, $1, $2, $3)
380
+ end
381
+ true
382
+ end
383
+ end
384
+
385
+ # Parse a year preceded by a apostrophe. Examples of formats handled:
386
+ #
387
+ # '2010
388
+ def self._parse_year(str, e) # :nodoc:
389
+ if str.sub!(/'(\d+)\b/, ' ')
390
+ e[:year] = $1.to_i
391
+ true
392
+ end
393
+ end
394
+
395
+ # Parse an abbreviated month name in isolation. Examples of formats handled:
396
+ #
397
+ # jan
398
+ # FEB
399
+ def self._parse_mon(str, e) # :nodoc:
400
+ if str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})\S*/io, ' ')
401
+ e[:mon] = Format::ABBR_MONTHS[$1.downcase]
402
+ true
403
+ end
404
+ end
405
+
406
+ # Parse a day of the month. Examples of formats handled:
407
+ #
408
+ # 12th
409
+ # 3rd
410
+ def self._parse_mday(str, e) # :nodoc:
411
+ if str.sub!(/(\d+)(st|nd|rd|th)\b/i, ' ')
412
+ e[:mday] = $1.to_i
413
+ true
414
+ end
415
+ end
416
+
417
+ # Parse a completely numeric string of 2-8,10,12,or 14 characters. Examples of formats handled:
418
+ #
419
+ # 12
420
+ # 034
421
+ # 0112
422
+ # 09034
423
+ # 090112
424
+ # 2009034
425
+ # 20090112
426
+ # 2009011210
427
+ # 200901121020
428
+ # 20090112102030
429
+ # 20090112t10
430
+ # 20090112t1020
431
+ # 20090112t102030
432
+ # 102030z
433
+ # 102030-0800
434
+ # 102030+0800
435
+ def self._parse_ddd(str, e) # :nodoc:
436
+ if str.sub!(
437
+ /([-+]?)(\d{2,14})
438
+ (?:
439
+ \s*
440
+ t?
441
+ \s*
442
+ (\d{2,6})?(?:[,.](\d*))?
443
+ )?
444
+ (?:
445
+ \s*
446
+ (
447
+ z\b
448
+ |
449
+ [-+]\d{1,4}\b
450
+ |
451
+ \[[-+]?\d[^\]]*\]
452
+ )
453
+ )?
454
+ /ix,
455
+ ' ')
456
+ case $2.size
457
+ when 2
458
+ if $3.nil? && $4
459
+ e[:sec] = $2[-2, 2].to_i
460
+ else
461
+ e[:mday] = $2[ 0, 2].to_i
462
+ end
463
+ when 4
464
+ if $3.nil? && $4
465
+ e[:sec] = $2[-2, 2].to_i
466
+ e[:min] = $2[-4, 2].to_i
467
+ else
468
+ e[:mon] = $2[ 0, 2].to_i
469
+ e[:mday] = $2[ 2, 2].to_i
470
+ end
471
+ when 6
472
+ if $3.nil? && $4
473
+ e[:sec] = $2[-2, 2].to_i
474
+ e[:min] = $2[-4, 2].to_i
475
+ e[:hour] = $2[-6, 2].to_i
476
+ else
477
+ e[:year] = ($1 + $2[ 0, 2]).to_i
478
+ e[:mon] = $2[ 2, 2].to_i
479
+ e[:mday] = $2[ 4, 2].to_i
480
+ end
481
+ when 8, 10, 12, 14
482
+ if $3.nil? && $4
483
+ e[:sec] = $2[-2, 2].to_i
484
+ e[:min] = $2[-4, 2].to_i
485
+ e[:hour] = $2[-6, 2].to_i
486
+ e[:mday] = $2[-8, 2].to_i
487
+ if $2.size >= 10
488
+ e[:mon] = $2[-10, 2].to_i
489
+ end
490
+ if $2.size == 12
491
+ e[:year] = ($1 + $2[-12, 2]).to_i
492
+ end
493
+ if $2.size == 14
494
+ e[:year] = ($1 + $2[-14, 4]).to_i
495
+ e[:_][:comp] = false
496
+ end
497
+ else
498
+ e[:year] = ($1 + $2[ 0, 4]).to_i
499
+ e[:mon] = $2[ 4, 2].to_i
500
+ e[:mday] = $2[ 6, 2].to_i
501
+ e[:hour] = $2[ 8, 2].to_i if $2.size >= 10
502
+ e[:min] = $2[10, 2].to_i if $2.size >= 12
503
+ e[:sec] = $2[12, 2].to_i if $2.size >= 14
504
+ e[:_][:comp] = false
505
+ end
506
+ when 3
507
+ if $3.nil? && $4
508
+ e[:sec] = $2[-2, 2].to_i
509
+ e[:min] = $2[-3, 1].to_i
510
+ else
511
+ e[:yday] = $2[ 0, 3].to_i
512
+ end
513
+ when 5
514
+ if $3.nil? && $4
515
+ e[:sec] = $2[-2, 2].to_i
516
+ e[:min] = $2[-4, 2].to_i
517
+ e[:hour] = $2[-5, 1].to_i
518
+ else
519
+ e[:year] = ($1 + $2[ 0, 2]).to_i
520
+ e[:yday] = $2[ 2, 3].to_i
521
+ end
522
+ when 7
523
+ if $3.nil? && $4
524
+ e[:sec] = $2[-2, 2].to_i
525
+ e[:min] = $2[-4, 2].to_i
526
+ e[:hour] = $2[-6, 2].to_i
527
+ e[:mday] = $2[-7, 1].to_i
528
+ else
529
+ e[:year] = ($1 + $2[ 0, 4]).to_i
530
+ e[:yday] = $2[ 4, 3].to_i
531
+ end
532
+ end
533
+ if $3
534
+ if $4
535
+ case $3.size
536
+ when 2, 4, 6
537
+ e[:sec] = $3[-2, 2].to_i
538
+ e[:min] = $3[-4, 2].to_i if $3.size >= 4
539
+ e[:hour] = $3[-6, 2].to_i if $3.size >= 6
540
+ end
541
+ else
542
+ case $3.size
543
+ when 2, 4, 6
544
+ e[:hour] = $3[ 0, 2].to_i
545
+ e[:min] = $3[ 2, 2].to_i if $3.size >= 4
546
+ e[:sec] = $3[ 4, 2].to_i if $3.size >= 6
547
+ end
548
+ end
549
+ end
550
+ if $4
551
+ e[:sec_fraction] = $4.to_i/10.0**$4.size
552
+ end
553
+ if $5
554
+ e[:zone] = $5
555
+ if e[:zone][0,1] == '['
556
+ o, n, = e[:zone][1..-2].split(':')
557
+ e[:zone] = n || o
558
+ if /\A\d/ =~ o
559
+ o = format('+%s', o)
560
+ end
561
+ e[:offset] = zone_to_diff(o)
562
+ end
563
+ end
564
+ true
565
+ end
566
+ end
567
+
568
+ private_class_method :_parse_day, :_parse_time,
569
+ :_parse_eu, :_parse_us, :_parse_iso, :_parse_iso2,
570
+ :_parse_jis, :_parse_vms, :_parse_sla, :_parse_dot,
571
+ :_parse_year, :_parse_mon, :_parse_mday, :_parse_ddd
572
+
573
+ # call-seq:
574
+ # _parse(str, comp=true) -> Hash
575
+ #
576
+ # Attempt to parse the string by trying a wide variety of
577
+ # date formats sequentially (unless a match is found by
578
+ # the fast Ragel-based parser). The +comp+ argument
579
+ # determines whether to convert 2-digit years to 4-digit
580
+ # years. If the +str+ is not in a supported format,
581
+ # an empty hash will be returned.
582
+ #
583
+ # This method searches for a match anywhere in the string,
584
+ # unlike most of the other ruby 1.9-only parsing methods
585
+ # which require that an exact match for the entire string.
586
+ def self._parse(str, comp=true)
587
+ if v = _ragel_parse(str)
588
+ return v
589
+ end
590
+
591
+ str = str.dup
592
+
593
+ e = {:_ => {:comp => comp}}
594
+ str.gsub!(/[^-+',.\/:@[:alnum:]\[\]]+/, ' ')
595
+
596
+ _parse_time(str, e)
597
+ _parse_day(str, e)
598
+
599
+ _parse_eu(str, e) ||
600
+ _parse_us(str, e) ||
601
+ _parse_iso(str, e) ||
602
+ _parse_jis(str, e) ||
603
+ _parse_vms(str, e) ||
604
+ _parse_sla(str, e) ||
605
+ _parse_dot(str, e) ||
606
+ _parse_iso2(str, e) ||
607
+ _parse_year(str, e) ||
608
+ _parse_mon(str, e) ||
609
+ _parse_mday(str, e) ||
610
+ _parse_ddd(str, e)
611
+
612
+ if str.sub!(/\b(bc\b|bce\b|b\.c\.|b\.c\.e\.)/i, ' ')
613
+ if e[:year]
614
+ e[:year] = -e[:year] + 1
615
+ end
616
+ end
617
+
618
+ if str.sub!(/\A\s*(\d{1,2})\s*\z/, ' ')
619
+ if e[:hour] && !e[:mday]
620
+ v = $1.to_i
621
+ if (1..31) === v
622
+ e[:mday] = v
623
+ end
624
+ end
625
+ if e[:mday] && !e[:hour]
626
+ v = $1.to_i
627
+ if (0..24) === v
628
+ e[:hour] = v
629
+ end
630
+ end
631
+ end
632
+
633
+ if e[:_][:comp]
634
+ if e[:cwyear]
635
+ if e[:cwyear] >= 0 && e[:cwyear] <= 99
636
+ e[:cwyear] += if e[:cwyear] >= 69
637
+ then 1900 else 2000 end
638
+ end
639
+ end
640
+ if e[:year]
641
+ if e[:year] >= 0 && e[:year] <= 99
642
+ e[:year] += if e[:year] >= 69
643
+ then 1900 else 2000 end
644
+ end
645
+ end
646
+ end
647
+
648
+ e[:offset] ||= zone_to_diff(e[:zone]) if e[:zone]
649
+
650
+ e.delete(:_)
651
+ e
652
+ end
653
+
654
+ if RUBY_VERSION >= '1.9.0'
655
+ # call-seq:
656
+ # [ruby 1-9 only] <br />
657
+ # _iso8601(str) -> Hash or nil
658
+ #
659
+ # Attempt to parse string using a wide variety of
660
+ # ISO 8601 based formats, including the civil,
661
+ # commercial, and ordinal formats.
662
+ # Returns a +Hash+ of values if the string was parsed, and +nil+ if not.
663
+ def self._iso8601(str)
664
+ if /\A\s*(([-+]?\d{2,}|-)-\d{2}-\d{2}|
665
+ ([-+]?\d{2,})?-\d{3}|
666
+ (\d{2}|\d{4})?-w\d{2}-\d|
667
+ -w-\d)
668
+ (t
669
+ \d{2}:\d{2}(:\d{2}([,.]\d+)?)?
670
+ (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/ix =~ str
671
+ _parse(str)
672
+ elsif /\A\s*(([-+]?(\d{2}|\d{4})|--)\d{2}\d{2}|
673
+ ([-+]?(\d{2}|\d{4}))?\d{3}|-\d{3}|
674
+ (\d{2}|\d{4})?w\d{2}\d)
675
+ (t?
676
+ \d{2}\d{2}(\d{2}([,.]\d+)?)?
677
+ (z|[-+]\d{2}(\d{2})?)?)?\s*\z/ix =~ str
678
+ _parse(str)
679
+ elsif /\A\s*(\d{2}:\d{2}(:\d{2}([,.]\d+)?)?
680
+ (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/ix =~ str
681
+ _parse(str)
682
+ elsif /\A\s*(\d{2}\d{2}(\d{2}([,.]\d+)?)?
683
+ (z|[-+]\d{2}(\d{2})?)?)?\s*\z/ix =~ str
684
+ _parse(str)
685
+ end
686
+ end
687
+
688
+ # call-seq:
689
+ # [ruby 1-9 only] <br />
690
+ # _rfc3339(str) -> Hash or nil
691
+ #
692
+ # Attempt to parse string using the RFC 3339 format,
693
+ # which is the same as the ISO8601 civil format requiring
694
+ # a time and time zone.
695
+ # Returns a +Hash+ of values if the string was parsed, and +nil+ if not.
696
+ def self._rfc3339(str)
697
+ if /\A\s*-?\d{4}-\d{2}-\d{2} # allow minus, anyway
698
+ (t|\s)
699
+ \d{2}:\d{2}:\d{2}(\.\d+)?
700
+ (z|[-+]\d{2}:\d{2})\s*\z/ix =~ str
701
+ _parse(str)
702
+ end
703
+ end
704
+
705
+ # call-seq:
706
+ # [ruby 1-9 only] <br />
707
+ # _xmlschema(str) -> Hash or nil
708
+ #
709
+ # Attempt to parse string using the XML schema format,
710
+ # which is similar to the ISO8601 civil format, with
711
+ # most parts being optional.
712
+ # Returns a +Hash+ of values if the string was parsed, and +nil+ if not.
713
+ def self._xmlschema(str)
714
+ if /\A\s*(-?\d{4,})(?:-(\d{2})(?:-(\d{2}))?)?
715
+ (?:t
716
+ (\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?
717
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
718
+ e = {}
719
+ e[:year] = $1.to_i
720
+ e[:mon] = $2.to_i if $2
721
+ e[:mday] = $3.to_i if $3
722
+ e[:hour] = $4.to_i if $4
723
+ e[:min] = $5.to_i if $5
724
+ e[:sec] = $6.to_i if $6
725
+ e[:sec_fraction] = $7.to_i/10.0**$7.size if $7
726
+ if $8
727
+ e[:zone] = $8
728
+ e[:offset] = zone_to_diff($8)
729
+ end
730
+ e
731
+ elsif /\A\s*(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?
732
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
733
+ e = {}
734
+ e[:hour] = $1.to_i if $1
735
+ e[:min] = $2.to_i if $2
736
+ e[:sec] = $3.to_i if $3
737
+ e[:sec_fraction] = $4.to_i/10.0**$4.size if $4
738
+ if $5
739
+ e[:zone] = $5
740
+ e[:offset] = zone_to_diff($5)
741
+ end
742
+ e
743
+ elsif /\A\s*(?:--(\d{2})(?:-(\d{2}))?|---(\d{2}))
744
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
745
+ e = {}
746
+ e[:mon] = $1.to_i if $1
747
+ e[:mday] = $2.to_i if $2
748
+ e[:mday] = $3.to_i if $3
749
+ if $4
750
+ e[:zone] = $4
751
+ e[:offset] = zone_to_diff($4)
752
+ end
753
+ e
754
+ end
755
+ end
756
+
757
+ # call-seq:
758
+ # [ruby 1-9 only] <br />
759
+ # _rfc2822(str) -> Hash or nil
760
+ #
761
+ # Attempt to parse string using the RFC 2822 format used
762
+ # in email. It's similar to the preferred HTTP format except specific
763
+ # offsets can be used.
764
+ # Returns a +Hash+ of values if the string was parsed, and +nil+ if not.
765
+ def self._rfc2822(str)
766
+ if /\A\s*(?:(?:#{Format::ABBR_DAYS.keys.join('|')})\s*,\s+)?
767
+ \d{1,2}\s+
768
+ (?:#{Format::ABBR_MONTHS.keys.join('|')})\s+
769
+ -?(\d{2,})\s+ # allow minus, anyway
770
+ \d{2}:\d{2}(:\d{2})?\s*
771
+ (?:[-+]\d{4}|ut|gmt|e[sd]t|c[sd]t|m[sd]t|p[sd]t|[a-ik-z])\s*\z/iox =~ str
772
+ e = _parse(str, false)
773
+ if $1.size < 4
774
+ if e[:year] < 50
775
+ e[:year] += 2000
776
+ elsif e[:year] < 1000
777
+ e[:year] += 1900
778
+ end
779
+ end
780
+ e
781
+ end
782
+ end
783
+
784
+ class << self;
785
+ # [ruby 1.9 only]
786
+ alias_method :_rfc822, :_rfc2822
787
+ end
788
+
789
+ # call-seq:
790
+ # [ruby 1-9 only] <br />
791
+ # _httpdate(str) -> Hash or nil
792
+ #
793
+ # Attempt to parse string using the 3 HTTP formats specified
794
+ # in RFC 2616.
795
+ # Returns a +Hash+ of values if the string was parsed, and +nil+ if not.
796
+ def self._httpdate(str)
797
+ if /\A\s*(#{Format::ABBR_DAYS.keys.join('|')})\s*,\s+
798
+ \d{2}\s+
799
+ (#{Format::ABBR_MONTHS.keys.join('|')})\s+
800
+ -?\d{4}\s+ # allow minus, anyway
801
+ \d{2}:\d{2}:\d{2}\s+
802
+ gmt\s*\z/iox =~ str
803
+ _rfc2822(str)
804
+ elsif /\A\s*(#{Format::DAYS.keys.join('|')})\s*,\s+
805
+ \d{2}\s*-\s*
806
+ (#{Format::ABBR_MONTHS.keys.join('|')})\s*-\s*
807
+ \d{2}\s+
808
+ \d{2}:\d{2}:\d{2}\s+
809
+ gmt\s*\z/iox =~ str
810
+ _parse(str)
811
+ elsif /\A\s*(#{Format::ABBR_DAYS.keys.join('|')})\s+
812
+ (#{Format::ABBR_MONTHS.keys.join('|')})\s+
813
+ \d{1,2}\s+
814
+ \d{2}:\d{2}:\d{2}\s+
815
+ \d{4}\s*\z/iox =~ str
816
+ _parse(str)
817
+ end
818
+ end
819
+
820
+ # call-seq:
821
+ # [ruby 1-9 only] <br />
822
+ # _jisx0301(str) -> Hash or nil
823
+ #
824
+ # Attempt to parse string using the JIS X 0301 date format or
825
+ # ISO8601 format.
826
+ # Returns a +Hash+ of values if the string was parsed, and +nil+ if not.
827
+ def self._jisx0301(str)
828
+ if /\A\s*[mtsh]?\d{2}\.\d{2}\.\d{2}
829
+ (t
830
+ (\d{2}:\d{2}(:\d{2}([,.]\d*)?)?
831
+ (z|[-+]\d{2}(:?\d{2})?)?)?)?\s*\z/ix =~ str
832
+ if /\A\s*\d/ =~ str
833
+ _parse(str.sub(/\A\s*(\d)/, 'h\1'))
834
+ else
835
+ _parse(str)
836
+ end
837
+ else
838
+ _iso8601(str)
839
+ end
840
+ end
841
+ end
842
+ end