reactjsmaterial-uidatepickerdate-fnsmui-x-date-picker

Mui Datepicker sets incorrect date


I am currently using Mui Datepicker library from @mui/x-date-pickers/DatePicker.

Here's my sample code

     <LocalizationProvider dateAdapter={AdapterDateFns}>
      <DatePicker
        inputFormat="MM/dd/yy"
        value={date}
        minDate={new Date()}
        disablePast
        onChange={(newValue) => {
          console.log("new value", newValue)
          setDate(newValue);
        }}
        renderInput={(props) => (
          <TextField
            autoFocus
            size="small"
            {...props}
            inputProps={{
              ...props.inputProps,
              autocomplete: 'off',
              placeholder: 'MM/DD/YY',
            }}
          />
        )}
      />
    </LocalizationProvider>

Now, I would like to use MM/dd/yy as the date format. But when I type in 10/30/99, it returns a date 10/30/1999. I would like it to return 10/30/2099. It is happening because the library is trying to predict the nearest year to 99 from the current date (reference date).

Is there a way to ensure I only get future dates. So in this case it returns the year 2099 instead of 1999.

I tried setting minDate={new Date()} and disablePast, but it didn't work.

codesandbox link here

Thanks in advance.


Solution

  • If I understand correctly, you want to use MM/dd/yy as the date format, but always get a future date as the result. For example, if you type in 10/30/99, you want to get 10/30/2099, not 10/30/1999.

    Unfortunately, the MUI X DatePicker does not have a built-in option to do that. It uses the date-fns library to parse the date string. This is similar to the shortYearCutoff option of jQuery DatePicker.

    According to date-fns documentation

    yy will try to guess the century of two digit year by proximity with referenceDate:

    For example:

    console.log(parse("11/11/99", "MM/dd/yy", new Date())); // 1999-11-11
    console.log(parse("11/11/73", "MM/dd/yy", new Date())); // 1973-11-11
    console.log(parse("11/11/72", "MM/dd/yy", new Date())); // 2072-11-11
    console.log(parse("11/11/50", "MM/dd/yy", new Date())); // 2050-11-11
    console.log(parse("11/11/7", "MM/dd/yy", new Date()));  // 2007-11-11
    

    One possible solution is to override parse method of AdapterDateFns.

    For example:

    class FutureDateAdapter extends AdapterDateFns {
      constructor(props = {}) {
        super(props);
        this.parse = (value, formatString) => {
          if (value === null) {
            return null;
          }
          const date = new Date();
          const newDate = addYears(date, 50);
          return parse(value, formatString, newDate);
        };
      }
    }
    

    And then:

    <LocalizationProvider dateAdapter={FutureDateAdapter}>
      <DatePicker
        inputFormat="MM/dd/yy"
        value={date}
        onChange={setDate}
        renderInput={(props) => (
          <TextField
            autoFocus
            size="small"
            {...props}
            inputProps={{
              ...props.inputProps,
              autoComplete: "off",
              placeholder: "MM/DD/YY",
            }}
          />
        )}
      />
    </LocalizationProvider>;
    

    UPDATED ANSWER

    It does not handle the case when the input date is between 01/01/23 and today. In that case, it will return the past date instead of the future date.

    If you want to get only the future date, you can use the startOfDay and compareAsc methods from date-fns instead.

    For example:

    class FutureDateAdapter extends AdapterDateFns {
      constructor(props = {}) {
        super(props);
        this.parse = (value, formatString) => {
          if (value === null) {
            return null;
          }
    
          const newDate = new Date();
          const parsedDate = parse(value, formatString, newDate);
          const today = startOfDay(newDate);
          if (compareAsc(parsedDate, today) === -1) {
            return addYears(parsedDate, 100);
          }
          return parsedDate;
        };
      }
    }
    

    You can see the whole example here.