How to Translate Numerical Values for a Macro's `CreationDate` and `ModificationDate`?

@DanThomas has created a very useful set of macros that allow you to copy and edit macros or actions in either KBM's native XML or in JSON. When looking at either version of the code for a macro, there are a couple of numerical values, such as these, taken from the JSON version of a macro I'm working on:

		"CreationDate": 721381695.292977,
		"ModificationDate": 758616880.810884,

How can I interpret those numbers as date and time values?

In looking around the Forum here, I find some interesting macros that will allow you to generated lists of the N most recently edited macros, so I presume that those macros are extracting the ModificationDate from all other macros. So that's one piece of the puzzle.

For my purposes, I would like to know which version of a macro is the most recent when I have saved multiple copies. That would be the N most recent, where N is 1, out of a selected group of macros, not the whole Editor, so that task may be beyond the current limits of that existing tool. I'm not sure and that's more work than translating the numbers right now.

I would also like to know the time order of multiple copies of a macro, especially when I've put cryptic notes into the macro name that I can no longer interpret. Again, above and beyond simply translating the numbers, which can manually give me that information.

At minimum, a function or a link to a units conversion web site where I could enter those values and gets dates and times back would do the job for now. Is there such a thing?

Given that, it would be interesting to embed that function or web access into a macro where I could simply select one of my macros in the KBM Editor and run the "Show Macro Dates" macro and get a text display that shows the macro name, the creation date, and the modification date. I could then copy that text and paste it into a Comment as the first action in the macro itself, which would be a help in keeping my macros annotated.

But first I need to be able to translate the numbers. Any pointers?

Thanks.

It's probably a "unix timestamp with microseconds". If I'm right, that makes your second date equal to "Friday, January 31, 1994, 06:01:20.810884 UTC".

Nope. That's the ModificationDate of a macro I changed recently. It may be seconds, but that's not the right clock.

Well then, it could be SECONDS() which is the number of microseconds since boot time. In that case, your second timestamp refers to 12 minutes ago.

Why translate the numbers @August? Since UNIXtime counts upwards from zero the most recent macro would be the one with the largest number, and getting a most recent list would require sorting the numbers in descending order.

Or have I misunderstood?

The problem is multiple versions of essentially the same macro where I am not sure whether I left a working or a test version active and the file names aren't helping and I'm hoping that listing the names in chronological order will help. Also being able to see elapsed time between different versions may be a memory jogger. Yes, I can do some of that with the coded numbers, but it's much less intuitive.

AND I would really like to be able to generate the CreatedDate for a macro as a calendar value so that I can include it my macro comments. Not functional, just person style and polish.

None of this is critical, it's all in the "wouldn't it be nice" category.

OK. In that case have a look here if you haven't already where you'll find the support that KM provides for working with date-times.

1 Like

If you use JXA to get the macro from the Keyboard Maestro engine, it will return creationDate as a JavaScript Date object:

const kmEditor = Application("Keyboard Maestro");
const uuid = "1C7E9AF3-12CD-40A3-9A39-068A82287A2C";

const result = kmEditor.macros.whose({id: {"=": uuid}});
if (result.length === 0)
	throw new Error(`Macro ${uuid} not found`);
const creationDate = result[0].creationDate()
console.log(`creationDate: ${creationDate}`);

I'm not sure of your use case, so I'll leave it at this.

1 Like

Add 978307200 seconds then convert from Unix time to something readable...

KM Timestamps to Real World.kmmacros (3.6 KB)

You should check the number for yourself -- I'm always hesitant about dates/times because I'm GMT, so relatively immune to time zone issues...

  1. Modify a macro
  2. Grab the integer part of the modification date from the XML
  3. Feed that into %ICUDateTimeMinus%<integer>%Seconds%yyyy-MM-dd HH:mm:ss% to find KM's epoch -- for me that's 2001-01-01 00:00:11 because it took a few seconds -- assume the 11 should be 00 because that's more sensible
  4. Use http://www.epochconverter.com/ to convert that into seconds, or pump it through Terminal's date command: date -j -f "%Y-%m-%d %T" "2001-01-01 00:00:00" "+%s"

Which is how I got the 978307200 offset.

If that "KM epoch" rings a bell -- IIRC it came up before with a KM update resetting dates on macros that hadn't been modified for a long time.

1 Like

Thanks Nige! That works!

I installed your macro and then ran the macro "[KMET] Edit Selected Objects as JSON Text" on it. I found these two lines near the top of the JSON code:

		"CreationDate": 774188485.77487,
		"ModificationDate": 774188596.905292,

I then ran your macro twice, pasting the above numbers in, and got the following results:

2025-07-14 05:21:25
2025-07-14 05:23:16

This says to me that you whipped this macro out in 1 minute and 51 seconds!

For timezone comparisons, your post above shows me a date of "6h" currently. When I click on that I see "Jul 14, 5:53 AM" which says to me that it took about 30 minutes between completing the macro and completing the post, if those two clocks are in sync. This also says to me that your macro is displaying the time in my local time zone, between 5 and 6 am, the same as the Forum does.

For comparison, I created a brand new macro. With KMET it looks like this:

[
	{
		"CreationDate": 774213186.225659,
		"UID": "33D3C8CD-A321-4149-9176-D267ED713D13",
		"Triggers": [],
		"ModificationDate": 774213186.225659,
		"Actions": []
	}
]

The creation date and modification date are the same, 2025-07-14 12:13:06. Again that time is in my local time zone, Pacific Time, in the mountains of Northern California. So it looks like that part of your macro works, despite your disclaimer.

It should be a simple matter for me (and a good exercise) to use this as the basis of a macro that uses KMET to put the JSON form of a macro onto the clipboard, extract the two date values that I want, run them through your conversion, and put text back onto the clipboard to paste into an "About This Macro" comment at the top of the macro that I'm working on. (Not a project for this week, but simple enough to do for fun some upcoming weekend.)

Thanks again. I hope it was fun for you. It certainly was quick!

I hate to disappoint you -- but duplicating a macro resets its creation date to "now" :wink:

If you're doing all that, consider instead @DanThomas's approach above of grabbing the dates with JXA. No need to parse the XML or JSON representation nor do the offset calculation.

I'd usually suggest AppleScript, but I suspect that JS has better options for stringifying dates without you having to write your own routine. Bar the date/time formatting, doing what you want is simple enough -- AS example:

tell application "Keyboard Maestro"
	set theID to item 1 of (get selectedMacros)
	set theCreation to creation date of macro id theID
	set theMod to modification date of macro id theID
end tell
set the clipboard to "Created on: " & theCreation & linefeed & "Last modified: " & theMod

...sets the clipboard to eg:

Created on: Tuesday, 21 May 2024 at 15:02:36
Last modified: Thursday, 10 July 2025 at 20:24:28
1 Like

Using AppleScriptObjC

Cocoa epoch seconds -->
NSDate -->
AppleScript date

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

--Extract Cocoa epoch seconds from deserialized xml

tell application id "com.stairways.keyboardmaestro.editor"
	set macroXML to xml of item 1 of (selected macros as list)
	set macroDict to (current application's NSString's stringWithString:macroXML)'s propertyList()
end tell
set {creDate, modDate} to macroDict's objectsForKeys:{"CreationDate", "ModificationDate"} notFoundMarker:"not found"

--Convert the Cocoa epoch seconds to AppleScript date

--STEP 1
-- Convert to Cocoa epoch seconds to NSDate
set ccCreDate to current application's NSDate's dateWithTimeIntervalSinceReferenceDate:creDate
set ccModDate to current application's NSDate's dateWithTimeIntervalSinceReferenceDate:modDate

--STEP 2
--NSDate to AppleScript date
set asCreDate to ccCreDate as date
set asModDate to ccModDate as date

set the clipboard to "Created:" & asCreDate & linefeed & "Modified:" & asModDate


(*
-- Optionally: Custom format NSDate to text for clipboard output

--STEP 1
--Make a date formatter
set dateFormatter to my dateFormatterWithFormat("YY-MM-dd HH:mm")

--STEP 2
--format NSDate to string format
set formattedCreation to (dateFormatter's stringFromDate:ccCreDate) as text
set formattedModification to (dateFormatter's stringFromDate:ccModDate) as text

set the clipboard to "Created:" & formattedCreation & linefeed & "Modified:" & formattedModification


on dateFormatterWithFormat(theFormat)
	set dateFormatter to current application's NSDateFormatter's alloc()'s init()
	dateFormatter's setDateFormat:theFormat
	return dateFormatter
end dateFormatterWithFormat
*)

2 Likes

A couple of comments/variations on the JXA route:

  1. macros.byId(uuid) suffices.
    No need for the expense of a full query like macros.whose({id: {"=": uuid}})
  2. Branching on macro.exists() is enough – no need, as it happens, for throwing and handling errors.

As for the particular (Date -> String) function that you prefer – lots of options, here is an approach to a timezone-adjusted ISO8601 date:

(The %JSONValue% token can return individual dates by their key)


Creation and Modification date of Macro by UUID.kmmacros (4.9 KB)


Expand disclosure triangle to view JS source
// ------------- ANY (DATE -> STRING) FUNCTION -------------

// dateString :: Date -> ISO8601 String
const dateString = dte =>
    new Date(dte - (6E4 * dte.getTimezoneOffset()))
        .toISOString()
        .slice(0, 19)


// - APPLIED TO CREATED AND MOD DATES OF MACRO IF FOUND --
const
    uuid =  kmvar.local_UUID,
    macro = Application("Keyboard Maestro").macros.byId(uuid);

return macro.exists()
    ? JSON.stringify(
        ["creationDate", "modificationDate"].reduce(
            (a, k) => ({
                ...a,
                [k]: dateString(macro[k]())
            }),
            { name: macro.name(), uuid }
        ),
        null, 2
    )
    : `Macro not found by UUID: '${uuid}'`

2 Likes

That's good to know.

AppleScript's date-to-string options are completely pants, especially if you want portability. You can't, for example, get the month as a number without jumping through a lot of hoops -- including accounting for localisation.

1 Like

JavaScript does benefit from continuous library (and, FWIW, performance) updates.

Date and time, in particular, will be getting quite a big update:

1 Like

Off Topic:

This is the first time I've heard "pants" used as a pejorative. Does "pants" imply "not Scottish"? My free-association takes me to SNL and "If it's not Scottish, it's crap!"

You are making it look really easy!

I used JavaScript for one project about 20 years ago when I was working at Adobe. I was updating the "Acrobat JavaScript API Reference" and used what I was documenting to learn how to massage individual Acrobat comments, changing their color and adding a title line to indicate their status. It was just for me, but I showed it to some Acrobat engineers who implemented something similar (but fewer features) in the next version of Acrobat. If you have an old copy of Acrobat 6, my name is in the credits.

And I've forgotten everything I knew about JavaScript because I haven't used it since...

And, I just discovered, enabling a macro resets it's modification date to "now".

I just ran @ComplexPoint's Creation and Modification date of Macro by UUID on itself. It turns out that somehow (doesn't fit my limited [mis]understanding of KBM's UUIDs) when I installed this macro, it was in my system with exactly the UUID value that Complex Point provided as sample input in the download. But when I enabled the macro to run it, the ModificationDate was a few seconds before "now".

So I undid the change (which allowed me to discover that in KBM when you undo a change you also roll back the mod date) duplicated the macro, and then enabled and ran the copy, which had the other UUID preset, and got the following:

Sweet!

I'm going to mark this as "Solved".

Thanks to everyone for your participation. It's pretty amazing to have experts in KBM, AppleScript, JavaScript, and more all chime in to pull a thing like this together. Better than AI!

2 Likes