Sort Safari history by date

Does anybody know how to sort Safari History (without opening Safari)…?

I am trying to read the history file but I get much “garbage” of the meaningful kind (but not yet meaningful to me):


set SafHist to alias (((path to home folder) as Unicode text) & "Library:Safari:History.plist") as string
set SafHistText to ReadFile(SafHist)

on ReadFile(SafHist)
	set x to ""
	try
		open for access alias SafHist
		set x to read alias SafHist
		close access alias SafHist
	on error
		close access alias SafHist
	end try
	if x ≠ "" then
		return x
	else
		return ""
	end if
end ReadFile

Any help appreciated…

Hi Eelco,

If you have Mac OS X 10.4, then System Events can be used to parse the property list file:
Example:

set historyFile to (path to home folder as Unicode text) & "Library:Safari:History.plist"
tell application "System Events"
	set historyFileContent to value of property list items of property list item "WebHistoryDates" of property list file historyFile
end tell

The problem is that the file is probably formatted in ‘binary’, as opposed to xml. To get around this the tool plutil can be used (btw System Events can read both formats with ease, but use this method if you can’t use the above):

try
	set historyFile to ((path to home folder as Unicode text) & "Library:Safari:History.plist") as alias
on error
	display dialog "Safari's history file does not exist!" with icon caution
	return
end try

set posixHistoryFile to quoted form of POSIX path of historyFile
do shell script "plutil -convert xml1 " & posixHistoryFile

try
	open for access historyFile
	set fileContent to read historyFile
	close access historyFile
on error
	close access historyFile
end try


-- Do something with fileContent


-- the binary format is superior to xml (faster)
-- and is the default in Tiger
do shell script "plutil -convert binary1 " & posixHistoryFile

As for saving changes into the file, you’ll have to get someone else to lend you a hand there (I haven’t really explored into property lists that much), but give me time and I will be happy to give it a go.

That’s exactly what I was looking for.
However, the |lastVisitedDate| in an array of 500 items is in the order of 174884083.4 which can’t be converted into a date string and is difficult to understand.
How to sort/filter those URL’s that were visited in say the last 2 days…?

The value is actually a numeric string ” eg. “174884083.4”. It’ll need confirmation from others, but the formula for turning this into the date of the last visit appears to be:

. the decimal place presumably representing tenths of a second and presumably being rounded off during the addition.

If that’s right, the formula for setting a cutoff “date” of forty-eight hours ago would be:

On my machine, the items produced by Qwerty’s System Events script are listed in date order, the most recent first. So to get the history for the past forty-eight hours:

set historyFile to (path to home folder as Unicode text) & "Library:Safari:History.plist"
set cutoff to (current date) - 2 * days - (date "Monday 1 January 2001 00:00:00") - (time to GMT)

tell application "System Events"
	set historyFileContent to value of property list items of property list item "WebHistoryDates" of property list file historyFile
end tell

set recentHistory to historyFileContent
repeat with i from 1 to (count historyFileContent)
	if (|lastVisitedDate| of item i of historyFileContent comes before cutoff) then
		if (i > 1) then
			set recentHistory to items 1 thru (i -1) of historyFileContent
		else
			set recentHistory to {}
		end if
		exit repeat
	end if
end repeat

recentHistory

But as I said, this needs confirmation from people in other time zones.

I ran the script at 8:04 p.m. E.S.T. and the last site returned was visited almost exactly 48 hours ago at 8:02.

Thanks, capitalj. That sounds hopeful.

I’ve just corrected the script. (See above.) Before, it included the last history item before the cutoff point.

I believe you’re right about the time calculation, Nigel.

I was going to say that the alternative approach below should avoid any error caused by an undefined variable, if the most recent date available occurs later than the cut-off. However, I see you’ve now trapped that one, too. :slight_smile:


set historyFile to (path to library folder from user domain as Unicode text) & "Safari:History.plist"
set cutoff to (current date) - 2 * days - (date "Monday, January 1, 2001 00:00:00") - (time to GMT) as string

tell application "System Events" to set recentHistory to value of (property list items of property list item "WebHistoryDates" of property list file historyFile whose value of property list item "lastVisitedDate" comes after cutoff)


I tried something like that, but it didn’t work. Obviously I can’t have tried it quite right. :confused:

Perhaps it only works as a one-liner. :stuck_out_tongue:

Edit: Ah. That’s what it was. I didn’t coerce ‘cutoff’ to string and so got an empty list as the result. My thinking was that for a short period in September 2032 (!), ‘cutoff’ will have more digits than earlier ‘lastVisitedDates’. It will begin with “1” and they with “9”, which means that, lexically, they’ll come after it.

But in fact, my script still has that problem. The Unicode text value is mentioned before the number in the comparison, so the number is coerced to Unicode text for the comparison. If the number was mentioned first, the Unicode text would would be coerced to number instead and the comparison would be numerical instead of lexical:

-- 'cutoff' is a number, |lastVisitedDate|s are Unicode texts.
-- Change this .
if (|lastVisitedDate| of item i of historyFileContent comes before cutoff) then

-- . to this:
if (cutoff comes after |lastVisitedDate| of item i of historyFileContent) then

Tiger has a new attribute considering numeric strings that allows numeric texts to be compared numerically instead of lexically. (Both items must be texts.) This might suit kai’s version of the script. I haven’t reset my system date to see if the attribute works with a ‘whose’ filter, but it might by 2032! :wink:

set historyFile to (path to library folder from user domain as Unicode text) & "Safari:History.plist"
set cutoff to (current date) - 2 * days - (date "Monday, January 1, 2001 00:00:00") - (time to GMT) as string

considering numeric strings
	tell application "System Events" to set recentHistory to value of (property list items of property list item "WebHistoryDates" of property list file historyFile whose value of property list item "lastVisitedDate" comes after cutoff)
end considering