convert date to unix format

I need to take a date variable and convert it into unix format (mm/dd/yyyy hh:mm:ss AM/PM) to use in “do shell script” any easy way to do this?

You could start with this, from Nigel Garvey’s Date Tips in ScriptBuilders:

tell {current date}
	copy beginning to end
	set end's month to January
	tell ((beginning's year) * 10000 + (beginning - (end - 3944592)) div 2629728 * 100 + (beginning's day)) as string
		text 7 thru 8 & "/" & text 5 thru 6 & "/" & text 1 thru 4
	end tell
end tell

display dialog result

Just rearrange the terms to get the format you wanted. If you want the current date anyway, then read man date to see how to format that.

Hi, LobsterMan. I haven’t made any assumptions here about which version of AppleScript you’re using:

on dateToUnix(theDate)
	set {day:d, year:y, time:t} to theDate
	
	copy theDate to b
	set b's month to January
	set m to (b - 2500000 - theDate) div -2500000
	
	tell (y * 10000 + m * 100 + d) as text
		set UnixDate to text 5 thru 6 & "/" & text 7 thru 8 & "/" & text 1 thru 4
	end tell
	
	set h24 to t div hours
	set h12 to (h24 + 11) mod 12 + 1
	if (h12 = h24) then
		set ampm to " AM"
	else
		set ampm to " PM"
	end if
	set min to t mod hours div minutes
	set s to t mod minutes
	
	tell (1000000 + h12 * 10000 + min * 100 + s) as text
		set UnixTime to text 2 thru 3 & ":" & text 4 thru 5 & ":" & text 6 thru 7 & ampm
	end tell
	
	return UnixDate & " " & UnixTime
end dateToUnix

dateToUnix(current date)

Oh dear. That’s bound to be a bad influence on kai. :stuck_out_tongue:

No offense intended, Nigel; Kai often astounds me as well. I admire his one-liners and enjoy trying to construct them as well.

You should add this ASDateToUnixDate conversion to DateTips (I have to my own copy) - I find them very useful and I’m sure others would too.

Nigel, thanks a million! I’ll try it out. :cool:
Adam, are you sure that’s AppleScript? :stuck_out_tongue: looks quite impressive, but WAY over my head, i’d have no idea how to use it…

Nigel:

I have often appreciated the assistance you have rendered. I think there is a simpler way to do this, but I am hung up on one part. The shell command [date] will take a number (x), and convert it to a date that is x seconds after 1/1/1970 00:00:00. By using Applescript, you can define a variable to this date, take any other date you want, and get the difference in seconds:

set aa to (date "Thursday, January 1, 1970 12:00:00 AM")
set a to choose file
tell application "Finder" to set b to a's modification date
set ab to (b - aa)

-->1.121682115E+9

Unfortunately, when that variable is sent to the shell, it cannot convert the exponent correctly, and returns a bogus date:


set aa to (date "Thursday, January 1, 1970 12:00:00 AM")
set a to choose file
tell application "Finder" to set b to a's modification date
set ab to (b - aa)

set c to do shell script "date  -r" & ab
c

-->Wed Dec 31 16:00:01 PST 1969

But, if you make the number a big ol’ string, it works:

set ab to "1121682115"
set c to do shell script "date  -r" & ab

c

-->"Mon Jul 18 03:21:55 PDT 2005"

I think that someone with a little more understanding of how to send numbers to the shell as strings could make this work, and perhaps simplify the conversion of both the date and the time formats.

What do all of you think?

It’s a bit of Garvey magic from DateTips (in ScriptBuilders). I’ve modified it by adding variable formats and use it like this:

set CurDate to date "Sunday, January 1, 2006 12:00:00 AM"
set Separator to first character of ((choose from list {"/ slash", ": colon", "“ n dash", ". period", " Space", "None"} with prompt "Choose a delimiter for your numerical date" without multiple selections allowed and empty selection allowed) as text)
if Separator = "N" then set Separator to ""
set Order to first character of ((choose from list {"1) Month/Day/Year", "2) Day/Month/Year", "3) Year/Month/Day"} with prompt "Choose the format of the numerical date" without multiple selections allowed and empty selection allowed) as text) as number

FormatDateAsNumbers(CurDate, Separator, Order)

to FormatDateAsNumbers(DateIn, sptr, Ordr)
	tell {DateIn}
		copy beginning to end
		set end's month to January
		tell ((beginning's year) * 10000 + (beginning - (end - 3944592)) div 2629728 * 100 + (beginning's day)) as string
			return item Ordr of {text 5 thru 6 & sptr & text 7 thru 8 & sptr & text 1 thru 4, text 7 thru 8 & sptr & text 5 thru 6 & sptr & text 1 thru 4, text 1 thru 4 & sptr & text 5 thru 6 & sptr & text 7 thru 8}
		end tell
	end tell
end FormatDateAsNumbers

Without the formatting of the string produced, the handler amounts to this:

set CurDate to date "Sunday, December 25, 2005 12:00:00 AM"
set Begin to CurDate
copy Begin to Ending -- setting them equal sets Begin when Ending is changed below. Copy produces a new variable.
set month of Ending to January -- so instead of Christmas, we've got Ending set to January 25, 2005
set aYear to (year of Begin) * 10000 -- adds four zeros to the year for later additions.
set aMo to (Begin - (Ending - 3944592)) div 2629728 -- mysteriously produces the month as a number; only Nigel knows where the integer values came from.
set bMo to aMo * 100 -- Adds two zeros to shift it for adding to the year * 10000 in the right place.
set aD to day of Begin -- produces the day of the month to add to the total below.
aYear + bMo + aD -- the date in reverse order YYYYMMDD without separators.

That’s the trouble with the Unix ‘date’ command. Not only is it slower than vanilla AppleScript, but it’s less versatile. :wink: I’m no expert on Unix, but I think there’s another command that formats numbers and can be included in the same shell script.

I included this in DateTips in a moment of exuberance. It’s just a version of my short-date routine that doesn’t use any variables. It uses “French Vanilla” to calculate the month number and, since this method requires both the original date and a January date derived from it, the lack of variables is got round by having a two-item list where the two dates are its ‘beginning’ and ‘end’. The two ‘tells’ not only hold intermediate values without using variables, but also spare us several instances of ‘of’. Otherwise, the script is very similar to the UnixDate part of the script I posted above.

Emmanuel Lévy’s “French Vanilla” method is mentioned in jj’s FAQs elsewhere on this site, and dates from before month-to-integer coercions were possible in AppleScript. (They were introduced with Panther.) The original version went something like this:

set theDate to (current date) -- or any AppleScript date

copy theDate to b
set b's month to January
set monthNum to 1 + (theDate - b + 1314864) div 2629728

2629728 seconds is one twelfth of 365.24 days ” ie. the average length of a calendar month ” and 1314864 is half that. These numbers only need to be approximately right. The month of date ‘b’ is January, but it has exactly the same year, day, and time as the subject date: so the difference between the two dates is exactly the same as the number of seconds from the beginning of the year to the beginning of the month of ‘theDate’. Add half an average month to that and divide the result by an average month, and we get the number of months in the year before the month of ‘theDate’. (The number of calendar months and average months is the same in this result. The differences in length are discarded with the remainder in the ‘div’ operation.) Finally, 1 is added to get the number of theDate’s month.

Richard Hartman later came up with a variation where the divisor is the approximate length of the shortest month of the year. The addition of the half month is unnecessary with this method. Also, one of the numbers in the range of divisors that works is 2500000, which is much easier to remember. Some wags called this variation “Vanille Américaine”.

copy theDate to b
set b's month to January
set monthNum to 1 + (theDate - b) div 2500000

Later still, I realised that Emmanuel’s own maths could be optimised slightly by adding one-and-a-half average months rather than just half of one. The result of the division is then 1 more than before, which saves having to add the 1 at the end. The numbers involved are those that appear in the variableless script that Adam has quoted above.

copy theDate to b
set b's month to January
set monthNum to (theDate - b + 3944592) div 2629728

None of these methods worked with dates earlier than 1904, because of an AppleScript bug which has only just been fixed with Tiger: subtracting one pre-1904 date from another would result in an overflow error. However, it turned out that the error didn’t occur when a later pre-1904 date was subtracted from an earlier one. So I came up with a double-negative version that combined both Richard’s idea and my own:

copy theDate to b
set b's month to January
set monthNum to (b - 2500000 - theDate ) div -2500000

Since theDate’s month might be the same as b’s, which would still give an error, 2500000 is first subtracted from b to ensure that theDate is subtracted from a date that’s earlier than it. Since the result of the subtraction is negative, the divisor’s made negative as well to give a positive end result. The subtraction of the 2500000 in this version is equivalent to adding the 1 in Richard’s version.

So much for that.

The other feature of my short-date process ” multiplying the year by 10000 and the month by 100 ” is a slightly more efficient way of getting the leading zeros. Taking the coming New Year’s Day:

(2006 * 10000 + 1 * 100 + 1) as string
--> "20060101"

It’s then necessary to extract the individual portions of this string. But four sums, one explicit number-to-string coercion, and three string extractions just beat two string concatenations with implicit number-to-string coercions and one explicit number-to-string coercion. At least, they did. I haven’t checked recently. :wink:

Besides formatting the number with a Unix command, the maximum possible difference between 1/1/1970 and any AppleScript date can be split into two parts and coerced separately:

set aa to (date "Thursday, January 1, 1970 12:00:00 AM")
set a to choose file
tell application "Finder" to set b to a's modification date
set ab to (b - aa)
set ab to ((ab div 100000000) as string) & (abs (ab mod 100000000)) div 1

set c to do shell script "date  -r" & ab

The Unix date interpretation of the result is rubbish if you go back before about 1902. But this is interesting (only worth testing on British systems):

set aa to (date "Thursday 1 January 1970 00:00:00")
set b to (date "Friday 1 January 1971 00:00:00")
set ab to (b - aa)
set ab to ((ab div 100000000) as string) & (abs (ab mod 100000000)) div 1

set c to do shell script "date  -r" & ab
--> "Fri Jan  1 01:00:00 BST 1971"

Every winter, when the UK goes back onto Greenwich Mean Time, the lunatic fringe of the road-safety lobby campaigns to have British Summer Time all year round. I think I vaguely remember us trying that for a couple of years in the early seventies. I’ll have to do some research…

Nigel,

You never cease to amaze me.

I was forgetting that the hi-order value might sometimes be zero:

set aa to (date "Thursday, January 1, 1970 12:00:00 AM")
set a to choose file
tell application "Finder" to set b to a's modification date
set ab to (b - aa)
if (ab div 100000000 is 0) then
	set ab to (ab mod 100000000) as string
else
	set ab to ((ab div 100000000) as string) & (abs (ab mod 100000000)) div 1
end if

set c to do shell script "date  -r" & ab

However, my assertion about rubbish results before 1902 still seems to be true.

Yes! Between the winters of 1968/1969 and 1970/1971, the UK remained on British Summer time. Unix knows that. I’m impressed!

how about adding “as string”? unfotunatly i’m away from my mac for the net few houres ant can’t test any of this, but i’ll sure test it and get back :slight_smile:


set aa to (date "Thursday, January 1, 1970 12:00:00 AM")
set a to choose file
tell application "Finder" to set b to a's modification date
set ab to (b - aa) as string

set c to do shell script "date  -r" & ab
c

-->Wed Dec 31 16:00:01 PST 1969

Browser: Firefox 1.0.7 (Debian package 1.0.7-1)
Operating System: Mac OS X (10.4)

And I still didn’t get it quite right, even then. This is it. Promise.

set aa to (date "Thursday 1 January 1970 00:00:00")
set a to choose file
tell application "Finder" to set b to a's modification date
set ab to (b - aa)
if (ab div 100000000 is 0) then
	set ab to ab as string
else
	set ab to ((ab div 100000000) as string) & text 2 thru 9 of ((100000000 + (abs (ab mod 100000000))) as string)
end if

set c to do shell script "date  -r" & ab

The range of values that work correctly with the shell script is “-2147483648” to “2147483647”, which equates on my (UK) machine to dates between “Fri Dec 13 20:45:53 GMT 1901” and “Tue Jan 19 03:14:07 GMT 2038”.

PS. ‘abs’ sneaked in here courtesy of the Satimage OSAX. Otherwise a short handler’s needed to turn negative numbers positive.

thank’s again nigel, i’ll try it.
i was thinking, the only way as lets you get dates as numbers is when comparing dates, so this wouldv’e worked:


set d to current date
set e to date "01/01/1970"
set f to d - e

tat would’ve given a number that sould work with unix, but the time difference in seconds there is too much for as to calculate, so it messes up. but this works, whichmeans the idea works:


set d to current date
set e to date "01/01/2000"
set f to d - e

it will give the number of seconds from year 2000, which proves the point, but is totaly useless.

A unix date stamp can obtained in Applescript with a simple subtraction.

(It is simply the integral number of seconds counting from the UTC start of 1970. This is readily converted, in Bash, to other formats, and is directly usable as an argument to some Bash commands).


property pdteUnixEpoch : missing value

on AS2UnixTime(dteAS)
	if pdteUnixEpoch is missing value then set pdteUnixEpoch to UnixEpoch()
	
	(dteAS - pdteUnixEpoch)
end AS2UnixTime

on UnixEpoch()
	-- Using date  "1/1/1970" is too parochial, consider:
	-- date "יום חמישי 1 ×™× ×•××¨ 1970 00:00:00"
	-- and it would actually fail in some locales e.g. China, 
	-- where a growing proportion of macs are bought ...
	-- http://www.nytimes.com/2011/07/25/technology/apple-sales-in-china-zoom-ahead-of-competitors.html
	-- date "1970年1月1日星期四 上午12:00:00"
	
	tell (current date)
		set {its year, its month, its day, its time} to {1970, 1, 1, 0}
		return it
	end tell
end UnixEpoch

One of Applescript’s more parochial beliefs is, however, that an integer above (2 ^ 29) - 1 is a real. So simple coercion to a string (of the number of seconds since the start of 1970) yields something illegible to Bash, for example, as I write:

1.311680605E+9

We need a function to rewrite this as a simple integer string.


property pMaxInt : (2 ^ 29) - 1

on BigIntegerString(Int)
	if Int > pMaxInt then
		set {dlm, my text item delimiters} to {my text item delimiters, "E+"}
		set {strReal, strOrder} to text items of (Int as string)
		set my text item delimiters to "."
		set strBigInt to first text item of (do shell script "echo '" & strReal & " * (10^" & strOrder & ")' | bc")
		set my text item delimiters to dlm
		return strBigInt
	else
		return (Int as string)
	end if
end BigIntegerString

So now, if a standard unix date stamp is too opaque, we can get a Unix string like: “Tue Jul 26 12:45:31 BST 2011”
(Or, by supplying a format argument to the Bash date -r seconds) command, any other format.

property pdteUnixEpoch : missing value
property pMaxInt : (2 ^ 29) - 1

on run
	-- SAMPLE DATE
	set dteMyDate to (current date) - (time to GMT)
	
	set lngUnixSecs to AS2UnixTime(dteMyDate)
	set strUnixSecs to BigIntegerString(lngUnixSecs)
	
	display alert "
   " & (do shell script "date -r " & strUnixSecs) & "
   or
   " & (do shell script "date -r " & strUnixSecs & " \"+%m/%d/%Y %H:%M:%S\"")
end run

on AS2UnixTime(dteAS)
	if pdteUnixEpoch is missing value then set pdteUnixEpoch to UnixEpoch()
	
	(dteAS - pdteUnixEpoch)
end AS2UnixTime

on UnixEpoch()
	tell (current date)
		set {its year, its month, its day, its time} to {1970, 1, 1, 0}
		return it
	end tell
end UnixEpoch

on BigIntegerString(Int)
	if Int > pMaxInt then
		set {dlm, my text item delimiters} to {my text item delimiters, "E+"}
		set {strReal, strOrder} to text items of (Int as string)
		set my text item delimiters to "."
		set strBigInt to first text item of (do shell script "echo '" & strReal & " * (10^" & strOrder & ")' | bc")
		set my text item delimiters to dlm
		return strBigInt
	else
		return (Int as string)
	end if
end BigIntegerString

This fails on systems which use the decimal comma…
Here’s a fix:

on BigIntegerString(Int)
	if Int > pMaxInt then
		set {dlm, my text item delimiters} to {my text item delimiters, "E+"}
		set {strReal, strOrder} to text items of (Int as string)
		if strReal contains "," then -- allow decimal comma
			set my text item delimiters to ","
			set strReal to text items of strReal
			set my text item delimiters to "."
			set strReal to strReal as string
		end if
		set my text item delimiters to "."
		set strBigInt to first text item of (do shell script "echo '" & strReal & " * (10^" & strOrder & ")' | bc")
		
		set my text item delimiters to dlm
		return strBigInt
	else
		return (Int as string)
	end if
end BigIntegerString