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.
- data/CHANGELOG +3 -0
- data/LICENSE +19 -0
- data/README.rdoc +314 -0
- data/Rakefile +135 -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
|