mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.57k stars 1.35k forks source link

[question] DatePickers without timezone #8620

Open david-ic3 opened 1 year ago

david-ic3 commented 1 year ago

Order ID or Support key πŸ’³ (optional)

19458

Duplicates

Latest version

The problem in depth πŸ”

It's a classical issue when dealing with dates, the timezone.

In our scenario we would like to have date pickers that are without timezone. Wherever they are or whatever the settings of the browser is, all users should see the same date (and time). It's a kind of universal date (we could say it's server but nowadays servers are also everywhere).

What is the advised way dealing with this scenario, taking into account the date is going to be persist ?

Thanks in advance

Your environment 🌎

No response

flaviendelangle commented 1 year ago

Hi,

Which time should people see ? Should it be the UTC time or a fixed timezone (the one of the country in which your company is for example) ?

In both case, it should be solved by #8261 which is a big ongoing topic

david-ic3 commented 1 year ago

Both UTC or a fixed timezone would be fine for us, merci

flaviendelangle commented 1 year ago

If you are using dayjs or moment, you should be able to already use UTC, the doc is here :+1:

oliviertassinari commented 1 year ago

@flaviendelangle As someone that isn't too versed in date libraries and UTC, I think that it would be interesting to update https://mui.com/x/react-date-pickers/adapters-locale/#use-utc-dates to explain a bit about what the UTC is for, what are the use cases. So below what I understand of its value:


From what I understand the best UTC example of a use case is the birthdate. With this logic:

import * as React from 'react';
import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';

export default function BasicDatePicker() {
  return (
    <LocalizationProvider dateAdapter={AdapterDayjs}>
        <DatePicker label="Basic date picker" onChange={(value) => {
          console.log('value', value.toDate().toISOString())
        }} />
    </LocalizationProvider>
  );
}

in the UTC+2 timezone (Paris), I get value 2023-06-05T22:00:00.000Z logged but I selected 2023-06-06. As a developer, I have to figure out how to send the correct date to the server. I can't use this or I will get bugs like https://github.com/sequelize/sequelize/issues/11597.

This seems a bit involved. I have to format it correct, e.g. https://stackoverflow.com/questions/17415579/how-to-iso-8601-format-a-date-with-timezone-offset-in-javascript I seem to get it to work:

import * as React from 'react';
import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';

function toIsoString(date) {
  var tzo = -date.getTimezoneOffset(),
      dif = tzo >= 0 ? '+' : '-',
      pad = function(num) {
          return (num < 10 ? '0' : '') + num;
      };

  return date.getFullYear() +
      '-' + pad(date.getMonth() + 1) +
      '-' + pad(date.getDate()) +
      'T' + pad(date.getHours()) +
      ':' + pad(date.getMinutes()) +
      ':' + pad(date.getSeconds()) +
      dif + pad(Math.floor(Math.abs(tzo) / 60)) +
      ':' + pad(Math.abs(tzo) % 60);
}

export default function BasicDatePicker() {
  return (
    <LocalizationProvider dateAdapter={AdapterDayjs}>
        <DatePicker label="Basic date picker" onChange={(value) => {
          console.log('value', toIsoString(value.toDate()), value.toDate().toISOString(), value.toDate())
        }} />
    </LocalizationProvider>
  );

Side notes:

  1. I wonder if the DX with a native date picker isn't simpler:
import * as React from 'react';

export default function BasicDatePicker() {
  return (
    <input type="date" onChange={(event) => {Β console.log(event.target.value); }} />
  );
}

It returns '2023-06-06' directly. So maybe if I'm using the type DATEONLY https://sequelize.org/docs/v7/other-topics/other-data-types/#dates I can store it directly in the database.

  1. I love https://www.youtube.com/watch?v=-5wpm-gesOY πŸ˜‚ "in November 2022, at the 27th General Conference on Weights and Measures, it was decided to abandon the leap second by or before 2035." we are saved
flaviendelangle commented 1 year ago

I love https://www.youtube.com/watch?v=-5wpm-gesOY joy

This one is a classic But the good news here is that we don't implement all this logic, we just let the date library do it (and they almost always rely on browser Intl API).


Now that we do support timezones, we could indeed document a little more what are the advised way of using timezones with a date picker (store the date in UTC being the most common for sure)

KayakinKoder commented 3 months ago

@flaviendelangle I'm a bit confused here sorry, and I work with time zones a lot. You asked:

Which time should people see ?

For a date picker, times aren't involved...right? What the user sees and selects is a date e.g. "September 1st, 2024" and what we as developers want to receive from that selection is the same: just the year/month/day that the user selected 2024-09-01. If we want time/time zones involved with the user's selection, we'd use a datetime picker.

The native browser datepicker works that way (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date); it really seems that involving times and time zones makes errors much more likely.

LukasTy commented 2 months ago

What the user sees and selects is a date e.g. "September 1st, 2024" and what we as developers want to receive from that selection is the same: just the year/month/day that the user selected 2024-09-01. If we want time/time zones involved with the user's selection, we'd use a datetime picker.

The native browser datepicker works that way (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date); it really seems that involving times and time zones makes errors much more likely.

@KayakinKoder I understand your position, but have you seen any feature-rich Date Picker component that is working like <input type="date" /> with binding a string value? πŸ€” Have you tried using timezone="UTC" on components and not caring about the time on your end? Do you still have issues using Picker components this way?

KayakinKoder commented 2 months ago

@LukasTy yes folks can use UTC (or any time zone really), but I've seen here and in all the other date picker repos I checked lots of support tickets ".getSelectedDate() gives me the wrong date!" Medium articles written about it, all due to time zone issues.

So it's mostly out of curiosity...why would date pickers want to involve times in the user-facing API? I'm guessing that to build the underlying day/month/year data you've got to use js Date related methods/data, why not then abstract that away from the user-facing API so that users don't have to think about it. Even if it's just an additional method .getSelectedDateOnly() that would return 2022-03-12 or whatever standard format.

I'm sure there's a good reason because you're right, every date picker component I looked at aside from the native browser component works this way.

LukasTy commented 2 months ago

So it's mostly out of curiosity...why would date pickers want to involve times in the user-facing API?

I'm sure there's a good reason because you're right, every date picker component I looked at aside from the native browser component works this way.

It's not about building data but manipulating it. Under the hood, all the Picker libraries are doing numerous date calculations to give developers the robust APIs they want. This is especially important when talking about various ways to add date validation.

I assume that internally native date input uses Date API to manipulate date and keeps it as a string on the input itself.

This could be something that we could experiment with. πŸ€”

@flaviendelangle have we ever considered an adapter (AdapterStringISODate) like this? Naturally, it would come with various limitations. πŸ˜ƒ

mwarger commented 1 month ago

Just chiming in... this would be a great addition. The date utilities are fantastic and it's well worth allowing the complexity...

But sometimes, I want to have someone pick a date, and I just want that date back. A nice wrapper around a native date input is literally all I need. It doesn't make any sense to jump through hoops when a Date picker (that has nothing to do with time or timezones) shows 2024-10-18 and when I read the date it shows 2024-10-19 because of some time zone offset based on when I used the input.