Easy way to convert AS dates into Epoch?

Wondering if there is an easy way to get accurate Epoch time (seconds since 1/1/1970) when inputting a specific date (NOT current date) in AppleScript format (example: “Friday, December 6, 2019 at 11:45:02 AM”).

I’ve really hodge-podged something together but the results are not matching www.epochconverter.com’s results for the same date/time. Results are off by about 16 hrs 9 mins (10hrs 9mins with time zone offset):

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

on run
	set itsDate to date "Friday, December 6, 2019 at 11:45:02 AM"
	
	set newDate to its makeNSDateFrom:(itsDate)
	set itsComps to its makeASDateFrom:newDate
	set itsYear to itsComps's |year|()
	set itsMonth to itsComps's |month|()
	set itsDay to itsComps's |day|()
	set itsTime to (itsComps's hour()) * hours + (itsComps's minute()) * minutes + (itsComps's |second|())
	
	set yearDiff to itsYear - 1970
	set unixTime to ((yearDiff * 31556926) + ((itsMonth - 1) * 2629743) + ((itsDay - 1) * 86400) + itsTime) as string
	
	set timeString to number_to_string(unixTime)
	
end run


on makeNSDateFrom:theASDate
	set {theYear, theMonth, theDay, theSeconds} to theASDate's {year, month, day, time}
	if theYear < 0 then set theYear to -theYear
	set theCalendar to current application's NSCalendar's currentCalendar()
	set newDate to theCalendar's dateWithEra:(theYear ≥ 0) |year|:theYear |month|:(theMonth as integer) |day|:theDay hour:0 minute:0 |second|:theSeconds nanosecond:0
	return newDate
end makeNSDateFrom:

on makeASDateFrom:theNSDate
	set theCalendar to current application's NSCalendar's currentCalendar()
	set comps to theCalendar's componentsInTimeZone:(missing value) fromDate:theNSDate
	tell (current date) to set {theASDate, year, day, its month, day, time} to ¬
		{it, comps's |year|(), 1, comps's |month|(), comps's |day|(), (comps's hour()) * hours + (comps's minute()) * minutes + (comps's |second|())}
	return comps
end makeASDateFrom:

on number_to_string(this_number)
	set this_number to this_number as string
	if this_number contains "E+" then
		set x to the offset of "." in this_number
		set y to the offset of "+" in this_number
		set z to the offset of "E" in this_number
		set the decimal_adjust to characters (y - (length of this_number)) thru ¬
			-1 of this_number as string as number
		if x is not 0 then
			set the first_part to characters 1 thru (x - 1) of this_number as string
		else
			set the first_part to ""
		end if
		set the second_part to characters (x + 1) thru (z - 1) of this_number as string
		set the converted_number to the first_part
		repeat with i from 1 to the decimal_adjust
			try
				set the converted_number to ¬
					the converted_number & character i of the second_part
			on error
				set the converted_number to the converted_number & "0"
			end try
		end repeat
		return the converted_number
	else
		if this_number contains "E-" then
			set x to the offset of "." in this_number
			set y to the offset of "-" in this_number
			set z to the offset of "E" in this_number
			if x is not 0 then
				set the first_part to text 1 thru (x - 1) of this_number
			else
				set the first_part to ""
			end if
			set the second_part to text (x + 1) thru (z - 1) of this_number
			set the converted_number to the first_part & second_part
			set n to text (y + 1) thru -1 of this_number as number
			set zero to "0."
			
			if n > 1 then
				repeat (n - 1) times
					set zero to zero & "0"
				end repeat
			end if
			set converted_number to zero & converted_number
		else
			set converted_number to this_number
		end if
	end if
	
	return converted_number
end number_to_string
set startDate to date "Thursday, January 1, 1970 at 12:00:00 AM"

set anyDate to current date
set anyDate to anyDate + (random number from -100 to 100) * days
set anyDate to anyDate - startDate

-->>1.572099129E+9
-->>date "Saturday, October 26, 2019 at 2:12:09 PM"


-->> 1.5755904E+9

set anyDate to anyDate - 0

-->> 1.5755904E+9

Thank you. I must have been way over thinking things when I get back the exponents because I did that at first. :-/

1 Like

The problem with Ed’s answer is that AppleScript ignores daylight savings. So if it was active in your time zone for one of the times but not the other, you will get the wrong result.

This requires 10.11 or later:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions

set theDate to date "Friday, 6 December 2019 at 11:45:02 am"
-- create NSDate
set cocoaDate to current application's NSDate's dateWithTimeInterval:0 sinceDate:theDate
set theSeconds to cocoaDate's timeIntervalSince1970()
1 Like

Yeah, that is what I discovered too. Thanks for your solution, too. I also then found a handler (when searching for Unix time versus Epoch time) that also returns in current time zone format which is what we’ll be using:

on unixDate(datetime)
	set command to "date -j -f '%A, %B %e, %Y at %I:%M:%S %p' '" & datetime & "'"
	set command to command & " +%s"
	
	set theUnixDate to do shell script command
	return theUnixDate
end unixDate

Is there any advantage/disadvantage of using the NSDate versus bash script?

NSDate is faster, but it means adding ASObjC to your script.

That looks a bit fragile, in that it relies on particular formatting. If you want to match AppleScript string format, you can use this:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions

set dateString to "Friday, 6 December 2019 at 11:45:02 am"
set df to current application's NSDateFormatter's new()
df's setDateStyle:(current application's NSDateFormatterFullStyle)
df's setTimeStyle:(current application's NSDateFormatterMediumStyle)
set theDate to df's dateFromString:dateString
return theDate's timeIntervalSince1970()