You will need to subtract the time zone offset of your local time zone from the Date instance, before you pass it to format from date-fns. For example:
const dt = new Date('2017-12-12');
const dtDateOnly = new Date(dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000);
console.log(format(dtDateOnly, 'YYYY-MM-DD')); // Always "2017-12-12"
Problem
You want to handle only the date part of the Date instance, because the time part does not make sense for birthdates. However, the Date object does not offer any "date-only" mode. You can access both its date and time parts in the local time zone or UTC. The problem is, that format from date-fns prints the output always in the local time zone.
When you executed the constructor only with the date part:
const dt = new Date('2017-12-12');
The JavaScript engine actually assumed a string in the incomplete ISO 8601 format and perfomed this:
const dt = new Date('2017-12-12T00:00:00.000Z');
It may still look "harmless" to you, but the date instance exposes the value not only in UTC, but also in the local time zone. If you construct the Date instance on the East Coast of the US, you will see the following output:
> const dt = new Date('2017-12-12');
> dt.toISOString()
'2017-12-12T00:00:00.000Z'
> dt.toString()
'Tue Dec 11 2017 19:00:00 GMT-0500 (EST)'
> d.toLocaleString()
'12/11/2017 7:00:00 PM'
Solution
If you know, that format from date-fns reads date and time parts from the date instance in the local time zone, you will need to make your date "looking like" the midnight in your local time zone and not in UTC, which you passed to the Date constructor. Then you will see the year, month and date numbers preserved. It means, that you need to subtract the time zone offset of your local time zone for the specified day. Date.prototype.getTimezoneOffset returns the offset, but with an inverted sign and in minutes.
const dt = new Date('2017-12-12');
// Tue Dec 11 2017 19:00:00 GMT-0500 (EST)
const dtDateOnly = new Date(dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000);
// Tue Dec 12 2017 00:00:00 GMT-0500 (EST)
console.log(format(dtDateOnly, 'YYYY-MM-DD'));
// Prints always "2017-12-12", regardless the time zone it executed in
However, such Date instance can be used only to format the date-only value. You cannot use it for computing date differences, for example, which would need the original and correct UTC value.
Alternative
If you need always the same date-only format and not the format specific to the current locale, you do not need date-fns. You can format the string by the concatenation of padded numbers:
const dt = new Date('2017-12-12');
const year = dt.getUTCFullYear()
const month = dt.getUTCMonth() + 1 // Date provides month index; not month number
const day = dt.getUTCDate()
// Print always "2017-12-12", regardless the time zone it executed in
console.log(year + '-' + padToTwo(month) + '-', padToTwo(day));
// Or use a template literal
console.log(`${year}-${padToTwo(month)}-${padToTwo(day)}`);
function padToTwo (number) {
return number > 9 ? number : '0' + number
}
Answer from Ferdinand Prantl on Stack OverflowYou will need to subtract the time zone offset of your local time zone from the Date instance, before you pass it to format from date-fns. For example:
const dt = new Date('2017-12-12');
const dtDateOnly = new Date(dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000);
console.log(format(dtDateOnly, 'YYYY-MM-DD')); // Always "2017-12-12"
Problem
You want to handle only the date part of the Date instance, because the time part does not make sense for birthdates. However, the Date object does not offer any "date-only" mode. You can access both its date and time parts in the local time zone or UTC. The problem is, that format from date-fns prints the output always in the local time zone.
When you executed the constructor only with the date part:
const dt = new Date('2017-12-12');
The JavaScript engine actually assumed a string in the incomplete ISO 8601 format and perfomed this:
const dt = new Date('2017-12-12T00:00:00.000Z');
It may still look "harmless" to you, but the date instance exposes the value not only in UTC, but also in the local time zone. If you construct the Date instance on the East Coast of the US, you will see the following output:
> const dt = new Date('2017-12-12');
> dt.toISOString()
'2017-12-12T00:00:00.000Z'
> dt.toString()
'Tue Dec 11 2017 19:00:00 GMT-0500 (EST)'
> d.toLocaleString()
'12/11/2017 7:00:00 PM'
Solution
If you know, that format from date-fns reads date and time parts from the date instance in the local time zone, you will need to make your date "looking like" the midnight in your local time zone and not in UTC, which you passed to the Date constructor. Then you will see the year, month and date numbers preserved. It means, that you need to subtract the time zone offset of your local time zone for the specified day. Date.prototype.getTimezoneOffset returns the offset, but with an inverted sign and in minutes.
const dt = new Date('2017-12-12');
// Tue Dec 11 2017 19:00:00 GMT-0500 (EST)
const dtDateOnly = new Date(dt.valueOf() + dt.getTimezoneOffset() * 60 * 1000);
// Tue Dec 12 2017 00:00:00 GMT-0500 (EST)
console.log(format(dtDateOnly, 'YYYY-MM-DD'));
// Prints always "2017-12-12", regardless the time zone it executed in
However, such Date instance can be used only to format the date-only value. You cannot use it for computing date differences, for example, which would need the original and correct UTC value.
Alternative
If you need always the same date-only format and not the format specific to the current locale, you do not need date-fns. You can format the string by the concatenation of padded numbers:
const dt = new Date('2017-12-12');
const year = dt.getUTCFullYear()
const month = dt.getUTCMonth() + 1 // Date provides month index; not month number
const day = dt.getUTCDate()
// Print always "2017-12-12", regardless the time zone it executed in
console.log(year + '-' + padToTwo(month) + '-', padToTwo(day));
// Or use a template literal
console.log(`${year}-${padToTwo(month)}-${padToTwo(day)}`);
function padToTwo (number) {
return number > 9 ? number : '0' + number
}
Only adding the @ferdinand-prantl answer. If you are using the date-fns, you can parse the string date ('2017-12-12') using the parseISO(here) fn from date-fns, which will complete the missing ISO 8601 format with your local time zone. When you use the format fn, you are going to keep the date.
const strDate = '2017-12-12';
const isoDate = parseISO(strDate);
const formattedDate = format(isoDate, 'YYYY-MM-DD');
console.log({strDate, isoDate, formattedDate})
//{
// strDate: '2017-12-12',
// isoDate: 2017-12-12T02:00:00.000Z,
// formattedDate: '2017-12-12'
//}
Specify a time zone for format()?
Add a disclaimer or remove Timezone page
Date-fns and UTC time
JS: parse time to pull out hours and minutes using date-fns, ignroing timezones
» npm install date-fns-tz
Hey! is there any way of setting the default timezone in date-fns? Im currently +1 locally but the server is also hosted where the timezone is +1. But I need all the time to refer to UCT 0 time. Anyone know how I can do this?
I am going to answer my own question with how I make it work in case it helps someone, but I am still looking to see if there is a better alternative of just using date-fns since mine seems a bit hackish:
What I do is remove the timezone from the ISO string and then use that time with date-fns:
let time = "2019-12-15T20:00:00.000Z".slice(0, -5)
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:00 PM, 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.
Here is one solution that manually formats the string using the vanilla JS Date object:
const date = new Date('2019-12-15T20:00:00.000Z');
function format(date) {
const hours = date.getUTCHours();
const minutes = date.getMinutes();
return (1 + ((hours - 1) % 12)) + ":" + minutes.toString().padStart(2, "0") + " " + ((hours > 11) ? "PM" : "AM");
}
format(date);