How Rails Can Reduce Your Monthly Headaches
Duration’s helping hand when months have varying lengths
All months have 30 days. Or 31 days. Oh, and February came to the party late with about 28.
We can live with that in our day-to-day lives by learning tricks and rhymes to remember how many days in a month. If we’re still unsure, we can just look at a calendar to confirm. But what about in code? How can we say “add a month”? Or see if something is one month ago? Do we use 30 days? 31 days?
def months ActiveSupport::Duration.new(self * 30.days, [[:months, self]]) end
It’s because of this concept of parts (
[:months, self]) that we get what we want when we do math with
DateTime specify the parts of the time when they can. This allows us to add or subtract a month without worrying about the number of days in that particular month. We can see this when using
> 1.month.parts => [[:months, 1]] > (2.months + 4.days).parts => [[:months, 2], [:days, 4]]
Great! So now we can do whatever we want with months and not have to worry about it. Well… almost. What’s the
#months uses to create a
Duration? This is
Fixnum value it uses in situations where using
parts doesn’t make sense, such as when comparing to a number of seconds.
> thirty_days, thirty_one_days = 30.days.to_i, 31.days.to_i => [2592000, 2678400] > 1.month == thirty_days => true > 1.month == thirty_one_days => false
This can turn into a problem when looking to see if two times were within a month of each other.
time2 - time1 < 1.month
1.month will always be
30.days, so cases such as July 31 and August 30 will not be within one month of each other. This is an issue.
We can avoid cases such as this if we rewrite it to apply
1.month directly on one of the times:
time1 + 1.month < time2
Moral of the story? Apply the result of
#month directly when using mathematical operations whenever you can. If for some reason you can’t, use caution. Is it okay that it is always 30 days? Would it be better to overshoot or undershoot with 31 or 28 days? Could it work to refactor or add an extra check? We’ve dealt with the month of February since King Numa Pompilius, the second king of Rome, introduced it to the calendar in 713 BC. With careful handling, we should be able to account for that tricky month as we keep working with it in computing.