Wednesday 18 March 2009

Ruby dates and times

Ruby has three classes for handling time, Date, DateTime and Time.

Time is part of the core language, while Date and DateTime are part of standard Ruby; to use Date and DateTime you will need to load in date.rb. Here is some code that initialises each with the current date/time.
t = Time.now
# => Fri Feb 06 08:56:27 +0000 2009
require 'date' # Needed for Date and DateTime
# => true
d = Date.today
# => #<Date: 4909737/2,0,2299161>
dt = DateTime.now
# => #<DateTime: 14140044704711/5760000,0,2299161>

As you can see, despite its name, the Time class holds the date as well as the time. The inspect method of Time gives a convenient output; not so Date or DateTime. However, the to_s method is a little more useful. The best option is to use the strftime method for all of them, which gives you full control over how time and dates are formated.
p t.inspect
# => "Fri Feb 06 08:56:27 +0000 2009"
p d.inspect
# => "#"
p t.to_s
# => "Fri Feb 06 08:56:27 +0000 2009"
p d.to_s
# => "2009-02-06"
p dt.to_s
# => "2009-02-06T08:56:10+00:00"
p d.strftime('%H%M on %d/%b/%y')
# => "0000 on 06/Feb/09"
p t.strftime('%H%M on %d/%b/%y')
# => "0856 on 06/Feb/09"
p dt.strftime('%H%M on %d/%b/%y')
# => "0856 on 06/Feb/09"

The full list of options (from here):
  %a - The abbreviated weekday name ("Sun")
%A - The full weekday name ("Sunday")
%b - The abbreviated month name ("Jan")
%B - The full month name ("January")
%c - The preferred local date and time representation
%d - Day of the month (01..31)
%H - Hour of the day, 24-hour clock (00..23)
%I - Hour of the day, 12-hour clock (01..12)
%j - Day of the year (001..366)
%m - Month of the year (01..12)
%M - Minute of the hour (00..59)
%p - Meridian indicator ("AM" or "PM")
%S - Second of the minute (00..60)
%U - Week number of the current year,
starting with the first Sunday as the first
day of the first week (00..53)
%W - Week number of the current year,
starting with the first Monday as the first
day of the first week (00..53)
%w - Day of the week (Sunday is 0, 0..6)
%x - Preferred representation for the date alone, no time
%X - Preferred representation for the time alone, no date
%y - Year without a century (00..99)
%Y - Year with century
%Z - Time zone name
%% - Literal "%" character

You can also create Date, Time and DateTime objects using the new method. Date and DateTime default to midnight on the 1st of January 1988, while Time defaults to the current date and time. With Date and DateTime you can set specific values. The parameter list starts with the biggest units, years, and gets smaller. Successive arguments are optional.
p Date.new.strftime('%H%M on %d/%b/%y')
# => "0000 on 01/Jan/88"
p DateTime.new.strftime('%H%M on %d/%b/%y')
# => "0000 on 01/Jan/88"
p Date.new(2006).strftime('%H%M on %d/%b/%y')
# => "0000 on 01/Jan/06"
p Date.new(2006, 4).strftime('%H%M on %d/%b/%y')
# => "0000 on 01/Apr/06"
p Date.new(2006, 4, 7).strftime('%H%M on %d/%b/%y')
# => "0000 on 07/Apr/06"
p DateTime.new(2006, 4, 7).strftime('%H%M on %d/%b/%y')
# => "0000 on 07/Apr/06"
p DateTime.new(2006, 4, 7, 8).strftime('%H%M on %d/%b/%y')
# => "0800 on 07/Apr/06"
p DateTime.new(2006, 4, 7, 8, 23).strftime('%H%M on %d/%b/%y')
# => "0823 on 07/Apr/06"

Using date and time examples.
d2 = d1 >> 2  # d2 will be two months later than d1
d2 = d1 << 2 # d2 will be two months earlier than d1
d.month
dt.day
d.wday # Day of week, Monday = 1
d.yday # Day of the year
d.zone # Time zone
d.leap? # Leap year?
dt.hour
dt.min
dt.sec
dt.sec_fraction

You can determine the different between two dates just be taking one from the other. The complication is that the result is a Rational.
d1 = Date.new 2004
d2 = Date.new 2005
d2 - d1
# => Rational(366, 1)

A Rational object consists of two numbers. Basically it is a fraction; the first number goes on top, the second number of the bottom (in mathematics, a rational number is one that can be expressed as a faction with finite digits; as opposed to, for example, pi, which is an irrational number). The number of days between the 1st January 2004 and 2005 is 366 divided by 1. You can freely use Date and DateTime objects together; the result is always the number of days expressed as a fraction, as a Rational object. You can convert your Rational object to an array cotaining the hours, minutes, second and the second fraction with Date.day_fraction_to_time.

According to here, Time is written in C, and is therefore some 20 times faster than Date/DateTime. However, it can only handle dates from 1970 to 2039 (Unix epoch)

Humanize time/date:
http://www.i76.nl/weblog/ruby-date-time

Struggling with Ruby: Contents Page

2 comments:

Anonymous said...

That's an interesting fact about Time. (speed difference) Good to know.

Unknown said...

Very NEAT content...helped me in needy!!!!