Parsing and formatting dates

Date.parse

Date.parse reads the common written forms and returns a Date, which is a day with no time and no zone. Adding an integer adds days, not seconds:

ruby -e 'require "date"; puts Date.parse("2026-11-04") + 30'

2026-12-04

2016-11-04, 4 November 2026 and 11/04/2026 all parse, and the last one is the reason a program should not rely on the guess: an American and a European reader disagree about which number is the month.

Time.strptime

When the input format is known, say it. Time.strptime takes a pattern and returns a Time, and an input that does not match raises instead of guessing:

ruby -e 'require "time"; p Time.strptime("05/11/2026 14:30", "%d/%m/%Y %H:%M")'

2026-11-05 14:30:00 +0000

%d is the day, %m the month, %Y the four-digit year, %H:%M the 24-hour time. A pattern with %y or %I is a source of off-by-a-century and off-by-twelve bugs; the long forms are worth the two extra characters.

Formatting the other way

strftime turns a date into a string, and iso8601 is the format to prefer when another program will read it:

ruby -e 'require "date"; p Date.new(2026, 11, 5).iso8601; p Date.new(2026, 11, 5).strftime("%A %d %B %Y")'

"2026-11-05"
"Thursday 05 November 2026"

%A is the full weekday and %B the full month, both in the language of the process locale — English here. A UI that must show a translated month formats it itself; a log keeps ISO 8601.

The month arithmetic trap

A month is not thirty days. Date#>> adds calendar months and clamps the day to the end of the target month, while adding seconds to a Time adds plain duration:

ruby -e 'require "date"; puts Date.new(2026, 1, 31) >> 1; puts Time.new(2026, 1, 31) + 30 * 86_400; puts Date.today'

2026-02-28 2026-03-02 00:00:00 +0000 2026-11-05

One month after 31 January is 28 February, which is what a subscription means. Thirty days after 31 January is 2 March, which is what a duration means. The two answers are both right for their question, and a billing bug is usually the wrong one picked.

What to watch for

Date.today uses the local zone, which on a server is often UTC and on a laptop is not. A report that says "yesterday" needs the zone named, not the default.

Date.parse accepts a date with no year and fills in the current one, and accepts impossible dates less strictly than Date.new. Validate the input with strptime and an explicit pattern when the source is a user or a file.

Next: Unicode, bytes and encodings

Article author: Arthur Isaev

Banner image