date-fns | How do I format to UTC
JavascriptDateDatetimeDate FnsJavascript Problem Overview
Problem
It looks like when I use the format()
function, it automatically convert the original UTC time into my timezone (UTC+8). I have been digging through their docs for hours and couldn't seem to find a way to default it to UTC time.
import { parseISO, format } from "date-fns";
const time = "2019-10-25T08:10:00Z";
const parsedTime = parseISO(time);
console.log(parsedTime); // 2019-10-25T08:10:00.000Z
const formattedTime = format(parsedTime, "yyyy-MM-dd kk:mm:ss");
console.log(formattedTime); // 2019-10-25 16:10:00 <-- 8 HOURS OFF!!
I have tried to use the package data-fns-tz
and use something like
format(parsedTime, "yyyy-MM-dd kk:mm:ss", {timeZone: "UTC"});
still no luck.
Please help!
Expected Output
2019-10-25 08:10:00
Actual Output
2019-10-25 16:10:00
Javascript Solutions
Solution 1 - Javascript
You were almost there. This works for me:
import { parseISO } from "date-fns";
import { format, utcToZonedTime } from "date-fns-tz";
const time = "2019-10-25T08:10:00Z";
const parsedTime = parseISO(time);
console.log(parsedTime); // 2019-10-25T08:10:00.000Z
const formatInTimeZone = (date, fmt, tz) =>
format(utcToZonedTime(date, tz),
fmt,
{ timeZone: tz });
const formattedTime = formatInTimeZone(parsedTime, "yyyy-MM-dd kk:mm:ss xxx", "UTC");
console.log(formattedTime); // 2019-10-25 08:10:00 +00:00
Behind the scenes
The date-fns[-tz] libraries stick to the built-in Date
data type that carries no TZ info.
Some functions treat it as a moment-in-time, but some like format
treat it more like a struct of calendaric components — year 2019, ..., day 25, hour 08, ....
Now the trouble is a Date
is internally only a moment in time. Its methods provide a mapping to/from calendaric components in local time zone.
So to represent a different time zone, date-fns-tz/utcToZonedTime
temporarily produces Date
instances which represent the wrong moment in time — just to get its calendaric components in local time to be what we want!
And the date-fns-tz/format
function's timeZone input affects only the template chars that print the time zone (XX..X
, xx..x
, zz..z
, OO..O
).
See https://github.com/marnusw/date-fns-tz/issues/36 for some discussion of this "shifting" technique (and of real use cases that motivated them)...
It's a bit low-level & risky, but the specific way I composed them above — formatInTimeZone()
— is I believe a safe recipe.
Solution 2 - Javascript
Note
The following solution will not work for all time zones, so if timezone accuracy is critical for your application you might want to try something like the answer from Beni. See this link for more info
I had the exact same question today and did some research to see if anyone has come up with anything better since this question was posed. I came across this solution which fit my needs and stylistic preference:
import { format, addMinutes } from 'date-fns';
function formatDate(date) {
return format(addMinutes(date, date.getTimezoneOffset()), 'yyyy-MM-dd HH:mm:ss');
}
Explanation
getTimezoneOffset
returns the number of minutes needed to convert that date to UTC. In PST (-0800 hours) it would return 480 whereas for somebody on CST (+0800 hours) it would return -480.
Solution 3 - Javascript
I would suggest using the built-in Date
util:
const date = new Date("2019-10-25T08:10:00Z");
const isoDate = date.toISOString();
console.log(`${isoDate.substr(0, 10)} ${isoDate.substr(11, 8)}`);
Outputs:
2019-10-25 08:10:00
Not a general solution for any format, but no external libraries required.
Solution 4 - Javascript
I had the same problem. What I do is remove the timezone from the ISO string and then use that time with date-fns:
let time = "2019-10-25T08:10:00Z".slice(0, -1)
The above is a time with no time zone, and because there is no timezone date-fns assumes the local timezone, so when you do:
format(parseISO(time), 'h:mm a')
you get: 8:10 AM, or whatever format you prefer. You just have to be careful with the string that you are slicing. If its always the same format then it should work.
Solution 5 - Javascript
I did something like this using date/fns and native date methods
import format from 'date-fns/format';
import parseISO from 'date-fns/parseISO';
export const adjustForUTCOffset = date => {
return new Date(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds(),
);
};
const formatDate = (dateString) = > {
const date = parseISO(dateString);
const dateWithOffset = adjustForUTCOffset(date)
return format(dateWithOffset, 'LLL dd, yyyy HH:mm')
}
Solution 6 - Javascript
Here is how I did it
const now = new Date()
const date = format(
new Date(now.toISOString().slice(0, -1)),
'yyyy-MM-dd HH:mm:ss'
)
I just removed the Z from the ISO string. I'm not sure if it solves this issue though
Solution 7 - Javascript
I guess
To construct the date as UTC before parsing would be helpful.
import { parseISO, format } from "date-fns";
const time = "2019-10-25T08:10:00Z";
const parsedTime = parseISO(new Date(Date.UTC(time)));
const formattedTime = format(parsedTime, "yyyy-MM-dd kk:mm:ss");
like this.
Solution 8 - Javascript
try
const formatDate = new Date().toISOString().substr(0, 19).replace('T', ' ');