Timestamp to Date, add time and show future date

Hi !

I am working on a node, that should give me a future date, based on todays date and a numerical value.

I want to forecast the future date of a maintenance based on total work hours and dayly work hours.

Example:
A machine needs maintenance at 800 work hours.
Work hours counter is at 350 now, meaning maintenance is due in 450 work hours.
That would be in (450/24) = 18,75 days if it would run 24 h/day.

But machine is running only 12 hours a day.
Therefore it will reach 800 at ((24/12)*(450/24)) = 37,5 days.

So I want to add 37,5 day to current date and display the future date.
How do I do this?

tried to calculate 37,5 days into millisecound and add it to timestamp, but this newly generated number I could not show as date.

I take current timestamp, and calculated milisecounds, and would like to get the future date,
tried several aproaches, non of them worked. What am I missing here ?

[
    {
        "id": "b23e867bc679ecbc",
        "type": "function",
        "z": "515b0c7217284cb8",
        "name": "add to timestamp",
        "func": "// 37,5 days are 900 hours, 54.000 minutes, 3.240.000 secounds, 3.240.000.000 millisecounds \nmsg.timestamp = msg.timestamp + 3240000000;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 1280,
        "wires": [
            [
                "20abace1d174c667"
            ]
        ]
    },
    {
        "id": "a963518aa448d6c1",
        "type": "inject",
        "z": "515b0c7217284cb8",
        "name": "Today",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 430,
        "y": 1280,
        "wires": [
            [
                "b23e867bc679ecbc"
            ]
        ]
    },
    {
        "id": "59663b9cbe337e20",
        "type": "debug",
        "z": "515b0c7217284cb8",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1110,
        "y": 1320,
        "wires": []
    },
    {
        "id": "20abace1d174c667",
        "type": "function",
        "z": "515b0c7217284cb8",
        "name": "Convert timestamp to date",
        "func": "if ( !msg.timestamp ) msg.timestamp = Math.round(+new Date());\n\nvar dt = new Date(msg.timestamp);\nvar msg = {\n\t'month':\tdt.getMonth() + 1,\n\t'day':\t\tdt.getDate(),\n\t'year':\t\tdt.getFullYear(),\n\t'hours':\tdt.getHours(),\n\t'mins':\t\tdt.getMinutes(),\n\t'msecs':\tdt.getMilliseconds()\n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 880,
        "y": 1320,
        "wires": [
            [
                "59663b9cbe337e20"
            ]
        ]
    },
    {
        "id": "c8b755a91d6ac6e5",
        "type": "inject",
        "z": "515b0c7217284cb8",
        "name": "Today",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 430,
        "y": 1320,
        "wires": [
            [
                "20abace1d174c667"
            ]
        ]
    },
    {
        "id": "2cfd1aa75f8d1ea6",
        "type": "function",
        "z": "515b0c7217284cb8",
        "name": "add to timestamp",
        "func": "// 37,5 days are 900 hours, 54.000 minutes, 3.240.000 secounds, 3.240.000.000 millisecounds \nvar newtimestamp = msg.timestamp + 3240000000;\nmsg.timestamp = newtimestamp;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 1240,
        "wires": [
            [
                "20abace1d174c667"
            ]
        ]
    },
    {
        "id": "d1f5de434b4e91c2",
        "type": "inject",
        "z": "515b0c7217284cb8",
        "name": "Today",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 430,
        "y": 1240,
        "wires": [
            [
                "2cfd1aa75f8d1ea6"
            ]
        ]
    },
    {
        "id": "9b587e722ece8efa",
        "type": "function",
        "z": "515b0c7217284cb8",
        "name": "set timestamp",
        "func": "// use a fix number\nmsg.timestamp = 1000000000;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 620,
        "y": 1360,
        "wires": [
            [
                "20abace1d174c667"
            ]
        ]
    },
    {
        "id": "472c163894bc80e9",
        "type": "inject",
        "z": "515b0c7217284cb8",
        "name": "Today",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 430,
        "y": 1360,
        "wires": [
            [
                "9b587e722ece8efa"
            ]
        ]
    }
]

moment can add days,
hours, minutes, seconds, years etc.
e.g. using change node.
Also possible in function node to, add moment to set up modules.

[{"id":"e0d3ea97982aa427","type":"inject","z":"1325dbcf1a62446d","name":"","props":[{"p":"payload"},{"p":"timestamp","v":"","vt":"date"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"37.5","payloadType":"num","x":110,"y":640,"wires":[["80097ba82304e5a4"]]},{"id":"80097ba82304e5a4","type":"change","z":"1325dbcf1a62446d","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$moment($$.timestamp).add($number($$.payload)*24, \"hours\").format(\"YYYY-MM-DD HH:mm:ss\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":640,"wires":[["8d29821ed9f858f9"]]},{"id":"8d29821ed9f858f9","type":"debug","z":"1325dbcf1a62446d","name":"debug 110","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":640,"wires":[]}]

[edit] change to adding hours to handle the .5 days

I think it would be a lot more efficient in a function node containing

msg.payload = new Date(new Date().getTime() + msg.payload * 24 * 3600 * 1000)
return msg;
[{"id":"e0d3ea97982aa427","type":"inject","z":"b08cb6bc45dd4866","name":"","props":[{"p":"payload"},{"p":"timestamp","v":"","vt":"date"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"37.5","payloadType":"num","x":150,"y":480,"wires":[["80097ba82304e5a4","a8473c1737067190"]]},{"id":"a8473c1737067190","type":"function","z":"b08cb6bc45dd4866","name":"Now + payload days","func":"msg.payload = new Date(new Date().getTime() + msg.payload * 24 * 3600 * 1000)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":480,"wires":[["6811400e8d8635e3"]]},{"id":"6811400e8d8635e3","type":"debug","z":"b08cb6bc45dd4866","name":"debug 111","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":480,"wires":[]}]
1 Like

Thank you for pinting out this approach ! Again, I learned something ....

Thank you Colin for this suggestion, it is a good idea !

Please note that in your original post, you made some accidental and unwarranted assumptions such as a day being 24 hours exactly - this is not always true.

This is why you should always do 2 things when working with date/time values:

  1. ALWAYS work in UTC, store in UTC, calculate in UTC. Convert user input to UTC at the point of input and convert UTC to local at the point of displaying to the user.
  2. ALWAYS use a library. There are far too many edge-cases and ongoing changes to date/time calculations to want to do this yourself. Your own calculations may work for several years and then break when you hit an edge-case. I know this from long experience, my first examples of this happened back in the '80's.

Thankfully, node-red is based on node.js and node.js has a built in international date/time library in the form of INTL. See Internationalization support | Node.js v14.21.1 Documentation (nodejs.org) and Intl - JavaScript | MDN (mozilla.org).

2 Likes

In the case explained in the original post, I don’t think that a high accuracy regarding date is required.
Depending on type of machine running, it could be more interesting to increase a counter every hour the machine is really running and trigger a message when the counter reach the limit.

Pierre RUWET

Even big players make mistakes in this area. Up until recently if, on an Android phone, at 21:00 one asked for an alarm in 12 hours then it set the alarm for 09:00 the next day, even there was a DST change over night. In that case it should have set it for 08:00 or 10:00 depending on the change.

Which is why sensible people always use a library. I still remember how painful it was to create a date/time library for an enterprise OLAP tool we were using at the time, it actually took several years before I stopped getting edge-case errors! (though I WAS young then! Hard to imagine, I know :older_adult:)

Not necessarily.

Using a library may not help. It depends what question you ask the library. In the particular case of triggering in 12 hours time, had they done the equivalent of the javascript code
const answer = new Date( new Date().getTime() + 12 * 3600 * 1000 )
they would have got the correct answer.

I'm rather tired this evening so not really in a place to work that through. But what happens if your +12hrs spans a DST boundary? Or indeed a leap second or similar? OK so a leap second probably not a problem in this case but hopefully you can see the issue it illustrates. Time is not clean and not fixed, there are lots of complexities in real-world elapsed time calculations. Perhaps overkill in this case but the point is not to make assumptions about time calculations being simple.

That is exactly the point. If I ask for an alarm in 12 hours then I want it in 12 hours. If I do that at 21:00 on the evening of a DST change then the alarm should time out at 08:00 or 10:00 the next morning, not at 09:00. If I happened to be on the phone to someone just before the clocks were set back, I might say "I will ring you back in half an hour", I would not say "I will ring you back half an hour ago".

That is exactly the point. If I ask for an alarm in 12 hours then I want it in 12 hours. If I do that at 21:00 on the evening of a DST change then the alarm should time out at 08:00 or 10:00 the next morning, not at 09:00. If I happened to be on the phone to someone just before the clocks were set back, I might say "I will ring you back in half an hour", I would not say "I will ring you back half an hour ago".

If I asked for an alarm in 12hr and the time was 9pm, i would expect to be woken at 9am whether there was a DST shift or not. would be annoyed to be woken an hour early or late. I doubt i would ask for 12 hr, i would normally specify a time.

Or maybe you don't? Perhaps you do want it in exactly 12hrs? Regardless of DST. An assumption either way right? Anyway, I'm too tired tonight to really think this through.

Suppose the clocks were about to go forward and I asked for an alarm in half an hour, would I mean that the alarm should have gone off 25 minutes ago?

My use case is not for waking up. If I wanted to be woken up at 9 would ask for an alarm at 9, not try and work out how many hours it is till then and tell it to alarm in 11 hours 42 minutes or whatever. My use case is starting my breadmaker when I go to bed at night. It has a timer that sets the completion time up to 13 hours ahead, and I want it to finish as late as possible in the morning so that the bread is as fresh as possible for lunch. Therefore I want an alarm in 13 hours in case I am not in the room when it finishes and I do not hear the breadmaker beep.

An hour is a fixed length, whether leap seconds or DST changes come along. A day however is arguably not fixed length, and if I asked for an alarm in 1 day then it would be reasonable for it to assume I meant it tu use the clock time to work that out.

You mean 30 minutes ago.
Do you expect any yes answers to that question?

Yes I do. It took me 5 minutes to work out which direction the clocks had to go.

No, so if the answer to when an alarm should go off in 1 hours time is 1 hour elapsed time, not clock time, then why should 12 hours be treated differently?

Your thinking time has no affect on an alarm once set.

Depending on what type of timer you wanted, an alarm or a countdown.

So really it depends on what type of alarm is required to work out how it should be set.

It was supposed to be a joke.

All I am doing is asking google to set an alarm in 12 hours.