Week Number in a Year

Most of the methods I’ve turned up for numbering the weeks in a year really number seven day intervals, so if day one happens to be a Wednesday, then these week numbers will run Wednesday to Wednesday - definitely crude.

I need to repeat an event on Tuesdays and Fridays, but also in a cyclic way, and want a message on Mondays and Thursdays reminding me to do it, and of which process is to be undertaken. The cycle goes like this:

Monday: remind to do A; Thursday: remind to do B;
Monday: remind to do C; Thursday: remind to do D;
Monday: remind to do A; etc. ad nauseum.

My thought was to use odd and even week numbers to do the switch, and weekdays of current date, to make the distinction because that seemed more straight-forward than hacking with months. Harking back to this thread where I had asked how to find the start and end of daylight savings time and been overwhelmed with clevernesses by sitcom, dant, jj, Nigel Garvey, Shars, dkmarsh, and Querty Denzel, I thought to ask again for a compact way to do this from the “Calendar-savvy” crowd.


See the %U, %V (ISO 8601) and %W options in AppleMods Strftime library. HTH

The UNIX “date” command also uses the strftime call.

do shell script "date +%U" -- or %V, or %W

Great stuff, Bruce and Hamish - man strftime tells all.

Here’s a vanilla routine I wrote earlier in the year. It seems to be about 100 times as fast ” and it returns an integer. :slight_smile:

-- ISO Standard: Weeks start on Mondays. A week that straddles a year boundary
-- belongs to the year in which it has more days. One consequence of this is that
-- the "first week" of any year contains 4th January.

on ISOWeekNumber(theDate)
	set aMonday to date "Monday 6 January 1000 00:00:00"
	set nextMonday to theDate - (theDate - aMonday) mod weeks + weeks
	set Jan4 to date "Saturday 4 January 1000 00:00:00"
	set Jan4's year to nextMonday's year
	if (Jan4 does not come before nextMonday) then set Jan4's year to (Jan4's year) - 1
	set baseMonday to Jan4 - (Jan4 - aMonday) mod weeks
	-- Return both the week number and the year to which the week belongs.
	return {week:(nextMonday - baseMonday) div weeks, year:(Jan4's year)}
end ISOWeekNumber

week of ISOWeekNumber(current date)

Nicely done, Nigel. as always. :cool:

So, with thanks to all, as always: here’s what my own kluge has become (or at least this piece of it with the actual Action removed):

set theDate to (current date) -- or stick any trial date in place of current date.
set theDay to (weekday of theDate) as string
set theWeek to ISOWeekNumber(theDate) mod 2 -- Courtesy Nigel Garvey (mod 2 just gets the two-week cycle).

if theDay is "Thursday" and theWeek is 1 then
	set Action to "Do Thing 1"
else if theDay is "Monday" and theWeek is 0 then
	set Action to "Do Thing 2"
else if theDay is "Thursday" and theWeek is 0 then
	set Action to "Do Thing 3"
else if theDay is "Monday" and theWeek is 1 then
	set Action to "Do Thing 4"
	set Action to "Do Nothing"
end if


on ISOWeekNumber(theDate)
	set aMonday to date "Monday, January 6, 1000 12:00:00 AM"
	set nextMonday to theDate - (theDate - aMonday) mod weeks + weeks
	set Jan4 to date "Saturday, January 4, 1000 12:00:00 AM"
	set Jan4's year to nextMonday's year
	if (Jan4 does not come before nextMonday) then set Jan4's year to (Jan4's year) - 1
	set baseMonday to Jan4 - (Jan4 - aMonday) mod weeks
	return (nextMonday - baseMonday) div weeks
end ISOWeekNumber

Hi Adam,

I think you might find an error on years with 53 week numbers. When it switches to week 1 on the next year, the week number will be odd again.

I made a script that created a week number calendar in iCal, but lost it when the computer went down. Still working on it. It uses the formula for calculating the week number from the Julian day, so had to do some reviewing.


I’m running Jaguar, so had to calculate the month number. Basically, here’s the formula, although Nigels is faster:

property m_list : {January, February, March, April, May, June, July, August, September, October, November, December}
-- get month, day, and year numbers
--set cur_date to (current date)
set cur_date to date "Tuesday, January 10, 2006 11:00:00 AM"
set m_const to month of cur_date
repeat with m from 1 to 12
	set this_m to item m of m_list
	if this_m is m_const then exit repeat
end repeat
set _month to m
set _day to day of cur_date
set _year to year of cur_date
WeekNum(_month, _day, _year)

-- returns week numbers
-- weeks begin on Monday
on WeekNum(_month, _day, _year)
	-- get julian day
	set a to (14 - _month) div 12
	set y to _year + 4800 - a
	set m to _month + 12 * a - 3
	set jd to _day + (153 * m + 2) div 5 + y * 365 + y div 4 - y div 100 + y div 400 - 32045
	-- get week number
	set d4 to (jd + 31741 - (jd mod 7)) mod 146097 mod 36524 mod 1461
	set L to d4 div 1460
	set d1 to ((d4 - L) mod 365) + L
	set week_num to d1 div 7 + 1
	return week_num
end WeekNum


Hi Kel;

My version has more problems than the 53rd week - thanks for pointing that out, though - I hadn’t thought of it.

As it happens, I’ve set my script up to show (using iNotify) four different .tif files each showing the site of the rotating activity. The activity is on Tuesday and Friday and if a site changes, I can just change one of the four .tif files. The .tif file displayed depends on whether the weekday of the current date is between Saturday and Tuesday or between Tuesday and Friday, and also depends on whether the week is an odd or even one. That way as soon as one passes, the script will display the .tif file for the next one coming up.

Setting it up like that requires fiddling the Saturday and Sunday possiblities into the next week (where the Tuesday of the actual next event was), or figuring out how to make Nigel’s handler start weeks on Saturday (which I didn’t do, giving the volume of leaves to be mulched here today). This is what I did:

set SpecDays to {"Saturday", "Sunday"}
-- then calculate the weekday, and the week number (Nigel's handler) and finally:
if theDay is in SpecDays then set theWeek to (theWeek + 1) mod 2
-- which slides them into the next week.

I wrote a script to test it, but I didn’t crunch it out into next year. I think that the first week is odd, and the last one is too (because no year has 364 days, so there’s always at least one in 53, I think), so it should work.
Now I’ll test because this only has to work for this year and next probably.

Thanks for your script - I’ll play with it too - speed really isn’t a factor for this.

ADDENDUM: You’re right, Kel - the new year’s rollover screws it up - my reasoning was flawed - the pre and post weeks are both always odd - so two sets of odd run together.

Hi, Kel and Adam. Things immediately get easier if the goal is to maintain an uninterrupted succession of odd and even weeks. Just decide on an “even” Monday in the past, find out how many weeks ago it was, and modulate the result by 2:

set cur_date to (current date)

set evenMonday to date "Monday 6 January 1000 00:00:00" -- or the following week, if preferred.
set theWeek to (cur_date - evenMonday) div weeks mod 2
--> 0 or 1

This is a job for “French Vanilla”! :slight_smile: Or this derivative:

set cur_date to (current date)

copy cur_date to b
set b's month to January
set _month to (b - 2500000 - cur_date) div -2500000

Just change the Mondays in the script to Saturdays. :slight_smile: From what you’ve posted, I think this might cover what you want to do:

set cur_date to (current date)

set evenSaturday to date "Saturday 4 January 1000 00:00:00" -- or the following week, if preferred.

set theWeek to (cur_date - evenSaturday) div weeks mod 2 --> 0 or 1
set theWeekday to (cur_date - evenSaturday) mod weeks div days --> 0 to 6

if (theWeekday is in {0, 3}) then -- It's Saturday or Tuesday.
	set tifName to item (theWeek * 2 + theWeekday div 3 + 1) of {"2.tif", "3.tif", "4.tif", "1.tif"}
	-- Change to the file of that name.
end if

As always, elegant, Nigel. With a bit of juggling (because the cycle has already started) I’m off and running in about half the instructions I was using myself. I might add that in spite of a now 37-year old Doctorate in Mechanical Engineering, I’m not ten percent as facile as you seem to be with the use of constructs like div and mod; a firm grasp of the calculus of the complex variable, diddling with eigenvalues, and fiddling with transforms, perhaps, but not div and mod, particulary when applied to dates.

Hi Adam Bell.
Here is some information on div and mod, especially when applied to dates.

div is used to find the whole number part of a division.

3 div 4 --> 0
10 div 3 --> 3
99.99 div 10 --> 9

With dates we can use it to find the count of a particular time unit:

(365 * days) div weeks --> 52 (the number of weeks in a year)
((current date) - (date "Friday, 29 April 2005 6:00:00 AM")) div days --> days since Mac OS X 10.4 came out

mod, on the other hand, gets the remainder of a division.

10 mod 3 --> 1
64 mod 16 --> 0
59.75 mod 10 --> 9.75

With dates, we can use it to find the time elapsed (in seconds) since the time unit started.

((current date) - (date "Tuesday, 24 January 1984 12:00:00 AM")) mod days as integer --> time in seconds since the day started
((current date) - (date "Monday, 6 January 1000 12:00:00 AM")) mod weeks as integer --> time in seconds since the new week started

So by combining the two we can find:
The time in days since the new week started: [Week starts on Monday, not Wednesday! Thanks Nigel Garvey]

((current date) - (date "Monday, 6 January 1000 12:00:00 AM")) mod weeks div days

The current hour in 12-hour mode (4:23 instead of 16:23): [Edited to include correct formatting of midnight thanks to Nigel Garvey]

((time of (current date)) div hours mod 12 + 11) mod 12 + 1

Formatted time remaining until New Years:

set a to (current date)
copy a to b
tell b to set {month of it, day, time} to {December, 31, 86399}
set timeRemaining to (b - a)
get (timeRemaining div weeks as string) & " weeks " & timeRemaining mod weeks div days & " days " & timeRemaining mod days div hours & " hours " & timeRemaining mod hours div minutes & " minutes " & timeRemaining mod minutes & " seconds remaining until New Years."

I have a sneaking suspicion that Adam’s 37-year old Doctorate in Mechanical Engineering means that he understands the div and mod functions well enough, Qwerty - though I’m equally sure that your very clear and concise explanation will be of value to many others. :slight_smile:

My impression is that Adam was referring more to the sheer fluency, elegance and agility with which Nigel applies such functions (along with so many other aspects of AppleScript). Indeed, Mr G. unfailingly demonstrates the kind of exceptional grasp and instinctive usage of the language that most of us can only aspire to… :smiley:

You’ve now induced in me a morbid fear of being asked something that involves diddling with eigenvalues. I hope you’re going to be around for a while. :wink:

I was aware of that kai. He said that he wanted some help with div and mod, particulary when applied to dates. Although I was clearly addressing it to Adam, I wanted anyone to be able to read it, so I decided to include an introduction. I didn’t care if he skipped the first half, I just wanted to show some examples using dates.
Although you said it was ‘clear and consise’, I am sure it could be improved and expanded to include other things. :slight_smile:

OK, kai. How much do I owe you? :rolleyes:


Your explanation of div and mod applied to dates much appreciated and very nicely explained. You might lift that and add it to jj’s “Core Language” stuff here. If you can’t post there, send it as a message to jj.

Good stuff.


To satisfy any residual curiosity, here’s the final script, working nicely thanks to many of your hints: (I’ve commented out the two tell statements because readers may not have either of those applications).

The result is a message (put up by FastScripts, which doesn’t do images) immediately below a tiff image (put up by iNotify, which doesn’t do text). For the terminally curious, this shows the person I wrote it for the boxed portion of the image on this page with medication injection sites (which must move around to avoid a reaction) indicated in four separate tiffs. All four have the same background (the image) to which arrows and text have been added. The message below tells when the next injection must be given (on Fridays and Tuesdays, as it happens), so every day this script tells the user when the next injection is due and where it will be.

set cur_date to (current date)
set evenFriday to date "Friday, January 12, 1900 12:00:00 AM"
set theWeek to (cur_date - evenFriday) div weeks mod 2 --> 0 or 1
set theWeekday to (cur_date - evenFriday) mod weeks div days --> 0 to 6
if theWeekday is in {1, 2, 3} then
	set daysToGo to 4 - theWeekday
	set theWeekday to 4
else if theWeekday is in {5, 6} then
	set daysToGo to 7 - theWeekday
	set theWeekday to 0
	set theWeek to theWeek + 1 mod 2
	set daysToGo to 0
end if
set ImageNum to (theWeek * 2 + theWeekday div 3 + 1)
if daysToGo = 0 then
	set dtg to "Today"
	set dtg to "   " & daysToGo & " days to go!" & return & "(including today)"
end if

--tell application "iNotify" to create new notification image "anImage" & ImageNum & ".tif" center vertically no center horizontally yes close automatically after pause pause for 3 screen 1 y position 21 x position 22
--tell application "FastScripts" to display message dtg dismissing after delay 3

Um… I’m sure I’ll think of something, Nigel… :wink: