AppleScript: convert long date text string into AppleScript date

I post the occasional script on here in the vague hope it will help someone sometime but suspect a lot of them are too specialist (in terms of being rather specifically related to my journal records in DEVONThink). However, this week I had a fairly intense scripting session trying to work out how to convert a name of a record like:

Wednesday 13 October 2021.md

into a date AppleScript would recognise.

Stripping the extension is easy (look for past help with that on the forum from @Bluefrog :wink:) but here’s the handler I devised for what I (at least) regard as the heavy lifting—actual conversion of a long text date to an actual AppleScript date.

Just invoke it with something like set theLongDate to my formatDate(theLongDateText).

on formatDate(LongDateStr)
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to " "
	-- break the text date down into its individual components using a space
	set the nameComponents to (text items of LongDateStr)
	-- item 1 is the day of the week so we ignore that
	-- item 2 is a text number so that's easy to convert to an integer
	set theDay to item 2 of nameComponents as integer
	-- item 3 is the name of the month, which we have to convert to an integer
	set theMonth to item 3 of nameComponents as string
	set monthList to {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
	repeat with i from 1 to the count of monthList
		-- here's the number of the month
		if item i of monthList is theMonth then set theMonth to i
	end repeat
	-- item 4 is a text number so that's easy to convert to an integer
	set theYear to item 4 of nameComponents as integer
	-- assemble from the pieces something that looks like a date
	set formattedDateText to theDay & "/" & theMonth & "/" & theYear as string
	set AppleScript's text item delimiters to astid
	-- set up a real AppleScript date
	set theDate to current date
	-- set the date using the bits and pieces we've assembled
	tell theDate to set {its day, its month, its year} to words of formattedDateText
	tell theDate to set {its hours, its minutes, its seconds} to {0, 0, 0}
	set formattedDate to theDate
	-- breathe a sigh of relief and return a real date
	return formattedDate
end formatDate

I’m sure there are better, faster ways, (and have no doubt someone will tell me so) but at least it works!

Stephen

5 Likes

And that’s always a win!

PS: Nicely commented too! :heart: :slight_smile:

Thanks: and you know why I wrote it! :wink:

Stephen

1 Like

I suppose the value returned is a Date object rather than a string?

Yes.

Edit: I just know you’re going to produce a single line of JavaScript, with a plethora of brackets and incomprehensible actions, that does the same thing!

Stephen

1 Like

<Sigh>. Of course, a little research on the forum would have revealed an infinitely more efficient way of doing this and would have saved much anguish.

On the bright side, I’ve learned odds and ends along the way!

Stephen

3 Likes

Well, it can be done a bit more succinctly.

const s ="Wednesday 13 October 2021";
const longMonths = ["January","February", "March", "April", "May", "June", 
"July", "August", "September", "October", "November","December"]; 
const splitDate = s.split(" "); /* split the date string at blanks and store in an array */
/* get the index of the month in the longMonths array, i.e. a number between 0 and 11.
  Add 1 to get the usual month number 1...12
*/
const monthNumber = longMonths.indexOf(splitDate[2])+1; 
/* Build a date object from the year, monthNumber and day. They are passed as a string
"yyyy-mm-dd" to the Date constructor.
*/
const myDate = new Date(`${splitDate[3]}-${monthNumber}-${splitDate[1]}`);
console.log(myDate);

Or even better

const s ="Wednesday 13 October 2021";
/* Use a regular expression to weed out the weekday and reformat the rest as 
"October 13, 2021 00:00:00" */
const fixedDate = s.replace(/^\w+\W+(\d+)\W+(\w+)\W+(\d+)$/,"$2 $1, $3 00:00:00");
const myDate = new Date(fixedDate);

But (big but): The latter does not really work reliably because it depends on the parsing of a date string (“October 13, 2021”) and this is, according to MDN, browser-dependend. Or more generally implementation-dependend. The above code run in node.js gives me October 12, 2021 at 22:00 – apparently it does something weird with the timezones.

Now, don’t complain about the parenthesis, brackets and stuff – you baited me into it, now you have to suffer the consequences.

1 Like

:rofl: True.

However, more significantly, I’m getting different results when using my original handler and the revised handler. The revised handler is this:

on formatDate(LongDateStr)
	set {dd, mm, yyyy} to words of (short date string of (date LongDateStr))
	return (short date string of (date LongDateStr))
	set formattedDate to (dd & "/" & mm & "/" & yyyy)
	return formattedDate
end formatDate

If I run both scripts on a record with the name Tuesday 2 March 2021.md my original script correctly populates the custom metadata. The revised handler returns 02/03/2021 (which is obviously correct) but populates the custom metadata with Wednesday 3 February 2021 (clearly reversing the day and month in the process).

My date settings in System Preferences are as follows:

2021-10-14_10-01-01

So I’m not quite sure why I’m getting two different results.

Stephen

Does the handler continue after the first return statement?

Apart from that, I don’t know how AppleScript parses and builds the date depending on global system settings (or not).

Sorry - twofold:

  • the first return statement was left in by mistake after I’d been testing it;
  • all of that shouldn’t really have been included in the reply to you: I wasn’t really expecting you to deal in detail with AppleScript problems in that context!

I think it’s better if I start a separate thread for something that may possibly be a bug (somewhere, in something!).

Stephen

I guess this code could be more stable (esp with different localizations):

on formatLongDate from LongDateStr by thisDelimeter
	set {saveTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, thisDelimeter}
	set {dd, mm, yyyy} to every text item of (short date string of (date LongDateStr))
	set AppleScript's text item delimiters to saveTID
	set formattedDate to (dd & "/" & mm & "/" & yyyy)
	return formattedDate
end formatLongDate

Thank you: certainly a lot more concise! :grin:

Stephen