module Sisimai::DateTime
Sisimai::DateTime
provide methods for dealing date and time.
Constants
- BASE_D
- BASE_L
- BASE_Y
- CONST_E
- CONST_P
- DayOfWeek
- MathematicalConstant
- MonthName
- TZ_OFFSET
- TimeUnit
- TimeZones
Public Class Methods
Abbreviation -> Tiemzone @param [String] argv1 Abbr. e.g.) JST, GMT, PDT @return [String, Nil] +0900, +0000, -0600 or nil if the argument is
invalid format or not supported abbreviation
@example Get the timezone string of “JST”
abbr2tz('JST') #=> '+0900'
# File lib/sisimai/datetime.rb, line 401 def abbr2tz(argv1) return nil unless argv1.is_a?(::String) return TimeZones[argv1] end
List of day of week @param [Boolean] argv1 Require full name @return [Array, String] List of day of week or day of week @example Get the names of each day of week
dayofweek() #=> [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] dayofweek(true) #=> [ 'Sunday', 'Monday', 'Tuesday', ... ]
# File lib/sisimai/datetime.rb, line 226 def dayofweek(argv1 = false) value = argv1 ? :full : :abbr return DayOfWeek[value] end
Month name list @param [Boolean] argv1 Require full name or not @return [Array, String] Month name list or month name @example Get the names of each month
monthname() #=> [ 'Jan', 'Feb', ... ] monthname(true) #=> [ 'January', 'February', 'March', ... ]
# File lib/sisimai/datetime.rb, line 215 def monthname(argv1 = false) value = argv1 ? :full : :abbr return MonthName[value] end
Parse date string; strptime() wrapper @param [String] argv1 Date string @return [String] Converted date string @see en.wikipedia.org/wiki/ISO_8601 @see www.ietf.org/rfc/rfc3339.txt @example Parse date string and convert to generic format string
parse("2015-11-03T23:34:45 Tue") #=> Tue, 3 Nov 2015 23:34:45 +0900 parse("Tue, Nov 3 2015 2:2:2") #=> Tue, 3 Nov 2015 02:02:02 +0900
# File lib/sisimai/datetime.rb, line 239 def parse(argv1) return nil unless argv1.is_a?(::String) return nil if argv1.empty? datestring = argv1 datestring.sub!(/[,](\d+)/, ', \1') # Thu,13 -> Thu, 13 datestring.sub!(/(\d{1,2}),/, '\1') # Apr,29 -> Apr 29 timetokens = datestring.split(' ') afternoon1 = 0 # (Integer) After noon flag altervalue = {} # (Hash) To store alternative values v = { Y: nil, # (Integer) Year M: nil, # (String) Month Abbr. d: nil, # (Integer) Day a: nil, # (String) Day of week, Abbr. T: nil, # (String) Time z: nil, # (Integer) Timezone offset } while p = timetokens.shift do # Parse each piece of time if p =~ /\A[A-Z][a-z]{2,}[,]?\z/ # Day of week or Day of week; Thu, Apr, ... p.gsub!(/,\z/, '') if p.end_with?(',') # "Thu," => "Thu" p = p[0,3] if p.size > 3 if DayOfWeek[:abbr].include?(p) # Day of week; Mon, Thu, Sun,... v[:a] = p elsif MonthName[:abbr].include?(p) # Month name abbr.; Apr, May, ... v[:M] = p end elsif p =~ /\A\d{1,4}\z/ # Year or Day; 2005, 31, 04, 1, ... if p.to_i > 31 # The piece is the value of an year v[:Y] = p.to_i else # The piece is the value of a day if v[:d] # 2-digit year? altervalue[:Y] = p unless v[:Y] else # The value is "day" v[:d] = p end end elsif cr = p.match(/\A([0-2]\d):([0-5]\d):([0-5]\d)\z/) || p.match(/\A(\d{1,2})[-:](\d{1,2})[-:](\d{1,2})\z/) # Time; 12:34:56, 03:14:15, ... # Arrival-Date: 2014-03-26 00-01-19 if cr[1].to_i < 24 && cr[2].to_i < 60 && cr[3].to_i < 60 # Valid time format, maybe... v[:T] = sprintf('%02d:%02d:%02d', cr[1].to_i, cr[2].to_i, cr[3].to_i) end elsif cr = p.match(/\A([0-2]\d):([0-5]\d)\z/) # Time; 12:34 => 12:34:00 if cr[1].to_i < 24 && cr[2].to_i < 60 v[:T] = sprintf('%02d:%02d:00', cr[1].to_i, cr[2].to_i) end elsif cr = p.match(/\A(\d\d?):(\d\d?)\z/) # Time: 1:4 => 01:04:00 v[:T] = sprintf('%02d:%02d:00', cr[1].to_i, cr[2].to_i) elsif p =~ /\A[APap][Mm]\z/ # AM or PM afternoon1 = 1 else # Timezone offset and others if p =~ /\A[-+][01]\d{3}\z/ # Timezone offset; +0000, +0900, -1000, ... v[:z] ||= p elsif p =~ /\A[(]?[A-Z]{2,5}[)]?\z/ # Timezone abbreviation; JST, GMT, UTC, ... v[:z] ||= abbr2tz(p) || '+0000' else # Other date format if cr = p.match(%r|\A(\d{4})[-/](\d{1,2})[-/](\d{1,2})\z|) # Mail.app(MacOS X)'s faked Bounce, Arrival-Date: 2010-06-18 17:17:52 +0900 v[:Y] = cr[1].to_i v[:M] = MonthName[:abbr][cr[2].to_i - 1] v[:d] = cr[3].to_i elsif cr = p.match(%r|\A(\d{4})[-/](\d{1,2})[-/](\d{1,2})T([0-2]\d):([0-5]\d):([0-5]\d)\z|) # ISO 8601; 2000-04-29T01:23:45 v[:Y] = cr[1].to_i v[:M] = MonthName[:abbr][cr[2].to_i - 1] v[:d] = cr[3].to_i if cr[3].to_i < 32 if cr[4].to_i < 24 && cr[5].to_i < 60 && cr[6].to_i < 60 v[:T] = sprintf('%02d:%02d:%02d', cr[4].to_i, cr[5].to_i, cr[6].to_i) end elsif cr = p.match(%r|\A(\d{1,2})/(\d{1,2})/(\d{1,2})\z|) # 4/29/01 11:34:45 PM v[:M] = MonthName[:abbr][cr[1].to_i - 1] v[:d] = cr[2].to_i v[:Y] = cr[3].to_i + 2000 v[:Y] -= 100 if v[:Y].to_i > ::DateTime.now.year + 1 elsif cr = p.match(%r|\A(\d{1,2})[-/](\d{1,2})[-/](\d{4})|) # 29-04-2017 22:22 v[:d] = cr[1].to_i if cr[1].to_i < 32 v[:M] = MonthName[:abbr][cr[2].to_i - 1] v[:Y] = cr[3].to_i end end end end # End of while() if v[:T] && afternoon1 > 0 # +12 t0 = v[:T] t1 = v[:T].split(':') v[:T] = sprintf('%02d:%02d:%02d', t1[0].to_i + 12, t1[1].to_i, t1[2].to_i) v[:T] = t0 if t1[0].to_i > 12 end v[:a] ||= 'Thu' # There is no day of week if !v[:Y].nil? && v[:Y].to_i < 200 # 99 -> 1999, 102 -> 2002 v[:Y] = v[:Y].to_i + 1900 end v[:z] ||= ::DateTime.now.zone.delete(':') # Adjust 2-digit Year if altervalue[:Y] && !v[:Y] # Check alternative value(Year) v[:Y] ||= if altervalue[:Y].to_i >= 82 # SMTP was born in 1982 1900 + altervalue[:Y].to_i else # 20XX 2000 + altervalue[:Y].to_i end end # Check each piece if v.value?(nil) # Strange date format warn sprintf(' ***warning: Strange date format [%s]', datestring) return nil end if v[:Y].to_i < 1902 || v[:Y].to_i > 2037 # -(2^31) ~ (2^31) return nil end # Build date string # Thu, 29 Apr 2004 10:01:11 +0900 return sprintf('%s, %s %s %s %s %s', v[:a], v[:d], v[:M], v[:Y], v[:T], v[:z]) end
Convert to Timezone string @param [Integer] argv1 Second to be converted @return [String] Timezone offset string @see tz2second @example Get timezone offset string of specified seconds
second2tz(12345) #=> '+0325'
# File lib/sisimai/datetime.rb, line 444 def second2tz(argv1) return '+0000' unless argv1.is_a?(::Integer) return nil if argv1.abs > TZ_OFFSET # UTC+14 + 1(DST?) digit = { :operator => '+' } digit[:operator] = '-' if argv1 < 0 digit[:hours] = (argv1.abs / 3600).to_i digit[:minutes] = ((argv1.abs % 3600) / 60).to_i timez = sprintf('%s%02d%02d', digit[:operator], digit[:hours], digit[:minutes]) return timez end
Convert to second @param [String] argv1 Digit and a unit of time @return [Integer] n: seconds
0: 0 or invalid unit of time
@example Get the value of seconds
to_second('1d') #=> 86400 to_second('2h') #=> 7200
# File lib/sisimai/datetime.rb, line 182 def to_second(argv1) return 0 unless argv1.is_a?(::String) getseconds = 0 unitoftime = TimeUnit.keys.join mathconsts = MathematicalConstant.keys.join if cr = argv1.match(/\A(\d+|\d+[.]\d+)([#{unitoftime}])?\z/) # 1d, 1.5w n = cr[1].to_f u = cr[2] || 'd' getseconds = n * TimeUnit[u].to_f elsif cr = argv1.match(/\A(\d+|\d+[.]\d+)?([#{mathconsts}])([#{unitoftime}])?\z/) # 1pd, 1.5pw n = cr[1].to_f || 1 n = 1 if n.to_i == 0 m = MathematicalConstant[cr[2]].to_f u = cr[3] || 'd' getseconds = n * m * TimeUnit[u].to_f else getseconds = 0 end return getseconds end
Convert to second @param [String] argv1 Timezone string e.g) +0900 @return [Integer, Nil] n: seconds or nil it the argument is invalid
format string
@see second2tz @example Convert '+0900' to seconds
tz2second('+0900') #=> 32400
# File lib/sisimai/datetime.rb, line 413 def tz2second(argv1) return nil unless argv1.is_a?(::String) ztime = 0 if cr = argv1.match(/\A([-+])(\d)(\d)(\d{2})\z/) digit = { :'operator' => cr[1], :'hour-10' => cr[2].to_i, :'hour-01' => cr[3].to_i, :'minutes' => cr[4].to_i, } ztime += (digit[:'hour-10'] * 10 + digit[:'hour-01']) * 3600 ztime += (digit[:'minutes'] * 60) ztime *= -1 if digit[:'operator'] == '-' return nil if ztime.abs > TZ_OFFSET return ztime elsif argv1 =~ /\A[A-Za-z]+\z/ return tz2second(TimeZones[argv1]) else return nil end end