Search code examples
javascriptdate

Parsing Date String Treats February as March


Trying to convert a date-time string from the database (stored local time) and keeping it local instead of being converted to UTC time. The one way I am approaching this is taking the date and splitting the parts and applying the values into a JavaScript date object. For some reason, I give it a date of 2023-02-01 and it thinks its March even when I parse the date value to be an integer and subtract 1.

Like wise:

  • 2023-01-01 is January
  • 2023-03-01 is March
  • 2023-04-01 is April
  • and so on...

const dateString = '2023-02-01T09:00:00Z'

const splitDateTime = new Date(dateString)
    .toISOString()
    .split('T'); // ['2023-02-01', '09:00:00']


const dateHalfSplit = splitDateTime[0].split('-'); // ['2023','02','01']
const timeHalfSplit = splitDateTime[1].split(':'); // ['09','00','00']

localDate = new Date();
localDate.setFullYear(parseInt(dateHalfSplit[0]));
localDate.setMonth(parseInt(dateHalfSplit[1]) - 1);
localDate.setDate(parseInt(dateHalfSplit[2]));
localDate.setHours(parseInt(timeHalfSplit[0]));
localDate.setMinutes(parseInt(timeHalfSplit[1]));
localDate.setSeconds(parseInt(timeHalfSplit[2].split('.')[0]));

console.info(localDate.toLocaleDateString('en-US', {
    day: '2-digit',
    year: 'numeric',
    month: 'long',
    hour: 'numeric',
    minute: '2-digit',
    hour12: true
}));

// Output: March 01, 2023 at 9:00 AM

Granted, this is not an ideal way to handle dates, from the database backend, but is there something I am missing that somehow this is not handling February correctly or could this be a browser bug?


Solution

  • Calling Date::setDate takes into account the local timezone and shifts the month for some reason. Set everyting at once with Date's constructor:

    const dateString = '2023-02-01T09:00:00Z'
    
    const splitDateTime = new Date(dateString)
        .toISOString()
        .split('T'); // ['2023-02-01', '09:00:00']
    
    const localDate = new Date(...splitDateTime[0].split('-').map((n, i) => i === 1 ? n - 1: n), ...splitDateTime[1].split(':').map(parseFloat));
    
    console.info(localDate.toLocaleDateString('en-US', {
        day: '2-digit',
        year: 'numeric',
        month: 'long',
        hour: 'numeric',
        minute: '2-digit',
        hour12: true
    }));

    An easier way would be just fix the timezone offset:

    const dateString = '2023-02-01T09:00:00Z'
    
    const localDate = new Date(dateString);
    localDate.setMinutes(localDate.getTimezoneOffset());
    
    console.info(localDate.toLocaleDateString('en-US', {
        day: '2-digit',
        year: 'numeric',
        month: 'long',
        hour: 'numeric',
        minute: '2-digit',
        hour12: true
    }));