Search code examples
reactjsdatereact-day-picker

How can I Post the correct week days using react-day-picker


In my React app I'm trying to send to the server week values using the library react-day-picker https://react-day-picker.js.org/examples/selected-week but its posting to the server the values with one day interval.

For example, When I pick in the client the week between 2021-03-07 to 2021-03-13 I can see in the log that the week was sent correctly, but in the server I get the values with 1 day offset like this:

  selectedDays: [
    '2021-03-06T22:00:00.000Z',
    '2021-03-07T22:00:00.000Z',
    '2021-03-08T22:00:00.000Z',
    '2021-03-09T22:00:00.000Z',
    '2021-03-10T22:00:00.000Z',
    '2021-03-11T22:00:00.000Z',
    '2021-03-12T22:00:00.000Z'
  ]

this is my client code:

console.log(data);
// I get here the correct week values

axios({
  method: "post",
  url: "http://localhost:3001/",
  data: data,
}).then(
  (res) => {
    console.log(res);
  },
  (error) => {
    console.log(error);
  }
);

My server code:

exports.createNewGoals = async (request, response) => {
   
 // I get here the values with one day interval.
 console.log(request.body.selectedDays);

}

I can guess this could be related to localization settings somehow but I didnt defined any custom localization settings. I'm running the project locally on Windows machine (he-il) in Chrome browser.

Where does the dates changes and how can I configure it correctly?

Any help will be appreciated


Solution

  • This is indeed a timezone mismatch. The client is sending local time to the server which is parsing/saving it as UTC, and since your timezone is ahead of GMT, 12:00AM will always a day behind. The server is likely configured to GMT which is good practice because a web server will have users in many time zones, so it needs a common way to accurately to store time. If you don't need the conversion, there are several methods depending on your control and requirements:

    1. Convert to UTC on the client

    In the example sandbox for getWeekDays, the client is selecting dates in local time and uses the moment library then converting back to Date. By default it is in user time to accurately capture the user's selection, which includes their timezone offset:

    Sun Mar 14 2021 00:00:00 GMT-0500 (Eastern Standard Time), 
    Mon Mar 15 2021 00:00:00 GMT-0400 (Eastern Daylight Time), 
    Tue Mar 16 2021 00:00:00 GMT-0400 (Eastern Daylight Time), 
    Wed Mar 17 2021 00:00:00 GMT-0400 (Eastern Daylight Time), 
    Thu Mar 18 2021 00:00:00 GMT-0400 (Eastern Daylight Time), 
    Fri Mar 19 2021 00:00:00 GMT-0400 (Eastern Daylight Time), 
    Sat Mar 20 2021 00:00:00 GMT-0400 (Eastern Daylight Time)
    

    If you want to capture the calendar date clicked on by the user, you can convert to UTC without changing time before converting to JavaScript Date using .utc(true). This is setting the value of the Date to 'the time in this location when it is 12:00AM in UTC'. The client will send it in local, but when this is converted on the server it will convert back to 12:00UTC on the expected date:

    function getWeekDays(weekStart) {
      const days = [moment(weekStart).utc(true).toDate()];
      for (let i = 1; i < 7; i += 1) {
        days.push(
          moment(weekStart)
            .add(i, 'days')
            .utc(true).toDate()
        );
      }
      console.log (days);
      return days;
    }
    

    Results in the following. (* Note I purposely chose last week when our offset changed-- it shouldn't matter to the server):

    Sat Mar 13 2021 19:00:00 GMT-0500 (Eastern Standard Time)
    Sun Mar 14 2021 20:00:00 GMT-0400 (Eastern Daylight Time)
    Mon Mar 15 2021 20:00:00 GMT-0400 (Eastern Daylight Time)
    Tue Mar 16 2021 20:00:00 GMT-0400 (Eastern Daylight Time)
    Wed Mar 17 2021 20:00:00 GMT-0400 (Eastern Daylight Time)
    Thu Mar 18 2021 20:00:00 GMT-0400 (Eastern Daylight Time)
    Fri Mar 19 2021 20:00:00 GMT-0400 (Eastern Daylight Time)
    
    1. Just send Dates

    If time conversion isn't necessary, its often a lot simpler to just send a date without the time:

     function getWeekDays(weekStart) {
          const days = [moment(weekStart).format('YYYY-MM-DD')];
          for (let i = 1; i < 7; i += 1) {
            days.push(
              moment(weekStart)
                .add(i, 'days')
                .format('YYYY-MM-DD')
            );
          }
          console.log (days);
          return days;
        }
    
    1. Convert to local on server

    Similarly, on the server you could convert the client time to local time. This is determined by the type handler on the request parser, but it can be converted back to local to get the user's local date:

    exports.createNewGoals = async (request, response) => {
       
     // I get here the values with one day interval.
     request.body.selectedDays.forEach(dt=> console.log(moment(dt).local()));
    
    }
    

    Or even simpler avoid any time confusion and type it as a string then ignore the local time component:

    exports.createNewGoals = async (request, response) => {
       
     // I get here the values with one day interval.
     request.body.selectedDays.forEach(dt=> console.log(dt.substring(0, 10)));
    
    }