PageRenderTime 309ms CodeModel.GetById 142ms app.highlight 158ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/date/format.rb

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