0
votes

I want to calculate the distance between two dates by month:

Q: start_date + n.months >= end_dates, what is the variable n?

start_date = Date.parse('2021-01-31')
end_date = Date.parse('2021-02-28')

## start_date + 1.months = 2021-02-28
## The answer 1 month, which is more clearable for human using
## Don't want to find the answer like 28 days or 0.93 month. (30 day a month)

First I tried to let each month is 30.days, but the answer will have some bias on some special dates. Each month's date is different, and the date on End of Feb month is always the problem.

Then I tried to install gems like date_diff, time_difference..., but no methods can do this, most of the output is 28 days but not 1 month.

For simple way, I can easily do the iterated loop to find the n, like:

start_date = Date.parse('2021-01-31')
end_date = Date.parse('2021-02-28')

def calc_diff(start_date, end_date)
  n = 0
  while start_date + n.months < end_date
     n += 1
  end
  n
end

Is there any better way to find the n months between two dates instead, but not use a loop?

Thank you.

3
@AniketShivamTiwari Nope, Most of the answers set the year's day at 365 and 30 days a month, or not handle the day's column (only handle date's month and year). It will cause the problem at some end of the month (especially Feb.).yao
when i think again your question, i see your target is more clearable for human using that mean from 31-1 to 29-2 is 1 month and so i wonder whether 29-2 to 31-3 is also just 1 month (human point of view) or 2 months ?Lam Phan

3 Answers

2
votes

My understanding of the question is consistent with the examples below. I have computed the difference between Date objects date1 and date2, where date2 >= date1.

require 'date'
def months_between(date1, date2)
  12*(date2.yr - date1.yr) + date2.mon - date1.mon + date2.day > date1.day ? 1 : 0
end
months_between Date.new(2020, 1, 22), Date.new(2020, 3, 21) #=> 2
months_between Date.new(2020, 1, 22), Date.new(2020, 3, 22) #=> 2 
months_between Date.new(2020, 1, 22), Date.new(2020, 3, 23) #=> 3
months_between Date.new(2020, 1, 22), Date.new(2021, 3, 21) #=> 14
months_between Date.new(2020, 1, 22), Date.new(2021, 3, 22) #=> 14
months_between Date.new(2020, 1, 22), Date.new(2021, 3, 23) #=> 15
1
votes
# find minimum n so that `start_date + n.months >= end_dates`
def calc_diff(start_date, end_date)
 diff = (end_date.yday - start_date.yday) / 30
 return diff if start_date + diff.months >= end_date
 diff + 1
end

calc_diff(Date.parse('2021-01-31'), Date.parse('2021-02-28')) # 1
calc_diff(Date.parse('2021-01-31'), Date.parse('2021-04-30')) # 3
calc_diff(Date.parse('2021-01-31'), Date.parse('2021-05-31')) # 4
calc_diff(Date.parse('2021-02-01'), Date.parse('2021-06-01')) # 4
calc_diff(Date.parse('2021-02-01'), Date.parse('2021-06-02')) # 5

1
votes

Thanks for @Cary's and @Lam's answer.

Here is my answer to find the n month.

# try to find the minimum n month between start_date and target_date
def calc_diff(start_date, target_date)
  months_diff = (target_date.year * 12 + target_date.month) - (start_date.year * 12 + start_date.month)

  ## need to check the end of month because some special case
  ## start date: 2020-01-31 ; end date 2020-06-30
  ## the minimum n month must be 5
  ## another special case of Feb must consider (test case 15)

  if start_date.day > target_date.day && !((start_date == start_date.end_of_month || target_date.month == 2) && (target_date == target_date.end_of_month))
    months_diff = months_diff - 1
  end
  puts months_diff # it will show the minimum whole n month

  # the target_date will between inside
  # (start_date + months_diff.months) <= target_date < (start_date + (months_diff + 1).months) 
  (start_date + months_diff.months)..(start_date + (months_diff + 1).months) 
end

The Test Cases:

## test case 1
## 6/15 - 7/15  => n = 5
calc_diff(Date.parse('2020-01-15'), Date.parse('2020-06-19'))

## test case 2
## 7/15 - 8/15  => n = 6
calc_diff(Date.parse('2020-01-15'), Date.parse('2020-07-15')) 

## test case 3
## 5/15 - 6/15  => n = 4
calc_diff(Date.parse('2020-01-15'), Date.parse('2020-06-01')) 

## test case 4 (special case)
## 6/30 - 7/31  => n = 5
calc_diff(Date.parse('2020-01-31'), Date.parse('2020-06-30'))

## test case 5
## 7/30 - 8/30  => n = 4
calc_diff(Date.parse('2020-04-30'), Date.parse('2020-07-31'))

## test case 6
## 6/30 - 7/30  => n = 2
calc_diff(Date.parse('2020-04-30'), Date.parse('2020-06-30'))  

## test case 7
## 5/31 - 6/30  => n = 4
calc_diff(Date.parse('2020-01-31'), Date.parse('2020-05-31'))

## test case 8
## 2/29 - 3/31  => n = 1
calc_diff(Date.parse('2020-01-31'), Date.parse('2020-02-29'))

## test case 9
## 6/29 - 7/29  => n = 4
calc_diff(Date.parse('2020-02-29'), Date.parse('2020-06-30'))

## test case 10
## 7/29 - 8/29  => n = 5
calc_diff(Date.parse('2020-02-29'), Date.parse('2020-07-31'))

## test case 11
## 1/31 - 2/29  => n = 0
calc_diff(Date.parse('2020-01-31'), Date.parse('2020-02-28'))

## test case 12
## 2/29 - 3/31  => n = 1
calc_diff(Date.parse('2020-01-31'), Date.parse('2020-03-01'))

## test case 13
## 1/17 - 2/17  => n = 0
calc_diff(Date.parse('2020-01-17'), Date.parse('2020-01-17'))

## test case 14
## 1/17 - 2/17  => n = 0
calc_diff(Date.parse('2020-01-17'), Date.parse('2020-01-18'))

## test case 15 (special case)
## 1/30 - 2/29  => n = 1
calc_diff(Date.parse('2019-12-30'), Date.parse('2020-02-28'))

## test case 16
## 2/29 - 3/30  => n = 2
calc_diff(Date.parse('2019-12-30'), Date.parse('2020-02-29'))