Javascript can be a humbling language at times (or even downright frustrating π‘!), especially when working with dates. I recently had an odd experience where I had written unit tests for a new feature I was finishing at work. When I ran the tests on my machine, they all passed β . However, much to my dismay at 4:30pm on a Thursday, when the tests ran in our continuous integration environment, the dates were coming out one day off! Here you can see exhibit A, the seemingly innocuous line of code responsible for generating my "mock date" in my test.
(Disclaimer: The input date format was not decided by me, but is how a date representation comes back from an API that my app is dependent on.)
const date = new Date('2022-07-01');
console.log(new Date(date).toLocaleDateString()); // 6/30/2022
Where I Went Wrong
First off, I should have recognized that it wasn't giving me the date I put into it, but instead 1 day prior. However, since date formatting wasn't the most important piece of what I was working on at the time, and given it was the end of the day and I was tired, I didn't notice that the date saved in my snapshot was 1 day behind what I was putting in.
Fast forward to the test code running in our CI environment - and π£ - it failed! I thought to myself, how could this be? I'm a professional Typescript / Javascript developer - surely I knew all the quirks of this language by now right!!??
This caused me to do some testing, thinking, and finally (what I should have done from the beginning) some googling - which landed me here:
The Culprit
As it turns out, when constructing a Date
using a string formatted like such YYYY-MM-DD
, the Date.parse
method uses a more strict form of the date input string specification - ISO-8601. This causes it to be parsed from the UTC frame of reference, at midnight no less. Since I am in an America/New_York
timezone (-4:00 EST or -5:00 EDT depending on the time of year) - midnight on any given day in UTC time is always "one day prior" in EST or EDT time - π€―.
There are two strategies you can employ when dealing with this slightly wonky behavior.
How I Dealt With It
So in my case, I needed to change the formatting of the timestamp slightly so that it fit our UI correctly. The source API gave me guaranteed dates in the EST/EDT timezone, and always in the format of YYYY-MM-DD
. Knowing this, I could simply refer to my created javascript date object in UTC time, since that's how it's getting parsed. This would prevent any accidental "1 day off" transformations, allowing me to apply my formatting and ensuring I'll always keep the date the exact same as the input date (assuming the input date's timezone is what I want to "keep" - which is true in this case).
function formatDateString(dateString: string) {
const date = new Date(dateString);
let day: string | number = date.getUTCDate();
day = day >= 10 ? day : "0" + day;
let month: string | number = date.getUTCMonth() + 1; // another JS quirk - month's are zero indexed 0-11
month = month >= 10 ? month : "0" + month;
const year = date.getUTCFullYear();
return `${month}/${day}/${year}`;
}
console.log(formatDateString("2022-07-01")); // 07/01/2022
Conclusion
I try not to work with dates directly too much, but rather use a library like date-fns or dayjs. In this case, I thought to myself "this is a simple date format transformation, what could go wrong?" In the end, an extra 30 minutes of my life spent getting it right, however overall I'm grateful for seeing this language quirk that I previously wasn't aware of!
Top comments (0)