# Finding a date on a weekday in a month

Is there an efficient way to find the date for any year of the first Sunday in April or the last Sunday in October (the dates when daylight savings time springs ahead and falls back)?

That’s hardcore math, -
You need a reference year to begin:
Rootyear- 2005
DatesOfRootyear - {“04.03.05”, “10.30.05”}
Each year you move forward, the date goes back 1.
Rootyear +1 = 2006
DatesOfRootyear + 1 : {“04.02.05”, “10.29.05”}
So you could write a program to calculate the changes and return a date for any year you enter. You would have to account for the date going from 01 to 30 for April and 01 to 31 for October.
I don’t know if leap year would change all that. You could still write it to deal with that, but once again, arrrg the math. :shock:
SC

Ugh… I was afraid of that. Easy enough to get the leap year number:

``````
set daysInYear to 365
if ((do shell script "date "+%Y"") as integer) mod 4 = 0 then set daysInYr to 366
``````

and I’ll have to work out the rest.

``````DST(2006)

on DST(theYear) -- Four-digit year as integer
set dstSpring to {"03", "02", "01", "06", "05", "04"}
set dstFall to {"30", "29", "28", "26", "25", "31"}
set theDiff to theYear - 2005
if (theDiff < 0 or theDiff > 5) then
display dialog "The year must be greater than " & return & "2005 and less than 2010"
return
end if
set SpringDST to ((theYear as string) & "04" & item (theDiff + 1) of dstSpring) as integer
set FallDST to ((theYear as string) & "10" & item (theDiff + 1) of dstFall) as integer
return {SpringDST, FallDST}
end DST
``````

I think this works:

``````display dialog "Year?" default answer ""
set yr to text returned of result
set yr to yr - 1900
set y to ((yr + yr div 4) as integer) mod 7
set startApril to (8 - y) mod 7
set endOctober to (7 - y) mod 7 + 28
if endOctober > 31 then set endOctober to endOctober - 7
{startApril, endOctober}

``````
• Dan

And here is a funny variation:

``````property knownDate : date "04/03/2005"
property knownYear : year of knownDate
property secsYear : 365.242199 * days

findFirstSundayApril(9000) --> "04/01/9000"

to findFirstSundayApril(y)
knownDate + ((y - knownYear) * secsYear)
end findFirstSundayApril
``````

That’s pretty cool! It only works between 1900 and 2099, but that should normally be good enough.

Otherwise, there’s this using AppleScript date maths:

``````on clockChangeDates(theYear)
if not (theYear's class is integer and theYear > 999 and theYear < 10000) then
error "The year must be an integer between 1000 and 9999"
end if
-- There's a date subtraction bug in AppleScript when both dates come before 1904.
set pre1904 to (theYear comes before 1904)
if pre1904 then set theYear to theYear + 1200

set knownSunday to date "Sunday, 5 January 1000 00:00:00"

set April7 to date "Monday, 7 April 1000 00:00:00"
set April7's year to theYear
set firstAprilSunday to April7 - (April7 - knownSunday) mod weeks

set October31 to date "Friday, 31 October 1000 00:00:00"
set October31's year to theYear
set lastOctoberSunday to October31 - (October31 - knownSunday) mod weeks

if (pre1904) then set {firstAprilSunday, lastOctoberSunday} to ¬
{firstAprilSunday - 3.78683424E+10, lastOctoberSunday - 3.78683424E+10}

return {firstAprilSunday, lastOctoberSunday}
end clockChangeDates

clockChangeDates((text returned of (display dialog "Year?" default answer "")) as integer)
``````

Oops, slight error:

``````display dialog "Year?" default answer ""
set yr to text returned of result
set yr to yr - 1900
set y to (yr + yr div 4) mod 7
set startApril to (7 - y) mod 7 + 1
set endOctober to (7 - y) mod 7 + 28
if endOctober > 31 then set endOctober to endOctober - 7
{startApril, endOctober}

``````

The last version gives April 0, 2013, but this should work. Actually it’s the inverse function, so to speak, of a much simpler mental exercise of getting the day of week for any date from September 1752 to (for me, anyhow) December 2099. It’s a lot harder for me to script than it is to do it mentally, and as an added benefit makes me very obnoxious.

• Dan

WOW!

–Open this script in a new Script Editor window.

>Program: Rtns First Selected Day of Selected Month & Year

>Ln: 1: - Proprietry Statement - i,e Fixed to Current Month & Year - Can be used in Place of Ln 2 if Generism isn’t Req’d.
set defaultAns to 7 & {month, year} of (current date)

>Dialog Msges passed to vars as otherwise, it would cause Ln: 2 to become unwieldy.
set {mthMsg, yrMsg} to {"Select Month Using Its Name or Numerical Equivalent: M or MM", "Select Year Using: YY or YYYY"}

(* → Important: The val: 7 Represents the Day of the Month & is HD Wired into this Script. This is because, you only want the First Day of -
the Month which falls inside the First Week of the Month.Thus, any Day in the First Week of any Month cannot exceed the 7th Day. *)

>Ln 2: Generic: Allows user to Select Month & Year in which First Selected Day is to be found
setdefaultAnsto (7 & {text returnedof (display dialogmthMsgdefault answer (monthof (current date) asstring)), text returnedof (display dialogyrMsgdefault answer (yearof (current date) asstring))})

>Generic: Allows user to Select First Day of Month to be Found
settargetDayto (item1of (choose from list {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}))

>Note: If first day of month is fixed jsut HD Wire it to this script - (i.e “Sunday” in Place of var: “targetDay”)

------>Repeat Loop Defined:<--------
>The Repeat with from Loop is used to Descend thru the days from the 7th day of the selected month of the selected year until the selected day has been located.

repeatwithifrom1to7

>Note: This Validation Would Only be Required if Loop Was Not Restricted to Processing From the & 7th Day of the Selected Month.
if ((weekday of ((date (defaultAns as string)) - (days * i)) as string) = targetDay and month of ((date (defaultAns as string)) - (days * 7)) is not (item 2 of defaultAns) as string) then

>Tests whether Day currently being processed is the Day Selected by User
if ((weekdayof ((date (defaultAnsasstring)) - (days * i)) asstring) = targetDay) then

>Debug Code - (Comment Out or Remove)
display dialog "first " & targetDay & " has been located"

>Descends thru each from the 7th - Passing each day being processed to var: “firstDayOfMth”
setfirstDayOfMthto (date (defaultAnsasstring)) - (days * i)

> Exits Loop when Target Day is Located
exitrepeat
else
>Debug Code - (Comment Out or Remove)
display dialogdate stringof ((date (defaultAnsasstring)) - (days * i))

endif

endrepeat

Rtns (Implicitly) First Selected Day of Selected Month & Year in Full & Date String Formats
{firstDayOfMth, date stringoffirstDayOfMth}

NovaScotian i hope this helps. However, if you need a script to rtn the last selected day of a month, then just start processing backwards from the first day of the proceeding month until the selected day has been reached. A few simple modifications to this script will do the trick.

shars

[This script was automatically tagged for color coded syntax by Convert Script to Markup Code]

This is the grungy math I was talking about:

Nigel, did you take Fortran back in the days?
SC :lol:

Here’s yet another approach. (If you think it could stand a bit of pruning, have no fear–I agree completely!

``````set chooseYear to text returned of (display dialog "Pick a year:" default answer ¬
year of (current date))

display dialog ("Daylight Savings Time dates for " & chooseYear & ":" & return & return & ¬
"Set clocks ahead on " & (changeClocks(chooseYear, "April", 1, 7)) & return & ¬
"Set clocks back on " & (changeClocks(chooseYear, "October", 25, 31)))

on changeClocks(theYear, theMonth, startDate, finishDate)
set theWeek to {}
repeat with n from startDate to finishDate
set end of theWeek to date (theMonth & n & ", " & theYear)
end repeat
repeat with n from 1 to 7
set theDay to item n of theWeek
if weekday of theDay is Saturday then
return (weekday of theDay & ", " & month of theDay & " " & day of theDay)
end if
end repeat
end changeClocks
``````

Edit: Of course, “Saturday” really needs to be “Sunday”; I was intending to encourage clockChangers to plan ahead, but obviously the script as written will produce erroneous results whenever April 1 or November 1 occur on a Sunday. :?

NovaScotian,

i apologise as there was a glitch in the solution i posted yesterday. However, this solution allows the user to select the first or last selected day of the selected month & year:

–Open this script in a new Script Editor window.

(* -------->Program’s Definition:<--------
This ia an Upd to the “Get First Selected Day in Selected Month Exp.scpt” script - Regaling the user the option of rtning the First or -
Last Selected Day of the Selected Month & Year. Thus, this ver also fixes a glitch on Ln: 2: When user selects a Month’s Numerical -
Equivlalent - (i.e 4 as opposed to April), Ln: 2 would error out returning the following Msg: “Invalid date and time 142005.”. *)

>Dialog Msges passed to vars as otherwise, it would cause Ln: 2 to become unwieldy.
set {mthMsg, yrMsg} to {"Select Month Using Its Full Name, Short Name or Numerical Equivalent: M or MM", "Select Year Using: YY or YYYY"}

(* → Important: The val: 1 Represents the First Day of the Month & is HD Wired into this Script. This is Req’d -
by Ln: 7 inside the If Statement Block, when the user chooses to Rtn the last (Selected) Day of the (Selected) -
Month & Year. *)

>Warning: AS’s Date Specifier (or rather Sys Prefs) is very tolerant in what it’s prepared to accept. Thus, it will also Supply Missing vals.

>Ln 2: Generic: Allows user to Select Month & Year in which First or Last Selected Day is to be Rtned
setdateEntryto (1 & {" " & text returnedof (display dialogmthMsgdefault answer (monthof (current date) asstring)), " " & text returnedof (display dialogyrMsgdefault answer (yearof (current date) asstring))})

>i. Allows user to Select Day of Selected Month & Year
> ii. Allows user to select whether First or Last Selected Day is to be Rtned.
set {targetDay, monthPos} to {(choose from list {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}) asstring, ((choose from list {"Start", "End"})) asstring}

>Catches & Processes User Cancellation
if (monthPos = "false" ortargetDay = "false") then

returndisplay dialog "User Cancelled" buttons {"OK"} default button1

>Processes Last Selected Day of Month Protocol
elseif (monthPos = "End") then
(* -->Uses “Date Incrementation Technique” to Rtn the Proceeding Month - Note: Day portion of date was set to 1 - (i.e 1st day of month) by Ln: 2
Important: Although dateEntry has not Yet been Converted to Real Date. This approach is necessary because, if user entered Month’s Name -
as apposed to its Numerical Equivalent (via Display Dialog on Ln: 2), then it wouldn’t be Poss to employ “Date Incrementation Technique”. *)
setitem2ofdateEntryto " " & (monthof (date (dateEntryasstring))) + 1 & " "

>Processes First Selected Day of Month Protocol
else
>Sets Day Portion of date to 7 - Note: Approach works because dateEntry has not Yet been Converted to Real Date
setitem1ofdateEntryto7
endif

>Note: If first day of month is fixed just HD Wire it into this script - (i.e “Sunday” in Place of var: “targetDay”)

>rdateEntry is formatted to ealDate - Used by Repeat Loop
setrealDatetodate (dateEntryasstring)

(*-------->Repeat Loop Defined:<--------
The Repeat with from Loop is used (depending on the user’s Selection) to Nav back from the 7th or the First -
color=olive Day of the Selected or Proceeding Month of the (Selected) Year until the (Selected) Day is Rtned. [/color]*)
repeatwithifrom1to7

>Note: This Validation Would Only be Required if Loop Was Not Restricted to Processing From the & 7th Day of the Selected Month.
if ((weekday of ((date (dateEntry as string)) - (days * i)) as string) = targetDay and month of ((date (dateEntry as string)) - (days * 7)) is not (item 2 of dateEntry) as string) then

>Tests whether Day currently being processed is the Day Selected by User
if ((weekdayof (realDate - (days * i)) asstring) = targetDay) then

>Debug Code Block - (Comment Out or Remove)
if (monthPos = "End") then
display dialog "Last " & targetDay & " has been located"
else
display dialog "First " & targetDay & " has been located"
endif

>Descends thru each from the 7th - Passing each day being processed to var: “firstDayOfMth”
setdayOfMthto (realDate - (days * i))

> Exits Loop when Target Day is Located
exitrepeat
else
>Debug Code - (Comment Out or Remove)
display dialogdate stringof (realDate - (days * i))

endif

endrepeat

Rtns (Implicitly) First or Last Selected Day of Selected Month & Year in both Full & Date String Formats
{dayOfMth, date stringofdayOfMth}

shars

[This script was automatically tagged for color coded syntax by Convert Script to Markup Code]

Thank you all. This was my first foray into the date class (other than getting current date) and I’ve learned a lot from these different approaches (and tried them all)

Sorry to open this thread again but I just wanted to post my attempt:

``````set the_year to text returned of (display dialog "Daylight saving period for year:" default answer "")
set the_year to (the_year + 8) mod 28 div 0.8
set daylight_saving_start to 7 - ((the_year + 1) mod 7)
set daylight_saving_end to 31 - ((the_year + 5) mod 7)
return {daylight_saving_start, daylight_saving_end}
``````

One liner:

``````tell ((text returned of (display dialog "Daylight saving period for year:" default answer "")) + 8) mod 28 div 0.8 to {7 - ((it + 1) mod 7), 31 - ((it + 5) mod 7)}
``````

Thanks

Ooookaayyy… That gives the correct results between 1900 and 2099. I believe this slight modification will give the correct results for any AD year in the Gregorian calendar:

``````set the_year to text returned of (display dialog "Daylight saving period for year:" default answer "")
set gregorian to the_year mod 400
set preCalc to (gregorian + 2008) mod 28 div 0.8 - gregorian div 100
set daylight_saving_start to 7 - (preCalc + 1) mod 7
set daylight_saving_end to 31 - (preCalc + 5) mod 7
return {daylight_saving_start, daylight_saving_end}
``````

Or:

``````tell (text returned of (display dialog "Daylight saving period for year:" default answer "")) mod 400 to tell (it + 2008) mod 28 div 0.8 - it div 100 to return {7 - (it + 1) mod 7, 31 - (it + 5) mod 7}
``````

Whoops, sorry, I only tested on recent years and 1000 - 1027.

Not to worry, Qwerty. I don’t imagine many people will need to know the US Daylight Saving Time dates much beyond the years covered by your very clever maths. (‘div 0.8’! I like that! :))

The catch is the three non-leap century years that occur every 400 years. My modification merely nudges your results by one day for each successive century of the 400 (counting from 0 to 399, which happens to work).

Although the length of any individual year, or group of years, depends on the years involved, any period of 400 years in the Gregorian calendar is 365 * 400 + 97 days. The nice thing about this figure is that it happens to be an exact number of weeks. Thus each period of 400 years begins on the same day of the week and its layout is exactly the same as the 400 years before and after it. So what works for one 400-year period works for all such periods in the same sequence. Handy.

Now that’s esoterica!

Just posting to say that Nigel Garvey’s improvement to mine can be shortened to this:

``````set theYear to text returned of (display dialog "Daylight saving period for year:" default answer "")
set gregorian to theYear mod 400
set preCalc to gregorian mod 28 div 0.8 - gregorian div 100
set daylightSavingStart to 7 - (preCalc + 5) mod 7
set daylightSavingEnd to 31 - (preCalc + 2) mod 7
return {daylightSavingStart, daylightSavingEnd}
``````

One line:

``````tell (text returned of (display dialog "Daylight saving period for year:" default answer "")) mod 400 to tell it mod 28 div 0.8 - it div 100 to {7 - (it + 5) mod 7, 31 - (it + 2) mod 7}
``````

Thanks again!