# Formatting negative time on a countdown timer

**URL:** <https://discourse.nodered.org/t/formatting-negative-time-on-a-countdown-timer/32718>\
**Category:** General\
**Created:** [10 September 2020 23:43 UTC](https://discourse.nodered.org/t/formatting-negative-time-on-a-countdown-timer/32718 "2020-09-10T23:43:05Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![DavidE1A](https://avatars.discourse-cdn.com/v4/letter/d/b19c9b/32.png) [@DavidE1A](https://discourse.nodered.org/u/DavidE1A)\
**Post date:** [10 September 2020 23:43 UTC](https://discourse.nodered.org/t/formatting-negative-time-on-a-countdown-timer/32718/1 "2020-09-10T23:43:05Z")

</div>

Hi again,  
I have the following code running a countdown timer. It works just fine, until the clock hits zero when it starts counting back up as a negative number. This in itself is fine (and actually quite useful for our needs), but I have two minor issues. One is that it displays as "-1:-1, -1:12 etc", so I need to somehow add leading zeros (I guess) to it when it's in negative. The other is a slightly bigger problem which is that it seems to jump straight from "0" to "-1:-1", so it basically skips a minute (the first minus minute should be "-00:01").

This function node is setting the remaining time (there are other buttons involved to start, stop, add and remove time on the fly):

```auto
var currentTime = msg.payload;
var lastTickTime = flow.get("lastTickTime") || currentTime;
var timeRemaining = flow.get("timeRemaining") || 0;
var gameState = flow.get("gameState");

if(gameState == "playing") {

    timeRemaining -= (currentTime - lastTickTime);
    
    flow.set("timeRemaining", timeRemaining);
    
}

msg.timeRemaining = timeRemaining;
flow.set("lastTickTime", currentTime);

node.status({fill:"green", shape:"dot", text:timeRemaining});

return msg;

```

And then this function formats the time to a simple mm:ss display:

```auto
var t = msg.timeRemaining / 1000;
var m = Math.floor(t / 60);
var s = Math.floor(t%60 % 60);

msg.formattedTimeRemaining = ("0" + m).slice(-2) + ":" + ("0" + s).slice(-2);

node.status({fill:"green", shape:"dot", msg:flow.formattedTimeRemaining});

return msg;

```

The node status on the first function counts down as 2000, 1000, 0, -1000, -2000 etc, so I guess I just need to figure out what I can add to the second function to format the negative time properly.

Any ideas?

---

<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:** [11 September 2020 00:38 UTC](https://discourse.nodered.org/t/formatting-negative-time-on-a-countdown-timer/32718/2 "2020-09-11T00:38:44Z")

</div>

I can't see anywhere that you are actually generating a timestamp?

You should probably be using a millisecond based timestamp (e.g. JavaScript Date) and then you could use JavaScript date/time functions to work everything out.

---

<div class="post-metadata">

**Author:** ![hamsando](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hamsando/32/23676_2.png) [@hamsando](https://discourse.nodered.org/u/hamsando)\
**Post date:** [11 September 2020 03:16 UTC](https://discourse.nodered.org/t/formatting-negative-time-on-a-countdown-timer/32718/3 "2020-09-11T03:16:07Z")

</div>

I appreciate you are trying to do it in a function node - and I don't necessarily have the bigger picture, but rather than re-inventing the wheel, are you open to using a separate node as the countdown timer. Something like stoptimer-varidelay ([https://flows.nodered.org/node/node-red-contrib-stoptimer-varidelay](https://flows.nodered.org/node/node-red-contrib-stoptimer-varidelay))?

---

<div class="post-metadata">

**Author:** ![DavidE1A](https://avatars.discourse-cdn.com/v4/letter/d/b19c9b/32.png) [@DavidE1A](https://discourse.nodered.org/u/DavidE1A)\
**Post date:** [11 September 2020 03:30 UTC](https://discourse.nodered.org/t/formatting-negative-time-on-a-countdown-timer/32718/4 "2020-09-11T03:30:32Z")

</div>

The timestamp comes into the first function as the msg.payload.

I'm not trying to reinvent anything here, just using what others have supplied (I'd have never of got this far on my own! 😅), and trying to tweak it a little. The timer works just fine as it is, and I've managed to add some buttons for adding and removing time successfully. It just seems to be something in the formatting that isn't quite right, or non existing, for when it goes into minus time. It's for an escape room timer/gamemaster software, so there's other buttons and arduinos going to be linked into it all, but I here (I think) are all the parts that relate to the timer section:

```auto
[
    {
        "id": "906339c5.ce4fa",
        "type": "inject",
        "z": "29662af6.96a146",
        "name": "Setup",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 190,
        "y": 100,
        "wires": [
            [
                "becf850a.cbcee"
            ]
        ]
    },
    {
        "id": "becf850a.cbcee",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "gameState",
                "pt": "flow",
                "to": "stopped",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "timeRemaining",
                "pt": "flow",
                "to": "3600000",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "currentTime",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "logging",
                "pt": "flow",
                "to": "true",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 360,
        "y": 100,
        "wires": [
            [
                "cc5aea0b.1742e"
            ]
        ]
    },
    {
        "id": "ca343107.aec8b",
        "type": "inject",
        "z": "29662af6.96a146",
        "name": "Start",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 190,
        "y": 240,
        "wires": [
            [
                "49b44f5a.132568"
            ]
        ]
    },
    {
        "id": "9266125d.112b38",
        "type": "inject",
        "z": "29662af6.96a146",
        "name": "Stop",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 610,
        "y": 240,
        "wires": [
            [
                "8157f92c.9324e"
            ]
        ]
    },
    {
        "id": "49b44f5a.132568",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "gameState",
                "pt": "flow",
                "to": "playing",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 380,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "8157f92c.9324e",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "gameState",
                "pt": "flow",
                "to": "stopped",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 810,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "91ccf9a6.2af778",
        "type": "inject",
        "z": "29662af6.96a146",
        "name": "Reset",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 1030,
        "y": 240,
        "wires": [
            [
                "b8b19971.5380a8"
            ]
        ]
    },
    {
        "id": "b8b19971.5380a8",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "timeRemaining",
                "pt": "flow",
                "to": "3600000",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1240,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "bf54624f.1f05b8",
        "type": "ui_button",
        "z": "29662af6.96a146",
        "name": "",
        "group": "e28e9683.8bc2e8",
        "order": 4,
        "width": 2,
        "height": 2,
        "passthru": true,
        "label": "Start",
        "tooltip": "",
        "color": "",
        "bgcolor": "green",
        "icon": "",
        "payload": "start",
        "payloadType": "str",
        "topic": "TIMER",
        "x": 170,
        "y": 180,
        "wires": [
            [
                "49b44f5a.132568",
                "e070cc33.ebeea"
            ]
        ]
    },
    {
        "id": "cf24c6ee.702378",
        "type": "ui_button",
        "z": "29662af6.96a146",
        "name": "",
        "group": "e28e9683.8bc2e8",
        "order": 5,
        "width": 2,
        "height": 2,
        "passthru": true,
        "label": "Stop",
        "tooltip": "",
        "color": "",
        "bgcolor": "red",
        "icon": "",
        "payload": "stop",
        "payloadType": "str",
        "topic": "TIMER",
        "x": 610,
        "y": 180,
        "wires": [
            [
                "8157f92c.9324e",
                "b7ffebb9.b20958"
            ]
        ]
    },
    {
        "id": "47ca99f6.2d9bf",
        "type": "ui_button",
        "z": "29662af6.96a146",
        "name": "",
        "group": "e28e9683.8bc2e8",
        "order": 6,
        "width": "2",
        "height": 2,
        "passthru": false,
        "label": "<b>!!! Reset !!!</b>",
        "tooltip": "",
        "color": "",
        "bgcolor": "red",
        "icon": "",
        "payload": "reset",
        "payloadType": "str",
        "topic": "TIMER",
        "x": 980,
        "y": 180,
        "wires": [
            [
                "b8b19971.5380a8",
                "f03e9c77.072b48"
            ]
        ]
    },
    {
        "id": "f03e9c77.072b48",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "reset",
        "links": [
            "7105f2a1.21812c",
            "ad7b2357.d2e098",
            "c1443e15.6728c8",
            "ab7c57db.9ca698",
            "a6cd94c7.4d6bd",
            "b91d3509.17212",
            "66ce8743.b007a8",
            "f6e1656c.d7af6",
            "a7abf569.ae721",
            "2d230649.2da632"
        ],
        "x": 1155,
        "y": 180,
        "wires": []
    },
    {
        "id": "ad7b2357.d2e098",
        "type": "link in",
        "z": "29662af6.96a146",
        "name": "reset-stop",
        "links": [
            "f03e9c77.072b48",
            "6a781a22.d020c4"
        ],
        "x": 515,
        "y": 180,
        "wires": [
            [
                "cf24c6ee.702378"
            ]
        ]
    },
    {
        "id": "9b7fbc0.3a3a0c8",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "timeRemaining",
                "pt": "flow",
                "to": "timeRemaining",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 620,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "a3531003.a731d",
        "type": "ui_button",
        "z": "29662af6.96a146",
        "name": "",
        "group": "e28e9683.8bc2e8",
        "order": 7,
        "width": 2,
        "height": "2",
        "passthru": false,
        "label": "+1 min",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "+1 min",
        "payloadType": "str",
        "topic": "TIMER",
        "x": 170,
        "y": 300,
        "wires": [
            [
                "77f6da55.553bcc",
                "ec0c7f2f.eb6ab8"
            ]
        ]
    },
    {
        "id": "77f6da55.553bcc",
        "type": "function",
        "z": "29662af6.96a146",
        "name": "",
        "func": "var timeRemaining = flow.get(\"timeRemaining\") || 0;\nvar addOne = 60000\n\ntimeRemaining += addOne;\n \nflow.set(\"timeRemaining\", timeRemaining);\n\nmsg.timeRemaining = timeRemaining;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 420,
        "y": 300,
        "wires": [
            [
                "9b7fbc0.3a3a0c8"
            ]
        ]
    },
    {
        "id": "d74ba333.870fc8",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "timeRemaining",
                "pt": "flow",
                "to": "timeRemaining",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1240,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "6873a943.66294",
        "type": "ui_button",
        "z": "29662af6.96a146",
        "name": "",
        "group": "e28e9683.8bc2e8",
        "order": 8,
        "width": 2,
        "height": "2",
        "passthru": false,
        "label": "+5 min",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "+5 min",
        "payloadType": "str",
        "topic": "TIMER",
        "x": 810,
        "y": 300,
        "wires": [
            [
                "e69b3482.115b38",
                "16cc4509.c631bb"
            ]
        ]
    },
    {
        "id": "e69b3482.115b38",
        "type": "function",
        "z": "29662af6.96a146",
        "name": "",
        "func": "var timeRemaining = flow.get(\"timeRemaining\") || 0;\nvar addOne = 60000*5\n\ntimeRemaining += addOne;\n \nflow.set(\"timeRemaining\", timeRemaining);\n\nmsg.timeRemaining = timeRemaining;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 1040,
        "y": 300,
        "wires": [
            [
                "d74ba333.870fc8"
            ]
        ]
    },
    {
        "id": "e070cc33.ebeea",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "start",
        "links": [
            "c1443e15.6728c8"
        ],
        "x": 295,
        "y": 180,
        "wires": []
    },
    {
        "id": "b7ffebb9.b20958",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "stop",
        "links": [
            "c1443e15.6728c8"
        ],
        "x": 735,
        "y": 180,
        "wires": []
    },
    {
        "id": "ec0c7f2f.eb6ab8",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "+1min",
        "links": [
            "c1443e15.6728c8"
        ],
        "x": 275,
        "y": 300,
        "wires": []
    },
    {
        "id": "16cc4509.c631bb",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "+5min",
        "links": [
            "c1443e15.6728c8"
        ],
        "x": 915,
        "y": 300,
        "wires": []
    },
    {
        "id": "e17ffd4e.433bf",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "timeRemaining",
                "pt": "flow",
                "to": "timeRemaining",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 620,
        "y": 360,
        "wires": [
            []
        ]
    },
    {
        "id": "486e099f.3951b",
        "type": "ui_button",
        "z": "29662af6.96a146",
        "name": "",
        "group": "e28e9683.8bc2e8",
        "order": 10,
        "width": 2,
        "height": "2",
        "passthru": false,
        "label": "-1 min",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "-1 min",
        "payloadType": "str",
        "topic": "TIMER",
        "x": 170,
        "y": 360,
        "wires": [
            [
                "af55c2c6.06b228",
                "ffd60e8e.f7d84"
            ]
        ]
    },
    {
        "id": "af55c2c6.06b228",
        "type": "function",
        "z": "29662af6.96a146",
        "name": "",
        "func": "var timeRemaining = flow.get(\"timeRemaining\") || 0;\nvar addOne = 60000\n\ntimeRemaining -= addOne;\n \nflow.set(\"timeRemaining\", timeRemaining);\n\nmsg.timeRemaining = timeRemaining;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 420,
        "y": 360,
        "wires": [
            [
                "e17ffd4e.433bf"
            ]
        ]
    },
    {
        "id": "374eb5e7.9cc7a2",
        "type": "change",
        "z": "29662af6.96a146",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "timeRemaining",
                "pt": "flow",
                "to": "timeRemaining",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1240,
        "y": 360,
        "wires": [
            []
        ]
    },
    {
        "id": "24347276.8a2706",
        "type": "ui_button",
        "z": "29662af6.96a146",
        "name": "",
        "group": "e28e9683.8bc2e8",
        "order": 11,
        "width": 2,
        "height": "2",
        "passthru": false,
        "label": "-5 min",
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "icon": "",
        "payload": "-5 min",
        "payloadType": "str",
        "topic": "TIMER",
        "x": 810,
        "y": 360,
        "wires": [
            [
                "dee13c37.ab0b",
                "60a4437.54128bc"
            ]
        ]
    },
    {
        "id": "dee13c37.ab0b",
        "type": "function",
        "z": "29662af6.96a146",
        "name": "",
        "func": "var timeRemaining = flow.get(\"timeRemaining\") || 0;\nvar addOne = 60000*5\n\ntimeRemaining -= addOne;\n \nflow.set(\"timeRemaining\", timeRemaining);\n\nmsg.timeRemaining = timeRemaining;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 1040,
        "y": 360,
        "wires": [
            [
                "374eb5e7.9cc7a2"
            ]
        ]
    },
    {
        "id": "ffd60e8e.f7d84",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "-1min",
        "links": [
            "c1443e15.6728c8"
        ],
        "x": 275,
        "y": 360,
        "wires": []
    },
    {
        "id": "60a4437.54128bc",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "-5min",
        "links": [
            "c1443e15.6728c8"
        ],
        "x": 915,
        "y": 360,
        "wires": []
    },
    {
        "id": "cc5aea0b.1742e",
        "type": "link out",
        "z": "29662af6.96a146",
        "name": "",
        "links": [],
        "x": 495,
        "y": 100,
        "wires": []
    },
    {
        "id": "a1304d26.6c6b98",
        "type": "inject",
        "z": "29662af6.96a146",
        "name": "Loop",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "1",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 170,
        "y": 480,
        "wires": [
            [
                "f8f9c3af.4996c8"
            ]
        ]
    },
    {
        "id": "f8f9c3af.4996c8",
        "type": "function",
        "z": "29662af6.96a146",
        "name": "",
        "func": "var currentTime = msg.payload;\nvar lastTickTime = flow.get(\"lastTickTime\") || currentTime;\nvar timeRemaining = flow.get(\"timeRemaining\") || 0;\nvar gameState = flow.get(\"gameState\");\n\nif(gameState == \"playing\") {\n\n timeRemaining -= (currentTime - lastTickTime);\n \n flow.set(\"timeRemaining\", timeRemaining);\n \n}\n\nmsg.timeRemaining = timeRemaining;\nflow.set(\"lastTickTime\", currentTime);\n\nnode.status({fill:\"green\", shape:\"dot\", text:timeRemaining});\n\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 340,
        "y": 480,
        "wires": [
            [
                "c7146f2b.9b30e"
            ]
        ]
    },
    {
        "id": "c7146f2b.9b30e",
        "type": "function",
        "z": "29662af6.96a146",
        "name": "Format time",
        "func": "var t = msg.timeRemaining / 1000;\nvar m = Math.floor(t / 60);\nvar s = Math.floor(t%60 % 60);\n\nmsg.formattedTimeRemaining = (\"0\" + m).slice(-2) + \":\" + (\"0\" + s).slice(-2);\n\nnode.status({fill:\"green\", shape:\"dot\", msg:flow.formattedTimeRemaining});\n\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 530,
        "y": 480,
        "wires": [
            [
                "5970c5ac.84e9fc"
            ]
        ]
    },
    {
        "id": "5970c5ac.84e9fc",
        "type": "join",
        "z": "29662af6.96a146",
        "name": "",
        "mode": "custom",
        "build": "merged",
        "property": "",
        "propertyType": "full",
        "key": "topic",
        "joiner": "\\n",
        "joinerType": "str",
        "accumulate": true,
        "timeout": "",
        "count": "1",
        "reduceRight": false,
        "reduceExp": "",
        "reduceInit": "",
        "reduceInitType": "num",
        "reduceFixup": "",
        "x": 1090,
        "y": 480,
        "wires": [
            [
                "3e91b62d.cfd2e2"
            ]
        ]
    },
    {
        "id": "3e91b62d.cfd2e2",
        "type": "uibuilder",
        "z": "29662af6.96a146",
        "name": "Game Timer and Clue Display",
        "topic": "",
        "url": "atlantis",
        "fwdInMessages": false,
        "allowScripts": false,
        "allowStyles": false,
        "copyIndex": true,
        "showfolder": false,
        "x": 1300,
        "y": 480,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "e28e9683.8bc2e8",
        "type": "ui_group",
        "z": "",
        "name": "Timer",
        "tab": "759f7fcb.6ad23",
        "order": 1,
        "disp": false,
        "width": "8",
        "collapse": false
    },
    {
        "id": "759f7fcb.6ad23",
        "type": "ui_tab",
        "z": "",
        "name": "Atlantis",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]

```

---

<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:** [10 November 2020 03:30 UTC](https://discourse.nodered.org/t/formatting-negative-time-on-a-countdown-timer/32718/5 "2020-11-10T03:30:41Z")

</div>

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