Get the creation date as short date (YYYY.MM.DD)

Hello,

what I want to do is that:

I’ve got folders, which contain thousands of png files, many of them having the same creation date. With an “on open folder” handler I want to get the creation date of each file, to put all the files having the same date in a newly created subfolder, which is named with that date. The name of the folder should have a short date format like “YYYY.MM.DD”.
Using the Finder’s “get creation date” and converting this to a string, however results in a time stamp in the long format like “Monday, January 4th 2016 12:31:04”. It’s obvious, that you can’t even parse only the date from such a string due to varying length of day and month names.
So, how is it possible to get the date in the short format directly? What other application (or scripting addition) than the Finder can I use to succeed?

Model: not relevant
AppleScript: 2.5
Browser: not relevant
Operating System: Mac OS X (10.10)

Hi,

you can get a short date string by

set shortDateString to short date string of (current date)

but it probably doesn’t match your requested format.
Alternatively extract the date components from the AppleScript date object and compose the date string

set {year:yr, month:mn, day:dy} to (current date)
set dateString to (yr as text) & "." & pad(mn as integer) & "." & pad(dy)

on pad(v)
	return text -2 thru -1 of ((v + 100) as text)
end pad

Hello Stefan,

many thanks for hints. The first works great and it was no problem to convert the result to the requested format. However, I wonder why I could not find this simple script row in any documentation.

Here is the total script for all, who may have similar tasks to do.


on open (folderItem)
	global currentFolder
	set currentFolder to ""
	
		tell application "Finder"
		
		copy (list folder folderItem) to folderList
		
		repeat with pngfile in folderList
			if the class of item ((folderItem as string) & pngfile) is document file then
				set currentItem to item ((folderItem as string) & pngfile)
				get the creation date of currentItem
				copy the result to temp
				set shortDateString to short date string of temp
				set folderDate to "20" & characters 7 through 8 of shortDateString & "." & characters 4 through 5 of shortDateString & "." & characters 1 through 2 of shortDateString
				copy ((folderItem as string) & folderDate & ":") to newFolder
				if newFolder is not currentFolder then
					make new folder at (folderItem as string) with properties {name:folderDate}
					set currentFolder to newFolder
				end if
				move currentItem to newFolder
			end if
		end repeat
	end tell
end open

When you use it, be aware, that the folder contents are sorted by creation date.

Having more than 6,000 objects in a folder, the script in the beginning runs boringly slow (approx. 1 object /sec). But it becomes faster and faster the more of these objects are moved into the subfolders. Running it in the background makes it even a bit faster, since it has not to refresh window contents and it does not slow down foreground applications.

As always, I would not use the Finder to do that.
I would not use list folder which is deprecated for years.
I would not extract date components from short date time which is dependent of the user’s settings.
For instance, you assume that today would be 09/01/16 but on my machine, it’s 2016/01/09.
This is why I choose to use StefanK’s code.

on open (folderItem)
	local currentFolder
	set currentFolder to ""
	set folderItem to first item of folderItem # ADDED
	tell application "System Events"
		set folderList to name of files of folderItem whose visible is true
		set folderItem to folderItem as text
		
		repeat with pngfile in folderList
			if class of disk item (folderItem & pngfile) is file then
				set currentItem to disk item (folderItem & pngfile)
				set {year:yr, month:mn, day:dy} to creation date of currentItem
				set folderDate to (yr as text) & "." & my pad(mn as integer) & "." & my pad(dy)
				set newFolder to folderItem & folderDate & ":"
				if newFolder is not currentFolder then
					make new folder at folder folderItem with properties {name:folderDate}
					set currentFolder to newFolder
				end if
				move currentItem to folder newFolder
			end if
		end repeat
	end tell
end open

on pad(v)
	return text -2 thru -1 of ((v + 100) as text)
end pad

Yvan KOENIG running El Capitan 10.11.2 in French (VALLAURIS, France) samedi 9 janvier 2016 19:06:04

Hello Yvan,

the script row “set folderList to name of files of folderItem whose visible is true” brings up the following error message:

“name of {}” can not be read

I tried this using MacOS 10.7, AppleScript 2.2.1 and System Events 1.3.5. Do I have to use newer versions?

Hi.

In a droplet, the parameter of the ‘open’ handler is a list of the dropped items, even when only one item has been dropped. You need to get the folder (assuming that’s what you’ve dropped) from the list before going for its files. I’ve changed the ‘open’ handler’s parameter name below to make this clearer:

on open droppedItems
	local currentFolder
	set currentFolder to ""
	
	tell application "System Events"
		repeat with thisItem in droppedItems
			if (class of disk item (thisItem as text) is folder) then
				set folderitem to thisItem
				set folderList to (name of files of folderitem whose visible is true)
				set folderitem to folderitem as text
				
				repeat with pngfile in folderList
					if class of disk item (folderitem & pngfile) is file then
						set currentItem to disk item (folderitem & pngfile)
						set {year:yr, month:mn, day:dy} to creation date of currentItem
						set folderDate to (yr as text) & "." & my pad(mn as integer) & "." & my pad(dy)
						set newFolder to folderitem & folderDate & ":"
						if newFolder is not currentFolder then
							make new folder at folder folderitem with properties {name:folderDate}
							set currentFolder to newFolder
						end if
						move currentItem to folder newFolder
					end if
				end repeat
			end if
		end repeat
	end tell
end open

on pad(v)
	return text -2 thru -1 of ((v + 100) as text)
end pad

The code could possibly be tightened up a little like this:

on open droppedItems
	tell application "System Events"
		repeat with thisItem in droppedItems
			set folderItem to disk item (thisItem as text)
			if (class of folderItem is folder) then
				set folderList to (files of folderItem whose visible is true)

				repeat with pngfile in folderList
					if (class of pngfile is file) then
						set {year:yr, month:mn, day:dy} to creation date of pngfile
						tell (yr * 10000 + mn * 100 + dy) as text to set folderDate to text 1 thru 4 & "." & text 5 thru 6 & "." & text 7 thru 8
						if (folder folderDate of folderItem exists) then
							set currentFolder to folder folderDate of folderItem
						else
							set currentFolder to (make new folder at folderItem with properties {name:folderDate})
						end if
						move pngfile to currentFolder
					end if
				end repeat
			end if
		end repeat
	end tell
end open

Sorry Nigel,

this doesn’t help anyway. This script now is hanging without an error message.
It seems to me that operations, which are normally done by the Finder should be done by the Finder in AppleScript too.

Well it does help in that it’s corrected the ‘open’ handler parameter error. If the script’s now hanging on your machine, it means there’s some incompatibility between the current System Events and that on your by now fairly old system. I’ll try the script on my even older 10.5 system to see if I can get any insights into where the problem may lie.

Ever since Mac OS X was introduced, the Finder has used System Events and other processes to do its dirty work. It also appears to have been largely neglected by Apple’s AppleScript developers in recent years. It’s therefore extremely slow in operation and prone to hanging in its own right. Experienced scripters try to avoid using it, especially where “thousands of files” are involved. Yvan suggested scripting System Events directly because it’s lower down the Apple event chain and therefore faster. But if you prefer to use the Finder, that’s up to you.

Hello

My fault. I wrongly deleted an instruction.
My message is corrected with the ADDED instruction.

I tested after applying copy paste to the embedded script so I am sure that it works.

When I have to write/test a droplet, I use an old trick allowing me to test in the Script Editor.

Here is the code used during this step.

--on open (folderItem)
local currentFolder
set currentFolder to ""
tell application "Finder"
	set folderitem to the selection as alias list
end tell
set folderitem to first item of folderitem # ADDED
tell application "System Events"
	set folderList to name of files of folderitem whose visible is true
	set folderitem to folderitem as text
	
	repeat with pngfile in folderList
		if class of disk item (folderitem & pngfile) is file then
			set currentItem to disk item (folderitem & pngfile)
			set {year:yr, month:mn, day:dy} to creation date of currentItem
			set folderDate to (yr as text) & "." & my pad(mn as integer) & "." & my pad(dy)
			set newFolder to folderitem & folderDate & ":"
			if newFolder is not currentFolder then
				make new folder at folder folderitem with properties {name:folderDate}
				set currentFolder to newFolder
			end if
			move currentItem to folder newFolder
		end if
	end repeat
end tell
--end open

on pad(v)
	return text -2 thru -1 of ((v + 100) as text)
end pad

When everything works, I just have to uncomment two instructions and delete the three triggering the hated Finder.
I just deleted the instruction sitting just below the three ones to delete.

Yvan KOENIG running El Capitan 10.11.2 in French (VALLAURIS, France) lundi 11 janvier 2016 15:46:46

I agree with Nigel here on this subject. When I used AppleScript on the old Mac OS systems I used the Finder to do all file handling. Since Mac OS X 10.2 I avoid the Finder as much as possible, only showing a dialog if needed.

OK. There are a couple of scripting differences between the System Events applications in 10.5.8 and 10.11.2:

Whereas in 10.11.2 you can use .

set currentFolder to (make new folder at folderItem with properties {name:folderDate})

. in 10.5.8 it has to be .

set currentFolder to (make new folder at end of folders of folderItem with properties {name:folderDate})

Luckily, this works on both systems, so it may work with 10.7 too.

Similarly .

move pngfile to currentFolder

. has to be .

move pngfile to end of files of currentFolder

. in 10.5.8. Unfortunately, this doesn’t work on both systems. I don’t know when the change occurred, so I can’t build a system check into the script, but the version below tries the current syntax with the first file and switches to the earlier syntax if that attempt fails. It works on both my systems, but of course that doesn’t necessarily mean it’ll work with 10.7 too. Also, I’ve only tried it with folders containing a few files. It may still hang if the folder contains thousands of files. I’m thinking about that…

on open droppedItems
	set possible to true
	
	tell application "System Events"
		repeat with thisItem in droppedItems
			set folderItem to disk item (thisItem as text)
			if (class of folderItem is folder) then
				set folderList to (files of folderItem whose visible is true)
				repeat with pngfile in folderList
					set {year:yr, month:mn, day:dy} to creation date of pngfile
					tell (yr * 10000 + mn * 100 + dy) as text to set folderDate to text 1 thru 4 & "." & text 5 thru 6 & "." & text 7 thru 8
					if (folder folderDate of folderItem exists) then
						set currentFolder to folder folderDate of folderItem
					else
						set currentFolder to (make new folder at end of folders of folderItem with properties {name:folderDate})
					end if
					if (possible) then
						try
							move pngfile to currentFolder
						on error
							set possible to false
							move pngfile to end of files of currentFolder
						end try
					else
						move pngfile to end of files of currentFolder
					end if
				end repeat
			end if
		end repeat
	end tell
end open

Thanks to all,

but unfortunately the script samples as given still do not work. I used MacOS 10.11.2 now together with AppleScript 2.5 and System Events 1.3.6 which, I think, are the newest versions.

The droplets are hanging from beginning, not processing one single file.

Stefan’s sample fails with the instruction “set folderList to name of files of folderitem whose visible is true”. I detected this by setting “display dialog” commands as a break after each row of the script. When I changed this to “set folderList to list folder folderitem” the script runs as expected and is extremely faster compared to the finder version of the script. It only also processes the “.DS” file. But when I changed the class check from “file” to “document file” - as it could be done with the Finder to prevent processing files which are not visible - it brings up an error message.

My suggestions were only about converting AppleScript dates into string, I’m not responsible for file system issues :wink:

document file is not a valid property name for System Events so it’s logical that it is rejected when you try to use it.
I carefully took care to get rid of hidden files with my proposal which - I repeat - behave flawlessly under 10.11.2.

Yvan KOENIG running El Capitan 10.11.2 in French (VALLAURIS, France) mardi 12 janvier 2016 16:04:19

Hello Stefan,

it’s not a system issue but caused by the great number of files in my folder.

I tried your script with only about 30 files and it run fine. But with more than 1500 files in the dropped folder, this instruction works extremely slow and gets a timeout after approx. 90 seconds.

List folder, on the other hand, processes more than 6000 files in the fraction of a second. I wonder why this command should be depreciated.:rolleyes:

Just look the dictionary of the Standard Additions osax !

list folder‚v : Return the contents of a specified folder
list folder file : an alias or file reference to the folder
[invisibles boolean] : List invisible files? (default is true)
→ list of text : a list of the items in the specified folder
This command is deprecated; use ˜tell application “System Events” to get the name of every item of folder .'.

Yvan KOENIG running El Capitan 10.11.2 in French (VALLAURIS, France) mardi 12 janvier 2016 16:59:57

I wondered it myself. I added a similar command with more functions in the AppleScript Toolbox. It also has a date formatter for your YYYY.MM.DD format :slight_smile:

That’s not a reason why it is depreacted, it just shows it is deprecated.

list folder and info for are deprecated since Leopard (late 2007) but both are still working.

I expect it’s the ‘whose’ filter choking on the large number of files.

I’ve been working on this today. The version below only uses System Events to get the required data. The script itself checks for visibilty and shell scripts do the subfolder creation and file moving. It works on both my 10.11 and 10.5 systems, handling a 6592-file folder on the latter (a slower machine) in about twenty seconds ” and that’s with BOINC running in the background.

On my 10.11 machine, an identical folder is handled in about five seconds. But it seems to be a good idea here to save the script before running it in Script Editor, otherwise Autosave could cause hanging problems too.

open {choose folder} -- For testing.

-- Droplet 'open' handler: process each item in the drop.
on open droppedItems
	repeat with i from 1 to (count droppedItems)
		process(item i of droppedItems)
	end repeat
end open

-- The main process for each item, in its own handler so that all variables are local and non-persistent.
on process(thisItem)
	-- Get a System Events reference to the item and check that it's a folder.
	tell application "System Events"
		set mainFolder to disk item (thisItem as text)
		set isFolder to (mainFolder's class is folder)
	end tell
	
	if (isFolder) then
		-- Script object having list properties (referenceable for speed of access to items) and the comparison handler for a custom sort.
		script o
			property fileNames : missing value
			property fileCreationDates : missing value
			property fileVisibles : missing value
			property sortedData : {}
			property dateGroupNames : {}
			
			-- Custom-sort comparison handler. Compares two lists from a list of lists ” each containing an integer representing a file's creation date, the file's name, and the value of the file's 'visible' property. The boolean returned to the sort will cause lists representing visible files to be sorted by creation date while those for invisible files are simply shunted to the end.
			on isGreater(a, b)
				-- List a is "greater" than (ie. should come after) list b if (file b is visible and file a has a later creation date) or (file a is invisible).
				return (((end of b) and (beginning of a > beginning of b)) or not (end of a))
			end isGreater
		end script
		
		-- Get the names, creation dates, and visibles of all the files in the folder.
		tell application "System Events"
			set {o's fileNames, o's fileCreationDates, o's fileVisibles} to {name, creation date, visible} of files of mainFolder
		end tell
		
		-- Collate the data into a list of lists, such that each sublist represents a file and contains an integer derived from its creation date in yyyymmdd format, the quoted form of its name, and its boolean 'visible' value.
		repeat with i from 1 to (count o's fileNames)
			set {year:yr, month:mn, day:dy} to item i of o's fileCreationDates
			set end of o's sortedData to {yr * 10000 + mn * 100 + dy, quoted form of item i of o's fileNames, item i of o's fileVisibles}
		end repeat
		-- Sort the lists of lists so that those representing visible files are grouped by creation date and any for invisible files are moved to the end.
		my CustomShellSort(o's sortedData, 1, -1, {comparer:o})
		
		-- Note the creation-date integer from the first sublist.
		set currentFolderDate to beginning of beginning of o's sortedData
		-- Get the POSIX path of the folder being processed as we'll be using shell scripts from now on.
		set mainFolderPosix to quoted form of POSIX path of thisItem
		
		-- Now work through the list of lists.
		repeat with i from 1 to (count o's sortedData)
			set {dateNo, fileName, isVisible} to item i of o's sortedData
			-- If we hit a list for an invisible file, there are no more visible ones.
			if (not isVisible) then exit repeat
			-- If we hit a list with a different file creation date, move the files whose names we've collected in connection with the previous date, start a new name list, and set the new creation date as the one being handled.
			if (dateNo is not currentFolderDate) then
				moveFiles(currentFolderDate, mainFolderPosix, o)
				set o's dateGroupNames to {}
				set currentFolderDate to dateNo
			end if
			-- Append the current file name to the list of names associated with the creation date being handled.
			set end of o's dateGroupNames to fileName
		end repeat
		-- When there are no more sublists to be parsed, move the files associated with the last creation date.
		moveFiles(currentFolderDate, mainFolderPosix, o)
	end if
end process

-- Create a "date" subfolder and move the file(s) with that creation date to it.
on moveFiles(currentFolderDate, mainFolderPosix, o)
	-- Derive a "yyyy.mm.dd" text from the yyyymmdd integer and construct a mkdir command to create a subfolder with that name.
	tell currentFolderDate as text to set folderDate to text 1 thru 4 & "." & text 5 thru 6 & "." & text 7 thru 8
	set subfolderPosix to mainFolderPosix & folderDate
	set mkdirCom to "mkdir -p " & subfolderPosix & ";"
	
	set z to (count o's dateGroupNames)
	-- We'll be using path expansion in the mv command(s) below to move multiple files, but we can't with single ones.
	if (z > 1) then
		set b1 to "{"
		set b2 to "} "
	else
		set b1 to ""
		set b2 to " "
	end if
	-- Build and execute mv command(s) to move the files to the subfolder in groups of 1000 or less. The mkdir command creating the subfolder is included in the first shell script.
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ","
	repeat with i from 1 to z by 1000
		set j to i + 999
		if (j > z) then set j to z
		do shell script (mkdirCom & "mv " & mainFolderPosix & b1 & items i thru j of o's dateGroupNames & b2 & subfolderPosix)
		set mkdirCom to ""
	end repeat
	set AppleScript's text item delimiters to astid
end moveFiles

-- Shell sort. Algorithm: Donald Shell, 1959. AppleScript implementation: Nigel Garvey, 2010.
on CustomShellSort(theList, l, r, customiser)
	script o
		property comparer : me
		property slave : me
		property lst : theList
		
		on shsrt(l, r)
			set step to (r - l + 1) div 2
			repeat while (step > 0)
				slave's setStep(step)
				repeat with j from (l + step) to r
					set v to item j of o's lst
					repeat with i from (j - step) to l by -step
						tell item i of o's lst
							if (comparer's isGreater(it, v)) then
								set item (i + step) of o's lst to it
							else
								set i to i + step
								exit repeat
							end if
						end tell
					end repeat
					set item i of o's lst to v
					slave's rotate(i, j)
				end repeat
				set step to (step / 2.2) as integer
			end repeat
		end shsrt
		
		-- Default comparison and slave handlers for an ordinary sort.
		on isGreater(a, b)
			(a > b)
		end isGreater
		
		on rotate(a, b)
		end rotate
		
		on setStep(a)
		end setStep
	end script
	
	-- Process the input parameters.
	set listLen to (count theList)
	if (listLen > 1) then
		-- Negative and/or transposed range indices.
		if (l < 0) then set l to listLen + l + 1
		if (r < 0) then set r to listLen + r + 1
		if (l > r) then set {l, r} to {r, l}
		
		-- Supplied or default customisation scripts.
		if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
		
		-- Do the sort.
		o's shsrt(l, r)
	end if
	
	return -- nothing.
end CustomShellSort