yao
yao

Reputation: 53

Calculate how many n.months between two dates

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.

Upvotes: 0

Views: 872

Answers (3)

yao
yao

Reputation: 53

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'))

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110745

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

Upvotes: 2

Lam Phan
Lam Phan

Reputation: 3811

# 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

Upvotes: 1

Related Questions