Add direct support for ISO 8601 formats

While writing my macro that shows upcoming rocket launches, one of my challenges was in trying to convert the ISO 8601-formatted time in the JSON, which looked like this:

2023-12-11T10:30:00Z

I needed to convert that into a unixtime value, which is done with the TIME() function. But that function only accepts input in the form of TIME(year, month, day, hour, min, sec). That meant lots of fun massaging the UTC time into a format that the TIME() function would read.

I'd love it if KM could support this natively; this should be valid input for any function expecting a date/time value:

TIME(local_myISOformattedTime)

KM is well aware of ISO8601 formats, even linking to the standards page from the Dates and Times wiki page (in the Date/Time Format section). Being able to directly use ISO 8601 format would be a big timesaver when dealing with date and time issues.

Unless I'm missing something obvious, and there was already a really easy way to do this conversion that I just didn't find, @peternlewis?

-rob.

Shell commands have sophisticated date manipulation. I'll google that.

It looks like the key to the solution is here:

I'll see if I can massage that into a KM macro.

Oh we had it in the macro, but opening a shell is slower than regex. We tested shell, JavaScript for automation, and substring. All were slower than the regex.

It’s not a matter of it not being possible, but rather it should be automatic if you use an approved format.

-rob.

I see. Well, your shell solution was intimidatingly long for me, so I was looking for a one liner using the date command. I'm getting close, so I'll keep working on it.

I didn't think I posted the shell solution - this is the one liner we tried:

time=$(date -jf "%Y-%m-%dT%H:%MZ %z" "$original_time +0000" +"%l:%M%p")

The problem is that opening the shell takes longer than running the regex. But the bigger point is still that I think this should be built into KM, given it's a standard :).

-rob.

My solution is very similar. I came up with it independently. I used a different method to calculate the local time.

You are right, it may be more efficient to make this into a native KM function, but now at least I have a way to convert Zulu to local. I understand my way. I will need more time to understand your way.

1 Like

Not only that, you don't want KM's underlying implementation of the idea to be calling a shell to do the calculation. After all, KM could simply do what we did - call the shell to do the work.

Your first sentence said you wanted to convert a date in this format: "2023-12-11T10:30:00Z" but your solution doesn't work with that format because you forgot the %S (Seconds) in your format string. You also included %z which presumably is for the time zone but which is not in your original input string.

I thought that part of the reason for doing this conversion was to include converting the input string into local time. It appears to me that your solution doesn't actually do the conversion. My solution converted to local time, and included the seconds and displayed the 3-letter time zone string correctly.

Sorry, I pasted an earlier version; here's the whole thing working:

$ original_time="2023-12-13T01:14:00Z"
$ time=$(date -jf "%Y-%m-%dT%H:%M:%SZ %z" "$original_time +0000" +"%l:%M%p")
$ echo $time
5:14PM

As seen above, it does - 01:14 UTC becomes 5:14PM local time. I didn't need the seconds or the time zone string, as this was to format the time for display in the HTML table, which just shows hh:mm and am/pm.

Yes, I agree yours does the full conversion. Playing with the format on the version we came up with should come to the same thing—but we didn't need more than what we got, so we stopped there :).

-rob.

1 Like

Noted.

2 Likes

@griffman @Airy @peternlewis

and, of course, the JS Date() constructor reads ISO8601 strings directly,
so a JavaScript for Automation action can also be useful:

Unix seconds integer from ISO8601 date string.kmmacros (3.4 KB)

1 Like

We actually tried one of those during our testing, though our version was more complicated as neither of us had used it before, and we were working in KM10—I still use 10.x for development, to make sure my macros will run on at least 10 and 11. So we spent a lot of time on the JXA wiki page figuring out the setup needed to get the variable into the JXA.

In the end, this is the JXA that I managed to get working:

(function myFunct () {
  const app = Application.currentApplication ();
  app.includeStandardAdditions = true;
  const kmInst = app.systemAttribute("KMINSTANCE");
  const kmeApp = Application("Keyboard Maestro Engine")

  const myDate = kmeApp.getvariable ("local_myISOdate", {instance: kmInst});
  const processedDate = Date.parse (myDate)/1000;
  return processedDate;
})()

Do you see any way to simplify that and still have it work in 10.x? As noted, my buddy and I are both very inexperienced with JXA.

But the bigger issue was that, at least as written above, that was notably slower than using the existing regex solution. I wrote a test case that uses all the variations we tried:

The "Regex" line was my original version, the "Calc variable" method is what I switched to, and the "Shell" one was just before we tried the "JXA" version.

I went with the calc variable option, as it was somewhat simpler and usually (not in this simple test case I built for some reason) a bit faster than the Regex version. I also ran your version on my 11.x machine, too:

So it's more than twice as fast as our JXA in v10, but still twice as slow as the regex solution.

thanks;
-rob.

For Keyboard Maestro version 10, you could prune out some of that and write:

Execute a JavaScript For Automation.kmactions (1.1 KB)

Keyboard Maestro Export

Expand disclosure triangle to view JS source
new Date(
    Application("Keyboard Maestro Engine")
    	.getvariable(
        	"local_myISOdate", {
            instance: (ObjC.unwrap(
                $.NSProcessInfo.processInfo.environment
                .objectForKey("KMINSTANCE")
            ) || "")
        }
    )
)
.getTime() / 1000;

Thanks ... that worked, though it's actually a bit slower (like, really just a bit, so small as to not really matter) than the version we have.

That's why I'd love to see native support for ISO 8601: Both JXA and shell incur the 'start an engine' overhead, whereas if it's native in KM, it should be lightning fast. (This is also why the two regex variants win, as they don't have any non-KM overhead ... at least that's my guess.)

-rob.

2 Likes