1

I'm using a Node/Express server to format a date before it's sent to the browser. The date I have is saved in the database as UTC, for example this:

2020-09-15 11:52:22.000

I now want to format this date, without changing it, and send it to browser, so not matter where the Node server is, and not matter where the browser is, it always shows this UTC date. People viewing this date will be in the US, UK, EU, but I'd like them all to see the same UTC date.

I'm in the UK, I'm using date-fns, and I'm running Node locally at the moment. My app already uses date-fns and I don't want to add another date/time dependency just for this one task.

If I do this:

const d = format(new Date('2020-09-15 11:52:22.000'), 'dd MMM yyyy HH:mm:ss')

I get: 15 Sep 2020 12:52:22... That is the date in my local time (UK, BST).

If there is one thing that confuses me, it's dates & times! :-)

In Node, how do I take 2020-09-15 11:52:22.000 and format it to get 15 Sep 2020 11:52:22 ..?

Stephen Last
  • 5,491
  • 9
  • 44
  • 85
  • See [*Why does Date.parse give incorrect results?*](https://stackoverflow.com/questions/2587345/why-does-date-parse-give-incorrect-results) – RobG Sep 15 '20 at 13:39
  • The answers here might help: [*Express time as CST in javascript - date-fns*](https://stackoverflow.com/questions/63385910/express-time-as-cst-in-javascript-date-fns). – RobG Sep 15 '20 at 21:57
  • Normally, the dates have a `Z` on the end, if it's UTC. I'm guessing that the date field on this database is of a type where the timezone isn't actually saved. – Brad Sep 16 '20 at 01:17

3 Answers3

2

Your problem is:

new Date('2020-09-15 11:52:22.000')

Using the built–in parser for unsupported formats is strongly discouraged. It's implementation dependent and often returns different results in different implementations.

Also, the string doesn't have a timezone so should be treated as local. So even if parsed correctly it will represent a different instant in time for each place with a different offset.

Date-fns has a capable parser so you should use it instead of the built–in paser. If you want the timestamp to be treated as UTC, the simplest way is to add a trailing "Z" and timezone token for parsing.

To support timezones for output, you have to include date-fns-tz. Following is some code you can run at npm.runkit. The format part appears to produce the correct result, however if a timezone token is added to the format string (i.e. 'X' or 'XX'), it shows the local timezone offset, not the one applied to the string so to show the offset correctly, you have to add it manually (I think this is a bug in date-fns-tz).

const dateFns = require("date-fns");
const { zonedTimeToUtc, utcToZonedTime, format } = require('date-fns-tz');

let timestamp = '2020-09-15 11:52:22.000';
let tz = 'Z';
let date = dateFns.parse(timestamp + tz, 'yyyy-MM-dd HH:mm:ss.SSSX', new Date()); 
// Show timestamp was parsed as UTC
console.log(date.toISOString()); // 2020-09-15T11:52:22.000Z

// Produce UTC output - need to set timezone first
let utcDate = utcToZonedTime(date, 'UTC');
// Manually add timezone to formatted string
console.log(format(utcDate, 'd MMM yyyy HH:mm \'UTC\'', 'UTC')); // 15 Sep 2020 11:52 UTC

Note that utcToZonedTime(date, 'UTC') actually creates a new date modified by the specified offset and assigns it to utcDate, which I think is a real kludge. It's only useful now if you know how it's been modified, you don't know what time it's actually supposed to represent. In this case it might be clear because we're using UTC, but other offsets are much more problematic.

It would save a lot of effort if initial timestamp was in a format that is supported by ECMAScript, e.g. the same as that produced by toISOString: 2020-09-15T11:52:22.000Z.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • 1
    Thanks @RobG... I'm using `date-fns` 2.14.0, and your example is using 1.28.5. Looks like things have changed. `parse()` now wants a third arg. However, what I'm trying to do is *format* the date I have. Looks like `format()` converts to local time even if you pass in a date object which is UTC. Grrrrr – Stephen Last Sep 15 '20 at 18:47
  • I have a date object here which when printed to the console shows: `2020-09-15T13:44:46.000Z`, this is UTC. `isDate()` returns `true` on this. When I pass this into `format()` is get `15 Sep 2020 14:44:46`...! That's not UTC anymore, its BST (+1 hour)! – Stephen Last Sep 15 '20 at 18:50
  • 1
    @StephenLast—updated with code for current version of date-fns and better explanation. Sorry for the previous junk, I shouldn't post when rushed for time… – RobG Sep 16 '20 at 01:17
0

You can try to do that (my timezone is +2 GTM)

  1. Parse your date to obtain an UTC string
const parsedDate = parse('2020-09-15 11:52:22.000', 'yyyy-MM-dd HH:mm:ss.SSS', new Date()).toUTCString(); 
// Tue, 15 Sep 2020 09:52:22 GMT
// or simply 
// new Date('2020-09-15 11:52:22.000').toUTCString();
    
// format a new date starting from UTC format 
// new Date(parsedDate) --> Tue Sep 15 2020 11:52:22 GMT+0200
const date = format(new Date(parsedDate), 'dd MMM yyyy HH:mm:ss');

the new Date(parsedDate) in format will reconvert utc to your timezone

dna
  • 2,097
  • 1
  • 11
  • 35
-1

You should use https://momentjs.com/

For example:

moment().format('Do MMMM YYYY, h:mm:ss');
Paul T. Rawkeen
  • 3,994
  • 3
  • 35
  • 51
Suhag Lapani
  • 655
  • 10
  • 18
  • Thanks! But I'd like to achieve this using native JavaScript or with `date-fns`, not `moment`. My project already has `date-fns` as a dependency, and it's used everywhere else. I don't want to add a new dependency, particularly one as big as `moment`, just for this very small task. – Stephen Last Sep 15 '20 at 13:41
  • Looks like moment is coming to an end: https://momentjs.com/docs/#/-project-status/ – Stephen Last Sep 16 '20 at 07:43