Finding the last Sunday of a month

Hello,
I need to find the date for the last Sunday in January, for any year. I
need this for a budgetary script I’m trying to write. Using the
Date/Time module, I’ve done this so far.

require ‘date’
now = DateTime.now
year = now.year
d = Date.new(now.year, 1, 31)
puts d.wday

yields: 3

This tells me that the last day of January is a Wednesday. But, I need
that last Sunday. Is there a method in ‘date’ that can give me the date
of the last Sunday? The last Sundays of each month are the boundaries
for my company’s budget periods.

Thanks,
Peter

On 9/26/07, Peter B. [email protected] wrote:

Hmm I believe that
d = Date.new( now.year, 1, 31 )
d - d.wday
should do the trick
HTH
Robert

Peter B. wrote:

yields: 3

The last Sunday of that month can be got this way, I think:

require ‘date’
now = DateTime.now
year = now.year
d = Date.new(now.year, 1, 31)
i = d.wday
last_sunday = Date.new(now.year, 1, (31-i))
puts last_sunday

The reference for dates is at:
http://corelib.rubyonrails.org/classes/Date.html

Cheers
mohit.

On Sep 26, 8:44 am, Peter B. [email protected] wrote:

Posted viahttp://www.ruby-forum.com/.
Not perfect, but something like this:

Cassady:~ yossef$ irb
irb(main):001:0> require ‘date’
=> true
irb(main):002:0> d = Date.new(2007, 2, 1)
=> #<Date: 4908265/2,0,2299161>
irb(main):003:0> d.to_s
=> “2007-02-01”
irb(main):004:0> d -= 1
=> #<Date: 4908263/2,0,2299161>
irb(main):005:0> d.to_s
=> “2007-01-31”
irb(main):006:0> d -= d.wday
=> #<Date: 4908257/2,0,2299161>
irb(main):007:0> d.to_s
=> “2007-01-28”
irb(main):008:0> exit
Cassady:~ yossef$ cal 1 2007
January 2007
S M Tu W Th F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

The “secret”, as I see it, is getting the last day of the month and
subtracting that date’s wday from it.

Note that I start by getting the first day of the next month and going
back one day. I’m not sure about a sure-fire way to always get the
last day of a month by going forward (because you can get errors
trying to hit the 31st of September), but going back one day from the
first of the next month should work just fine.

Yes, thanks Joseff. That’s what worked for me. Subtracting the “weekday”
from the “monthday.”

Thanks to all of you. Mohit’s worked for me. It’s quite ingenious,
actually, because it’s simply subtracing the date of the week, which is
3 for Wednesday, from 31, the last day of the month. 31 - 3 = 28, so,
Sunday is the 28th. I love Ruby!

Thanks again,
Peter

On Sep 26, 9:00 am, Yossef M. [email protected] wrote:

d = Date.new(now.year, 1, 31)
Peter
irb(main):003:0> d.to_s
Cassady:~ yossef$ cal 1 2007

Note that I start by getting the first day of the next month and going
back one day. I’m not sure about a sure-fire way to always get the
last day of a month by going forward (because you can get errors
trying to hit the 31st of September), but going back one day from the
first of the next month should work just fine.

I think using -1 as the day of month gets you the last day of the
month.

irb(main):002:0> d = Date.new(2007, 1, -1)
=> #<Date: 4908263/2,0,2299161>
irb(main):003:0> d.to_s
=> “2007-01-31”
irb(main):004:0> (d - d.wday).to_s
=> “2007-01-28”

–Dale

Peter B. wrote:

Thanks to all of you. Mohit’s worked for me. It’s quite ingenious,
actually, because it’s simply subtracing the date of the week, which is
3 for Wednesday, from 31, the last day of the month. 31 - 3 = 28, so,
Sunday is the 28th. I love Ruby!

Thanks again,
Peter

Actually, I think mine just reached your first but everyone had
essentially the same suggestion. By the way, the suggestion about
subtracting from the 1st of the next month is perhaps smarter! That
way, you don’t hv to worry about what the last day of the month is (31,
30, 28, 29?) I think it’s really smart to just avoid all of that and go
back from the start of the next month!

Cheers,
Mohit.
9/26/2007 | 10:11 PM.

Actually, I think mine just reached your first but everyone had
essentially the same suggestion. By the way, the suggestion about
subtracting from the 1st of the next month is perhaps smarter! That
way, you don’t hv to worry about what the last day of the month is (31,
30, 28, 29?) I think it’s really smart to just avoid all of that and go
back from the start of the next month!

Cheers,
Mohit.
9/26/2007 | 10:11 PM.

Point taken. You’re right. Thanks.

On Sep 26, 9:10 am, Dale M. [email protected] wrote:

of the last Sunday? The last Sundays of each month are the boundaries
irb(main):001:0> require ‘date’
=> #<Date: 4908257/2,0,2299161>
28 29 30 31
I think using -1 as the day of month gets you the last day of the
month.

irb(main):002:0> d = Date.new(2007, 1, -1)
=> #<Date: 4908263/2,0,2299161>
irb(main):003:0> d.to_s
=> “2007-01-31”
irb(main):004:0> (d - d.wday).to_s
=> “2007-01-28”

–Dale

Hey, you’re right! At least that seems to work.

Thanks!

(Note: Getting this from you is apparently easier than reading the
documentation for Date.new (really Date.civil), which says “m and d
can be negative, in which case they count backwards from the end of
the year and the end of the month respectively.”)

OK. Now, I’ve got that last Sunday’s date in January. And, I’ve now
delineated all of the successive Sundays thereafter. Each of them is a
budgetary fencepost for me. So, can someone help me to determine what
budget period, out of all 13 of them, that today’s date, or any day for
that matter, would fall into? I think that all of my budget dates below
are actually strings, so, they don’t parse with dates as they are.

Thanks,
Peter

Here’s what I have now. It works. I just need to see what today, or any
day, would fall into.

require ‘date’
now = DateTime.now
year = now.year
d = Date.new(now.year, 2, 1)
d.to_s
d -= 1
d.to_s
d -= d.wday
i = d.wday
d.to_s
firstbudgetfence = d

t = Time.now
t = t.strftime("%Y-%m-%d")
budget1 = Date.new(now.year, 1, 1)
budget2 = firstbudgetfence
budget3 = firstbudgetfence + 28
budget4 = budget3 + 28
budget5 = budget4 + 28
budget6 = budget5 + 28
budget7 = budget6 + 28
budget8 = budget7 + 28
budget9 = budget8 + 28
budget10 = budget9 + 28
budget11 = budget10 + 28
budget12 = budget11 + 28
budget13 = budget12 + 28

Mohit S. wrote:

OK, I’m confused - is your budget fencepost the last Sunday or the
Sunday every 4 weeks from the end of Jan?

Cheers,
Mohit.
9/26/2007 | 10:56 PM.

It’s a 28-day cycle, like a lunar cycle. The last Sunday in January is
the first fencepost, with the 1st budgetary period being from January 1
to that Sunday. Then, that Sunday is the first day of the 2nd budget
period, and so on.

But, I think I’ve got it now. My problem was my use of Time.now instead
of DateTime.now. DateTime.now allows me to compare these budgetary dates
with today’s date. Thanks for everything.

Cheers,
Peter

OK, I’m confused - is your budget fencepost the last Sunday or the
Sunday every 4 weeks from the end of Jan?

Cheers,
Mohit.
9/26/2007 | 10:56 PM.

Anyway, if you have:

now = DateTime.now
this_month = now.month
this_budget_fencepost = \\ based on what we discussed earlier ///

then, you can find the budget fencepost for today using that…

If your budget posts are every 4 weeks from the last Sunday of January,
you should be able to get the number of days since the first
num_of_days = this_budget_fencepost - first_fencepost
fencepost_number = num_of_days/ 28

You need to put in some check if the date is earlier than the first
fencepost and check the numbers it actually produces, but roughly that
should do it for you.

Cheers,
Mohit.
9/26/2007 | 10:59 PM.

On Sep 26, 8:44 am, Peter B. [email protected] wrote:

yields: 3

Just for fun:

y=cal[/\d+/]
d = cal 1 #{y}.scan(/^.{20}$/)[-1].
split[-1].to_i

Mohit S. wrote:

Anyway, if you have:

now = DateTime.now
this_month = now.month
this_budget_fencepost = \\ based on what we discussed earlier ///

then, you can find the budget fencepost for today using that…

If your budget posts are every 4 weeks from the last Sunday of January,
you should be able to get the number of days since the first
num_of_days = this_budget_fencepost - first_fencepost
fencepost_number = num_of_days/ 28

You need to put in some check if the date is earlier than the first
fencepost and check the numbers it actually produces, but roughly that
should do it for you.

Cheers,
Mohit.
9/26/2007 | 10:59 PM.

I basically just did a comparsion for the time.now between each of the
budget periods. If between 0 and 1, then budget1; if between 1 and 2,
then budget2, and so on. A little painful, but it’s done with about 12
lines of code. I can live with that. Thanks.

I was at work and am getting into this one late, but there it is.

I would like to have something that handles the different month lengths,
and not just February. I checked to see what the value of a day was
this way:

p Time.gm(2007, ‘feb’, 2, 1, 1, 1) - Time.gm(2007, ‘feb’, 1, 1, 1, 1)

returns 86400 which is the number for exactly one day.

Therefore, I have this approach to get the day:

t = Time.gm(2007, ‘feb’, 1, 1, 1, 1) # to find for January
p (t - (one_day * t.wday)).day

:slight_smile:

Lloyd L. wrote:

t = Time.gm(2007, ‘feb’, 1, 1, 1, 1) # to find for January
p (t - (one_day * t.wday)).day

oops. one_day = 86400

Peter B. wrote:

If your budget posts are every 4 weeks from the last Sunday of January,
9/26/2007 | 10:59 PM.

I basically just did a comparsion for the time.now between each of the
budget periods. If between 0 and 1, then budget1; if between 1 and 2,
then budget2, and so on. A little painful, but it’s done with about 12
lines of code. I can live with that. Thanks.

Whatever works, I guess :slight_smile:

I’ve always preferred a calculation to 12 comparisons. That way, it
scales well when the periods change - you just need to change a division
factor :slight_smile: but like I said, whatever works for you!

By the way, are you calculating all 12 budget fenceposts every time you
need to find out which one the current day falls in?

There are numerous optimizations possible…

  • You could consider putting all the budget posts in an array and
    iterating over the array using a smaller loop code.
  • You could check what month it is and check only the budget posts
    (month-1) and (month) and (month+1). If I’m not wrong, no more than 3
    budget periods will fall in the same month - it’s a bit convoluted
    though :stuck_out_tongue:
  • If you are in fact creating the budget fenceposts as you go along, you
    don’t need to generate all 12… just generate till you find the one you
    are looking for.

Anyway, there are a few options - enjoy!

Cheers,
Mohit.
9/26/2007 | 11:38 PM.

There are numerous optimizations possible…

  • You could consider putting all the budget posts in an array and
    iterating over the array using a smaller loop code.
  • You could check what month it is and check only the budget posts
    (month-1) and (month) and (month+1). If I’m not wrong, no more than 3
    budget periods will fall in the same month - it’s a bit convoluted
    though :stuck_out_tongue:
  • If you are in fact creating the budget fenceposts as you go along, you
    don’t need to generate all 12… just generate till you find the one you
    are looking for.

Anyway, there are a few options - enjoy!

Cheers,
Mohit.
9/26/2007 | 11:38 PM.

Good suggestions all, Mohit. But, I just had to get this thing working
first, to get “the work out,” so to speak. And, yes, when I have time, I
will look at what you suggest. Cheers.