2

I am trying to created a list of year-week (equivalent to mySQL's YEARWEEK(date,1)) falling between two date values in Rails. List is generating perfectly if start-date and end-date are falling in same year. Here is my code:

  campaign_start_date = "2013-08-02 06:59:00"
  campaing_end_date = "2013-09-01 06:59:00"

  start_year = DateTime.parse(campaign_start_date).cwyear
  start_week = "%04d%02d" % [start_year, DateTime.parse(campaign_start_date).cweek]

  end_year = DateTime.parse(campaing_end_date).cwyear
  end_week = "%04d%02d" % [end_year, DateTime.parse(campaing_end_date).cweek]

  if start_year == end_year
    (start_week..end_week).each{ |i| result << i }
  else
    # need to build a suitable logic here. to handle the case when duration spans over multiple years. for example started in 01-Nov-14 and ended in 01-May-15
  end

  return result

there will be no problem with above date values, it will fall to if case and the result I will get is:

[
  "201331",
  "201332",
  "201332",
  "201333",
  "201334",
  "201335"
]

which is also what I exactly want. BUT if my start-date and end-date values are these for example:

  campaign_start_date = "2014-07-23 06:59:00"
  campaing_end_date = "2015-03-01 06:59:00"

means falling in different years, then it need different logic that the one I have in if condition because for these date values (start_week=201430 and end_week=201509) the if condition is not suitable here because it would generate 80 values, which is wrong because number of weeks between these dates are not 80. Need help to develop the logic for else case. May be its easy but right now I am just tired to dig it any deeper.

Special attention: the solutions should care about commercial year and commercial week (refer .cwyear and .cweek functions of rails) For example yearweek of 2016-01-01 would be 201553 not 201601

any help in this regard would be much appreciated.

alishad
  • 139
  • 1
  • 2
  • 11

4 Answers4

1

Thank you to those who replied t. I've finally solved the problem like this:

  campaign_weeks = []

  campaign_start_date = "2014-07-23 06:59:00" # or any date
  campaing_end_date = "2015-03-01 06:59:00" # or any date

  start_year = DateTime.parse(campaign_start_date).cwyear
  start_cweek_of_the_campaign = "%04d%02d" % [start_year, DateTime.parse(campaign_start_date).cweek]

  end_year = DateTime.parse(campaing_end_date).cwyear
  end_cweek_of_the_campaign = "%04d%02d" % [end_year, DateTime.parse(campaing_end_date).cweek]

  if start_year == end_year
    (start_cweek_of_the_campaign..end_cweek_of_the_campaign).each do |w|
      campaign_weeks << ("%04d%02d" % [start_year, w])
    end
  else
    (start_year..end_year).each do |y|
      first_cweek_number_of_the_year = (y == start_year) ? start_cweek_of_the_campaign : 1
      last_cweek_number_of_the_year = (y == end_year) ? end_cweek_of_the_campaign : DateTime.new(y, 12, 28).cweek
      (first_cweek_number_of_the_year .. last_cweek_number_of_the_year).each do |w|
        campaign_weeks << ("%04d%02d" % [y, w])
      end
    end
  end

  return campaign_weeks

Notes: 28th Dec always fall in the last cweek/iso-week of the year. last ISO week of the year is either 52 or 53.

Reference: http://en.wikipedia.org/wiki/ISO_week_date#Last_week

Got some hint from this answer: Calculating the number of weeks in a year with Ruby

Community
  • 1
  • 1
alishad
  • 139
  • 1
  • 2
  • 11
0

The bottleneck is (start_week..end_week) range. It apparently goes through hundred (since we are on decimals):

 2014xx ⇒ 201452 ⇒ 201453 ⇒ ... ⇒ 201499 ⇒ 201500 ⇒ ...

You should probably filter your range, like:

r = (start_week..end_week)
r.to_a.reject { |e| e[-2..-1].to_i > 52 }

Depending on how you count weeks (=-based, or 1-based,) the 201500 should be likely filtered as well:/

r.to_a.select { |e| e[-2..-1].to_i.between? 1, 52 }
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
0

Try this out; it will work for any set of dates irrespective of whether the years are the same or not:

campaign_start_date = "2014-07-23 06:59:00"
campaign_end_date = "2015-03-01 06:59:00"

start_date = DateTime.parse(campaign_start_date)
end_date = DateTime.parse(campaign_end_date)

while start_date < end_date
  puts "%04d%02d" % [start_date.cw_year, start_date.cweek]
  start_date = start_date + 7.days
end
Prakash Murthy
  • 12,923
  • 3
  • 46
  • 74
  • it won't cares about commercial year and commercial week i.e `.cwyear` and `.cweek`.... for example if date is `2016-01-01` then its year date would be `201553` and not `201601` – alishad Apr 10 '15 at 14:46
  • Use the syntax you already had for formatting the output. I updated the answer. – Prakash Murthy Apr 10 '15 at 15:18
  • thanks @prakash, but start date can be any day of the week. like if start date was a Wednesday then 2nd week would not start on (start_date + 7.days). The second week would start on next Monday – alishad Apr 13 '15 at 06:57
0

A bit late in the discussion but here is what I used to get the number of commercial weeks between two dates:

def cweek_diff(start_date, end_date)
  return if end_date < start_date
  cweek_diff = (end_date.cweek - start_date.cweek) + 1
  cwyear_diff = end_date.cwyear - start_date.cwyear
  cyear_diff * 53 + cweek_diff - cwyear_diff
end

It worked perfectly in my case. Hope it helps ;)

Olivier
  • 696
  • 4
  • 12