home_run 0.9.0-x86-mswin32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. data/CHANGELOG +3 -0
  2. data/LICENSE +19 -0
  3. data/README.rdoc +314 -0
  4. data/Rakefile +135 -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