motion-date 1.0

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.
@@ -0,0 +1,1316 @@
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
+ class Date
5
+
6
+ module Format # :nodoc:
7
+
8
+ MONTHS = {
9
+ 'january' => 1, 'february' => 2, 'march' => 3, 'april' => 4,
10
+ 'may' => 5, 'june' => 6, 'july' => 7, 'august' => 8,
11
+ 'september'=> 9, 'october' =>10, 'november' =>11, 'december' =>12
12
+ }
13
+
14
+ DAYS = {
15
+ 'sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday'=> 3,
16
+ 'thursday' => 4, 'friday' => 5, 'saturday' => 6
17
+ }
18
+
19
+ ABBR_MONTHS = {
20
+ 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4,
21
+ 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8,
22
+ 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12
23
+ }
24
+
25
+ ABBR_DAYS = {
26
+ 'sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3,
27
+ 'thu' => 4, 'fri' => 5, 'sat' => 6
28
+ }
29
+
30
+ ZONES = {
31
+ 'ut' => 0*3600, 'gmt' => 0*3600, 'est' => -5*3600, 'edt' => -4*3600,
32
+ 'cst' => -6*3600, 'cdt' => -5*3600, 'mst' => -7*3600, 'mdt' => -6*3600,
33
+ 'pst' => -8*3600, 'pdt' => -7*3600,
34
+ 'a' => 1*3600, 'b' => 2*3600, 'c' => 3*3600, 'd' => 4*3600,
35
+ 'e' => 5*3600, 'f' => 6*3600, 'g' => 7*3600, 'h' => 8*3600,
36
+ 'i' => 9*3600, 'k' => 10*3600, 'l' => 11*3600, 'm' => 12*3600,
37
+ 'n' => -1*3600, 'o' => -2*3600, 'p' => -3*3600, 'q' => -4*3600,
38
+ 'r' => -5*3600, 's' => -6*3600, 't' => -7*3600, 'u' => -8*3600,
39
+ 'v' => -9*3600, 'w' =>-10*3600, 'x' =>-11*3600, 'y' =>-12*3600,
40
+ 'z' => 0*3600,
41
+
42
+ 'utc' => 0*3600, 'wet' => 0*3600,
43
+ 'at' => -2*3600, 'brst'=> -2*3600, 'ndt' => -(2*3600+1800),
44
+ 'art' => -3*3600, 'adt' => -3*3600, 'brt' => -3*3600, 'clst'=> -3*3600,
45
+ 'nst' => -(3*3600+1800),
46
+ 'ast' => -4*3600, 'clt' => -4*3600,
47
+ 'akdt'=> -8*3600, 'ydt' => -8*3600,
48
+ 'akst'=> -9*3600, 'hadt'=> -9*3600, 'hdt' => -9*3600, 'yst' => -9*3600,
49
+ 'ahst'=>-10*3600, 'cat' =>-10*3600, 'hast'=>-10*3600, 'hst' =>-10*3600,
50
+ 'nt' =>-11*3600,
51
+ 'idlw'=>-12*3600,
52
+ 'bst' => 1*3600, 'cet' => 1*3600, 'fwt' => 1*3600, 'met' => 1*3600,
53
+ 'mewt'=> 1*3600, 'mez' => 1*3600, 'swt' => 1*3600, 'wat' => 1*3600,
54
+ 'west'=> 1*3600,
55
+ 'cest'=> 2*3600, 'eet' => 2*3600, 'fst' => 2*3600, 'mest'=> 2*3600,
56
+ 'mesz'=> 2*3600, 'sast'=> 2*3600, 'sst' => 2*3600,
57
+ 'bt' => 3*3600, 'eat' => 3*3600, 'eest'=> 3*3600, 'msk' => 3*3600,
58
+ 'msd' => 4*3600, 'zp4' => 4*3600,
59
+ 'zp5' => 5*3600, 'ist' => (5*3600+1800),
60
+ 'zp6' => 6*3600,
61
+ 'wast'=> 7*3600,
62
+ 'cct' => 8*3600, 'sgt' => 8*3600, 'wadt'=> 8*3600,
63
+ 'jst' => 9*3600, 'kst' => 9*3600,
64
+ 'east'=> 10*3600, 'gst' => 10*3600,
65
+ 'eadt'=> 11*3600,
66
+ 'idle'=> 12*3600, 'nzst'=> 12*3600, 'nzt' => 12*3600,
67
+ 'nzdt'=> 13*3600,
68
+
69
+ 'afghanistan' => 16200, 'alaskan' => -32400,
70
+ 'arab' => 10800, 'arabian' => 14400,
71
+ 'arabic' => 10800, 'atlantic' => -14400,
72
+ 'aus central' => 34200, 'aus eastern' => 36000,
73
+ 'azores' => -3600, 'canada central' => -21600,
74
+ 'cape verde' => -3600, 'caucasus' => 14400,
75
+ 'cen. australia' => 34200, 'central america' => -21600,
76
+ 'central asia' => 21600, 'central europe' => 3600,
77
+ 'central european' => 3600, 'central pacific' => 39600,
78
+ 'central' => -21600, 'china' => 28800,
79
+ 'dateline' => -43200, 'e. africa' => 10800,
80
+ 'e. australia' => 36000, 'e. europe' => 7200,
81
+ 'e. south america' => -10800, 'eastern' => -18000,
82
+ 'egypt' => 7200, 'ekaterinburg' => 18000,
83
+ 'fiji' => 43200, 'fle' => 7200,
84
+ 'greenland' => -10800, 'greenwich' => 0,
85
+ 'gtb' => 7200, 'hawaiian' => -36000,
86
+ 'india' => 19800, 'iran' => 12600,
87
+ 'jerusalem' => 7200, 'korea' => 32400,
88
+ 'mexico' => -21600, 'mid-atlantic' => -7200,
89
+ 'mountain' => -25200, 'myanmar' => 23400,
90
+ 'n. central asia' => 21600, 'nepal' => 20700,
91
+ 'new zealand' => 43200, 'newfoundland' => -12600,
92
+ 'north asia east' => 28800, 'north asia' => 25200,
93
+ 'pacific sa' => -14400, 'pacific' => -28800,
94
+ 'romance' => 3600, 'russian' => 10800,
95
+ 'sa eastern' => -10800, 'sa pacific' => -18000,
96
+ 'sa western' => -14400, 'samoa' => -39600,
97
+ 'se asia' => 25200, 'malay peninsula' => 28800,
98
+ 'south africa' => 7200, 'sri lanka' => 21600,
99
+ 'taipei' => 28800, 'tasmania' => 36000,
100
+ 'tokyo' => 32400, 'tonga' => 46800,
101
+ 'us eastern' => -18000, 'us mountain' => -25200,
102
+ 'vladivostok' => 36000, 'w. australia' => 28800,
103
+ 'w. central africa' => 3600, 'w. europe' => 3600,
104
+ 'west asia' => 18000, 'west pacific' => 36000,
105
+ 'yakutsk' => 32400
106
+ }
107
+
108
+ [MONTHS, DAYS, ABBR_MONTHS, ABBR_DAYS, ZONES].each do |x|
109
+ x.freeze
110
+ end
111
+
112
+ class Bag # :nodoc:
113
+
114
+ def initialize
115
+ @elem = {}
116
+ end
117
+
118
+ def method_missing(t, *args, &block)
119
+ t = t.to_s
120
+ set = t.chomp!('=')
121
+ t = t.intern
122
+ if set
123
+ @elem[t] = args[0]
124
+ else
125
+ @elem[t]
126
+ end
127
+ end
128
+
129
+ def to_hash
130
+ @elem.reject{|k, v| /\A_/ =~ k.to_s || v.nil?}
131
+ end
132
+
133
+ # XXX this is required in MacRuby otherwise it conflicts with NSObject#zone
134
+ def zone; @elem[:zone]; end
135
+ def zone=(z); @elem[:zone]=z; end
136
+ end
137
+
138
+ end
139
+
140
+ def emit(e, f) # :nodoc:
141
+ case e
142
+ when Numeric
143
+ sign = %w(+ + -)[e <=> 0]
144
+ e = e.abs
145
+ end
146
+
147
+ s = e.to_s
148
+
149
+ if f[:s] && f[:p] == '0'
150
+ f[:w] -= 1
151
+ end
152
+
153
+ if f[:s] && f[:p] == "\s"
154
+ s[0,0] = sign
155
+ end
156
+
157
+ if f[:p] != '-'
158
+ s = s.rjust(f[:w], f[:p])
159
+ end
160
+
161
+ if f[:s] && f[:p] != "\s"
162
+ s[0,0] = sign
163
+ end
164
+
165
+ s = s.upcase if f[:u]
166
+ s = s.downcase if f[:d]
167
+ s
168
+ end
169
+
170
+ def emit_w(e, w, f) # :nodoc:
171
+ f[:w] = [f[:w], w].compact.max
172
+ emit(e, f)
173
+ end
174
+
175
+ def emit_n(e, w, f) # :nodoc:
176
+ f[:p] ||= '0'
177
+ emit_w(e, w, f)
178
+ end
179
+
180
+ def emit_sn(e, w, f) # :nodoc:
181
+ if e < 0
182
+ w += 1
183
+ f[:s] = true
184
+ end
185
+ emit_n(e, w, f)
186
+ end
187
+
188
+ def emit_z(e, w, f) # :nodoc:
189
+ w += 1
190
+ f[:s] = true
191
+ emit_n(e, w, f)
192
+ end
193
+
194
+ def emit_a(e, w, f) # :nodoc:
195
+ f[:p] ||= "\s"
196
+ emit_w(e, w, f)
197
+ end
198
+
199
+ def emit_ad(e, w, f) # :nodoc:
200
+ if f[:x]
201
+ f[:u] = true
202
+ f[:d] = false
203
+ end
204
+ emit_a(e, w, f)
205
+ end
206
+
207
+ def emit_au(e, w, f) # :nodoc:
208
+ if f[:x]
209
+ f[:u] = false
210
+ f[:d] = true
211
+ end
212
+ emit_a(e, w, f)
213
+ end
214
+
215
+ private :emit, :emit_w, :emit_n, :emit_sn, :emit_z,
216
+ :emit_a, :emit_ad, :emit_au
217
+
218
+ def strftime(fmt='%F')
219
+ fmt.gsub(/%([-_0^#]+)?(\d+)?([EO]?(?::{1,3}z|.))/m) do
220
+ f = {}
221
+ m = $&
222
+ s, w, c = $1, $2, $3
223
+ if s
224
+ s.scan(/./) do |k|
225
+ case k
226
+ when '-'; f[:p] = '-'
227
+ when '_'; f[:p] = "\s"
228
+ when '0'; f[:p] = '0'
229
+ when '^'; f[:u] = true
230
+ when '#'; f[:x] = true
231
+ end
232
+ end
233
+ end
234
+ if w
235
+ f[:w] = w.to_i
236
+ end
237
+ case c
238
+ when 'A'; emit_ad(DAYNAMES[wday], 0, f)
239
+ when 'a'; emit_ad(ABBR_DAYNAMES[wday], 0, f)
240
+ when 'B'; emit_ad(MONTHNAMES[mon], 0, f)
241
+ when 'b'; emit_ad(ABBR_MONTHNAMES[mon], 0, f)
242
+ when 'C', 'EC'; emit_sn((year / 100).floor, 2, f)
243
+ when 'c', 'Ec'; emit_a(strftime('%a %b %e %H:%M:%S %Y'), 0, f)
244
+ when 'D'; emit_a(strftime('%m/%d/%y'), 0, f)
245
+ when 'd', 'Od'; emit_n(mday, 2, f)
246
+ when 'e', 'Oe'; emit_a(mday, 2, f)
247
+ when 'F'
248
+ if m == '%F'
249
+ format('%.4d-%02d-%02d', year, mon, mday) # 4p
250
+ else
251
+ emit_a(strftime('%Y-%m-%d'), 0, f)
252
+ end
253
+ when 'G'; emit_sn(cwyear, 4, f)
254
+ when 'g'; emit_n(cwyear % 100, 2, f)
255
+ when 'H', 'OH'; emit_n(hour, 2, f)
256
+ when 'h'; emit_ad(strftime('%b'), 0, f)
257
+ when 'I', 'OI'; emit_n((hour % 12).nonzero? || 12, 2, f)
258
+ when 'j'; emit_n(yday, 3, f)
259
+ when 'k'; emit_a(hour, 2, f)
260
+ when 'L'
261
+ f[:p] = nil
262
+ w = f[:w] || 3
263
+ u = 10**w
264
+ emit_n((sec_fraction * u).floor, w, f)
265
+ when 'l'; emit_a((hour % 12).nonzero? || 12, 2, f)
266
+ when 'M', 'OM'; emit_n(min, 2, f)
267
+ when 'm', 'Om'; emit_n(mon, 2, f)
268
+ when 'N'
269
+ f[:p] = nil
270
+ w = f[:w] || 9
271
+ u = 10**w
272
+ emit_n((sec_fraction * u).floor, w, f)
273
+ when 'n'; emit_a("\n", 0, f)
274
+ when 'P'; emit_ad(strftime('%p').downcase, 0, f)
275
+ when 'p'; emit_au(if hour < 12 then 'AM' else 'PM' end, 0, f)
276
+ when 'Q'
277
+ s = ((ajd - UNIX_EPOCH_IN_AJD) / MILLISECONDS_IN_DAY).round
278
+ emit_sn(s, 1, f)
279
+ when 'R'; emit_a(strftime('%H:%M'), 0, f)
280
+ when 'r'; emit_a(strftime('%I:%M:%S %p'), 0, f)
281
+ when 'S', 'OS'; emit_n(sec, 2, f)
282
+ when 's'
283
+ s = ((ajd - UNIX_EPOCH_IN_AJD) / SECONDS_IN_DAY).round
284
+ emit_sn(s, 1, f)
285
+ when 'T'
286
+ if m == '%T'
287
+ format('%02d:%02d:%02d', hour, min, sec) # 4p
288
+ else
289
+ emit_a(strftime('%H:%M:%S'), 0, f)
290
+ end
291
+ when 't'; emit_a("\t", 0, f)
292
+ when 'U', 'W', 'OU', 'OW'
293
+ emit_n(if c[-1,1] == 'U' then wnum0 else wnum1 end, 2, f)
294
+ when 'u', 'Ou'; emit_n(cwday, 1, f)
295
+ when 'V', 'OV'; emit_n(cweek, 2, f)
296
+ when 'v'; emit_a(strftime('%e-%b-%Y'), 0, f)
297
+ when 'w', 'Ow'; emit_n(wday, 1, f)
298
+ when 'X', 'EX'; emit_a(strftime('%H:%M:%S'), 0, f)
299
+ when 'x', 'Ex'; emit_a(strftime('%m/%d/%y'), 0, f)
300
+ when 'Y', 'EY'; emit_sn(year, 4, f)
301
+ when 'y', 'Ey', 'Oy'; emit_n(year % 100, 2, f)
302
+ when 'Z'; emit_au(strftime('%:z'), 0, f)
303
+ when /\A(:{0,3})z/
304
+ t = $1.size
305
+ sign = if offset < 0 then -1 else +1 end
306
+ fr = offset.abs
307
+ ss = fr.div(SECONDS_IN_DAY) # 4p
308
+ hh, ss = ss.divmod(3600)
309
+ mm, ss = ss.divmod(60)
310
+ if t == 3
311
+ if ss.nonzero? then t = 2
312
+ elsif mm.nonzero? then t = 1
313
+ else t = -1
314
+ end
315
+ end
316
+ case t
317
+ when -1
318
+ tail = []
319
+ sep = ''
320
+ when 0
321
+ f[:w] -= 2 if f[:w]
322
+ tail = ['%02d' % mm]
323
+ sep = ''
324
+ when 1
325
+ f[:w] -= 3 if f[:w]
326
+ tail = ['%02d' % mm]
327
+ sep = ':'
328
+ when 2
329
+ f[:w] -= 6 if f[:w]
330
+ tail = ['%02d' % mm, '%02d' % ss]
331
+ sep = ':'
332
+ end
333
+ ([emit_z(sign * hh, 2, f)] + tail).join(sep)
334
+ when '%'; emit_a('%', 0, f)
335
+ when '+'; emit_a(strftime('%a %b %e %H:%M:%S %Z %Y'), 0, f)
336
+ else
337
+ m
338
+ end
339
+ end
340
+ end
341
+
342
+ # alias_method :format, :strftime
343
+
344
+ def asctime() strftime('%c') end
345
+
346
+ alias_method :ctime, :asctime
347
+
348
+ def iso8601() strftime('%F') end
349
+
350
+ def rfc3339() iso8601 end
351
+
352
+ def xmlschema() iso8601 end # :nodoc:
353
+
354
+ def rfc2822() strftime('%a, %-d %b %Y %T %z') end
355
+
356
+ alias_method :rfc822, :rfc2822
357
+
358
+ def httpdate() new_offset(0).strftime('%a, %d %b %Y %T GMT') end # :nodoc:
359
+
360
+ def jisx0301
361
+ if jd < 2405160
362
+ iso8601
363
+ else
364
+ case jd
365
+ when 2405160...2419614
366
+ g = 'M%02d' % (year - 1867)
367
+ when 2419614...2424875
368
+ g = 'T%02d' % (year - 1911)
369
+ when 2424875...2447535
370
+ g = 'S%02d' % (year - 1925)
371
+ else
372
+ g = 'H%02d' % (year - 1988)
373
+ end
374
+ g + strftime('.%m.%d')
375
+ end
376
+ end
377
+
378
+ =begin
379
+ def beat(n=0)
380
+ i, f = (new_offset(HOURS_IN_DAY).day_fraction * 1000).divmod(1)
381
+ ('@%03d' % i) +
382
+ if n < 1
383
+ ''
384
+ else
385
+ '.%0*d' % [n, (f / Rational(1, 10**n)).round]
386
+ end
387
+ end
388
+ =end
389
+
390
+ def self.num_pattern? (s) # :nodoc:
391
+ /\A%[EO]?[CDdeFGgHIjkLlMmNQRrSsTUuVvWwXxYy\d]/ =~ s || /\A\d/ =~ s
392
+ end
393
+
394
+ private_class_method :num_pattern?
395
+
396
+ def self._strptime_i(str, fmt, e) # :nodoc:
397
+ fmt.scan(/%([EO]?(?::{1,3}z|.))|(.)/m) do |s, c|
398
+ a = $&
399
+ if s
400
+ case s
401
+ when 'A', 'a'
402
+ return unless str.sub!(/\A(#{Format::DAYS.keys.join('|')})/io, '') ||
403
+ str.sub!(/\A(#{Format::ABBR_DAYS.keys.join('|')})/io, '')
404
+ val = Format::DAYS[$1.downcase] || Format::ABBR_DAYS[$1.downcase]
405
+ return unless val
406
+ e.wday = val
407
+ when 'B', 'b', 'h'
408
+ return unless str.sub!(/\A(#{Format::MONTHS.keys.join('|')})/io, '') ||
409
+ str.sub!(/\A(#{Format::ABBR_MONTHS.keys.join('|')})/io, '')
410
+ val = Format::MONTHS[$1.downcase] || Format::ABBR_MONTHS[$1.downcase]
411
+ return unless val
412
+ e.mon = val
413
+ when 'C', 'EC'
414
+ return unless str.sub!(if num_pattern?($')
415
+ then /\A([-+]?\d{1,2})/
416
+ else /\A([-+]?\d{1,})/
417
+ end, '')
418
+ val = $1.to_i
419
+ e._cent = val
420
+ when 'c', 'Ec'
421
+ return unless _strptime_i(str, '%a %b %e %H:%M:%S %Y', e)
422
+ when 'D'
423
+ return unless _strptime_i(str, '%m/%d/%y', e)
424
+ when 'd', 'e', 'Od', 'Oe'
425
+ return unless str.sub!(/\A( \d|\d{1,2})/, '')
426
+ val = $1.to_i
427
+ return unless (1..31) === val
428
+ e.mday = val
429
+ when 'F'
430
+ return unless _strptime_i(str, '%Y-%m-%d', e)
431
+ when 'G'
432
+ return unless str.sub!(if num_pattern?($')
433
+ then /\A([-+]?\d{1,4})/
434
+ else /\A([-+]?\d{1,})/
435
+ end, '')
436
+ val = $1.to_i
437
+ e.cwyear = val
438
+ when 'g'
439
+ return unless str.sub!(/\A(\d{1,2})/, '')
440
+ val = $1.to_i
441
+ return unless (0..99) === val
442
+ e.cwyear = val
443
+ e._cent ||= if val >= 69 then 19 else 20 end
444
+ when 'H', 'k', 'OH'
445
+ return unless str.sub!(/\A( \d|\d{1,2})/, '')
446
+ val = $1.to_i
447
+ return unless (0..24) === val
448
+ e.hour = val
449
+ when 'I', 'l', 'OI'
450
+ return unless str.sub!(/\A( \d|\d{1,2})/, '')
451
+ val = $1.to_i
452
+ return unless (1..12) === val
453
+ e.hour = val
454
+ when 'j'
455
+ return unless str.sub!(/\A(\d{1,3})/, '')
456
+ val = $1.to_i
457
+ return unless (1..366) === val
458
+ e.yday = val
459
+ when 'L'
460
+ return unless str.sub!(if num_pattern?($')
461
+ then /\A([-+]?\d{1,3})/
462
+ else /\A([-+]?\d{1,})/
463
+ end, '')
464
+ # val = Rational($1.to_i, 10**3)
465
+ val = Rational($1.to_i, 10**$1.size)
466
+ e.sec_fraction = val
467
+ when 'M', 'OM'
468
+ return unless str.sub!(/\A(\d{1,2})/, '')
469
+ val = $1.to_i
470
+ return unless (0..59) === val
471
+ e.min = val
472
+ when 'm', 'Om'
473
+ return unless str.sub!(/\A(\d{1,2})/, '')
474
+ val = $1.to_i
475
+ return unless (1..12) === val
476
+ e.mon = val
477
+ when 'N'
478
+ return unless str.sub!(if num_pattern?($')
479
+ then /\A([-+]?\d{1,9})/
480
+ else /\A([-+]?\d{1,})/
481
+ end, '')
482
+ # val = Rational($1.to_i, 10**9)
483
+ val = Rational($1.to_i, 10**$1.size)
484
+ e.sec_fraction = val
485
+ when 'n', 't'
486
+ return unless _strptime_i(str, "\s", e)
487
+ when 'P', 'p'
488
+ return unless str.sub!(/\A([ap])(?:m\b|\.m\.)/i, '')
489
+ e._merid = if $1.downcase == 'a' then 0 else 12 end
490
+ when 'Q'
491
+ return unless str.sub!(/\A(-?\d{1,})/, '')
492
+ val = Rational($1.to_i, 10**3)
493
+ e.seconds = val
494
+ when 'R'
495
+ return unless _strptime_i(str, '%H:%M', e)
496
+ when 'r'
497
+ return unless _strptime_i(str, '%I:%M:%S %p', e)
498
+ when 'S', 'OS'
499
+ return unless str.sub!(/\A(\d{1,2})/, '')
500
+ val = $1.to_i
501
+ return unless (0..60) === val
502
+ e.sec = val
503
+ when 's'
504
+ return unless str.sub!(/\A(-?\d{1,})/, '')
505
+ val = $1.to_i
506
+ e.seconds = val
507
+ when 'T'
508
+ return unless _strptime_i(str, '%H:%M:%S', e)
509
+ when 'U', 'W', 'OU', 'OW'
510
+ return unless str.sub!(/\A(\d{1,2})/, '')
511
+ val = $1.to_i
512
+ return unless (0..53) === val
513
+ e.__send__(if s[-1,1] == 'U' then :wnum0= else :wnum1= end, val)
514
+ when 'u', 'Ou'
515
+ return unless str.sub!(/\A(\d{1})/, '')
516
+ val = $1.to_i
517
+ return unless (1..7) === val
518
+ e.cwday = val
519
+ when 'V', 'OV'
520
+ return unless str.sub!(/\A(\d{1,2})/, '')
521
+ val = $1.to_i
522
+ return unless (1..53) === val
523
+ e.cweek = val
524
+ when 'v'
525
+ return unless _strptime_i(str, '%e-%b-%Y', e)
526
+ when 'w'
527
+ return unless str.sub!(/\A(\d{1})/, '')
528
+ val = $1.to_i
529
+ return unless (0..6) === val
530
+ e.wday = val
531
+ when 'X', 'EX'
532
+ return unless _strptime_i(str, '%H:%M:%S', e)
533
+ when 'x', 'Ex'
534
+ return unless _strptime_i(str, '%m/%d/%y', e)
535
+ when 'Y', 'EY'
536
+ return unless str.sub!(if num_pattern?($')
537
+ then /\A([-+]?\d{1,4})/
538
+ else /\A([-+]?\d{1,})/
539
+ end, '')
540
+ val = $1.to_i
541
+ e.year = val
542
+ when 'y', 'Ey', 'Oy'
543
+ return unless str.sub!(/\A(\d{1,2})/, '')
544
+ val = $1.to_i
545
+ return unless (0..99) === val
546
+ e.year = val
547
+ e._cent ||= if val >= 69 then 19 else 20 end
548
+ when 'Z', /\A:{0,3}z/
549
+ return unless str.sub!(/\A((?:gmt|utc?)?[-+]\d+(?:[,.:]\d+(?::\d+)?)?
550
+ |[[:alpha:].\s]+(?:standard|daylight)\s+time\b
551
+ |[[:alpha:]]+(?:\s+dst)?\b
552
+ )/ix, '')
553
+ val = $1
554
+ e.zone = val
555
+ offset = zone_to_diff(val)
556
+ e.offset = offset
557
+ when '%'
558
+ return unless str.sub!(/\A%/, '')
559
+ when '+'
560
+ return unless _strptime_i(str, '%a %b %e %H:%M:%S %Z %Y', e)
561
+ else
562
+ return unless str.sub!(Regexp.new('\\A' + Regexp.quote(a)), '')
563
+ end
564
+ else
565
+ case c
566
+ when /\A\s/
567
+ str.sub!(/\A\s+/, '')
568
+ else
569
+ return unless str.sub!(Regexp.new('\\A' + Regexp.quote(a)), '')
570
+ end
571
+ end
572
+ end
573
+ end
574
+
575
+ private_class_method :_strptime_i
576
+
577
+ def self._strptime(str, fmt='%F')
578
+ str = str.dup
579
+ e = Format::Bag.new
580
+ return unless _strptime_i(str, fmt, e)
581
+
582
+ if e._cent
583
+ if e.cwyear
584
+ e.cwyear += e._cent * 100
585
+ end
586
+ if e.year
587
+ e. year += e._cent * 100
588
+ end
589
+ end
590
+
591
+ if e._merid
592
+ if e.hour
593
+ e.hour %= 12
594
+ e.hour += e._merid
595
+ end
596
+ end
597
+
598
+ unless str.empty?
599
+ e.leftover = str
600
+ end
601
+
602
+ e.to_hash
603
+ end
604
+
605
+ def self.s3e(e, y, m, d, bc=false)
606
+ unless String === m
607
+ m = m.to_s
608
+ end
609
+
610
+ if y && m && !d
611
+ y, m, d = d, y, m
612
+ end
613
+
614
+ if y == nil
615
+ if d && d.size > 2
616
+ y = d
617
+ d = nil
618
+ end
619
+ if d && d[0,1] == "'"
620
+ y = d
621
+ d = nil
622
+ end
623
+ end
624
+
625
+ if y
626
+ y.scan(/(\d+)(.+)?/)
627
+ if $2
628
+ y, d = d, $1
629
+ end
630
+ end
631
+
632
+ if m
633
+ if m[0,1] == "'" || m.size > 2
634
+ y, m, d = m, d, y # us -> be
635
+ end
636
+ end
637
+
638
+ if d
639
+ if d[0,1] == "'" || d.size > 2
640
+ y, d = d, y
641
+ end
642
+ end
643
+
644
+ if y
645
+ y =~ /([-+])?(\d+)/
646
+ if $1 || $2.size > 2
647
+ c = false
648
+ end
649
+ iy = $&.to_i
650
+ if bc
651
+ iy = -iy + 1
652
+ end
653
+ e.year = iy
654
+ end
655
+
656
+ if m
657
+ m =~ /\d+/
658
+ e.mon = $&.to_i
659
+ end
660
+
661
+ if d
662
+ d =~ /\d+/
663
+ e.mday = $&.to_i
664
+ end
665
+
666
+ if c != nil
667
+ e._comp = c
668
+ end
669
+
670
+ end
671
+
672
+ private_class_method :s3e
673
+
674
+ def self._parse_day(str, e) # :nodoc:
675
+ if str.sub!(/\b(#{Format::ABBR_DAYS.keys.join('|')})[^-\d\s]*/io, ' ')
676
+ e.wday = Format::ABBR_DAYS[$1.downcase]
677
+ true
678
+ =begin
679
+ elsif str.sub!(/\b(?!\dth)(su|mo|tu|we|th|fr|sa)\b/i, ' ')
680
+ e.wday = %w(su mo tu we th fr sa).index($1.downcase)
681
+ true
682
+ =end
683
+ end
684
+ end
685
+
686
+ def self._parse_time(str, e) # :nodoc:
687
+ if str.sub!(
688
+ /(
689
+ (?:
690
+ \d+\s*:\s*\d+
691
+ (?:
692
+ \s*:\s*\d+(?:[,.]\d*)?
693
+ )?
694
+ |
695
+ \d+\s*h(?:\s*\d+m?(?:\s*\d+s?)?)?
696
+ )
697
+ (?:
698
+ \s*
699
+ [ap](?:m\b|\.m\.)
700
+ )?
701
+ |
702
+ \d+\s*[ap](?:m\b|\.m\.)
703
+ )
704
+ (?:
705
+ \s*
706
+ (
707
+ (?:gmt|utc?)?[-+]\d+(?:[,.:]\d+(?::\d+)?)?
708
+ |
709
+ [[:alpha:].\s]+(?:standard|daylight)\stime\b
710
+ |
711
+ [[:alpha:]]+(?:\sdst)?\b
712
+ )
713
+ )?
714
+ /ix,
715
+ ' ')
716
+
717
+ t = $1
718
+ e.zone = $2 if $2
719
+
720
+ t =~ /\A(\d+)h?
721
+ (?:\s*:?\s*(\d+)m?
722
+ (?:
723
+ \s*:?\s*(\d+)(?:[,.](\d+))?s?
724
+ )?
725
+ )?
726
+ (?:\s*([ap])(?:m\b|\.m\.))?/ix
727
+
728
+ e.hour = $1.to_i
729
+ e.min = $2.to_i if $2
730
+ e.sec = $3.to_i if $3
731
+ e.sec_fraction = Rational($4.to_i, 10**$4.size) if $4
732
+
733
+ if $5
734
+ e.hour %= 12
735
+ if $5.downcase == 'p'
736
+ e.hour += 12
737
+ end
738
+ end
739
+ true
740
+ end
741
+ end
742
+
743
+ =begin
744
+ def self._parse_beat(str, e) # :nodoc:
745
+ if str.sub!(/@\s*(\d+)(?:[,.](\d*))?/, ' ')
746
+ beat = Rational($1.to_i)
747
+ beat += Rational($2.to_i, 10**$2.size) if $2
748
+ secs = Rational(beat, 1000)
749
+ h, min, s, fr = self.day_fraction_to_time(secs)
750
+ e.hour = h
751
+ e.min = min
752
+ e.sec = s
753
+ e.sec_fraction = fr * 86400
754
+ e.zone = '+01:00'
755
+ true
756
+ end
757
+ end
758
+ =end
759
+
760
+ def self._parse_eu(str, e) # :nodoc:
761
+ if str.sub!(
762
+ /'?(\d+)[^-\d\s]*
763
+ \s*
764
+ (#{Format::ABBR_MONTHS.keys.join('|')})[^-\d\s']*
765
+ (?:
766
+ \s*
767
+ (c(?:e|\.e\.)|b(?:ce|\.c\.e\.)|a(?:d|\.d\.)|b(?:c|\.c\.))?
768
+ \s*
769
+ ('?-?\d+(?:(?:st|nd|rd|th)\b)?)
770
+ )?
771
+ /iox,
772
+ ' ') # '
773
+ s3e(e, $4, Format::ABBR_MONTHS[$2.downcase], $1,
774
+ $3 && $3[0,1].downcase == 'b')
775
+ true
776
+ end
777
+ end
778
+
779
+ def self._parse_us(str, e) # :nodoc:
780
+ if str.sub!(
781
+ /\b(#{Format::ABBR_MONTHS.keys.join('|')})[^-\d\s']*
782
+ \s*
783
+ ('?\d+)[^-\d\s']*
784
+ (?:
785
+ \s*
786
+ (c(?:e|\.e\.)|b(?:ce|\.c\.e\.)|a(?:d|\.d\.)|b(?:c|\.c\.))?
787
+ \s*
788
+ ('?-?\d+)
789
+ )?
790
+ /iox,
791
+ ' ') # '
792
+ s3e(e, $4, Format::ABBR_MONTHS[$1.downcase], $2,
793
+ $3 && $3[0,1].downcase == 'b')
794
+ true
795
+ end
796
+ end
797
+
798
+ def self._parse_iso(str, e) # :nodoc:
799
+ if str.sub!(/('?[-+]?\d+)-(\d+)-('?-?\d+)/, ' ')
800
+ s3e(e, $1, $2, $3)
801
+ true
802
+ end
803
+ end
804
+
805
+ def self._parse_iso2(str, e) # :nodoc:
806
+ if str.sub!(/\b(\d{2}|\d{4})?-?w(\d{2})(?:-?(\d))?\b/i, ' ')
807
+ e.cwyear = $1.to_i if $1
808
+ e.cweek = $2.to_i
809
+ e.cwday = $3.to_i if $3
810
+ true
811
+ elsif str.sub!(/-w-(\d)\b/i, ' ')
812
+ e.cwday = $1.to_i
813
+ true
814
+ elsif str.sub!(/--(\d{2})?-(\d{2})\b/, ' ')
815
+ e.mon = $1.to_i if $1
816
+ e.mday = $2.to_i
817
+ true
818
+ elsif str.sub!(/--(\d{2})(\d{2})?\b/, ' ')
819
+ e.mon = $1.to_i
820
+ e.mday = $2.to_i if $2
821
+ true
822
+ elsif /[,.](\d{2}|\d{4})-\d{3}\b/ !~ str &&
823
+ str.sub!(/\b(\d{2}|\d{4})-(\d{3})\b/, ' ')
824
+ e.year = $1.to_i
825
+ e.yday = $2.to_i
826
+ true
827
+ elsif /\d-\d{3}\b/ !~ str &&
828
+ str.sub!(/\b-(\d{3})\b/, ' ')
829
+ e.yday = $1.to_i
830
+ true
831
+ end
832
+ end
833
+
834
+ def self._parse_jis(str, e) # :nodoc:
835
+ if str.sub!(/\b([mtsh])(\d+)\.(\d+)\.(\d+)/i, ' ')
836
+ era = { 'm'=>1867,
837
+ 't'=>1911,
838
+ 's'=>1925,
839
+ 'h'=>1988
840
+ }[$1.downcase]
841
+ e.year = $2.to_i + era
842
+ e.mon = $3.to_i
843
+ e.mday = $4.to_i
844
+ true
845
+ end
846
+ end
847
+
848
+ def self._parse_vms(str, e) # :nodoc:
849
+ if str.sub!(/('?-?\d+)-(#{Format::ABBR_MONTHS.keys.join('|')})[^-]*
850
+ -('?-?\d+)/iox, ' ')
851
+ s3e(e, $3, Format::ABBR_MONTHS[$2.downcase], $1)
852
+ true
853
+ elsif str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})[^-]*
854
+ -('?-?\d+)(?:-('?-?\d+))?/iox, ' ')
855
+ s3e(e, $3, Format::ABBR_MONTHS[$1.downcase], $2)
856
+ true
857
+ end
858
+ end
859
+
860
+ def self._parse_sla(str, e) # :nodoc:
861
+ if str.sub!(%r|('?-?\d+)/\s*('?\d+)(?:\D\s*('?-?\d+))?|, ' ') # '
862
+ s3e(e, $1, $2, $3)
863
+ true
864
+ end
865
+ end
866
+
867
+ def self._parse_dot(str, e) # :nodoc:
868
+ if str.sub!(%r|('?-?\d+)\.\s*('?\d+)\.\s*('?-?\d+)|, ' ') # '
869
+ s3e(e, $1, $2, $3)
870
+ true
871
+ end
872
+ end
873
+
874
+ def self._parse_year(str, e) # :nodoc:
875
+ if str.sub!(/'(\d+)\b/, ' ')
876
+ e.year = $1.to_i
877
+ true
878
+ end
879
+ end
880
+
881
+ def self._parse_mon(str, e) # :nodoc:
882
+ if str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})\S*/io, ' ')
883
+ e.mon = Format::ABBR_MONTHS[$1.downcase]
884
+ true
885
+ end
886
+ end
887
+
888
+ def self._parse_mday(str, e) # :nodoc:
889
+ if str.sub!(/(\d+)(st|nd|rd|th)\b/i, ' ')
890
+ e.mday = $1.to_i
891
+ true
892
+ end
893
+ end
894
+
895
+ def self._parse_ddd(str, e) # :nodoc:
896
+ if str.sub!(
897
+ /([-+]?)(\d{2,14})
898
+ (?:
899
+ \s*
900
+ t?
901
+ \s*
902
+ (\d{2,6})?(?:[,.](\d*))?
903
+ )?
904
+ (?:
905
+ \s*
906
+ (
907
+ z\b
908
+ |
909
+ [-+]\d{1,4}\b
910
+ |
911
+ \[[-+]?\d[^\]]*\]
912
+ )
913
+ )?
914
+ /ix,
915
+ ' ')
916
+ case $2.size
917
+ when 2
918
+ if $3.nil? && $4
919
+ e.sec = $2[-2, 2].to_i
920
+ else
921
+ e.mday = $2[ 0, 2].to_i
922
+ end
923
+ when 4
924
+ if $3.nil? && $4
925
+ e.sec = $2[-2, 2].to_i
926
+ e.min = $2[-4, 2].to_i
927
+ else
928
+ e.mon = $2[ 0, 2].to_i
929
+ e.mday = $2[ 2, 2].to_i
930
+ end
931
+ when 6
932
+ if $3.nil? && $4
933
+ e.sec = $2[-2, 2].to_i
934
+ e.min = $2[-4, 2].to_i
935
+ e.hour = $2[-6, 2].to_i
936
+ else
937
+ e.year = ($1 + $2[ 0, 2]).to_i
938
+ e.mon = $2[ 2, 2].to_i
939
+ e.mday = $2[ 4, 2].to_i
940
+ end
941
+ when 8, 10, 12, 14
942
+ if $3.nil? && $4
943
+ e.sec = $2[-2, 2].to_i
944
+ e.min = $2[-4, 2].to_i
945
+ e.hour = $2[-6, 2].to_i
946
+ e.mday = $2[-8, 2].to_i
947
+ if $2.size >= 10
948
+ e.mon = $2[-10, 2].to_i
949
+ end
950
+ if $2.size == 12
951
+ e.year = ($1 + $2[-12, 2]).to_i
952
+ end
953
+ if $2.size == 14
954
+ e.year = ($1 + $2[-14, 4]).to_i
955
+ e._comp = false
956
+ end
957
+ else
958
+ e.year = ($1 + $2[ 0, 4]).to_i
959
+ e.mon = $2[ 4, 2].to_i
960
+ e.mday = $2[ 6, 2].to_i
961
+ e.hour = $2[ 8, 2].to_i if $2.size >= 10
962
+ e.min = $2[10, 2].to_i if $2.size >= 12
963
+ e.sec = $2[12, 2].to_i if $2.size >= 14
964
+ e._comp = false
965
+ end
966
+ when 3
967
+ if $3.nil? && $4
968
+ e.sec = $2[-2, 2].to_i
969
+ e.min = $2[-3, 1].to_i
970
+ else
971
+ e.yday = $2[ 0, 3].to_i
972
+ end
973
+ when 5
974
+ if $3.nil? && $4
975
+ e.sec = $2[-2, 2].to_i
976
+ e.min = $2[-4, 2].to_i
977
+ e.hour = $2[-5, 1].to_i
978
+ else
979
+ e.year = ($1 + $2[ 0, 2]).to_i
980
+ e.yday = $2[ 2, 3].to_i
981
+ end
982
+ when 7
983
+ if $3.nil? && $4
984
+ e.sec = $2[-2, 2].to_i
985
+ e.min = $2[-4, 2].to_i
986
+ e.hour = $2[-6, 2].to_i
987
+ e.mday = $2[-7, 1].to_i
988
+ else
989
+ e.year = ($1 + $2[ 0, 4]).to_i
990
+ e.yday = $2[ 4, 3].to_i
991
+ end
992
+ end
993
+ if $3
994
+ if $4
995
+ case $3.size
996
+ when 2, 4, 6
997
+ e.sec = $3[-2, 2].to_i
998
+ e.min = $3[-4, 2].to_i if $3.size >= 4
999
+ e.hour = $3[-6, 2].to_i if $3.size >= 6
1000
+ end
1001
+ else
1002
+ case $3.size
1003
+ when 2, 4, 6
1004
+ e.hour = $3[ 0, 2].to_i
1005
+ e.min = $3[ 2, 2].to_i if $3.size >= 4
1006
+ e.sec = $3[ 4, 2].to_i if $3.size >= 6
1007
+ end
1008
+ end
1009
+ end
1010
+ if $4
1011
+ e.sec_fraction = Rational($4.to_i, 10**$4.size)
1012
+ end
1013
+ if $5
1014
+ e.zone = $5
1015
+ if e.zone[0,1] == '['
1016
+ o, n, = e.zone[1..-2].split(':')
1017
+ e.zone = n || o
1018
+ if /\A\d/ =~ o
1019
+ o = format('+%s', o)
1020
+ end
1021
+ e.offset = zone_to_diff(o)
1022
+ end
1023
+ end
1024
+ true
1025
+ end
1026
+ end
1027
+
1028
+ private_class_method :_parse_day, :_parse_time, # :_parse_beat,
1029
+ :_parse_eu, :_parse_us, :_parse_iso, :_parse_iso2,
1030
+ :_parse_jis, :_parse_vms, :_parse_sla, :_parse_dot,
1031
+ :_parse_year, :_parse_mon, :_parse_mday, :_parse_ddd
1032
+
1033
+ def self._parse(str, comp=true)
1034
+ str = str.dup
1035
+
1036
+ e = Format::Bag.new
1037
+
1038
+ e._comp = comp
1039
+
1040
+ str.gsub!(/[^-+',.\/:@[:alnum:]\[\]]+/, ' ')
1041
+
1042
+ _parse_time(str, e) # || _parse_beat(str, e)
1043
+ _parse_day(str, e)
1044
+
1045
+ _parse_eu(str, e) ||
1046
+ _parse_us(str, e) ||
1047
+ _parse_iso(str, e) ||
1048
+ _parse_jis(str, e) ||
1049
+ _parse_vms(str, e) ||
1050
+ _parse_sla(str, e) ||
1051
+ _parse_dot(str, e) ||
1052
+ _parse_iso2(str, e) ||
1053
+ _parse_year(str, e) ||
1054
+ _parse_mon(str, e) ||
1055
+ _parse_mday(str, e) ||
1056
+ _parse_ddd(str, e)
1057
+
1058
+ if str.sub!(/\b(bc\b|bce\b|b\.c\.|b\.c\.e\.)/i, ' ')
1059
+ if e.year
1060
+ e.year = -e.year + 1
1061
+ end
1062
+ end
1063
+
1064
+ if str.sub!(/\A\s*(\d{1,2})\s*\z/, ' ')
1065
+ if e.hour && !e.mday
1066
+ v = $1.to_i
1067
+ if (1..31) === v
1068
+ e.mday = v
1069
+ end
1070
+ end
1071
+ if e.mday && !e.hour
1072
+ v = $1.to_i
1073
+ if (0..24) === v
1074
+ e.hour = v
1075
+ end
1076
+ end
1077
+ end
1078
+
1079
+ if e._comp
1080
+ if e.cwyear
1081
+ if e.cwyear >= 0 && e.cwyear <= 99
1082
+ e.cwyear += if e.cwyear >= 69
1083
+ then 1900 else 2000 end
1084
+ end
1085
+ end
1086
+ if e.year
1087
+ if e.year >= 0 && e.year <= 99
1088
+ e.year += if e.year >= 69
1089
+ then 1900 else 2000 end
1090
+ end
1091
+ end
1092
+ end
1093
+
1094
+ e.offset ||= zone_to_diff(e.zone) if e.zone
1095
+
1096
+ e.to_hash
1097
+ end
1098
+
1099
+ def self._iso8601(str) # :nodoc:
1100
+ if /\A\s*(([-+]?\d{2,}|-)-\d{2}-\d{2}|
1101
+ ([-+]?\d{2,})?-\d{3}|
1102
+ (\d{2}|\d{4})?-w\d{2}-\d|
1103
+ -w-\d)
1104
+ (t
1105
+ \d{2}:\d{2}(:\d{2}([,.]\d+)?)?
1106
+ (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/ix =~ str
1107
+ _parse(str)
1108
+ elsif /\A\s*(([-+]?(\d{2}|\d{4})|--)\d{2}\d{2}|
1109
+ ([-+]?(\d{2}|\d{4}))?\d{3}|-\d{3}|
1110
+ (\d{2}|\d{4})?w\d{2}\d)
1111
+ (t?
1112
+ \d{2}\d{2}(\d{2}([,.]\d+)?)?
1113
+ (z|[-+]\d{2}(\d{2})?)?)?\s*\z/ix =~ str
1114
+ _parse(str)
1115
+ elsif /\A\s*(\d{2}:\d{2}(:\d{2}([,.]\d+)?)?
1116
+ (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/ix =~ str
1117
+ _parse(str)
1118
+ elsif /\A\s*(\d{2}\d{2}(\d{2}([,.]\d+)?)?
1119
+ (z|[-+]\d{2}(\d{2})?)?)?\s*\z/ix =~ str
1120
+ _parse(str)
1121
+ end
1122
+ end
1123
+
1124
+ def self._rfc3339(str) # :nodoc:
1125
+ if /\A\s*-?\d{4}-\d{2}-\d{2} # allow minus, anyway
1126
+ (t|\s)
1127
+ \d{2}:\d{2}:\d{2}(\.\d+)?
1128
+ (z|[-+]\d{2}:\d{2})\s*\z/ix =~ str
1129
+ _parse(str)
1130
+ end
1131
+ end
1132
+
1133
+ def self._xmlschema(str) # :nodoc:
1134
+ if /\A\s*(-?\d{4,})(?:-(\d{2})(?:-(\d{2}))?)?
1135
+ (?:t
1136
+ (\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?
1137
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
1138
+ e = Format::Bag.new
1139
+ e.year = $1.to_i
1140
+ e.mon = $2.to_i if $2
1141
+ e.mday = $3.to_i if $3
1142
+ e.hour = $4.to_i if $4
1143
+ e.min = $5.to_i if $5
1144
+ e.sec = $6.to_i if $6
1145
+ e.sec_fraction = Rational($7.to_i, 10**$7.size) if $7
1146
+ if $8
1147
+ e.zone = $8
1148
+ e.offset = zone_to_diff($8)
1149
+ end
1150
+ e.to_hash
1151
+ elsif /\A\s*(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?
1152
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
1153
+ e = Format::Bag.new
1154
+ e.hour = $1.to_i if $1
1155
+ e.min = $2.to_i if $2
1156
+ e.sec = $3.to_i if $3
1157
+ e.sec_fraction = Rational($4.to_i, 10**$4.size) if $4
1158
+ if $5
1159
+ e.zone = $5
1160
+ e.offset = zone_to_diff($5)
1161
+ end
1162
+ e.to_hash
1163
+ elsif /\A\s*(?:--(\d{2})(?:-(\d{2}))?|---(\d{2}))
1164
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
1165
+ e = Format::Bag.new
1166
+ e.mon = $1.to_i if $1
1167
+ e.mday = $2.to_i if $2
1168
+ e.mday = $3.to_i if $3
1169
+ if $4
1170
+ e.zone = $4
1171
+ e.offset = zone_to_diff($4)
1172
+ end
1173
+ e.to_hash
1174
+ end
1175
+ end
1176
+
1177
+ def self._rfc2822(str) # :nodoc:
1178
+ if /\A\s*(?:(?:#{Format::ABBR_DAYS.keys.join('|')})\s*,\s+)?
1179
+ \d{1,2}\s+
1180
+ (?:#{Format::ABBR_MONTHS.keys.join('|')})\s+
1181
+ -?(\d{2,})\s+ # allow minus, anyway
1182
+ \d{2}:\d{2}(:\d{2})?\s*
1183
+ (?:[-+]\d{4}|ut|gmt|e[sd]t|c[sd]t|m[sd]t|p[sd]t|[a-ik-z])\s*\z/iox =~ str
1184
+ e = _parse(str, false)
1185
+ if (($1.nil?) || ($1.size < 4))
1186
+ if e[:year] < 50
1187
+ e[:year] += 2000
1188
+ elsif e[:year] < 1000
1189
+ e[:year] += 1900
1190
+ end
1191
+ end
1192
+ e
1193
+ end
1194
+ end
1195
+
1196
+ class << self; alias_method :_rfc822, :_rfc2822 end
1197
+
1198
+ def self._httpdate(str) # :nodoc:
1199
+ if /\A\s*(#{Format::ABBR_DAYS.keys.join('|')})\s*,\s+
1200
+ \d{2}\s+
1201
+ (#{Format::ABBR_MONTHS.keys.join('|')})\s+
1202
+ -?\d{4}\s+ # allow minus, anyway
1203
+ \d{2}:\d{2}:\d{2}\s+
1204
+ gmt\s*\z/iox =~ str
1205
+ _rfc2822(str)
1206
+ elsif /\A\s*(#{Format::DAYS.keys.join('|')})\s*,\s+
1207
+ \d{2}\s*-\s*
1208
+ (#{Format::ABBR_MONTHS.keys.join('|')})\s*-\s*
1209
+ \d{2}\s+
1210
+ \d{2}:\d{2}:\d{2}\s+
1211
+ gmt\s*\z/iox =~ str
1212
+ _parse(str)
1213
+ elsif /\A\s*(#{Format::ABBR_DAYS.keys.join('|')})\s+
1214
+ (#{Format::ABBR_MONTHS.keys.join('|')})\s+
1215
+ \d{1,2}\s+
1216
+ \d{2}:\d{2}:\d{2}\s+
1217
+ \d{4}\s*\z/iox =~ str
1218
+ _parse(str)
1219
+ end
1220
+ end
1221
+
1222
+ def self._jisx0301(str) # :nodoc:
1223
+ if /\A\s*[mtsh]?\d{2}\.\d{2}\.\d{2}
1224
+ (t
1225
+ (\d{2}:\d{2}(:\d{2}([,.]\d*)?)?
1226
+ (z|[-+]\d{2}(:?\d{2})?)?)?)?\s*\z/ix =~ str
1227
+ if /\A\s*\d/ =~ str
1228
+ _parse(str.sub(/\A\s*(\d)/, 'h\1'))
1229
+ else
1230
+ _parse(str)
1231
+ end
1232
+ else
1233
+ _iso8601(str)
1234
+ end
1235
+ end
1236
+
1237
+ t = Module.new do
1238
+
1239
+ private
1240
+
1241
+ def zone_to_diff(zone) # :nodoc:
1242
+ zone = zone.downcase
1243
+ if zone.sub!(/\s+(standard|daylight)\s+time\z/, '')
1244
+ dst = $1 == 'daylight'
1245
+ else
1246
+ dst = zone.sub!(/\s+dst\z/, '')
1247
+ end
1248
+ if Format::ZONES.include?(zone)
1249
+ offset = Format::ZONES[zone]
1250
+ offset += 3600 if dst
1251
+ elsif zone.sub!(/\A(?:gmt|utc?)?([-+])/, '')
1252
+ sign = $1
1253
+ if zone.include?(':')
1254
+ hour, min, sec, = zone.split(':')
1255
+ elsif zone.include?(',') || zone.include?('.')
1256
+ hour, fr, = zone.split(/[,.]/)
1257
+ min = Rational(fr.to_i, 10**fr.size) * 60
1258
+ else
1259
+ case zone.size
1260
+ when 3
1261
+ hour = zone[0,1]
1262
+ min = zone[1,2]
1263
+ else
1264
+ hour = zone[0,2]
1265
+ min = zone[2,2]
1266
+ sec = zone[4,2]
1267
+ end
1268
+ end
1269
+ offset = hour.to_i * 3600 + min.to_i * 60 + sec.to_i
1270
+ offset *= -1 if sign == '-'
1271
+ end
1272
+ offset
1273
+ end
1274
+
1275
+ end
1276
+
1277
+ extend t
1278
+ include t
1279
+
1280
+ end
1281
+
1282
+ class DateTime < Date
1283
+
1284
+ def strftime(fmt='%FT%T%:z')
1285
+ super(fmt)
1286
+ end
1287
+
1288
+ def self._strptime(str, fmt='%FT%T%z')
1289
+ super(str, fmt)
1290
+ end
1291
+
1292
+ def iso8601_timediv(n) # :nodoc:
1293
+ strftime('T%T' +
1294
+ if n < 1
1295
+ ''
1296
+ else
1297
+ '.%0*d' % [n, (sec_fraction / Rational(1, 10**n)).round]
1298
+ end +
1299
+ '%:z')
1300
+ end
1301
+
1302
+ private :iso8601_timediv
1303
+
1304
+ def iso8601(n=0)
1305
+ super() + iso8601_timediv(n)
1306
+ end
1307
+
1308
+ def rfc3339(n=0) iso8601(n) end
1309
+
1310
+ def xmlschema(n=0) iso8601(n) end # :nodoc:
1311
+
1312
+ def jisx0301(n=0)
1313
+ super() + iso8601_timediv(n)
1314
+ end
1315
+
1316
+ end