The Marketo REST Asset API returns invalid ISO 8601 DateTimes

Honestly not sure how this bug exists (any good Date library will prevent it from happening) but Asset API endpoints return a format that isn’t valid ISO 8601. That’s likely to error out when you try to parse it using your language of choice.

For example, the Browse Programs By Folder endpoint returns:

    "success": true,
    "errors": [],
    "requestId": "49e7#187171eaf51",
    "warnings": [],
    "result": [
            "name": "Meeting Request",
            "description": "",
            "createdAt": "2020-08-26T19:55:30Z+0000",
            "updatedAt": "2021-01-04T21:45:19Z+0000",
            "url": "",
            "folderId": {
                "id": 1005,
                "type": "Folder"
            "folderType": "Marketing Folder",
            "parent": {
                "id": 45,
                "type": "Folder"
            "path": "/Marketing Activities/Default/Active Marketing Programs/Meeting Request",
            "isArchive": false,
            "isSystem": false,
            "accessZoneId": 1,
            "workspace": "Default",
            "id": 1005

createdAt is stringified as  "2020-08-26T19:55:30Z+0000". That string contains both the Z that stands for “UTC” in ISO 8601 and the offset +0000 which means exactly the same thing!

In V8 (NodeJS), for one of many examples, the string is unparseable:

> new Date("2020-08-26T19:55:30Z+0000")
⋖ Invalid Date

To work around it, you can strip out known weirdness from the end:

> let fixedDate = "2020-08-26T19:55:30Z+0000".replace(/Z\+0000$|\+0000Z$/,"Z");
> new Date(fixedDate);
⋖ Wed Aug 26 2020 15:55:30 GMT-0400 (Eastern Daylight Time)

(I replaced both Z+0000 and +0000Z since if they can mess up one way, they might mess up the other way in the future!)

Yes, it’s invalid

I wondered if this DateTime format is truly invalid per the ISO Standard, or if it's simply uncommon and maybe overlooked by some languages.

It’s invalid, as ISO 8601 allows Z or +/- , but not both:

C.1.3.2 Date and Time
A date/time string is composed according to one of three representations as illustrated in the following three examples:

• 2001-02-03T09:30:01
• 2004-01-01T10:10:10Z
• 2004-01-01T10:10:10+05:00

Zone-offset may be omitted or included. Time zone designation consists of either a 'Z' to indicate UTC, or a '+' or '-' to indicate "ahead of UTC" or "behind UTC", followed by a 2-digit hour, followed optionally by a colon and the 2-digit minutes.

Some invalid formats are forgiven

Some types of invalid ISO 8601 may still be okay, depending on the language + implementation. For example, using a space instead of the T (spaces aren’t valid 8601) works in V8:

> new Date("2020-08-26 19:55:30Z");
⋖ Wed Aug 26 2020 15:55:30 GMT-0400 (Eastern Daylight Time)

The ECMAScript standard only requires support for yyyy-MM-dd'T'HH:mm:ss with optional Z or +/- offset. But a JavaScript engine is permitted additional “implementation-specific heuristics”:

The function first attempts to parse the format of the String according to the rules called out in Date Time String Format ( If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date format. Unrecognisable Strings or dates containing illegal element values in the format String shall cause Date.parse to return NaN.

So Chrome, Safari, and Firefox could each have product-specific additional formats. (As of this writing I don’t believe that’s the case, but it was in the past and was plenty frustrating.)