I’ve been tackling this for the final stage of my “next recurrence in iCal” project elsewhere in this forum. Contrary to my fear that it would require an external AppleScript Studio or Objective-C file, it turns out to be doable with just AppleScript and the Unix “date” command. The latter can return a date that’s so many seconds from the beginning of the Unix “era” (1st January 1970 00:00:00 GMT) and also accepts an environment variable “TZ” that governs the time zone in which the date is expressed.
(* Convert an ISO-format date string to an AppleScript date. *)
on isotToDate(isot)
set theDate to date "Saturday 1 January 1583 00:00:00"
set n to (text 1 thru 8 of isot) as integer
set theDate's year to n div 10000 - 1
set theDate's month to n mod 10000 div 100
set theDate's day to n mod 100
if ((count isot) > 8) then
set n to (text 10 thru 15 of isot) as integer
set theDate's time to n div 10000 * hours + n mod 10000 div 100 * minutes + n mod 100
end if
return theDate
end isotToDate
(* Transpose a date/time from GMT to a given time zone. *)
on GMTtoTZ(GMTDate, TZ)
-- Subtract date "Thursday 1 January 1970 00:00:00" from the GMT date. Result in seconds, as text.
copy GMTDate to date19700101
tell date19700101 to set {year, its month, day, time} to {1970, 1, 1, 0}
set eraTime to (GMTDate - date19700101)
if (eraTime > 99999999) then
set eraTime to (eraTime div 100000000 as text) & text 2 thru 9 of (100000000 + eraTime mod 100000000 as integer as text)
else if (eraTime < -99999999) then
set eraTime to (eraTime div 100000000 as text) & text 3 thru 10 of (-100000000 + eraTime mod 100000000 as integer as text)
else
set eraTime to eraTime as text
end if
return isotToDate(do shell script ("TZ='" & TZ & "' /bin/date -r " & eraTime & " +%Y%m%dT%H%M%S"))
end GMTtoTZ
(* Transpose a date/time from a given time zone to GMT. *)
on TZtoGMT(TZDate, timeZoneID)
-- Initial stab. The difference between the local date when GMT is the given date is usually the same as
-- the difference between the given date when it's local and the GMT date we actually want .
set GMTDate to TZDate - (GMTtoTZ(TZDate, timeZoneID) - TZDate)
-- . but not around the time the clocks go forward. If the GMT obtained doesn't correspond to the given local date,
-- shift to a nearby local date where the above DOES work, get a new GMT, then unshift it by the same amount.
set checkDate to GMTtoTZ(GMTDate, timeZoneID)
if (checkDate is not TZDate) then
if (GMTDate > checkDate) then -- "Clocks forward" is towards GMT.
set shift to GMTDate - checkDate
else -- "Clocks forward" is away from GMT.
set shift to -days
end if
set nearbyDate to TZDate + shift
set GMTDate to nearbyDate - (GMTtoTZ(nearbyDate, timeZoneID) - nearbyDate) - shift
end if
return GMTDate
end TZtoGMT
(* Transpose a date/time from one time zone to another and/or correct a date in the missing hour when the clocks go forward. *)
on transposeDate(theDate, timeZone1ID, timeZone2ID)
if (timeZone1ID is "GMT") then
set GMTDate to theDate
else
set GMTDate to TZtoGMT(theDate, timeZone1ID)
end if
if (timeZone2ID is "GMT") then return GMTDate
return GMTtoTZ(GMTDate, timeZone2ID)
end transposeDate
-- Demo: Transpose from UK time to US Eastern time.
transposeDate(date "Monday 22 June 2009 23:32:28", "Europe/London", "US/Eastern")
--> date "Monday 22 June 2009 18:32:28"
Edits: A couple of comments clarified in the script. Minor bug fix. transposeDate() handler rewritten to avoid superfluous conversions to GMT.
More recently, isotToDate() handler rewritten to cope with the misbegotten date handling in Snow Leopard.
More recently still, GMTtoTZ() handler rewritten, principally to change the method used to convert the large eraTime number to a decimal string for use in the shell script. Thanks to Yvan Koenig for pointing out the weakness in the original.