lib/date/format.rb


DEFINITIONS

This source file includes following functions.


   1  # format.rb: Written by Tadayoshi Funaba 1999-2002
   2  # $Id: format.rb,v 2.8 2002-06-08 00:39:51+09 tadf Exp $
   3  
   4  class Date
   5  
   6    MONTHS = {
   7      'january'  => 1, 'february' => 2, 'march'    => 3, 'april'    => 4,
   8      'may'      => 5, 'june'     => 6, 'july'     => 7, 'august'   => 8,
   9      'september'=> 9, 'october'  =>10, 'november' =>11, 'december' =>12
  10    }
  11  
  12    DAYS = {
  13      'sunday'   => 0, 'monday'   => 1, 'tuesday'  => 2, 'wednesday'=> 3,
  14      'thursday' => 4, 'friday'   => 5, 'saturday' => 6
  15    }
  16  
  17    ABBR_MONTHS = {
  18      'jan'      => 1, 'feb'      => 2, 'mar'      => 3, 'apr'      => 4,
  19      'may'      => 5, 'jun'      => 6, 'jul'      => 7, 'aug'      => 8,
  20      'sep'      => 9, 'oct'      =>10, 'nov'      =>11, 'dec'      =>12
  21    }
  22  
  23    ABBR_DAYS = {
  24      'sun'      => 0, 'mon'      => 1, 'tue'      => 2, 'wed'      => 3,
  25      'thu'      => 4, 'fri'      => 5, 'sat'      => 6
  26    }
  27  
  28    ZONES = {
  29      'ut'  =>  0*3600, 'gmt' =>  0*3600, 'est' => -5*3600, 'edt' => -4*3600,
  30      'cst' => -6*3600, 'cdt' => -5*3600, 'mst' => -7*3600, 'mdt' => -6*3600,
  31      'pst' => -8*3600, 'pdt' => -7*3600,
  32      'a'   =>  1*3600, 'b'   =>  2*3600, 'c'   =>  3*3600, 'd'   =>  4*3600,
  33      'e'   =>  5*3600, 'f'   =>  6*3600, 'g'   =>  7*3600, 'h'   =>  8*3600,
  34      'i'   =>  9*3600, 'k'   => 10*3600, 'l'   => 11*3600, 'm'   => 12*3600,
  35      'n'   => -1*3600, 'o'   => -2*3600, 'p'   => -3*3600, 'q'   => -4*3600,
  36      'r'   => -5*3600, 's'   => -6*3600, 't'   => -7*3600, 'u'   => -8*3600,
  37      'v'   => -9*3600, 'w'   =>-10*3600, 'x'   =>-11*3600, 'y'   =>-12*3600,
  38      'z'   =>  0*3600,
  39      'utc' =>  0*3600, 'wet' =>  0*3600, 'bst' =>  1*3600, 'wat' => -1*3600,
  40      'at'  => -2*3600, 'ast' => -4*3600, 'adt' => -3*3600, 'yst' => -9*3600,
  41      'ydt' => -8*3600, 'hst' =>-10*3600, 'hdt' => -9*3600, 'cat' =>-10*3600,
  42      'ahst'=>-10*3600, 'nt'  =>-11*3600, 'idlw'=>-12*3600, 'cet' =>  1*3600,
  43      'met' =>  1*3600, 'mewt'=>  1*3600, 'mest'=>  2*3600, 'mesz'=>  2*3600,
  44      'swt' =>  1*3600, 'sst' =>  2*3600, 'fwt' =>  1*3600, 'fst' =>  2*3600,
  45      'eet' =>  2*3600, 'bt'  =>  3*3600, 'zp4' =>  4*3600, 'zp5' =>  5*3600,
  46      'zp6' =>  6*3600, 'wast'=>  7*3600, 'wadt'=>  8*3600, 'cct' =>  8*3600,
  47      'jst' =>  9*3600, 'east'=> 10*3600, 'eadt'=> 11*3600, 'gst' => 10*3600,
  48      'nzt' => 12*3600, 'nzst'=> 12*3600, 'nzdt'=> 13*3600, 'idle'=> 12*3600
  49    }
  50  
  51    def self.__strptime(str, fmt, elem)
  52      fmt.scan(/%[EO]?.|./o) do |c|
  53        cc = c.sub(/\A%[EO]?(.)\Z/o, '%\\1')
  54        case cc
  55        when /\A\s/o
  56          str.sub!(/\A[\s\v]+/o, '')
  57        when '%A', '%a'
  58          return unless str.sub!(/\A([a-z]+)\b/io, '')
  59          val = DAYS[$1.downcase] || ABBR_DAYS[$1.downcase]
  60          return unless val
  61          elem[:wday] = val
  62        when '%B', '%b', '%h'
  63          return unless str.sub!(/\A([a-z]+)\b/io, '')
  64          val = MONTHS[$1.downcase] || ABBR_MONTHS[$1.downcase]
  65          return unless val
  66          elem[:mon] = val
  67        when '%C'
  68          return unless str.sub!(/\A(\d+)/o, '')
  69          val = $1.to_i
  70          elem[:cent] = val
  71        when '%c'
  72          return unless __strptime(str, '%a %b %e %H:%M:%S %Y', elem)
  73        when '%D'
  74          return unless __strptime(str, '%m/%d/%y', elem)
  75        when '%d', '%e'
  76          return unless str.sub!(/\A ?(\d+)/o, '')
  77          val = $1.to_i
  78          return unless (1..31) === val
  79          elem[:mday] = val
  80        when '%F'
  81          return unless __strptime(str, '%Y-%m-%d', elem)
  82        when '%G'
  83          return unless str.sub!(/\A([-+]?\d+)/o, '')
  84          val = $1.to_i
  85          elem[:cwyear] = val
  86        when '%g'
  87          return unless str.sub!(/\A(\d+)/o, '')
  88          val = $1.to_i
  89          return unless (0..99) === val
  90          elem[:cwyear] = val
  91          elem[:cent] ||= if val >= 69 then 19 else 20 end
  92        when '%H', '%k'
  93          return unless str.sub!(/\A ?(\d+)/o, '')
  94          val = $1.to_i
  95          return unless (0..24) === val
  96          elem[:hour] = val
  97        when '%I', '%l'
  98          return unless str.sub!(/\A ?(\d+)/o, '')
  99          val = $1.to_i
 100          return unless (1..12) === val
 101          elem[:hour] = val
 102        when '%j'
 103          return unless str.sub!(/\A(\d+)/o, '')
 104          val = $1.to_i
 105          return unless (1..366) === val
 106          elem[:yday] = val
 107        when '%M'
 108          return unless str.sub!(/\A(\d+)/o, '')
 109          val = $1.to_i
 110          return unless (0..59) === val
 111          elem[:min] = val
 112        when '%m'
 113          return unless str.sub!(/\A(\d+)/o, '')
 114          val = $1.to_i
 115          return unless (1..12) === val
 116          elem[:mon] = val
 117        when '%n'
 118          return unless __strptime(str, ' ', elem)
 119        when '%p', '%P'
 120          return unless str.sub!(/\A([ap])(?:m\b|\.m\.)/io, '')
 121          elem[:merid] = if $1.downcase == 'a' then 0 else 12 end
 122        when '%R'
 123          return unless __strptime(str, '%H:%M', elem)
 124        when '%r'
 125          return unless __strptime(str, '%I:%M:%S %p', elem)
 126        when '%S'
 127          return unless str.sub!(/\A(\d+)/o, '')
 128          val = $1.to_i
 129          return unless (0..60) === val
 130          elem[:sec] = val
 131        when '%s'
 132          return unless str.sub!(/\A(\d+)/o, '')
 133          val = $1.to_i
 134          elem[:seconds] = val
 135        when '%T'
 136          return unless __strptime(str, '%H:%M:%S', elem)
 137        when '%t'
 138          return unless __strptime(str, ' ', elem)
 139        when '%U', '%W'
 140          return unless str.sub!(/\A(\d+)/o, '')
 141          val = $1.to_i
 142          return unless (0..53) === val
 143          elem[if c == '%U' then :wnum0 else :wnum1 end] = val
 144        when '%u'
 145          return unless str.sub!(/\A(\d+)/o, '')
 146          val = $1.to_i
 147          return unless (1..7) === val
 148          elem[:cwday] = val
 149        when '%V'
 150          return unless str.sub!(/\A(\d+)/o, '')
 151          val = $1.to_i
 152          return unless (1..53) === val
 153          elem[:cweek] = val
 154        when '%v'
 155          return unless __strptime(str, '%e-%b-%Y', elem)
 156        when '%w'
 157          return unless str.sub!(/\A(\d+)/o, '')
 158          val = $1.to_i
 159          return unless (0..6) === val
 160          elem[:wday] = val
 161        when '%X'
 162          return unless __strptime(str, '%H:%M:%S', elem)
 163        when '%x'
 164          return unless __strptime(str, '%m/%d/%y', elem)
 165        when '%Y'
 166          return unless str.sub!(/\A([-+]?\d+)/o, '')
 167          val = $1.to_i
 168          elem[:year] = val
 169        when '%y'
 170          return unless str.sub!(/\A(\d+)/o, '')
 171          val = $1.to_i
 172          return unless (0..99) === val
 173          elem[:year] = val
 174          elem[:cent] ||= if val >= 69 then 19 else 20 end
 175        when '%Z', '%z'
 176          return unless str.sub!(/\A([a-z0-9:+-]+(?:\s+dst\b)?)/io, '')
 177          val = $1
 178          elem[:zone] = val
 179          offset = zone_to_diff(val)
 180          elem[:offset] = offset
 181        when '%%'
 182          return unless str.sub!(/\A%/o, '')
 183        when '%+'
 184          return unless __strptime(str, '%a %b %e %H:%M:%S %Z %Y', elem)
 185        when '%1'
 186          return unless str.sub!(/\A(\d+)/o, '')
 187          val = $1.to_i
 188          elem[:jd] = val
 189        when '%2'
 190          return unless __strptime(str, '%Y-%j', elem)
 191        when '%3'
 192          return unless __strptime(str, '%F', elem)
 193        else
 194          return unless str.sub!(Regexp.new('\\A' + Regexp.quote(c)), '')
 195        end
 196      end
 197  
 198      if cent = elem.delete(:cent)
 199        if elem[:cwyear]
 200          elem[:cwyear] += cent * 100
 201        end
 202        if elem[:year]
 203          elem[:year] += cent * 100
 204        end
 205      end
 206  
 207      if merid = elem.delete(:merid)
 208        if elem[:hour]
 209          elem[:hour] %= 12
 210          elem[:hour] += merid
 211        end
 212      end
 213  
 214      str
 215    end
 216  
 217    private_class_method :__strptime
 218  
 219    def self._strptime(str, fmt='%F')
 220      elem = {}
 221      elem if __strptime(str.dup, fmt, elem)
 222    end
 223  
 224    PARSE_MONTHPAT = ABBR_MONTHS.keys.join('|')
 225    PARSE_DAYPAT   = ABBR_DAYS.  keys.join('|')
 226  
 227    def self._parse(str, comp=false)
 228      str = str.dup
 229  
 230      str.gsub!(/[^-+.\/:0-9a-z]+/ino, ' ')
 231  
 232      # day
 233      if str.sub!(/(#{PARSE_DAYPAT})\S*/ino, ' ')
 234        wday = ABBR_DAYS[$1.downcase]
 235      end
 236  
 237      # time
 238      if str.sub!(
 239                  /(\d+):(\d+)(?::(\d+))?
 240                   (?:
 241                     \s*
 242                     ([ap])(?:m\b|\.m\.)
 243                   )?
 244                   (?:
 245                     \s*
 246                     (
 247                       [a-z]+(?:\s+dst)?\b
 248                     |
 249                       [-+]\d+(?::?\d+)
 250                     )
 251                   )?
 252                  /inox,
 253                  ' ')
 254        hour = $1.to_i
 255        min = $2.to_i
 256        sec = $3.to_i if $3
 257  
 258        if $4
 259          hour %= 12
 260          if $4.downcase == 'p'
 261            hour += 12
 262          end
 263        end
 264  
 265        zone = $5
 266      end
 267  
 268      # eu
 269      if str.sub!(
 270                  /(\d+)\S*
 271                   \s+
 272                   (#{PARSE_MONTHPAT})\S*
 273                   (?:
 274                     \s+
 275                     (-?\d+)
 276                   )?
 277                  /inox,
 278                  ' ')
 279        mday = $1.to_i
 280        mon = ABBR_MONTHS[$2.downcase]
 281  
 282        if $3
 283          year = $3.to_i
 284          if $3.size > 2
 285            comp = false
 286          end
 287        end
 288  
 289      # us
 290      elsif str.sub!(
 291                     /(#{PARSE_MONTHPAT})\S*
 292                      \s+
 293                      (\d+)\S*
 294                      (?:
 295                        \s+
 296                        (-?\d+)
 297                      )?
 298                     /inox,
 299                     ' ')
 300        mon = ABBR_MONTHS[$1.downcase]
 301        mday = $2.to_i
 302  
 303        if $3
 304          year = $3.to_i
 305          if $3.size > 2
 306            comp = false
 307          end
 308        end
 309  
 310      # iso
 311      elsif str.sub!(/([-+]?\d+)-(\d+)-(-?\d+)/no, ' ')
 312        year = $1.to_i
 313        mon = $2.to_i
 314        mday = $3.to_i
 315  
 316        if $1.size > 2
 317          comp = false
 318        elsif $3.size > 2
 319          comp = false
 320          mday, mon, year = year, mon, mday
 321        end
 322  
 323      # jis
 324      elsif str.sub!(/([MTSH])(\d+)\.(\d+)\.(\d+)/no, ' ')
 325        e = { 'M'=>1867,
 326              'T'=>1911,
 327              'S'=>1925,
 328              'H'=>1988
 329            }[$1]
 330        year = $2.to_i + e
 331        mon = $3.to_i
 332        mday = $4.to_i
 333  
 334      # vms
 335      elsif str.sub!(/(-?\d+)-(#{PARSE_MONTHPAT})[^-]*-(-?\d+)/ino, ' ')
 336        mday = $1.to_i
 337        mon = ABBR_MONTHS[$2.downcase]
 338        year = $3.to_i
 339  
 340        if $1.size > 2
 341          comp = false
 342          year, mon, mday = mday, mon, year
 343        elsif $3.size > 2
 344          comp = false
 345        end
 346  
 347      # sla
 348      elsif str.sub!(%r|(-?\d+)/(\d+)(?:/(-?\d+))?|no, ' ')
 349        mon = $1.to_i
 350        mday = $2.to_i
 351  
 352        if $3
 353          year = $3.to_i
 354          if $3.size > 2
 355            comp = false
 356          end
 357        end
 358  
 359        if $1.size > 2
 360          comp = false
 361          year, mon, mday = mon, mday, year
 362        end
 363  
 364      # ddd
 365      elsif str.sub!(
 366                     /([-+]?)(\d{4,14})
 367                      (?:
 368                        \s*
 369                        T?
 370                        \s*
 371                        (\d{2,6})
 372                      )?
 373                      (?:
 374                        \s*
 375                        (
 376                          Z
 377                        |
 378                          [-+]\d{2,4}
 379                        )
 380                        \b
 381                      )?
 382                     /nox,
 383                     ' ')
 384        case $2.size
 385        when 4
 386          mon  = $2[ 0, 2].to_i
 387          mday = $2[ 2, 2].to_i
 388        when 6
 389          year = ($1 + $2[ 0, 2]).to_i
 390          mon  = $2[ 2, 2].to_i
 391          mday = $2[ 4, 2].to_i
 392        when 8, 10, 12, 14
 393          year = ($1 + $2[ 0, 4]).to_i
 394          mon  = $2[ 4, 2].to_i
 395          mday = $2[ 6, 2].to_i
 396          hour = $2[ 8, 2].to_i if $2.size >= 10
 397          min  = $2[10, 2].to_i if $2.size >= 12
 398          sec  = $2[12, 2].to_i if $2.size >= 14
 399          comp = false
 400        end
 401        if $3
 402          case $3.size
 403          when 2, 4, 6
 404            hour = $3[ 0, 2].to_i
 405            min  = $3[ 2, 2].to_i if $3.size >= 4
 406            sec  = $3[ 4, 2].to_i if $3.size >= 6
 407          end
 408        end
 409        zone = $4
 410      end
 411  
 412      if str.sub!(/\b(bc\b|bce\b|b\.c\.|b\.c\.e\.)/ino, ' ')
 413        if year
 414          year = -year + 1
 415        end
 416      end
 417  
 418      if comp and year
 419        if year >= 0 and year <= 99
 420          if year >= 69
 421            year += 1900
 422          else
 423            year += 2000
 424          end
 425        end
 426      end
 427  
 428      elem = {}
 429      elem[:year] = year if year
 430      elem[:mon] = mon if mon
 431      elem[:mday] = mday if mday
 432      elem[:hour] = hour if hour
 433      elem[:min] = min if min
 434      elem[:sec] = sec if sec
 435      elem[:zone] = zone if zone
 436      offset = zone_to_diff(zone) if zone
 437      elem[:offset] = offset if offset
 438      elem[:wday] = wday if wday
 439      elem
 440    end
 441  
 442    def self.zone_to_diff(str)
 443      abb, dst = str.downcase.split(/\s+/o, 2)
 444      if ZONES.include?(abb)
 445        offset  = ZONES[abb]
 446        offset += 3600 if dst
 447      elsif /\A([+-])(\d{2}):?(\d{2})?\Z/no =~ str
 448        offset = $2.to_i * 3600 + $3.to_i * 60
 449        offset *= -1 if $1 == '-'
 450      end
 451      offset
 452    end
 453  
 454    def strftime(fmt='%F')
 455      o = ''
 456      fmt.scan(/%[EO]?.|./o) do |c|
 457        cc = c.sub(/^%[EO]?(.)$/o, '%\\1')
 458        case cc
 459        when '%A'; o << DAYNAMES[wday]
 460        when '%a'; o << ABBR_DAYNAMES[wday]
 461        when '%B'; o << MONTHNAMES[mon]
 462        when '%b'; o << ABBR_MONTHNAMES[mon]
 463        when '%C'; o << '%02d' % (year / 100.0).floor             # P2,ID
 464        when '%c'; o << strftime('%a %b %e %H:%M:%S %Y')
 465        when '%D'; o << strftime('%m/%d/%y')                      # P2,ID
 466        when '%d'; o << '%02d' % mday
 467        when '%e'; o <<  '%2d' % mday
 468        when '%F'; o << strftime('%Y-%m-%d')                      # ID
 469        when '%G'; o << '%.4d' %  cwyear                          # ID
 470        when '%g'; o << '%02d' % (cwyear % 100)                   # ID
 471        when '%H'; o << '%02d' %   hour
 472        when '%h'; o << strftime('%b')                            # P2,ID
 473        when '%I'; o << '%02d' % ((hour % 12).nonzero? or 12)
 474        when '%j'; o << '%03d' % yday
 475        when '%k'; o <<  '%2d' %   hour                           # AR,TZ,GL
 476        when '%l'; o <<  '%2d' % ((hour % 12).nonzero? or 12)     # AR,TZ,GL
 477        when '%M'; o << '%02d' % min
 478        when '%m'; o << '%02d' % mon
 479        when '%n'; o << "\n"                                      # P2,ID
 480        when '%P'; o << if hour < 12 then 'am' else 'pm' end      # GL
 481        when '%p'; o << if hour < 12 then 'AM' else 'PM' end
 482        when '%R'; o << strftime('%H:%M')                         # ID
 483        when '%r'; o << strftime('%I:%M:%S %p')                   # P2,ID
 484        when '%S'; o << '%02d' % sec
 485        when '%s'                                                 # TZ,GL
 486          d = ajd - type.jd_to_ajd(type.civil_to_jd(1970,1,1), 0)
 487          s = (d * 86400).to_i
 488          o << '%d' % s
 489        when '%T'; o << strftime('%H:%M:%S')                      # P2,ID
 490        when '%t'; o << "\t"                                      # P2,ID
 491        when '%U', '%W'
 492          a = type.civil_to_jd(year, 1, 1, ns?) + 6
 493          k = if c == '%U' then 0 else 1 end
 494          w = (jd - (a - ((a - k) + 1) % 7) + 7) / 7
 495          o << '%02d' % w
 496        when '%u'; o <<   '%d' % cwday                            # P2,ID
 497        when '%V'; o << '%02d' % cweek                            # P2,ID
 498        when '%v'; o << strftime('%e-%b-%Y')                      # AR,TZ
 499        when '%w'; o <<   '%d' % wday
 500        when '%X'; o << strftime('%H:%M:%S')
 501        when '%x'; o << strftime('%m/%d/%y')
 502        when '%Y'; o << '%.4d' %  year
 503        when '%y'; o << '%02d' % (year % 100)
 504        when '%Z'; o << zone
 505        when '%z'; o << zone                                      # ID
 506        when '%%'; o << '%'
 507        when '%+'; o << strftime('%a %b %e %H:%M:%S %Z %Y')       # TZ
 508        when '%1'; o <<   '%d' % jd
 509        when '%2'; o <<   strftime('%Y-%j')
 510        when '%3'; o <<   strftime('%Y-%m-%d')
 511        else;      o << c
 512        end
 513      end
 514      o
 515    end
 516  
 517  # alias_method :format, :strftime
 518  
 519    def asctime() strftime('%c') end
 520  
 521    alias_method :ctime, :asctime
 522  
 523  end
 524  
 525  class DateTime < Date
 526  
 527    def self._strptime(str, fmt='%FT%T%Z')
 528      super(str, fmt)
 529    end
 530  
 531    def strftime(fmt='%FT%T%Z')
 532      super(fmt)
 533    end
 534  
 535  end