Ask any Ruby developer what led them to embrace the language, and most will praise its “beauty,” comparing Ruby’s syntax to a eloquent poem or piece of art. Admittedly, this sort of aggrandization used to elicit a sneer from yours truly, although at that point I’d never actually looked into the language to determine what all of the fuss was about. That is, until one day a single line of Ruby code caught my attention while surfing online:
Made possible through the Active Support gem, when executed, this line of code will return the date and time occuring exactly seven days prior to its execution. Today being Valentine’s day, executing this code now will return:
Thu Feb 14 11:10:26 -0500 2008
Although I’m not yet prepared to place the language alongside the Picasso’s and Chagall’s hanging in the Met, since encountering this mere line of code I’ve been utterly hooked on the language. But, this example is just the tip of the iceberg when it comes to Ruby’s intuitive date and time parsing and calculation capabilities. In this tutorial, I’ll introduce you to some of the language’s temporal features, helping you to turn what is traditionally a frustrating aspect of programming into something that’s fun and productive.
An Exploration of Time
The best way to become familiar with Ruby’s time features is to fire up the interactive Ruby shell (IRB) and start experimenting. To do so, open a terminal and execute irb. Start simple and determine the current date and time, done with the DateTime class’ now method:
irb>DateTime.now => Thu, 14 Feb 2008 13:19:21 -0500
You can take advantage of a number of methods to learn more about the current time and date:
irb>DateTime.now.year => 2008 irb>DateTime.now.month => 2 irb>DateTime.now.day => 14 irb>DateTime.now.hour => 09 irb>DateTime.now.min =>32 irb>DateTime.now.sec =>59
You also can use the DateTime class to learn more about a given date’s surroundings. For instance, what will the date be four years from now, and what was it four years ago?
irb>DateTime.now.years_since(4) => Tue, 14 Feb 2012 13:37:42 -0500 irb>DateTime.now.years_since(4) => Sat, 14 Feb 2004 13:37:42 -0500
You also can use DateTime’s methods to calculate the beginning of the current month and beginning of the current quarter:
irb>DateTime.now.beginning_of_month.to_date => Fri, 01 Feb 2008 irb>DateTime.now.beginning_of_quarter.to_date => Tue, 01 Jan 2008
How about determining the current day of the week? You can use the strftime method to determine this and so much more. strftime works similarly to PHP’s strftime() function, accepting a time, bject, and formatting it according to a set of directives. The list of directives is too long to list here, so refer to the previous link for a complete breakdown. Returning to the objective, use strftime to learn more about today’s weekday:
irb>DateTime.now.strftime("%A") => Thursday
Remember the somewhat user-unfriendly string returned by Time.now? What if you wanted to recreate the current date/time in a friendlier form? You can use several of strftime’s directives to create whatever format you please:
irb>DateTime.now.strftime("%b %d, %Y") => "Feb 14, 2008" irb>DateTime.now.strftime("Today is %A, %I:%M%p") => "Today is Thursday, 12:21PM" irb>DateTime.now.strftime("%Y-%m-%d") => "2008-02-14"
Calculating the Number of Days Between Two Dates
The previous examples are all interesting, but programmers don’t always live in the here and now. Often, they’re tasked with working with dates residing well into the future and the past. For instance, what if you wanted to calculate the distance between Memorial Day and the Fourth of July?
irb>memorial = DateTime.new(2008,05,31) => Sat, 31 May 2008 00:00:00 +0000 irb>fourth = DateTime.new(2008,07,04) => Fri, 04 Jul 2008 00:00:00 +0000 irb>days = (fourth - memorial).to_i => 34
Never Be Late Again with Chronic
Ruby’s built-in time and date parsing and calculation capabilities are great, but some really interesting work has been done by the community to really enhance the ability to perform some powerful temporal calculations. One of my favorite such projects is Chronic, a date/time parser that makes it possible to reference dates in a fascinating and natural fashion. Install Chronic through Ruby’s gem system:
%>gem install chronic
Once installed, fire up irb again and retrieve the Chronic library:
Next, follow along with these examples to learn more about what Chronic has to offer:
irb>Chronic.parse('yesterday') => Wed 13 Feb 2008 12:00:00 -0500 2008 irb>Chronic.parse('yesterday').to_date => Wed, 13 Feb 2008 irb>Chronic.parse('last monday').to_date => Mon, 11 Feb 2008 irb>Chronic.parse('next month').to_date => Sun, 16 Mar 2008 irb> Chronic.parse('3 months ago this friday at 3:45pm') => Thu Nov 15 15:45:00 -0500 2007
Chronic offers a second benefit that can be immensely useful if you’re dealing with free-form dates. Suppose you wanted to analyze a data file that contains a bunch of dates, converting each to a Date object. These dates used inconsistent formatting, however, with some using dash separators, and others using slash separators. Chronic recognizes both:
irb>Chronic.parse('07-04-2008').to_date => Fri, 04 Jul 2008 irb>Chronic.parse('07/04/2008').to_date => Fri, 04 Jul 2008
Suppose the inconsistency problem was worse than you expected, and you found an occasional date used a dash to separate the month and day, but a slash to separate the day and year? Chronic is capable of handling these too:
irb>Chronic.parse('07-04/2008').to_date => Fri, Jul 04 2008
Rails and Dates
At the beginning of this tutorial, I referenced the great date-related features available through the Active Support gem. Active Support is an integral part of the Rails framework, meaning you can take advantage of all Active Support has to offer within your Rails applications. For instance, suppose you wanted to retrieve a list of users who have logged in within the last seven days:
@users_recently_active = User.find(:all, :conditions => ["users.last_login < ?", 7.days.ago], :order => "last_login DESC")
User-Friendly Temporal Messages
Within Rails views, you can take advantage of other date/time-specific features introduced by another key gem used by Rails, named ActionView. For instance, suppose you wanted to provide users with a somewhat more friendly message indicating the last time a specific user logged in. The message might look something like this:
This user was last visible 4 days ago
To implement this, all you need to do is use the ActionView helper time_ago_in_words:
This user was last visible <%= time_ago_in_words(user.last_login)%> ago
I hope this brief tutorial regarding Ruby’s amazing date and time manipulation capabilities gives you cause to consider exploring this powerful language! If you’d like to share a tip, email me at jasonATwjgilmore.com!
About the Author
W. Jason Gilmore is a freelance Web developer, consultant, and technical writer. He’s the author of several books, including the best-selling Beginning PHP and MySQL 5: Novice to Professional, Second Edition (Apress, 2006. 913pp.).