Complicated date calculations

I started with 4 PDF files, each containing 25 pages, with each page containing 4 graphics. So, 4 x 25 x 4 = 400 images. The images are of puzzles, and the 4 PDF files represent Very Easy (VE), Easy (EE), Moderate (MM), and Hard (HH). If I use the leftmost 2 images of each of the 25 VE pages for Monday, the left 2 of EE for Tuesday, the right 2 of EE for Wednesday, etc. I end up with 350 images, 50 for each day of the week, or just about a year’s worth.

So, graphically, I have 4 25-page documents with 4 images on each page, like this:

VE: A A EE: A A MM: A A HH: A A
B B B B B B B B

Acrobat nicely lets one export cropped versions of a page, and asks whether you just want the current page or that same crop position from all pages in the PDF file. Since the graphics are all positioned the same, an export of the top left image, for instance, will give me twenty-five files, each named with the root name I enter plus a sequential prefix. E.g., VE_01.eps, VE_02.eps. Actually, I’m taking two crops for each day from each file, so I’d name the top left crops VE_Mon_A_01.eps, …, VE_Mon_A_25.eps, and the bottom left crops VE_Mon_B_01.eps, …, VE_Mon_B_25.eps. The EE file, getting Tuesday and Wednesday, would produce EE_Tue_A_01.eps, . . . , EE_Tue_A_25.eps, EE_Tue_B_01.eps, . . . , EE_Tue_B_25.eps, EE_Wed_A_01.eps, . . . , EE_Wed_A_25.eps, EE_Wed_B_01.eps, . . . , EE_Wed_B_25.eps. And so forth, for the rest of the week.

What I need to do is to have a script that will rename these .eps files, inserting the actual date they’ll be used, with the starting date set to Monday, Jan 22, 2007. I.e., what I want is something like this:

VE_Mon_A_01.eps → 070122_Mon.eps
VE_Mon_B_01.eps → 070129_Mon.eps
VE_Mon_A_02.eps → 070203_Mon.eps
. . .
VE_Mon_A_25.eps → 071224_Mon.eps
VE_Mon_B_25.eps → 071231_Mon.eps

EE_Tue_A_01.eps → 070123_Tue.eps

EE_Tue_B_25.eps → 080101_Tue.eps

HH_Sun_B_25.eps → 080106_Sun.eps

In reality, each of my 100 pages has not only 4 puzzles but also the 4 answers, making 800 files to rename. Seems like the kind of thing for which scripting is designed! My initial thought was perhaps to use a simple file-renaming program to change the two-digit sequential numbering of the A and B sets of graphics to a three digit number, with the A’s ending in zero and the B’s in five, and remove the letter code useful only for sequencing. I’d get this:

VE_Mon_A_01.eps → VE_Mon_010.eps → (10-5)/5 = 1
VE_Mon_B_01.eps → VE_Mon_015.eps → (15-5)/5 = 2
VE_Mon_A_02.eps → VE_Mon_020.eps → (20-5)/5 = 3
. . .

By subtracting 5 from the sequence number and dividing the remainder by 5, I’d get a week number, as shown above. Perhaps this could then be used to calculate the dates for the file renaming? Or perhaps just calculating a day of the year number (jan 22 = 22, obviously) based on multiples of 7 for each day starting on 1/22/07, and then calculating the date from the day of year?

Any ideas or existing code to make this easy? I can do it in Applescript, Perl, bash . . .

Phew! :wink:

set theFolder to (choose folder) -- Assumed to contain only the files in question.
tell application "Finder" to set oldNames to name of theFolder's files

set baseDate to (date "Monday 22 January 2007 00:00:00") - 2 * weeks -- First day of "fortnight 0".
set shortDays to "MonTueWedThuFriSatSun" as Unicode text

considering case
	repeat with thisName in oldNames
		set dayOffset to (text 10 thru 11 of thisName) * 2 * weeks
		if (character 8 of thisName is "B") then set dayOffset to dayOffset + weeks
		set shortDay to text 4 thru 6 of thisName
		set dayOffset to dayOffset + (offset of shortDay in shortDays) div 3 * days
		set {day:d, month:m, year:y} to (baseDate + dayOffset)
		set newName to text 3 thru 8 of ((y * 10000 + m * 100 + d) as Unicode text) & "_" & shortDay & ".eps"
		tell application "Finder" to set name of file thisName of theFolder to newName
	end repeat
end considering

o.O

Wasn’t my topic, but jeebus way to early in the morning for me to contemplate that LOL

Nigel has been up for 5 hours when you start your day, James (although I can’t imagine anyone else answering this as efficiently at any time of day) :lol:

Ahh, I wasn’t aware of Nigels location =) And yeah I was in no way implying that I could write a solution anywhere near this efficient, I’m not overly comfortable with date forumlas like I am with the shell. Thats why I love these topics though… it’s great to watch you guys at work!

Nigel (and Kai) are in England (not on DST yet) and StefanK is Swiss, one hour ahead of Nigel. We’re on DST so we’ve picked up an hour on Europe for now, England is 3 hours ahead of me, 5 ahead of you. Stephan is 4 and 6 respectively. I don’t know whether or when England and Switzerland use DST.

Switzerland uses DST (we call it MESZ) from last sunday in March to last sunday in Oktober,

That might explain why the date/time on this BBS are an hour ahead as seen from here. :rolleyes: But I thought you guys went onto DST on the first Sunday in April. :confused:

A new law was passed last winter to start earlier this spring and stop later this fall. Theoretically it’s an energy saving attempt so the number of hours in the evening when houses are lit up will be reduced. :rolleyes: Anyway, given the amount of commerce between the US and Canada, we went along immediately. The rule becomes: Start: Second Sunday in March; End: First Sunday in November - two weeks earlier than yours and one week later.

Ah. Right. Thanks for the information. Now I come to think about it, I think one of the advertised virtues of OS 10.4.9 is about new Daylight Saving Times. I assumed it was just Arizona going solo again. :wink:

Nigel, that code worked perfectly! Incredibly elegant, a thing of beauty, and so powerful! Another year of Sudoku puzzles prepped for the fans . . . who might be more enjoyably spending their time doing AppleScripting, if only they knew!

Many thanks!

I modified your and Qwerty Denzel’s script for DST to suit (not clever, just straight-forward):



-- Modified form of a script by Querty Denzel & Nigel Garvey
set tYear to text returned of (display dialog "Daylight saving period for the year:" default answer "")
set C to choose from list {"Canada", "USA", "Great Britain", "Europe"} with prompt "DST Where?"
set D to getDST(tYear)
set Starts to date ("April " & item 1 of D & ", " & tYear & " 2:00:00 AM")
set Ends to date ("October " & item 2 of D & ", " & tYear & " 2:00:00 AM")
if C is {"Europe"} or 2008 > tYear then
	set DST to {Starts, Ends}
else if C is {"Great Britain"} then
	set DST to {Starts - weeks, Ends}
else
	set DST to {Starts - 2 * weeks, Ends + 1 * weeks}
end if
DST

to getDST(theYear)
	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}
end getDST

Hi, Adam.

I’d forgotten about that one. :slight_smile: But Europe “ my part of it, at least “ goes onto Summer Time on the last Sunday of March. For us, you need to set DST to {Starts - weeks, Ends} or modify the handler output.

Done, Nigel.

Hi Adam,

Adding to Nigel’s and Stefan’s info about Europe…

The DST is the same for the whole European Union and Europe in general except Russia when the change takes effect @ 02.:00 AM instead of 01:00 as it’s for the rest of the EU countries. So Adam’s code would be…


-- Modified form of a script by Querty Denzel & Nigel Garvey
set tYear to text returned of (display dialog "Daylight saving period for the year:" default answer "")
set C to (choose from list {"Canada", "USA", "EU", "Russia"} with prompt "DST Where?")
set D to getDST(tYear)
set {Starts1, Starts2} to {date ("April " & item 1 of D & ", " & tYear & " 1:00:00 AM"), date ("April " & item 1 of D & ", " & tYear & " 2:00:00 AM")}
set {Ends1, Ends2} to {date ("October " & item 2 of D & ", " & tYear & " 1:00:00 AM"), date ("October " & item 2 of D & ", " & tYear & " 2:00:00 AM")}
if C is {"Russia"} then
	set DST to {Starts2 - weeks, Ends2}
else if C is {"EU"} then
	set DST to {Starts1 - weeks, Ends1}
else
	set DST to {Starts2 - 2 * weeks, Ends2 + 1 * weeks}
end if
DST

to getDST(theYear)
	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}
end getDST

For the rest of the continents/countries to complete the code it needs more effort, as DST and laws varies a lot.

More info: When we change our clock, Worldwide daylight saving, EU law

Y.A.

Thanks for the link and the further refinement, yannis. :slight_smile:

I should perhaps point out to casual readers that the times returned by this script are themselves in Daylight Saving Time or British Summer Time or whatever equivalent you use. In the UK, for example, the clocks go forward for Summer Time at 01:00:00 GMT, which instantly becomes 02:00:00 BST. They go back again in the autumn at 02:00:00 BST, which instantly becomes 01:00:00 GMT. The idea is to leave an hour between midnight and the time change to avoid confusion with the midnight date change.

Here’s a script that relies on OS X’s built-in Daylight Savings Times (as discovered from this source).
It should hopefully clear-up the issue of all the different time-zones that we live in. Which isn’t a bad thing, you know. :wink:

property monthAbbrs : "JanFebMarAprMayJunJulAugSepOctNovDec"

on parseDumpedDate(dateString)
	set a to date (text (word 4) thru (word -2) of dateString)
	set day of a to word 3 of dateString
	set month of a to ((offset of (word 2 of dateString) in monthAbbrs) + 2) div 3
	return a
end parseDumpedDate

set chosenYear to text returned of (display dialog "Daylight Saving Times for Year:" default answer (year of (current date)))

try
	set DSTs to run script ("{" & text 1 thru -3 of ¬
		(do shell script "/usr/sbin/zdump -v /etc/localtime | sed -E -e '/" & chosenYear & "/!d' -e 's|^[^ ]+ *|{utcDate:\"|g' -e 's/ = /\", localDate:\"/' -e 's/ isdst=1/\", isDST:true},¬/' -e 's/ isdst=0/\", isDST:false},¬/'") ¬
			& "}")
on error
	display dialog "No Daylight Savings Times found for " & chosenYear
	return
end try

set t to chosenYear & " Daylight Savings Times for " & (do shell script "/usr/bin/readlink /etc/localtime | sed -E -e 's|.*zoneinfo/||' -e 's|(.+)/(.+)|\\2, \\1|'") & ":" & return & return
set isDayBeforeMonth to day of date ("1/2" & " 2000") is 1

set oldItem to null
repeat with anItem in DSTs
	if (oldItem is not null and oldItem's isdst is not anItem's isdst) then
		if (anItem's isdst) then
			set t to t & "Begins "
		else
			set t to t & "Ends "
		end if
		set date1 to parseDumpedDate(oldItem's localdate)
		tell date1 to if (isDayBeforeMonth) then
			set t to t & its weekday & space & day & space & its month & "," & return & tab
		else
			set t to t & its weekday & space & its month & space & day & "," & return & tab
		end if
		set t to t & "changing from " & time string of date1 & " to " & time string of parseDumpedDate(anItem's localdate) & return
	end if
	set oldItem to anItem
end repeat

display dialog t buttons "OK" default button 1

As I understand that QD, you’d have to change your time zone setting to the place where you wanted to know if it was not where you were.

Ah, I thought someone would be interested in that. :wink: Try this:

property monthAbbrs : "JanFebMarAprMayJunJulAugSepOctNovDec"

set firstLevel to paragraphs of (do shell script "ls -p /usr/share/zoneinfo | sed -e '/\\//!d' -e 's|/||'")

set firstChoice to choose from list firstLevel
if (firstChoice is false) then error number -128

set secondLevel to paragraphs of (do shell script "ls /usr/share/zoneinfo/" & firstChoice)

set secondChoice to choose from list secondLevel
if (secondChoice is false) then error number -128

set zonePath to "/usr/share/zoneinfo/" & firstChoice & "/" & secondChoice



on parseDumpedDate(dateString)
	set a to date (text (word 4) thru (word -2) of dateString)
	set day of a to word 3 of dateString
	set month of a to ((offset of (word 2 of dateString) in monthAbbrs) + 2) div 3
	return a
end parseDumpedDate

set chosenYear to text returned of (display dialog "Daylight Saving Times for Year:" default answer (year of (current date)))

try
	set DSTs to run script ("{" & text 1 thru -3 of ¬
		(do shell script "/usr/sbin/zdump -v " & zonePath & " | sed -E -e '/" & chosenYear & "/!d' -e 's|^[^ ]+ *|{utcDate:\"|g' -e 's/ = /\", localDate:\"/' -e 's/ isdst=1/\", isDST:true},¬/' -e 's/ isdst=0/\", isDST:false},¬/'") ¬
			& "}")
on error
	display dialog "No Daylight Savings Times found for " & chosenYear & " at " & secondChoice & ", " & firstChoice
	return
end try

set t to chosenYear & " Daylight Savings Times for " & secondChoice & ", " & firstChoice & ":" & return & return
set isDayBeforeMonth to day of date ("1/2" & " 2000") is 1

set oldItem to null
repeat with anItem in DSTs
	if (oldItem is not null and oldItem's isdst is not anItem's isdst) then
		if (anItem's isdst) then
			set t to t & "Begins "
		else
			set t to t & "Ends "
		end if
		set date1 to parseDumpedDate(oldItem's localdate)
		tell date1 to if (isDayBeforeMonth) then
			set t to t & its weekday & space & day & space & its month & "," & return & tab
		else
			set t to t & its weekday & space & its month & space & day & "," & return & tab
		end if
		set t to t & "changing from " & time string of date1 & " to " & time string of parseDumpedDate(anItem's localdate) & return
	end if
	set oldItem to anItem
end repeat

display dialog t buttons "OK" default button 1

So, does the other work for you? If so, I should really move this to Code Exchange, no?

Wow! :lol:

You certainly should post it to Code Exchange, QD; and leave it here too as the end of the thread.

Thanks;

Adam