# JSONata $toMillis() in change node will not convert an ISO complaint date string

**URL:** <https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003>\
**Category:** General\
**Tags:** jsonata\
**Created:** [13 May 2026 09:09 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003 "2026-05-13T09:09:43Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![IanH](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/ianh/32/95002_2.png) [@IanH](https://discourse.nodered.org/u/IanH)\
**Post date:** [13 May 2026 09:09 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/1 "2026-05-13T09:09:43Z")

</div>

I have another anomaly when using JSONata in a change node, and it is to do with time/date objects (of course).  
I am using a **node-red-contrib-sun-event-trigger** to produce a sunrise trigger, and also capture the days other sun related events. on start up it produces an object with several key:value pairs for the days event, with the value being a date string like **"2026-05-12T20:48:34.671Z"** which is ISO compatible. But when I try to convert this to a date number with **$toMillis()** in a change node I get the error.

`Invalid JSONata expression: Argument 1 of function "toMillis" does not match function signature`

The Jsonata statement works in [try.json.org](http://try.json.org) when tested against a copy of the data fed to the node.

I can convert it the date string with some JS in a function node (val is the date string)

```auto
    else if (isValidDateString(val)) {
        // If it's a valid date string, parse directly
        millis = new Date(val.trim()).getTime();
    }
    else {
        // Try to coerce to string and parse
        let strVal = String(val).trim();
        if (isValidDateString(strVal)) {
            millis = new Date(strVal).getTime();
        }

```

Here is the simplified flow that exhibits the behaviour.

```auto
[
    {
        "id": "e75342e810b228f7",
        "type": "debug",
        "z": "fb3b8eb733c0c5e0",
        "name": "Discrete Event",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 860,
        "y": 860,
        "wires": []
    },
    {
        "id": "0683c88ce677ce7a",
        "type": "change",
        "z": "fb3b8eb733c0c5e0",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "sunrise",
                "pt": "msg",
                "to": "$toMillis(payload.sunrise)",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 640,
        "y": 860,
        "wires": [
            [
                "e75342e810b228f7"
            ]
        ]
    },
    {
        "id": "6627d82026f11d88",
        "type": "switch",
        "z": "fb3b8eb733c0c5e0",
        "name": "",
        "property": "payload.sunEvent",
        "propertyType": "msg",
        "rules": [
            {
                "t": "null"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 410,
        "y": 960,
        "wires": [
            [
                "3a8e0dd0848ee708",
                "f50672bec3e58cfa",
                "0683c88ce677ce7a",
                "e75342e810b228f7"
            ],
            [
                "8ed74b07effbab9b"
            ]
        ]
    },
    {
        "id": "bee2284c3912022b",
        "type": "junction",
        "z": "fb3b8eb733c0c5e0",
        "x": 300,
        "y": 960,
        "wires": [
            [
                "0a11d8da95898da4",
                "6627d82026f11d88"
            ]
        ]
    },
    {
        "id": "792599f715b4c8b7",
        "type": "sun-event-inject",
        "z": "fb3b8eb733c0c5e0",
        "name": "",
        "latitude": "-35.31120",
        "longitude": "149.68053",
        "event": "sunrise",
        "offset": "0",
        "injectEventTimesAfterStartup": true,
        "x": 210,
        "y": 960,
        "wires": [
            [
                "bee2284c3912022b"
            ]
        ]
    },
    {
        "id": "70b8f91ff26d4635",
        "type": "global-config",
        "env": [],
        "modules": {
            "node-red-contrib-sun-event-trigger": "1.1.1"
        }
    }
]

```

Although I have a workaround that suits me using the function node and converting all date strings to millis when received, it has taken me a while to develop a work around what is I believe is a bug in JSONata implementation... is there something in the node-red-contrib-sun-event-trigger node output I cannot see the cause.

ta

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [13 May 2026 10:23 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/2 "2026-05-13T10:23:02Z")

</div>

I've just fed the ISO date/time string you provided through a change node set to convert to millis and it worked fine, no errors.

```json
[{"id":"6f334eaa7fed3055","type":"inject","z":"b2f18a716bd20f99","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2026-05-12T20:48:34.671Z","payloadType":"str","x":190,"y":2020,"wires":[["e727dc4d59e1a0e6"]]},{"id":"fd63f2ac0b883f2b","type":"debug","z":"b2f18a716bd20f99","name":"debug 38","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":620,"y":2020,"wires":[]},{"id":"e727dc4d59e1a0e6","type":"change","z":"b2f18a716bd20f99","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$toMillis(payload)\t","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":2020,"wires":[["fd63f2ac0b883f2b"]]}]

```

Did I miss something?

Incidentally, I use [node-red-contrib-sun-position (node) - Node-RED](https://flows.nodered.org/node/node-red-contrib-sun-position) and have a startup flow that writes sun and moon details to MQTT, and updates the sun and moon positions every minute for good measure.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/8/b/8bbc095aaea225a1651fa0072ec91fd80797d041.png)

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [13 May 2026 10:26 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/3 "2026-05-13T10:26:58Z")

</div>

Ah! Got it! 😃

You've unfortunately fallen into one of JavaScripts more confusing traps.

The value you shared is what happens to a JavaScript timestamp when it is serialised to a string - for example, when you dump it to the debug output.

The error you are getting is because your input _is not_, in fact an ISO timestamp string - but rather an actual JavaScript Date timestamp. `$toMillis` doesn't actually work with that - presumably because it is unnecessary, you already have the data in millis.

---

<div class="post-metadata">

**Author:** ![IanH](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/ianh/32/95002_2.png) [@IanH](https://discourse.nodered.org/u/IanH)\
**Post date:** [14 May 2026 09:53 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/4 "2026-05-14T09:53:52Z")

</div>

> [@TotallyInformation](#):
>
> The error you are getting is because your input _is not_, in fact an ISO timestamp string - but rather an actual JavaScript Date timestamp. `$toMillis` doesn't actually work with that - presumably because it is unnecessary, you already have the data in millis.

I knew it was not an ISO timestamp string, but did think it was a string that contained a date string that followed the ISO requirements.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/8/2/82ff964547ab13c374b7e3f8d98aa7ce8ecca5d5.png)

generated by the following less the last row (it causes an error as previously described)

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/0/b/0ba55e9887b1db7e91b348e63ba9ddbd1afffd5a.png)

now if I change the variable in the last line from nowJSDate to the other variables in created in the node, only sunrise and nowJSDate throw the error, the rest work.

but looking at the debug printout, there is no difference in string format for sunrise, sunriseString, nowJSDate, nowNRDate, or the string inputed to create sunriseJSONataData.

So they are different, but debug does not show the difference between a JSdate, and a string.

Maybe the debug should display JSDate differently from ISO dates, and Strings.

Is there any way to change a JSDate to an ISO date? maybe it should be an extension of the $toMillis() function that if the input is a JSdate (assuming it can recognise that), then it converts it to ISO date (millis).

otherwise we are restricted to using s function node to do the conversion (which I have done).

So many traps when using time/date values, maybe it should be a topic of its own

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [14 May 2026 11:17 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/5 "2026-05-14T11:17:24Z")

</div>

> [@IanH](#):
>
> Is there any way to change a JSDate to an ISO date

Yes, but why? You wanted milliseconds and that's what a JS Date is.

> [@IanH](#):
>
> debug printout, there is no difference in string format

This is a JavaScript limitation. `new Date()` returns a number of milliseconds but within the JavaScript context, `const newDate = new Date()` is a Date object. Weird but that's what we've got. To display a date object in a web interface, it has to be converted to something and JavaScript Date objects have a built-in `toString()` function that outputs the ISO timestamp you see in debug (unless you click on the value - debug will rotate through different views for values that look like timestamps). It is not necessarily possible to know if something is a large integer or a Date timestamp.

> [@IanH](#):
>
> So many traps when using time/date values, maybe it should be a topic of its own

Indeed. It has always been complex in JavaScript to properly deal with dates and times. However, that is about to be resolved with the new Temporal API. Previously, it has been dealt with using a 3rd-party library.

Node-RED has the `moment` library built into JSONata for you to use. I also previously created `node-red-contrib-moment`. You might want to use one of them if you need more ways to process and standardise dates and times.

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [14 May 2026 13:29 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/6 "2026-05-14T13:29:51Z")

</div>

> [@TotallyInformation](#):
>
> the new Temporal API

Woo, holy complexity, Batman

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [14 May 2026 13:38 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/7 "2026-05-14T13:38:26Z")

</div>

> [@Buckskin](#):
>
> Woo, holy complexity,

Well, that's JavaScript for you! The Temporal API should be available in node.js v26 I think. There is, I believe, also a polyfill for anyone wanting to use it now: **`@js-temporal/polyfill`**

We should also not forget that the `INTL` API also has various formatting options for dates and times.

Temporal should remove the need for Moment.

---

<div class="post-metadata">

**Author:** ![IanH](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/ianh/32/95002_2.png) [@IanH](https://discourse.nodered.org/u/IanH)\
**Post date:** [15 May 2026 02:32 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/8 "2026-05-15T02:32:55Z")

</div>

Thanks all and I have now cracked it.  
The JS date is an object containing a string and a bunch of methods you use in JS to manipulate, but JSONata cannot access the methods, just an object that contains a string which is why $toMillis() does not work (it wants a string not an object).

To convert a JS Date to millis you first need to convert the JSDate object to a string, remove the extra quotes and then convert it to millis.

So, the JSONata statement is

```auto
$toMillis(	
   $replace(
	$string(theJSdateObject),
         '"', 
         ""
    )	
)

```

So thanks all for your help, and JSONata $type() function and a quick copilot search on JS date Objects.

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [15 May 2026 09:37 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/9 "2026-05-15T09:37:44Z")

</div>

> [@IanH](#):
>
> To convert a JS Date to millis you first need to convert the JSDate object to a string, remove the extra quotes and then convert it to millis.

No! As I've already stated, I think more than once, you do not need to do any of this dance at all.

In a function node:

```javascript
msg.dt = new Date(msg.payload)
msg.ts = Number(msg.dt)
msg.dt2 = msg.dt.toISOString()
return msg

```

You can pass dt2 to JSONata's `$toMillis` function. However, the result is IDENTICAL to msg.ts

In other words, and stated again, to output milliseconds from a JavaScript Date object, you simply treat it as a number. The `Number()` part is not actually necessary since the default value of a Date object IS THE MILLSECONDS. But Monaco objects if you don't force the type to be numeric.

Even easier, the change node and the inject node both have a "timestamp" option which also injects the millis number for _now_.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/b/d/bd4e8dc71330b3b0ebaa5fd05908b3563d5fdec2.png)

---

<div class="post-metadata">

**Author:** ![IanH](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/ianh/32/95002_2.png) [@IanH](https://discourse.nodered.org/u/IanH)\
**Post date:** [16 May 2026 00:59 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/10 "2026-05-16T00:59:05Z")

</div>

Yep, thanks for the Function node Solution, I knew that (but you have a more simple method than I used.  
The purpose of publishing the JSONata solution was for those who don't want to work in JS, or want to do a one off in a change node

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [16 May 2026 11:10 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/11 "2026-05-16T11:10:19Z")

</div>

> [@IanH](#):
>
> The purpose of publishing the JSONata solution was for those who don't want to work in JS, or want to do a one off in a change node

Sorry, we seem to be not quite joining up here. The point I've made is that there are **no cases** where anyone needs the complexity you've outlined. Any timestamp being transferred between nodes in Node-RED is either going to be already in Milliseconds or will be an ISO string. Unless someone is dealing with a different string format created elsewhere. The _only_ reason for using the $millis function in JSONata would be in those last cases where the data is _already_ in a string format.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [30 May 2026 11:10 UTC](https://discourse.nodered.org/t/jsonata-tomillis-in-change-node-will-not-convert-an-iso-complaint-date-string/101003/12 "2026-05-30T11:10:35Z")

</div>

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.
