Date#_parse_jis

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