Meanwhile, back at the farm

The personal blog of Michael Farmer

Getting the week number of the month

Comments

Spherical combination puzzle of the 3x3x3 Rubi...

Image via Wikipedia

I’m working on a project where I need to get all kinds of information about dates.  One of the latest puzzles I ran into was how to determine what week of the month a given date is.  I thought of many long ways to do this like iterating through the month and incrementing a counter when I hit Saturday, but that sounded very inefficient and unscalable to me.  So I decided to see if I could figure out how to do it mathematically.  It was a good puzzle and took me about an hour to figure out (math puzzles aren’t my specialty) but I finally got it.  Here is what I came up with:

require 'date'
def get_week_of_month(d, iso?=false)
  dm = Date.new(d.year, d.month, 1)</code>
 
  # i is a format string that sets this to use ISO or not
  i = iso? ? '%u' : '%w'
 
  # x is the day of the week of the first day of the month
  x = dm.strftime(i).to_i + 1
 
  # n is the day number of the month
  n = d.day
 
  # o is an offset used to calculate weeks for months that don't start on
  # the first day of the month. It essentially makes the month always start
  # on Sunday (or Monday if ISO).
  o = (n+(x-1))
 
  # r is the remainder indicator. We have to add 1 if there is a remainder
  r = (o%7) == 0 ? 0 : 1
 
  return ((o/7) + r).to_i
end

The first thing I did was assume that all months start on Sunday which gave me a nice grid to play around with. Then I started playing with the math and realized that if I divide any number by 7 that got me really close.  If I dropped the remainder, then add 1 that did it every time.  Well, almost.  Saturdays were then off by 1 because they evenly divided into 7.  That was easy to fix though. I just rounded up the remainder to 1 if it was there and then left it zero if it wasn’t. Hence the “r” variable.

The next problem to tackle was handling the months when the first of the month wasn’t on Sunday.  To do this I figured out there would have to be some kind of offset.  Simple math showed me the offset was the number of the day of the week of the date I was evaluating plus the day of the week of the first day of the month minus 1.  By applying that to the original formula, I essentially reset my calendar to starting on the first Sunday of the month and my original formula began to work again.

The final problem to tackle was supporting the ISO week number standard which has weeks start on Monday instead of Sunday.  That was easily solved by just changing the beginning of the month reference point.  In ruby, and probably many other languages, that just meant changing the format string from a %w to a %u.

Anyway, this was a really fun puzzle and now I’m glad it’s over so I can move on.

Reblog this post [with Zemanta]

Written by mikefarmer

September 18th, 2009 at 6:07 pm

Posted in Development

Tagged with , ,

blog comments powered by Disqus