Finding Todays Date
Finding Today s Date
Problem
You need to create an object that represents the current date and time, or a time in the future or past.
Solution
The factory method Time.now creates a Time object containing the current local time. If you want, you can then convert it to GMT time by calling Time#gmtime. The gmtime method actually modifies the underlying time object, though it doesn't follow the Ruby naming conventions for such methods (it should be called something like gmtime!).
now = Time.now # => Sat Mar 18 16:58:07 EST 2006 now.gmtime # => Sat Mar 18 21:58:07 UTC 2006 #The original object was affected by the time zone conversion. now # => Sat Mar 18 21:58:07 UTC 2006
To create a DateTime object for the current local time, use the factory method DateTime.now. Convert a DateTime object to GMT by calling DateTime#new_offset with no argument. Unlike Time#gmtime, this method returns a second DateTime object instead of modifying the original in place.
require 'date' now = DateTime.now # => # now.to_s # => "2006-03-18T16:58:07-0500" now.new_offset.to_s # => "2006-03-18T21:58:07Z" #The original object was not affected by the time zone conversion. now.to_s # => "2006-03-18T16:58:07-0500"
Discussion
Both Time and DateTime objects provide accessor methods for the basic ways in which the Western calendar and clock divide a moment in time. Both classes provide year, month, day, hour (in 24-hour format), min, sec, and zone accessors. Time#isdst lets you know if the underlying time of a Time object has been modified by Daylight Saving Time in its time zone. DateTime pretends Daylight Saving Time doesn't exist.
now_time = Time.new now_datetime = DateTime.now now_time.year # => 2006 now_ datetime.year # => 2006 now_time.hour # => 18 now_ datetime.hour # => 18 now_time.zone # => "EST" now_ datetime.zone # => "-0500" now_time.isdst # => false
You can see that Time#zone and DateTime#zone are a little different. Time#zone returns a time zone name or abbreviation, and DateTime#zone returns a numeric offset from GMT in string form. You can call DateTime#offset to get the GMT offset as a number: a fraction of a day.
now_datetime.offset # => Rational(-5, 24) # -5 hours
Both classes can also represent fractions of a second, accessible with Time#usec (that is, μsec or microseconds) and DateTime#sec_fraction. In the example above, the DateTime object was created after the Time object, so the numbers are different even though both objects were created within the same second.
now_time.usec # => 247930 # That is, 247930 microseconds now_datetime.sec_fraction # => Rational(62191, 21600000000) # That is, about 287921 microseconds
The date library provides a Date class that is like a DateTime, without the time. To create a Date object containing the current date, the best strategy is to create a DateTime object and use the result in a call to a Date factory method. DateTime is actually a subclass of Date, so you only need to do this if you want to strip time data to make sure it doesn't get used.
class Date def Date.now return Date.jd(DateTime.now.jd) end end puts Date.now # 2006-03-18
In addition to creating a time object for this very moment, you can create one from a string (see Recipe 3.2) or from another time object (see Recipe 3.5). You can also use factory methods to create a time object from its calendar and clock parts: the year, month, day, and so on.
The factory methods Time.local and Time.gm take arguments Time object for that time. For local time, use Time.local; for GMT, use Time.gm. All arguments after year are optional and default to zero.
Time.local(1999, 12, 31, 23, 21, 5, 1044) # => Fri Dec 31 23:21:05 EST 1999 Time.gm(1999, 12, 31, 23, 21, 5, 22, 1044) # => Fri Dec 31 23:21:05 UTC 1999 Time.local(1991, 10, 1) # => Tue Oct 01 00:00:00 EDT 1991 Time.gm(2000) # => Sat Jan 01 00:00:00 UTC 2000
The DateTime equivalent of Time.local is the civil factory method. It takes almost but not quite the same arguments as Time.local:
[year, month, day, hour, minute, second, timezone_offset, date_of_calendar_reform].
The main differences from Time.local and Time.gmt are:
- There's no separate usec argument for fractions of a second. You can represent fractions of a second by passing in a rational number for second.
- All the arguments are optional. However, the default year is 4712 BCE, which is probably not useful to you.
- Rather than providing different methods for different time zones, you must pass in an offset from GMT as a fraction of a day. The default is zero, which means that calling DateTime.civil with no time zone will give you a time in GMT.
DateTime.civil(1999, 12, 31, 23, 21, Rational(51044, 100000)).to_s # => "1999-12-31T23:21:00Z" DateTime.civil(1991, 10, 1).to_s # => "1991-10-01T00:00:00Z" DateTime.civil(2000).to_s # => "2000-01-01T00:00:00Z"
The simplest way to get the GMT offset for your local time zone is to call offset on the result of DateTime.now. Then you can pass the offset into DateTime.civil:
my_offset = DateTime.now.offset # => Rational(-5, 24) DateTime.civil(1999, 12, 31, 23, 21, Rational(51044, 100000), my_offset).to_s # => "1999-12-31T23:21:00-0500"
Oh, and there's the calendar-reform thing, too. Recall that Time objects can only represent dates from a limited range (on 32-bit systems, dates from the 20th and 21st centuries). DateTime objects can represent any date at all. The price of this greater range is that DateTime needs to worry about calendar reform when dealing with historical dates. If you're using old dates, you may run into a gap caused by a switch from the Julian calendar (which made every fourth year a leap year) to the more accurate Gregorian calendar (which occasionally skips leap years).
This switch happened at different times in different countries, creating differentlysized gaps as the local calendar absorbed the extra leap days caused by using the Julian reckoning for so many centuries. Dates created within a particular country's gap are invalid for that country.
By default, Ruby assumes that Date objects you create are relative to the Italian calendar, which switched to Gregorian reckoning in 1582. For American and Commonwealth users, Ruby has provided a constant Date::ENGLAND, which corresponds to the date that England and its colonies adopted the Gregorian calendar. DateTime's constructors and factory methods will accept Date::ENGLAND or Date::ITALY as an extra argument denoting when calendar reform started in that country. The calendar reform argument can also be any old Julian day, letting you handle old dates from any country:
#In Italy, 4 Oct 1582 was immediately followed by 15 Oct 1582. # Date.new(1582, 10, 4).to_s # => "1582-10-04" Date.new(1582, 10, 5).to_s # ArgumentError: invalid date Date.new(1582, 10, 4).succ.to_s # => "1582-10-15" #In England, 2 Sep 1752 was immediately followed by 14 Sep 1752. # Date.new(1752, 9, 2, Date::ENGLAND).to_s # => "1752-09-02" Date.new(1752, 9, 3, Date::ENGLAND).to_s # ArgumentError: invalid date Date.new(1752, 9, 2, DateTime::ENGLAND).succ.to_s # => "1752-09-14" Date.new(1582, 10, 5, Date::ENGLAND).to_s # => "1582-10-05"
You probably won't need to use Ruby's Gregorian conversion features: it's uncommon that computer applications need to deal with old dates that are both known with precision and associated with a particular locale.
See Also
- A list of the dates of Gregorian conversion for various countries (http://www.polysyllabic.com/GregConv.html)
- Recipe 3.7, "Converting Between Time Zones
- Recipe 3.8, "Checking Whether Daylight Saving Time Is in Effect"