The FooDoo Lounge

Date Strings

Index

History

Back when I had a really slow machine, I became interested in the fastest possible way of getting a date string in something other than the supplied format. I engaged a few people in discussion about it, including Emmanuel Lévy, who writes the excellent, freeware AppleScript editor called Smile.

Most of it was reasonably simple and fast to do, except getting a month number. Various things were suggested including the common repeat loop and offset methods, but they were all either slow, verbose, or both. Then Emmanuel posted his wonderful numeric method, which was dubbed French Vanilla. I put the fastest methods together into handlers and posted them to AS Users & MACSCRPT. Richard Hartman posted a slightly compacted version of it to one of the lists some time later and I now use a version of this, called monthNum. Nigel Garvey optimised it even further and his version can be found here and also on MacScripter.

A lot of this really is history now - for those running late model machines at least - as AppleScript 1.9.2 added a few extra properties to the date object, which make redundant some of the previously required convolutions. There are also very few situations in which the difference between a painfully slow and inefficient handler and a very fast one will make a noticeable difference to your code.

The date object

This is an AppleScript data type which I believe is represented internally as the number of seconds since 1 Jan 1904. Dates display differently depending on the settings in your Date & Time Control Panel, i.e. they are returned in the language and format you've set there.

This is why it's a Very Bad Idea™ to coerce the date to a string and parse it by getting word 3 or similar methods. Such constructs are almost guaranteed to break on systems that use a different date format. If you live in the US, then that means pretty much every other country in the world.

set cDate to current date
--> date "Monday, 11 November 2002 11:11:11 AM"

class of cDate
--> date

When you put the keyword date in front of any string that can be coerced to a date, it becomes an AppleScript date object:

set dStr to "11/11/02"
return date dStr
--> date "Monday, 11 November 2002 12:00:00 AM"

AS is exceedingly flexible as to the strings it will coerce to date objects. It accepts all sorts of partial information and a wide range of delimiters. It can't transpose year-last to year-first dates, or vice versa, but other than that it's very good at interpreting strings.

Object properties

The results you see vary slightly depending on language and D/T settings. If your system is set to display times in 24 hour format, this is reflected in the time string. See AppleScript's dictionary for a list of date properties.

date string of cDate
--> "Monday, 11 November 2002"

time string of cDate
--> "11:11:11 AM"

day of cDate
--> 11

weekday of cDate
--> Monday

month of cDate
--> November

These last two are constants, not strings. AS 1.3.7 (which shipped with OS 8.6) and higher allows them to be coerced to strings.

year of cDate
--> 2002

time of cDate -- The number of seconds since midnight
--> 40271

AS 1.9.2 (OS 10.3) added a new property - short date string - and a coercion - month as integer (or number) - to the date object. The new property returns a date string in the format set in your International Preferences. Mine looks like this:

short date string of cDate
--> "11/11/02"

The as integer coercion for month means we can return a number directly without a repeat loop or a French Vanilla type routine:

month of cDate as integer
--> 11

Because Macs store dates internally as a number, date objects can be used arithmetically:

set mDate to date "Monday, 11 November 2002 12:00:00 AM"
cDate - mDate
--> 40271

Subtracting one date from another returns an integer (as above) whilst subtracting or adding an integer (assumed to be a value in seconds) from/to a date object returns a date object.

Building Blocks

The object properties provide the day and year in a form that can be coerced to string. Time string is already there but may (depending on your DT settings) use colons as the delimiter, making it unsuitable for use in file names. Versions of AS prior to V1.3.7 (OS 8.6) are unable to provide month or day names as strings and versions of AS prior to 1.9.2 cannot supply a month number directly.

The remaining issues then, are to provide a month number (if considering OS versions prior to 10.3), a time string for use in file names and truncated month and day names. The truncated names are simple if one assumes OS 8.6 or later:

text 1 through 3 of (month of cDate as string)
--> "Nov"

The date handlers and Snippets section demonstrate backward compatible methods for doing this, which basically involve a list of short month names and the use of French Vanilla to get the index of the appropriate list item.

Month Numbers

The most common method used is a repeat loop. This one returns the loop counter and will exit the repeat loop when the condition is met:

set monthConstants to {January, February, March, April, May, June, July, August, September, October, November, December}
repeat with m from 1 to 12
    if (month of cDate) = item m of monthConstants then return m
end repeat
--> 11

Emmanuel Levy's French Vanilla method is considerably faster and more compact than anything else I've ever seen, though the difference in speed will be inconsequential for most purposes:

copy cDate to b
set month of b to January
return (1 + ((cDate - b + 1314864) div 2629728))
--> 11

This version uses a tweak suggested by Richard Hartman on the AS Users list:

copy cDate to dateTemp
set month of dateTemp to January
return (1 + (cDate - dateTemp) div 2.5E+6)

Nigel Garvey devised a further refinement/optimisation of FV, which he posted to the new Code Exchange section of MacScripter and which looks like this:

copy cDate to date2
set date2's month to January
return (cDate - date2 + 3944592) div 2629728

It appears that a long if/else statement can come close to this on speed with some software/hardware combinations. This was reported by 'has' to AS Users. It was not the case for me using a G4/400 under OS 9.0.4, but 'has' was able to get faster results than I could overall, using a 300MHz iBook under OS 8.6.

I wouldn't personally recommend this due to the excessive character count of such a construct, though it was interesting to note that it was significantly faster than an equivalent repeat loop, such as the one shown above.

File name friendly time strings

A simple tids based algorithm for replacing the time string delimiters. Note: this assumes that the time string uses colons as separators:

set timeString to time string of cDate
tell AppleScript -- Just saves typing
    set oD to text item delimiters
    set text item delimiters to ":"
    set timeStringItems to text items of timeString -- {"11", "11", "11 AM"}
    set text item delimiters to "-" -- Change this to something else if req.
    set newTimeString to timeStringItems as string
    set text item delimiters to oD
    return newTimeString
end tell
--> "11-11-11 AM"

See the handlers page for a copy of this.

Adding leading zeros to day or month numbers

It is often useful to add leading zeros to numbers less than 10 so that they will sort properly. This method was written by Nigel Garvey:

set someDate to date "Tuesday, 1 January 2002 12:00:00 AM"
text 2 thru 3 of ((100 + (someDate's day)) as string)
--> "01"

Whilst there is no huge advantage in checking whether the zero is required, this method tests as faster than the above under OS 9.1/AS 1.5.5:

set dd to someDate's day
if dd < 10 then set dd to ("0" & dd)

The advantage is not so much in the fact that it's text based rather than numeric, but that it will, on average, only add the zero a third of the time because months have around 30 days and only 9 of them require leading zeros. The situation is reversed for month numbers.

The other issue (not that any of this really matters in practical terms, but anyway...) is that different versions of AS produce differing comparative results. NG tested his numeric method against the text based one under AS 1.3.7 and his proved faster. I tested them under AS 1.5.5 and got quite different results. It seems that hardware type has a role to play as well.

The moral of this story is that it's probably somewhat futile to spend a lot of time testing these things in this context because there are too many variables involved and because they are already very fast. Don't let that stop you though. ;-)

Code Snippets

This section just puts all the parts requiring processing together so they can be copied out easily. They assume a date object in a variable called myDate:

set myDate to date "Saturday, 1 February 2003 12:34:56 AM"

Day of the month with leading zero

For all OS versions. The first will be slightly faster under some circumstnaces.

set myDay to text 2 thru 3 of ((100 + (myDate's day)) as string)
--> "01"

Or:

set myDay to text -2 thru -1 of ("0" & (myDate's day))
--> "01"

Abbreviated month name

The most complex operation on older systems. This French Vanilla based code works on any English language system and is very fast:

copy myDate to dateTemp
set month of dateTemp to January
set shortMonthList to {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
set shortMonth to shortMonthList's item ((myDate - dateTemp + 3944592) div 2629728)
--> "Feb"

This is for OS 8.6 (AS 1.3.7) or later:

set shortMonth to text 1 thru 3 of (myDate's month as string)
--> "Feb"

Month number with leading zero

Again, somewhat convoluted if backward compatibility is required.

copy myDate to dateTemp
set month of dateTemp to January
set numMonth to text 2 thru 3 of ((100 + ((myDate - dateTemp + 3944592) div 2629728)) as string)
--> "02"

This works only under OS 10.3 (AS 1.9.2) or later:

set numMonth to text 2 thru 3 of ((100 + (myDate's month as integer)) as string)
--> "02"

Handlers

Complete handlers can be put together from the information contained on this page or selected from amongst the ones shown here.

 

The FooDoo Lounge is Copyright © Richard Morton 2002-2005

|| url: http://www.foodoo.sunreal.com.au/info/date_info.html
|| created: 21-Nov-03, 7:28 PM; updated: 24-Aug-04, 6:00 PM
|| size: 57185 bytes