Date Formatting in JavaScript (and OptoScript)

There are many options for date formatting in JavaScript, among which are
toString() which results in “Tue Jul 18 2017 13:28:48 GMT-0700 (PDT)”,
toUTCString() that gives “Tue, 18 Jul 2017 20:28:48 GMT”,
and finally toISOString() for “2017-07-18T20:28:48.297Z”, as well as methods that return each piece with getFullYear()_, getHours(), getMilliseconds(), etc. (see full list).

None of these, however, provide 12 hour time or AM / PM status, which is usually easier to actually read. In order to get very specifically formatted date information I use a function node in Node-RED to work date strings with JavaScript before displaying them in groov.
Here is one example to get the date format “01:28PM Tue 17-07-18

var d = new Date();
var tstring = ('0'+((d.getHours()+11)%12 + 1)).slice(-2)+':';
tstring += ('0'+d.getMinutes()).slice(-2) + ((d.getHours() >= 12)? 'PM ':'AM ');
// tstring = 01:28PM
tstring += d.toString().substring(0,4) + d.toISOString().substring(2,10);
// tstring = 01:28PM Tue 17-07-18

Appending the character ‘0’ to the front and using slice(-2) ensures that the number has a leading zero but at most two digits, since getHours(), getMinutes() and other such methods return exact integers, thus when called at 4:01 in the morning they return 4 and 1 respectively but that is processed to output "04:01AM ".
If you wish to put a leading zero on many fields it may clean up your code to define a simple function:

function twoDig(val) {return (('0'+val).slice(-2));}

So to get the time formatted as “4:01AM”, with a leading zero on minutes but not hours, simply use:

((d.getHours()+11)%12 + 1)+':'+twoDig(d.getMinutes()) + ((d.getHours() >= 12)? 'PM ':'AM ');

The key line to get from 24-hour time to 12-hour time is (d.getHours()+11)%12 + 1, and ((d.getHours() >= 12)? 'PM ':'AM ') appends 'PM ’ if the hour is 12 - 23 and 'AM ’ if the hour is 0 (midnight) - 11.
Note that 'PM ’ is not fixed, and simply 'p ’ for “01:28p Tue 17-07-18” works great here.

The final part, appending the day and date, just grabs the first four characters from the toString() formatting, which contains only the three character day and a space, and the last eight characters of the first ten from toISOString() (does not include “20” for “2017”). To get the full “2017-07-18” date simply change the function to d.toISOString().substring(0,10);

It is also possible to build a custom day of the week and date without the to-string functions at all, just using getFullYear() getMonth(), getDate() and getDay():

var weekday = ["Sunday ","Monday ","Tuesday ","Wednesday ","Thursday ","Friday ","Saturday "];
var dstring = weekday[d.getDay()] + twoDig(d.getMonth())+'/'+twoDig(d.getDate())+'/'+d.getFullYear();
// dstring = Tuesday 06/18/2017

Simply build an array of days of the week, with free choice of spelling, capitalization, and language, then use getDay() which returns a number 0-6 to reference it. This would also work to get the text for which month it is from an array of 12 string.
Numerical month, date, and year after that are trivial, and day/month may benifit from leading zeros to keep string length consistent.

And there it is! Go ahead and arrange these basic methods and tricks to get all kinds of interesting, unique date formats from Node-RED.

Here’s one way to create a similar string "07/18/2017 Tue 1:28PM" with OptoScript:

GetSubString("SunMonTueWedThuFriSat",GetDayOfWeek()*3, 3, curr_day);
NumberToString((GetHours()+11)%12 + 1, curr_12hour);
NumberToString(GetMinutes(), curr_minutes);
if(GetMinutes() < 10) then
  curr_minutes = "0" + curr_minutes;
formatted_date = formatted_date+" "+curr_day+" "+curr_12hour+":"+curr_minutes;
if(GetHours() >= 12) then
  formatted_date += "PM";
  formatted_date += "AM";

You could also make and reference a string table for day of the week (or even month) like the JavaScript example, but the three letter abbreviation from substring makes things neater and saves on a bit of storage.

Just like JavaScript this is very flexible and ultimately comes down to string manipulation, so to get "2017-7-18 Tue 1:28p":

GetSubString("SunMonTueWedThuFriSat",GetDayOfWeek()*3, 3, curr_day);
NumberToString(GetYear(), curr_year);
NumberToString(GetMonth(), curr_month);
NumberToString(GetDay(), curr_date);
NumberToString((GetHours()+11)%12 + 1, curr_12hour);
NumberToString(GetMinutes(), curr_minutes);
if(GetMinutes() < 10) then
  curr_minutes = "0" + curr_minutes;
formatted_date = curr_year+"-"+curr_month+"-"+curr_date+" "+curr_day+" "+curr_12hour+":"+curr_minutes;
if(GetHours() >= 12) then
  formatted_date += Chr(112);
  formatted_date += Chr(97);

You are free to put day, date, and time in any order you like with any characters separating them - just make sure formatted_date is long enough to hold whatever you’re building.
Note that for example curr_12hour could be one character for 1-9 or two characters 10-12, unless you append a "0" like I did with minutes. GetDay() and GetMonth() will return single digits as well, but for minutes it is necessary to add a zero when less than ten otherwise 12:04 appears as 12:4.

Good luck & have fun!

I just stumbled on a really simple way to convert Epoch or Unix time to a human readable time stamp using Node-RED.

Use a change node.
Configure it like so;

Your Epoch time goes in on the payload; 1600154034059
And your UTC time stamp comes out on the payload; 2020-09-15T07:13:54.512Z

1 Like

Thanks @Beno, this has come up for multiple customers of mine! Neat shortcut!

On the Optoscript example, one word of caution is to not use the GetYear, GetMonth, GetDay, etc functions together since time functions are nondeterministic. For instance, the call to GetHours() could happen at 11:59, then the call to GetMinutes() could be at 12:00. Those functions are fine if you just need the month or the year, etc, but they shouldn’t be used in combination.

Instead, use the GetDateTime method that returns all the above values into an integer table and then use the values out of the table.

See Mary’s example and comments on Convert Integer 32 to Timestamp String for the proper way to do this.

1 Like