Why can I not retrieve the day of a modification date directly?

    --Running under AppleScript 2.8, MacOS 13.4.1
    use AppleScript version "2.4" -- Yosemite (10.10) or later
    
    set theFile to alias "Macintosh HD:Users:UserNameGoesHere:Desktop:blahBlahBlah.psd"
    tell application "Finder"
        set md to modification date of theFile
        -->date "Thursday, February 23, 2023 at 10:35:30"
        try
            set mdD to day of (modification date of theFile)
        on error
            -->Finder got an error: Can’t get day of modification date of document file "HH23-002-001-R_31308-1.psd" of folder "Desktop" of folder "UserNameGoesHere" of folder "Users" of startup disk.
        end try
        set mdD to day of md
        -->23    
    end tell
    

The reason is that day of (modification date of theFile) is compiled as a specifier to something the Finder itself doesn’t actually understand. It (the Finder) only understands the (modification date of theFile) part. Splitting the line into two commands makes it clear to the compiler the that day has to be got from the result of a separate command sent to the Finder. Hope this makes sense. :slight_smile:

1 Like

Does that mean that
set md to modification date of file
does an implicit get first to retrieve the value from the Object Specifier, whereas
day of (modification date of file) does not do that?

How would users recognize these cases where a set is needed from (for example) the scripting dictionary?

Yes, with or without the variable assignment.

Correct.

set isn’t doing anything specific that doesn’t happen of its own accord by the time execution reaches the end of the statement, which is the point where assignment occurs.

The scenario you’re wanting to recognise is when some form of dereferencing is required (either by way of get, or some other means such as coercion). This will be the case whenever you’re retrieving a property of an element, and want to use the value of that property at the point of retrieval, i.e. within the same line. The property will remain only partially dereferenced until after the line has fully executed, so will require get in order to have it evaluated prematurely.

However, if the property is retrieved on one line, by the time execution reaches the line that comes after, the value of the property will be available for use without the need for any special measures, whether or not the value has been assigned to a variable (it will have been assigned to and stored in result, anyway).

Yes. In the first case, the script sends the compiled specifier modification date of theFile (but of course with variable’s contents substituted for the variable itself) to the Finder. The Finder responds by reading the modification date from the file and returning it to the script in the form of an AppleScript date object. It undoubtedly uses routines built into macOS to do both the reading and the date conversion.

In the second case, the compiled specifier is day of modification date of theFile. This doesn’t break any syntax rules as far as the compiler’s concerned and is what is sent (again substituting the variable’s contents) to the Finder. But the Finder’s AppleScript implementation has no facility for getting days from dates, which is what the specifier specifies, so it returns an error instead.

It can be confusing at first because, back in the running script, the day can be extracted from the returned date even if the command to do this is also in the Finder ‘tell’ statement!

Broadly speaking, getting things that belong to things of things can a bit of a minefield. What seems like a reasonable chain of 'of’s to a person may not be so within a particular application — either because something in that chain isn’t implemented by that application or because the application itself has a limit on the complexity of the specifiers it can interpret.

It’s best always to keep in mind that a script is something that tells itself and other things what to do and its code is therefore contextual. As far as possible (and sensible!), one should be aware of the context and not mix code for different contexts in the same statement. And be prepared to be caught out occasionally. That’s part of the fun of AppleScript. :wink:

1 Like

Sorry, that was my typo – I intended to write get, not set.

Thanks for the explanations, also to @Nigel_Garvey and @Fredrik71. It all seems to be due to the awkward mix of object-oriented and messaging paradigms in AS.

modification date is a property of Item with type date (according to the scripting dictionary). It’s most definitely not an element. And in my book, a date object contains a day property.

It’s simply AppleScript muddying the waters here by giving the impression that one could access an object’s property by writing property of object or object's property (like object.property in other languages). And then flipping you the bird by only treating this as a property in some situations (like the assignment with set or in a cast/coercion), but not in others (like accessing a property of the object-valued property).

In a real OO language, you’d write file.modificationDate.day and be done. I

I really appreciate the answers and the further discussion. I feel pretty sheepish since my first instinct was to use “get” but I placed it outside the parenthesis surrounding the modification date. I’m not sure if that prevented it from properly resolving the call or not.

I still can’t resolve the problem here. I was trying to simply get a listing of files in a folder that had been modified today. None of these files are more than a week old, so I thought I could get this with one line. Apparently not.

 tell application "Finder"
    set fileList to every file of the entire contents of (path to the desktop folder) whose (day of (get (modification date))) is 17
end tell

Which gives me a big fat “Finder got an error: Can’t get modification date.”

find on the command line with an appropriate mtime option might do the trick.

set midnight to (current date)
set midnight's time to 0
tell application "Finder"
	set fileList to every file of desktop whose modification date comes after midnight
end tell

Or, much faster:

set midnight to (current date)
set midnight's time to 0
tell application "Finder"
	set {dateList, theFiles} to {modification date, it} of every file of desktop
end tell
set fileList to {}
repeat with i from 1 to (count dateList)
	if ((item i of dateList) comes after midnight) then
		set end of fileList to item i of theFiles
	end if
end repeat
return fileList
3 Likes

Mighty fine Nigel!

Both nice solutions, but that one-liner taught me a new qualifier!

The challenge is that the mac’s default find works in 24-hour blocks, so this will find files (not folders) modified within the last 24 hours rather than the current calendar day. GNU find comes with a -daystart option that begins at midnight.

% find ~/Desktop -type f -mtime 0

Or more properly:

% find ~/Desktop -type f -mtime -1d

I have GNU find installed as gfind. This would list files modified after midnight:

% gfind ~/Desktop -type f -daystart -mtime 0

The default find does allow you to combine units though so you can simulate the beginning of the day like so:

% find ~/Desktop -type f -mtime -22h12m

If the target folder contains only a few files, my favorite is Nigel’s first script. Its very compact and as fast as any other Finder-based solution.

However, I noticed that Paul uses the entire-contents property, which suggests that the number of files could be large. With a target folder containing 392 files in 85 folders, Nigel’s first and second scripts took 12.714 and 1.187 seconds, respectively. FWIW, a slight edit of Nigel’s second script took 1.218 second.

set theFolder to (choose folder)
set dateToday to date string of (current date)
tell application "Finder"
	set {dateList, theFiles} to {modification date, it} of every file of the entire contents of theFolder
end tell
set fileList to {}
repeat with i from 1 to (count dateList)
	if (date string of (item i of dateList)) is dateToday then
		set end of fileList to (item i of theFiles as alias)
	end if
end repeat
return fileList