javascriptreactjsnext.jsdate-fnsdate-fns-tz

date-fns showing invalid interval when comparing dates using isWithinInterval and a users timezone


I am getting back UTC time from an API, so in my next project, i am converting the UTC times to the users local time based on a timezone setting coming also from the API.

I am trying to use the isWithinInterval function of date-fns to check if the current time (in that users timezone) is within a start and end time, but it keeps throwing back a

invalid interval

At the top of the file, i am declaring the current time based on timezone;

const nowInUsersTimezone = new Date().toLocaleString('en-AU', { timeZone: user.timezone })

When i console log this, it shows perfect.

When i got to use the isWithinInterval im using the following to check if the nowInUsersTimezone is between 2 dates, if it is, then i just print within, but i cant get it to work;

{
    isWithinInterval(
        parseISO(nowInUsersTimezone),
        {
            start: parseISO(formatInTimeZone(job.start_time, user.timezone, 'dd/MM/yyyy, h:mm:ss aaa')),
            end: parseISO(formatInTimeZone(job.end_time, user.timezone, 'dd/MM/yyyy, h:mm:ss aaa')),
        }
    ) && (
        <>
            <p>Within</p>
        </>
    )
}

console logging the start and end dates works perfect, just when its in the isWithinInterval doesnt work.


Solution

  • Looking at the documentation the isWithinInterval function from date-fns library checks if a date is within a given interval. It expects Date objects as arguments, not ISO strings.

    The toLocaleString method returns a string, not a Date object, and the format of this string depends on the locale.

    I would suggest using parseISO with ISO strings. But new Date() with a timezone conversion does not return an ISO string. That's likely why you're getting the "Invalid Interval" error.

    Heres how i would do it:

    const now = new Date();
    const nowInUsersTimezone = new Date(
      now.toLocaleString('en-AU', { timeZone: user.timezone })
    );
    
    const jobStart = new Date(
      job.start_time.toLocaleString('en-AU', { timeZone: user.timezone })
    );
    const jobEnd = new Date(
      job.end_time.toLocaleString('en-AU', { timeZone: user.timezone })
    );
    
    {
      isWithinInterval(nowInUsersTimezone, {
        start: jobStart,
        end: jobEnd,
      }) && (
        <>
          <p>Within</p>
        </>
      )
    }
    

    EDIT: Changed approach

    It sounds like the issue is not with the isWithinInterval function itself, but rather with the comparison of times. The first thing to check is whether the start time is earlier than the end time, and that nowInUsersTimezone is in between these two times.

    const user = {
      timezone: 'Australia/Sydney' //Example set
    };
    
    const job = {
      start_time: new Date('2023-07-29T00:00:00Z'), 
      end_time: new Date('2023-07-29T23:59:59Z'),
    };
    
    
    // create a new date object for the current time
    const now = new Date();
    
    // convert it to the user's timezone
    const nowInUsersTimezone = new Date(now.toLocaleString('en-US', { timeZone: user.timezone }));
    
    // convert job start and end times to the user's timezone
    const jobStart = new Date(job.start_time.toLocaleString('en-US', { timeZone: user.timezone }));
    const jobEnd = new Date(job.end_time.toLocaleString('en-US', { timeZone: user.timezone }));
    
    // check the interval
    if(isWithinInterval(nowInUsersTimezone, { start: jobStart, end: jobEnd })) {
        console.log("Within");
    } else {
        console.log("Not within");
    }
    
    console.log("Now in user's timezone: ", nowInUsersTimezone);
    console.log("Job start time in user's timezone: ", jobStart);
    console.log("Job end time in user's timezone: ", jobEnd);