How can I calculate the number of weeks since a given date in Ruby?
Asked Answered
S

3

11

Objective

I am trying to calculate the distance in weeks since a given date without jumping through hoops. I'd prefer to do it in plain Ruby, but ActiveSupport is certainly an acceptable alternative.

My Code

I wrote the following, which seems to work but looks like the long way around to me.

require 'date'

DAYS_IN_WEEK = 7.0 

def weeks_since date_string
  date  = Date.parse date_string
  days  = Date.today - date
  weeks = days / DAYS_IN_WEEK
  weeks.round 2
end

weeks_since '2015-06-15'
#=> 32.57

ActiveSupport's #weeks_since takes a number of weeks as its argument, so it doesn't fit this use case. Ruby's Date class doesn't seem to have anything relevant, either.

Alternatives?

Is there a better built-in solution or well-known algorithm for calculating the number of weeks separating a pair of dates? I'm not trying to code-golf this, as readability trumps brevity, but simply to learn whether Ruby natively supports the type of date arithmetic I've coded by hand.

Serialize answered 29/1, 2016 at 17:20 Comment(6)
Weeks do not start on the same day across the world. Which definition are you taking? Or, by week, do you actually mean days/7? For example based on Monday-starting week, Jan 30, 2016 and Jan 31, 2016 belong to the same week, but based on Sunday-starting week, they belong to different weeks.Stine
Using times it would be (t2 - t1) / 604800. Which is (60 * 60 * 24 * 7) = 1 week in seconds. otherwise your solution will also do a similar thing.Makassar
@Stine I think he is defining week in a much purer sense of 7 contiguous days.Makassar
@Makassar You're correct. I'm defining weeks as blocks of 7 contiguous days, rather than "work weeks" or "calendar weeks."Serialize
You could say, "distance in meters" or "time in weeks", but not "distance in weeks". :-)Cootch
if you just mean 7-day increments, as the code you've got does, I don't see anything wrong with the code you've got or any reason to improve it.Overexpose
R
16
require 'date'

str = '2015-06-15'

Date.parse(str).step(Date.today, 7).count                  # => 33
Date.parse(str).upto(Date.today).count.fdiv(7).round(2)    # => 32.71
Reservation answered 29/1, 2016 at 19:57 Comment(4)
Okay, I see Date#step defined, but even after reading the docs, I'm not sure why your example works.Serialize
Date#step accepts an other Date object, and a number. It will return an Enumerator object, consist of the days difference between those two dates. Date#upto(some_date) is equivalent as Date#step(some_date, 1).Reservation
It is helpful to point out that the second argument pertains to the number of days per step. This is not well documented as of Ruby 2.4.1.Mccaslin
To be more precise, the first one should be Date.parse(str).step(Date.today, 7).count - 1 so that (Date.current - 7.days).step(Date.current, 7).count - 1 == 1Protocol
O
8

Might be easier to convert the dates to time and then divide the time difference by a week. You can round it however you want or ceil.

def weeks_since(date_string)
  time_in_past = Date.parse(date_string).to_time
  now = Time.now
  time_difference = now - time_in_past
  (time_difference / 1.week).round(2)
end
Oxus answered 14/3, 2018 at 11:30 Comment(0)
L
7

in_weeks (Rails 6.1+)

Rails 6.1 introduces new ActiveSupport::Duration conversion methods like in_seconds, in_minutes, in_hours, in_days, in_weeks, in_months, and in_years.

As a result, now, your problem can be solved as:

date_1 = Time.parse('2020-10-18 00:00:00 UTC')
date_2 = Time.parse('2020-08-13 03:35:38 UTC') 

(date_2 - date_1).seconds.in_weeks.to_i.abs
# => 9

Here is a link to the corresponding PR.

Lenis answered 19/10, 2020 at 1:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.