Maintenance of Ruby 2.0.0 ended on February 24, 2016. Read more
Object
When 'time' is required, Time is extended with additional methods for parsing and converting Times.
This library extends the Time class with the following conversions between date strings and Time objects:
date-time defined by RFC 2822
HTTP-date defined by RFC 2616
dateTime defined by XML Schema Part 2: Datatypes (ISO 8601)
various formats handled by Date._parse
custom formats handled by Date._strptime
All examples assume you have loaded Time with:
require 'time'
All of these examples were done using the EST timezone which is GMT-5.
t = Time.now t.iso8601 # => "2011-10-05T22:26:12-04:00" t.rfc2822 # => "Wed, 05 Oct 2011 22:26:12 -0400" t.httpdate # => "Thu, 06 Oct 2011 02:26:12 GMT"
parse takes a string representation of a Time and attempts to parse it using a heuristic.
Date.parse("2010-10-31") #=> 2010-10-31 00:00:00 -0500
Any missing pieces of the date are inferred based on the current date.
# assuming the current date is "2011-10-31" Time.parse("12:00") #=> 2011-10-31 12:00:00 -0500
We can change the date used to infer our missing elements by passing a second object that responds to mon, day and year, such as Date, Time or DateTime. We can also use our own object.
class MyDate attr_reader :mon, :day, :year def initialize(mon, day, year) @mon, @day, @year = mon, day, year end end d = Date.parse("2010-10-28") t = Time.parse("2010-10-29") dt = DateTime.parse("2010-10-30") md = MyDate.new(10,31,2010) Time.parse("12:00", d) #=> 2010-10-28 12:00:00 -0500 Time.parse("12:00", t) #=> 2010-10-29 12:00:00 -0500 Time.parse("12:00", dt) #=> 2010-10-30 12:00:00 -0500 Time.parse("12:00", md) #=> 2010-10-31 12:00:00 -0500
parse also accepts an optional block. You can use this block to specify how to handle the year component of the date. This is specifically designed for handling two digit years. For example, if you wanted to treat all two digit years prior to 70 as the year 2000+ you could write this:
Time.parse("01-10-31") {|year| year + (year < 70 ? 2000 : 1900)} #=> 2001-10-31 00:00:00 -0500 Time.parse("70-10-31") {|year| year + (year < 70 ? 2000 : 1900)} #=> 1970-10-31 00:00:00 -0500
strptime works similar to parse
except that instead of using a
heuristic to detect the format of the input string, you provide a second
argument that is describes the format of the string. For example:
Time.strptime("2000-10-31", "%Y-%m-%d") #=> 2000-10-31 00:00:00 -0500
Parses date
as an HTTP-date defined by RFC 2616 and converts
it to a Time object.
ArgumentError is raised if date
is not compliant with RFC 2616
or if the Time class cannot represent specified
date.
See httpdate for more information on this format.
You must require 'time' to use this method.
# File time.rb, line 476 def httpdate(date) if /\A\s* (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\x20 (\d{2})\x20 (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20 (\d{4})\x20 (\d{2}):(\d{2}):(\d{2})\x20 GMT \s*\z/ix =~ date self.rfc2822(date) elsif /\A\s* (?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\x20 (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d)\x20 (\d\d):(\d\d):(\d\d)\x20 GMT \s*\z/ix =~ date year = $3.to_i if year < 50 year += 2000 else year += 1900 end self.utc(year, $2, $1.to_i, $4.to_i, $5.to_i, $6.to_i) elsif /\A\s* (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\x20 (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20 (\d\d|\x20\d)\x20 (\d\d):(\d\d):(\d\d)\x20 (\d{4}) \s*\z/ix =~ date self.utc($6.to_i, MonthValue[$1.upcase], $2.to_i, $3.to_i, $4.to_i, $5.to_i) else raise ArgumentError.new("not RFC 2616 compliant date: #{date.inspect}") end end
Parses date
using Date._parse and converts it to a Time object.
If a block is given, the year described in date
is converted
by the block. For example:
Time.parse(...) {|y| 0 <= y && y < 100 ? (y >= 69 ? y + 1900 : y + 2000) : y}
If the upper components of the given time are broken or missing, they are
supplied with those of now
. For the lower components, the
minimum values (1 or 0) are assumed if broken or missing. For example:
# Suppose it is "Thu Nov 29 14:33:20 GMT 2001" now and # your time zone is GMT: now = Time.parse("Thu Nov 29 14:33:20 GMT 2001") Time.parse("16:30", now) #=> 2001-11-29 16:30:00 +0900 Time.parse("7/23", now) #=> 2001-07-23 00:00:00 +0900 Time.parse("Aug 31", now) #=> 2001-08-31 00:00:00 +0900 Time.parse("Aug 2000", now) #=> 2000-08-01 00:00:00 +0900
Since there are numerous conflicts among locally defined time zone abbreviations all over the world, this method is not intended to understand all of them. For example, the abbreviation “CST” is used variously as:
-06:00 in America/Chicago, -05:00 in America/Havana, +08:00 in Asia/Harbin, +09:30 in Australia/Darwin, +10:30 in Australia/Adelaide, etc.
Based on this fact, this method only understands the time zone
abbreviations described in RFC 822 and the system time zone, in the order
named. (i.e. a definition in RFC 822 overrides the system time zone
definition.) The system time zone is taken from Time.local(year, 1,
1).zone
and Time.local(year, 7, 1).zone
. If the
extracted time zone abbreviation does not match any of them, it is ignored
and the given time is regarded as a local time.
ArgumentError is raised if Date._parse cannot extract information from
date
or if the Time class cannot
represent specified date.
This method can be used as a fail-safe for other parsing methods as:
Time.rfc2822(date) rescue Time.parse(date) Time.httpdate(date) rescue Time.parse(date) Time.xmlschema(date) rescue Time.parse(date)
A failure of ::parse should be checked, though.
You must require 'time' to use this method.
# File time.rb, line 323 def parse(date, now=self.now) comp = !block_given? d = Date._parse(date, comp) if !d[:year] && !d[:mon] && !d[:mday] && !d[:hour] && !d[:min] && !d[:sec] && !d[:sec_fraction] raise ArgumentError, "no time information in #{date.inspect}" end year = d[:year] year = yield(year) if year && !comp make_time(year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now) end
Parses date
as date-time defined by RFC 2822 and converts it
to a Time object. The format is identical to the
date format defined by RFC 822 and updated by RFC 1123.
ArgumentError is raised if date
is not compliant with RFC 2822
or if the Time class cannot represent specified
date.
See rfc2822 for more information on this format.
You must require 'time' to use this method.
# File time.rb, line 425 def rfc2822(date) if /\A\s* (?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*,\s*)? (\d{1,2})\s+ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+ (\d{2,})\s+ (\d{2})\s* :\s*(\d{2})\s* (?::\s*(\d{2}))?\s+ ([+-]\d{4}| UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix =~ date # Since RFC 2822 permit comments, the regexp has no right anchor. day = $1.to_i mon = MonthValue[$2.upcase] year = $3.to_i hour = $4.to_i min = $5.to_i sec = $6 ? $6.to_i : 0 zone = $7 # following year completion is compliant with RFC 2822. year = if year < 50 2000 + year elsif year < 1000 1900 + year else year end year, mon, day, hour, min, sec = apply_offset(year, mon, day, hour, min, sec, zone_offset(zone)) t = self.utc(year, mon, day, hour, min, sec) t.localtime if !zone_utc?(zone) t else raise ArgumentError.new("not RFC 2822 compliant date: #{date.inspect}") end end
Parses date
using Date._strptime and converts it to a Time object.
If a block is given, the year described in date
is converted
by the block. For example:
Time.strptime(...) {|y| y < 100 ? (y >= 69 ? y + 1900 : y + 2000) : y}
Below is a list of the formating options:
The abbreviated weekday name (“Sun”)
The full weekday name (“Sunday”)
The abbreviated month name (“Jan”)
The full month name (“January”)
The preferred local date and time representation
Century (20 in 2009)
Day of the month (01..31)
Date (%m/%d/%y)
Day of the month, blank-padded ( 1..31)
Equivalent to %Y-%m-%d (the ISO 8601 date format)
Equivalent to %b
Hour of the day, 24-hour clock (00..23)
Hour of the day, 12-hour clock (01..12)
Day of the year (001..366)
hour, 24-hour clock, blank-padded ( 0..23)
hour, 12-hour clock, blank-padded ( 0..12)
Millisecond of the second (000..999)
Month of the year (01..12)
Minute of the hour (00..59)
Newline (n)
Fractional seconds digits, default is 9 digits (nanosecond)
millisecond (3 digits)
microsecond (6 digits)
nanosecond (9 digits)
Meridian indicator (“AM” or “PM”)
Meridian indicator (“am” or “pm”)
time, 12-hour (same as %I:%M:%S %p)
time, 24-hour (%H:%M)
Number of seconds since 1970-01-01 00:00:00 UTC.
Second of the minute (00..60)
Tab character (t)
time, 24-hour (%H:%M:%S)
Day of the week as a decimal, Monday being 1. (1..7)
Week number of the current year, starting with the first Sunday as the first day of the first week (00..53)
VMS date (%e-%b-%Y)
Week number of year according to ISO 8601 (01..53)
Week number of the current year, starting with the first Monday as the first day of the first week (00..53)
Day of the week (Sunday is 0, 0..6)
Preferred representation for the date alone, no time
Preferred representation for the time alone, no date
Year without a century (00..99)
Year with century
Time zone as hour offset from UTC (e.g. +0900)
Time zone name
Literal “%” character
# File time.rb, line 392 def strptime(date, format, now=self.now) d = Date._strptime(date, format) raise ArgumentError, "invalid strptime format - `#{format}'" unless d if seconds = d[:seconds] if offset = d[:offset] Time.at(seconds).localtime(offset) else Time.at(seconds) end else year = d[:year] year = yield(year) if year && block_given? make_time(year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now) end end
Parses date
as a dateTime defined by the XML Schema and
converts it to a Time object. The format is a
restricted version of the format defined by ISO 8601.
ArgumentError is raised if date
is not compliant with the
format or if the Time class cannot represent
specified date.
See xmlschema for more information on this format.
You must require 'time' to use this method.
# File time.rb, line 525 def xmlschema(date) if /\A\s* (-?\d+)-(\d\d)-(\d\d) T (\d\d):(\d\d):(\d\d) (\.\d+)? (Z|[+-]\d\d:\d\d)? \s*\z/ix =~ date year = $1.to_i mon = $2.to_i day = $3.to_i hour = $4.to_i min = $5.to_i sec = $6.to_i usec = 0 if $7 usec = Rational($7) * 1000000 end if $8 zone = $8 year, mon, day, hour, min, sec = apply_offset(year, mon, day, hour, min, sec, zone_offset(zone)) self.utc(year, mon, day, hour, min, sec, usec) else self.local(year, mon, day, hour, min, sec, usec) end else raise ArgumentError.new("invalid date: #{date.inspect}") end end
Return the number of seconds the specified time zone differs from UTC.
Numeric time zones that include minutes, such as -10:00
or
+1330
will work, as will simpler hour-only time zones like
-10
or +13
.
Textual time zones listed in ZoneOffset are also supported.
If the time zone does not match any of the above, zone_offset
will check if the local time zone (both with and without potential Daylight
Saving Time changes being in effect) matches zone
. Specifying
a value for year
will change the year used to find the local
time zone.
If zone_offset
is unable to determine the offset, nil will be
returned.
# File time.rb, line 132 def zone_offset(zone, year=self.now.year) off = nil zone = zone.upcase if /\A([+-])(\d\d):?(\d\d)\z/ =~ zone off = ($1 == '-' ? -1 : 1) * ($2.to_i * 60 + $3.to_i) * 60 elsif /\A[+-]\d\d\z/ =~ zone off = zone.to_i * 3600 elsif ZoneOffset.include?(zone) off = ZoneOffset[zone] * 3600 elsif ((t = self.local(year, 1, 1)).zone.upcase == zone rescue false) off = t.utc_offset elsif ((t = self.local(year, 7, 1)).zone.upcase == zone rescue false) off = t.utc_offset end off end
Returns a string which represents the time as RFC 1123 date of HTTP-date defined by RFC 2616:
day-of-week, DD month-name CCYY hh:mm:ss GMT
Note that the result is always UTC (GMT).
You must require 'time' to use this method.
# File time.rb, line 602 def httpdate t = dup.utc sprintf('%s, %02d %s %0*d %02d:%02d:%02d GMT', RFC2822_DAY_NAME[t.wday], t.day, RFC2822_MONTH_NAME[t.mon-1], t.year < 0 ? 5 : 4, t.year, t.hour, t.min, t.sec) end
Returns a string which represents the time as date-time defined by RFC 2822:
day-of-week, DD month-name CCYY hh:mm:ss zone
where zone is [+-]hhmm.
If self
is a UTC time, -0000 is used as zone.
You must require 'time' to use this method.
# File time.rb, line 569 def rfc2822 sprintf('%s, %02d %s %0*d %02d:%02d:%02d ', RFC2822_DAY_NAME[wday], day, RFC2822_MONTH_NAME[mon-1], year < 0 ? 5 : 4, year, hour, min, sec) + if utc? '-0000' else off = utc_offset sign = off < 0 ? '-' : '+' sprintf('%s%02d%02d', sign, *(off.abs / 60).divmod(60)) end end
Returns a string which represents the time as a dateTime defined by XML Schema:
CCYY-MM-DDThh:mm:ssTZD CCYY-MM-DDThh:mm:ss.sssTZD
where TZD is Z or [+-]hh:mm.
If self is a UTC time, Z is used as TZD. [+-]hh:mm is used otherwise.
fractional_digits
specifies a number of digits to use for
fractional seconds. Its default value is 0.
You must require 'time' to use this method.
# File time.rb, line 626 def xmlschema(fraction_digits=0) fraction_digits = fraction_digits.to_i s = strftime("%FT%T") if fraction_digits > 0 s << strftime(".%#{fraction_digits}N") end s << (utc? ? 'Z' : strftime("%:z")) end