home_run 0.9.0-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/LICENSE +19 -0
- data/README.rdoc +314 -0
- data/Rakefile +136 -0
- data/bench/cpu_bench.rb +279 -0
- data/bench/dt_garbage_bench.rb +11 -0
- data/bench/dt_mem_bench.rb +14 -0
- data/bench/garbage_bench.rb +11 -0
- data/bench/mem_bench.rb +14 -0
- data/bin/home_run +91 -0
- data/default.mspec +12 -0
- data/ext/1.8/date_ext.so +0 -0
- data/ext/1.9/date_ext.so +0 -0
- data/ext/date.rb +7 -0
- data/ext/date/format.rb +842 -0
- data/ext/date_ext.c +4548 -0
- data/ext/date_parser.c +367 -0
- data/ext/date_parser.rl +134 -0
- data/ext/datetime.c +2804 -0
- data/ext/extconf.rb +6 -0
- data/spec/date/accessor_spec.rb +176 -0
- data/spec/date/add_month_spec.rb +26 -0
- data/spec/date/add_spec.rb +23 -0
- data/spec/date/boat_spec.rb +38 -0
- data/spec/date/civil_spec.rb +147 -0
- data/spec/date/commercial_spec.rb +153 -0
- data/spec/date/constants_spec.rb +44 -0
- data/spec/date/conversions_spec.rb +246 -0
- data/spec/date/day_spec.rb +73 -0
- data/spec/date/downto_spec.rb +17 -0
- data/spec/date/eql_spec.rb +16 -0
- data/spec/date/format_spec.rb +52 -0
- data/spec/date/gregorian_spec.rb +52 -0
- data/spec/date/hash_spec.rb +11 -0
- data/spec/date/julian_spec.rb +129 -0
- data/spec/date/leap_spec.rb +19 -0
- data/spec/date/minus_month_spec.rb +25 -0
- data/spec/date/minus_spec.rb +51 -0
- data/spec/date/next_prev_spec.rb +108 -0
- data/spec/date/ordinal_spec.rb +83 -0
- data/spec/date/parse_spec.rb +442 -0
- data/spec/date/parsing_spec.rb +77 -0
- data/spec/date/relationship_spec.rb +28 -0
- data/spec/date/step_spec.rb +109 -0
- data/spec/date/strftime_spec.rb +223 -0
- data/spec/date/strptime_spec.rb +201 -0
- data/spec/date/succ_spec.rb +20 -0
- data/spec/date/today_spec.rb +15 -0
- data/spec/date/upto_spec.rb +17 -0
- data/spec/datetime/accessor_spec.rb +218 -0
- data/spec/datetime/add_month_spec.rb +26 -0
- data/spec/datetime/add_spec.rb +36 -0
- data/spec/datetime/boat_spec.rb +43 -0
- data/spec/datetime/constructor_spec.rb +142 -0
- data/spec/datetime/conversions_spec.rb +54 -0
- data/spec/datetime/day_spec.rb +73 -0
- data/spec/datetime/downto_spec.rb +39 -0
- data/spec/datetime/eql_spec.rb +17 -0
- data/spec/datetime/format_spec.rb +59 -0
- data/spec/datetime/hash_spec.rb +11 -0
- data/spec/datetime/leap_spec.rb +19 -0
- data/spec/datetime/minus_month_spec.rb +25 -0
- data/spec/datetime/minus_spec.rb +77 -0
- data/spec/datetime/next_prev_spec.rb +138 -0
- data/spec/datetime/now_spec.rb +18 -0
- data/spec/datetime/parse_spec.rb +390 -0
- data/spec/datetime/parsing_spec.rb +77 -0
- data/spec/datetime/relationship_spec.rb +28 -0
- data/spec/datetime/step_spec.rb +155 -0
- data/spec/datetime/strftime_spec.rb +118 -0
- data/spec/datetime/strptime_spec.rb +117 -0
- data/spec/datetime/succ_spec.rb +24 -0
- data/spec/datetime/upto_spec.rb +39 -0
- data/spec/spec_helper.rb +59 -0
- metadata +154 -0
data/ext/date/format.rb
ADDED
@@ -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
|