So you’re building your first Ruby project (how exciting!), and you come across the need to get the date, or the time, or both. Maybe this is not your first Ruby rodeo, and you just need a quick refresher on how to turn this crazy return value into something nicely readable with human eyes. Either way, this is the blog post for you.
You may know that Ruby has a class called DateTime, or Time, in the same way it has other classes like String, Integer, Object, and so forth. What is the difference between Time and Datetime? Since I’m going to assume you’re using a newer version of Ruby (2.0+), the bottom line is: not much. Pick one for consistency’s sake. Some recommend DateTime, since that’s the one preferred by ActiveRecord (and we sure do love ActiveRecord!). Others recommend Time, because it’s thought to be better for data relating to current, future, and near-past dates and times, and these are likely to be the majority of practical use. The same folks would go on to say that DateTime is better only for dealing with the not-so-near past, for example if you wanted to convert Shakespeare’s birthday (it’s April 1564 btw). It’s worth mentioning that the Ruby documentation takes the stance of the latter. There is some dissent on the matter of handling time zones and daylight savings. If you don’t need to consider those factors, either Time or DateTime will suit your purposes.
(Don’t forget to $ require ‘date’ if you want to use DateTime. It’s a Ruby gem.)
What we can all agree on, however, is that you should use Time.now or DateTime.now instead of Time/DateTime.new to capture an instance of the time at creation. Time/Date.new should be used when you are entering a predetermined date into your program. You can pass it the appropriate information as arguments and create an instance that is new to the program, but not an instance of now. While .new without arguments does work to capture an instance of the current time, using .now makes your intentions clearer and therefore your code more readable. Time.now and Time.new produce an almost identical return value, but the difference is extremely evident in the difference between DateTime.now and DateTime.new. Try it out for yourself.
When you create an instance of the time, the return value is not exactly friendly to human eyes. You have something like this:
d = DateTime.now #=> 2020–12–19T13:12:34–08:00
Personally, if I’m trying to read some data about a date and a time, I’d prefer something a little nicer. Like this:
more_readable #=> “Sat, December 19, 2020 at 12:34pm”
Luckily, Ruby has an absolutely stunning built-in method called .strftime. This is shorthand for string-format-time. It uses directives, which look a lot like RegEx (another blog post for another time), to tell Ruby how to format the time into your desired string. All directives begin with a % character. You chain them together with appropriate spacing or separation characters, and it’s like magic. Here are some examples, using the datetime we captured above, d.
d.strftime(“Captured at %I:%M%P” ) #=> “Captured at 12:34pm”d.strftime(“Written on %m/%d/%y”) #=> “Written on 12/19/2020”
Let’s break it down. In the first example, we used three directives: %I, %M, and %P. The first two are separated by a colon, which as you can see, shows up in the resulting string as just another character to separate the hour and the minutes. In the second example, we use different directives: %m, %d, and %y, all separated by a slash which, again, translates cleanly. Notice that the rest of the return message (“written on”) can simply be included inside the string quotes with the directives. We don’t need to take the extra step to parse the datetime and then interpolate the results in a string, although in some cases you may want to. Thanks, Ruby!
Here is a list of what I find to be the most useful directives:
Date (Year, Month, Day):%Y — Year (four digits. 2020)%y — abbreviated year (last two digits… 20)
#nice to add an ‘ before to produce => ’20%m — Month of the year, zero-padded (01..12)%_m blank-padded ( 1..12)%-m no-padded (1..12)%B — The full month name (``January’’)%^B uppercased (``JANUARY’’)%b — The abbreviated month name (``Jan’’)%^b uppercased (``JAN’’)%d — Day of the month, zero-padded (01..31)%-d no-padded (1..31)%e — Day of the month, blank-padded ( 1..31)%j — Day of the year (001..366)Time (Hour, Minute, Second, Subsecond):%H — Hour of the day, 24-hour clock, zero-padded (00..23)%k — Hour of the day, 24-hour clock, blank-padded ( 0..23)%I — Hour of the day, 12-hour clock, zero-padded (01..12)%l — Hour of the day, 12-hour clock, blank-padded ( 1..12)%P — Meridian indicator, lowercase (``am’’ or ``pm’’)%p — Meridian indicator, uppercase (``AM’’ or ``PM’’)%M — Minute of the hour (00..59)%S — Second of the minute (00..60)%L — Millisecond of the second (000..999)Weekday:%A — The full weekday name (``Sunday’’)%^A uppercased (``SUNDAY’’)%a — The abbreviated name (``Sun’’)%^a uppercased (``SUN’’)%u — Day of the week (Monday is 1, 1..7)%w — Day of the week (Sunday is 0, 0..6)Time zone:%z — Time zone as hour and minute offset from UTC (e.g. +0900)%:z — hour and minute offset from UTC with a colon (e.g. +09:00)%::z — hour, minute and second offset from UTC (e.g. +09:00:00)%:::z — hour, minute and second offset from UTC
(e.g. +09, +09:30, +09:30:30)Literal string:%n — Newline character (\n)%t — Tab character (\t)%% — Literal ``%’’ characterCombinations:%c — date and time (%a %b %e %T %Y)%D — Date (%m/%d/%y)%F — The ISO 8601 date format (%Y-%m-%d)%v — VMS date (%e-%b-%Y)%r — 12-hour time (%I:%M:%S %p)%R — 24-hour time (%H:%M)%T — 24-hour time with second (%H:%M:%S)%+ — date(1) (%a %b %e %H:%M:%S %Z %Y)
(visit https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html for the comprehensive list)
You might be thinking, ‘Wow! So nice. But how am I supposed to learn all these directives?’ The answer is that you don’t have to learn them all. And you probably shouldn’t waste valuable brain space on something so painfully technical. Find a documentation (perhaps this blogpost?) that provides the information for you, and bookmark it. As long as you understand the concept, simply reference it every time you need to frankenstein together a parsed date or time, and eventually the most frequently used ones will stick in your brain. Or not! And that’s okay too. As long as you have the internet, you’ll be fine. I’m told all the time by many, many people in this industry that even the best and most senior developers look up syntax all. the. time. Work smarter, not harder.