Skip to content

Instantly share code, notes, and snippets.

@hmcfletch
Created July 28, 2011 06:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hmcfletch/1111103 to your computer and use it in GitHub Desktop.
Save hmcfletch/1111103 to your computer and use it in GitHub Desktop.
Determine the number of months between two time stamps
# A direct calculation of the number of months between two dates
class Time
class << self
def months_between(start_date, end_date)
return -months_between(end_date, start_date) if end_date < start_date
s = start_date
t = end_date
whole_months = (t.year * 12 + t.month) - (s.year * 12 + s.month)
# handle variable length months
s_last_day = days_in_month(s.month, s.year)
t_last_day = days_in_month(t.month, t.year)
if s.day == s_last_day && t.day == t_last_day && s.day >= t.day
elsif s.day == s_last_day && t.day == t_last_day && s.day > t.day
return whole_months
elsif s.day == s_last_day && s.day > t.day
return whole_months - 1
elsif s.day == s_last_day && s.day == t.day
elsif s.day == s_last_day && s.day < t.day
return whole_months
elsif t.day == t_last_day && s.day >= t.day
elsif t.day == t_last_day && s.day < t.day
return whole_months
elsif s.day > t.day
return whole_months - 1
elsif s.day == t.day
elsif s.day < t.day
return whole_months
end
# handle time part
[:hour, :min, :sec].each do |part|
if s.send(part) > t.send(part)
return whole_months - 1
elsif s.send(part) <= t.send(part)
return whole_months
end
end
whole_months
end
end
end
# This is a very simple yet potentially inefficient way of determining the months between two dates
# The method takes longer the farther the start and end dates are from each other
class Time
class << self
def months_between2(start_date, end_date)
return -months_between2(end_date, start_date) if end_date < start_date
count = 1
while true
return count - 1 if (start_date + count.months) > end_date
count += 1
end
end
end
end
# A method with lots of test cases comparing the two methods of calculating the number of months between two dates
# The test runs both methods of calculation against the two given dates and compares them. Outputs when there are differences.
def self.test_months
# a set of start and end date
start_dates = [ '2011-02-12', '2011-02-27', '2011-02-28', '2011-03-13', '2011-03-30', '2011-03-31',
'2011-04-29', '2011-03-30', '2011-05-30', '2011-05-31' ]
end_dates = [ '2011-02-12', '2011-02-14', '2011-04-25', '2011-04-30', '2011-05-17', '2011-05-30',
'2011-05-31', '2011-07-25', '2011-07-30', '2011-07-31', '2011-08-05', '2012-02-28',
'2012-02-29' ]
# a set of times fo append to above dates
time_stamps = [ '00:00:00', '00:00:00', '00:00:00', '01:56:23', '04:23:42', '12:04:34', '16:32:03', '22:22:22' ]
# a set of start and end dates that proved useful in finding edge cases
specials = [ [ '2011-03-31 16:32:03', '2011-07-31 12:04:34' ],
[ '2011-03-31 22:22:22', '2012-02-29 12:04:34' ],
[ '2011-03-30 22:22:22', '2012-02-29 01:56:23' ],
[ '2011-05-31 12:04:34', '2011-07-31 00:00:00' ],
[ '2011-02-28 01:56:23', '2012-02-29 00:00:00' ],
[ '2011-03-30 22:22:22', '2012-02-29 00:00:00' ],
[ '2011-02-28 04:23:42', '2011-07-31 00:00:00' ],
[ '2011-02-28 16:32:03', '2011-04-30 00:00:00' ],
[ '2011-02-28 16:32:03', '2011-07-31 12:04:34' ],
[ '2011-02-28 16:32:03', '2012-02-28 00:00:00' ],
[ '2011-03-30 22:22:22', '2012-02-29 04:23:42' ],
[ '2011-03-31 16:32:03', '2011-04-30 04:23:42' ],
[ '2011-05-31 04:23:42', '2012-02-29 01:56:23' ],
[ '2011-03-31 12:04:34', '2011-04-30 04:23:42' ],
[ '2011-03-31 01:56:23', '2011-04-30 00:00:00' ],
[ '2011-03-31 04:23:42', '2012-02-29 00:00:00' ],
[ '2011-05-31 16:32:03', '2012-02-29 01:56:23' ]
]
# A set of test cases that cover all combinations of:
# start_date is last day of the month
# end_date is last day of the month
# start_date.day <=> end_date.day
# start_date.day == end_date.day && start_date.day <=> end_date.day
special2 = [ [ '2011-03-31 00:00:00', '2011-04-30 00:00:00' ], # 1
[ '2011-03-31 03:12:43', '2011-05-31 13:32:54' ], # 2
[ '2011-03-31 06:17:23', '2011-05-31 06:17:23' ], # 3
[ '2011-03-31 00:00:00', '2011-05-31 00:00:00' ], # 4
[ '2011-04-30 00:00:00', '2011-05-31 00:00:00' ], # 5
[ '2011-03-31 00:00:00', '2011-06-27 00:00:00' ], # 6
[ '2011-02-28 03:12:43', '2011-07-28 13:32:54' ], # 7
[ '2011-02-28 06:17:23', '2011-07-28 06:17:23' ], # 8
[ '2011-02-28 13:32:54', '2011-07-28 03:12:43' ], # 9
[ '2011-02-28 00:00:00', '2011-05-29 00:00:00' ], # 10
[ '2011-03-30 00:00:00', '2012-02-29 00:00:00' ], # 11
[ '2011-04-30 03:12:43', '2011-06-30 13:32:54' ], # 12
[ '2011-04-30 06:17:23', '2011-06-30 06:17:23' ], # 13
[ '2011-04-30 13:32:54', '2011-06-30 03:12:43' ], # 14
[ '2011-04-30 00:00:00', '2011-07-31 00:00:00' ], # 15
[ '2011-03-17 00:00:00', '2011-07-15 00:00:00' ], # 16
[ '2011-03-17 03:12:43', '2011-08-17 13:32:54' ], # 17
[ '2011-03-17 06:17:23', '2011-08-17 06:17:23' ], # 18
[ '2011-01-17 13:32:54', '2011-07-12 03:12:43' ], # 19
[ '2011-01-17 00:00:00', '2011-07-12 00:00:00' ], # 20
[ '2011-03-12 00:00:00', '2011-09-23 00:00:00' ], # 21
]
total_right = 0
total_wrong = 0
start_dates.each do |s|
end_dates.each do |e|
st = Time.parse("#{s} #{time_stamps[rand(time_stamps.length)]}")
et = Time.parse("#{e} #{time_stamps[rand(time_stamps.length)]}")
m1 = Time.months_between(st, et)
m2 = Time.months_between2(st, et)
if m1 != m2
puts "#{st} <=> #{et} :: #{m1} <=> #{m2}"
total_wrong += 1
else
total_right += 1
end
end
end
puts "-------------------------------"
(specials + special2).each do |s|
st = Time.parse(s[0])
et = Time.parse(s[1])
m1 = Time.months_between(st, et)
m2 = Time.months_between2(st, et)
if m1 != m2
puts "#{st} <=> #{et} :: #{m1} <=> #{m2}"
total_wrong += 1
else
total_right += 1
end
end
[total_right, total_wrong]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment