
| Tech | Version |
| -------------------- | ------- |
| @material-ui/pickers |^3.2.10|
| material-ui |^4.9.2|
| TypeScript |^3.7.5|
| React |16.12.0|
| Peer library |@date-io/date-fns@^1.3.6 / date-fns@^2.0.0-beta.5| |
The JSON format for DateTimes uses UTC timezone, so this is how I send those dates to the server.
On the server I get something like 2020-02-27T23:47:00.000Z, because it's 00:47 in my Tz (GMT+1)
Now, I don't want the end date of some event to be 28th and some hour that is there just because someone picked the value at that time. I want the selected value to be the beginning of the day.
IMO this is how a DatePicker should work. It should select dates, not date and some random time that happen to be the time when you pick the value.
https://codesandbox.io/s/material-demo-l6coz
Sorry guys, you would need to fiddle with your timezone settings and clock settings.
I guess this is happening here with the mergeDateAndTime call:
Definitely no. If you want to use only date make date={startOfDay(date)}
But the datepicker should use the return the same time as passed from the user
@dmtrKovalenko It won't work if there is no default date (default value is null). Do you know if there's any way to globally override the behavior of DatePicker to only select the date or I'll have to create a HOC?
You can override mergeDateAndTime method of date-io adapter. Hmm let me think
It would benefit from giving devs an ability to make the dates inclusive or non-inclusive. For example: "2020-03-07T23:59:59.999Z" or "2020-03-07T00:00:00.000Z"
when I pass an UTC value to the TimePicker component I'm having problems with timezone too. it shows -3 hours of the value that I passed
I pass 12:00 it set 9:00
Looking at the issue history of react-datepicker and react-day-picker, it seems that we should do something about it.
It would benefit from giving devs an ability to make the dates inclusive or non-inclusive. For example: "2020-03-07T23:59:59.999Z" or "2020-03-07T00:00:00.000Z"
@pietmichal Interesting proposal, what do we need the inclusive mode for? Also, shouldn't we also consider the timezone as part of the problem? If we reset the time, should we reset it in the timezone of the input value of of UTC+0?
@dmtrKovalenko What do you think of the tradeoff taken by react-day-picker? They set to time to 00:00:00 UTC+0 to be compared with us or react-datepicker.
I think it is weird. They do have issues like "why it is setting time to 12:00" (https://github.com/gpbl/react-day-picker/issues/199) and I tent to leave it as is.
Because if we are changing time it is the behavior user cannot control. He can easily control annulation time manually (moreover we are requiring using date library, so the user will not need to do date.setHours(0).setMinutes(0).setSeconds(0)).
What if I need to build a datetime picker? Google approach is to have 2 separate controls – it will be painful if datepicker will always reset local time.
I am not seeing any issues here. In this particular case, it needs to properly control the timezone before passing date to the picker.
What's the problem?
@dmtrKovalenko Right, the problem seems to be rooted from confusion into how timezone works in the database, server, network, and browser sides. I don't see anything wrong in the component.
What if the solution was on the documentation side instead?
@oliviertassinari I don't really have any confusion in how timezone works between client/server. I've been doing this for some time already. The issue for me is an unexpected behavior from the picker - current time is set as a time part of the selected Date object. Giving some emphasis on that in docs could help.
Usually when I worked with a date picker it selected just the date (setting time to start of the day), so I never had to transform it to startOfDay(date). Personally I believe this should be the default behavior, but since we lack an actual date type in JS, then different opinions will come into play.
I understand you guys might think otherwise and want to include the time in date picker for some reason.
I never had to transform it to startOfDay(date)
@slykar why do you need this requirement in the first place?
An interesting comparison with native pickers: https://codesandbox.io/s/material-demo-tvdjs?file=/demo.js.
It is returning an input string actually (2020-03-12). If you will use the second argument of picker and do the same – you will get exactly the same result.
I never had to transform it to startOfDay(date)
@slykar why do you need this requirement in the first place?
User needs to select an _end date_ of a campaign. When you think about the date it's only natural to assume the date starts when the day starts? User does not want the campaign to end on selected _end date_ + _"whatever time was when I selected the end date"_.
My point is - date pickers are for dates, not dates + times. If I wanted a date + time I would use a Date-Time picker. Now this is all confusing of course, because there is no native date type in JS like you get one in Python. For me, a sensible compromise is to set the time part to the beginning of the day when a date is selected.
Mentioning in the docs that time is included when using MUI DatePicker sounds ok for me. It should be clear how to control the time. Maybe a parameter to DatePicker like includeTime could be added.
It's just my nitpicking to how MUI DatePicker defaults works, but it's a matter of opinion and expectations.
I'm not sure but I was using KeyboardDatePicker and when I select a date from the actual picker, the time part is set to 00:00:00 so all good for me, that original date I picked does not get changed.
However, now, the problem happens to me when I manually type-in a date in the input field say (05/07/2020). I would then get a value e.g. 2020-05-06T10:00:00Z, where time part was when I typed in the value.
I'm not sure if there is a difference between picking via the actual picker and entering it manually via the input field, but I would agree to @slykar that providing a prop to allow the user to choose whether or not to include the time would be a great help.
This is because date (and time) pickers actually pick the wrong times. See https://github.com/mui-org/material-ui-pickers/issues/1358#issuecomment-628015527
I built an absurd workaround today, so the returned value of a DatePicker is an ISO8601-String (which is unambiguous), instead of a Date (which is ambiguous):
import React from 'react';
import { DatePicker as MuiDatePicker } from '@material-ui/pickers';
import DateFnsUtils from '@date-io/date-fns';
/*
* Beware workarounds involving bugs in material-ui-pickers' design.
*
* See https://github.com/mui-org/material-ui-pickers/issues/1358#issuecomment-628015527
*
* @material-ui/pickers operate on a Date, but we really want a String.
* These funky DateUtils let @material-ui/pickers pick dates in the local
* timezone ... but they ensure outside callers only see ISO8601 Strings.
*/
/**
* Convert a _local-time_ value to an ISO-8601 Date string.
*
* For instance: given 2020-05-13T03:59:50.000Z, if we're in UTC-4,
* return "2020-05-12".
*
* Why? Because material-ui selects dates in local time, not in UTC. If we
* were to run date.toISOString(), that would convert to UTC and then
* convert to String; but if we convert to UTC, that changes the date.
*/
function jsDateToLocalISO8601DateString(date) {
return [
String(date.getFullYear()),
String(101 + date.getMonth()).substring(1),
String(100 + date.getDate()).substring(1),
].join('-');
}
function dateStringToLocalDate(s) {
if (!s) return null;
return new DateFnsUtils().parse(s, 'yyyy-MM-dd');
}
export default function DatePicker({ label, value, onChange }) {
const handleChange = React.useCallback(date => {
onChange({ target: { value: date ? jsDateToLocalISO8601DateString(date) : null } });
}, [onChange]);
return (
<MuiDatePicker
label={label}
value={dateStringToLocalDate(value)}
format="PP"
onChange={handleChange}
/>
);
}
I need to record only the date in the database and I'm having problems with the timezone as well. I even managed to solve it through function, but create other problems. It would be great if you had the option of just passing the date without hours.
I understand that there exists confusion regarding this topic about the implementation and there are various opinions about them.
However, as pointed out by @michaeljarizala , why is the outcome not consistent when "KeyboardDatePicker" is used. If typed the time is being taken from the local timezone, hence the date being shown is one day prior. But if date is being chosen from the dialog, then the time is 00:00 (midnight) and the date is correct.
Is this not a bug? Irrespective of the implementation choice, shouldn't the result be consistent irrespective of whether the date is typed or chosen from the dialog
Demo Link: https://codesandbox.io/s/old-waterfall-m6mxw?file=/src/index.js
P.S. This behaviour only occurs if date is picked from the dialog before anything has been typed at all. If you type using keyboard, then all the subsequent dates chosen from the dialog box also take the time from local time zone and are one day prior.
@dakshsaraf I confirm the behavior, I agree, it's inconsistent we could solve this problem but it's outside the scope of this one. Could you open a new issue?
For what it's worth here is a workaround to fix this issue which at least satisfies my project's requirements. The idea is to set time to middle of the day (i.e. to 12:00.000 p.m.) and later convert the date to UTC time zone. This will change the actual time according to what your timezone difference is (in my case it were UTC-5 and UTC+1), but the date should stay unchanged within same day's boundaries.
Here is a snippet to achieve this (using Luxon):
dt.endOf('month').set({ hour: 12, minute: 0, second: 0, millisecond: 0 }).setZone('utc').toISO()
At last step we generate date string in ISO format which, when supplied as value to <DatePicker>, works as expected for both time zones.
For what it's worth here is a workaround to fix this issue which at least satisfies my project's requirements. The idea is to set time to middle of the day (i.e. to 12:00.000 p.m.) and later convert the date to UTC time zone. This will change the actual time according to what your timezone difference is (in my case it were UTC-5 and UTC+1), but the date should stay unchanged within same day's boundaries.
This fails in many timezones. The most notable is New Zealand Daylight Time. https://en.wikipedia.org/wiki/UTC%2B13:00
There is already a correct workaround in the comments of this issue: https://github.com/mui-org/material-ui-pickers/issues/1526#issuecomment-628237301
This fails in many timezones.
I wouldn't say "many", but I agree that this is not a universal solution, more like a workaround. Nevertheless it satisfies the needs of my project, and I believe it may work for other projects too which operate in timezones with time difference to UTC <= 11 hours.
I saw your solution, just did not want to add complex logic to my project at this time. Peace! :-)
I found a hacky way of doing it, but very easy to implement : set thet date at 12:00. This solution is native javascript thus can be used without new depandancy. This way, no matter what the browser does to change your date, you will be in the right day.
const addHours = (date, hours) => {
const newDate = new Date();
newDate.setTime(date.getTime() + hours * 60 * 60 * 1000);
return newDate;
};
Hacky, doesn't manage UTC +13 and UTC +14, but it works.
I found a hacky way of doing it, but very easy to implement : set thet date at 12:00. This solution is native javascript thus can be used without new depandancy. This way, no matter what the browser does to change your date, you will be in the right day.
[...]
Hacky, doesn't manage UTC +13 and UTC +14, but it works.
Actually, setting date to 12:00 fails when two users communicate in different timezones.
2020-10-20T12:00-10:00 -- October 20th2020-10-21T01:00+03:00 -- October 21stI wrote a correct solution at https://github.com/mui-org/material-ui-pickers/issues/1526#issuecomment-628237301. The only correct solution I'm aware of is to pick ISO8601 Strings.
@dmtrKovalenko I now count 4 workarounds in this issue's comments, and only one is correct. I don't mean to offend anybody -- I'm a teacher, not a hater. I think at this point, we should count what we've seen so far. Counting workarounds suggests 75% of people who have worked around this problem have created other problems. And by my count, 100% of people who have actually _found_ this issue would not encounter it if the date picker picked ISO8601 Strings.
@adamhooper you can imagine that DatePicker is all the time chooses the ISO string of start of the day in the user local timezone. It doesn't really matter what to return a string of 2020-10-20T12:00-10:00 or 2020-10-21T00:00Z the point of time still the same.
You don't need to play and try to change the timezone of the picked date manually. I all the time seeing people who don't understand the concept of timezone and trying to do the following
function jsDateToLocalISO8601DateString(date) {
return [
String(date.getFullYear()),
String(101 + date.getMonth()).substring(1),
String(100 + date.getDate()).substring(1),
].join('-');
}
And this is the root of the issue. You need to preserve the user local timezone
If this is a date picker there is no reason to ignore the time part of the value.
For example, if the event is end at the 00:00 of 10th of October in UTC, for the person with timezone -12:00 it will end not on the 10th of October, but on the 9th of October 12:00. And the DatePicker should show the date of 9th of October in the user local timezone.
If this is a date picker there is no reason to ignore the time part of the value.
A date _has_ no time. This is the fundamental confusion:
A JavaScript Date is a timestamp: one moment in our universally-shared sequence of instants. In Java, it's an Instant. In SQL, it's a TIMESTAMP. In Ruby/Python, it's a datetime. In Unix, it's a timestamp. Timestamps don't have timezones: I can write, "shout 'Yay' at 1603205557094ms" to programmers around the world, and we'll all shout "Yay!" at the exact same time.
A date -- YYYY-MM-DD is _not self-contained_. Different people who look at the same date will interpret it to mean different things. You can disambiguate a date by presenting it alongside a timezone. Without context, a Date is ambiguous _by definition_. Algorithms cannot resolve the ambiguity.
A date is akin to a month. Or a year. Or a quarter. Or "a.m./p.m."
@adamhooper honestly you contradict yourself. Date (in javascript) – is a timestamp, yes it is a number of (mili)seconds from the 01/01/1970 00:00 UTC. And this is the world we live in.
A date has time. And this time is 00:00. This is the rule (at least for this particular library). From the perspective of timezones and timestamps – the user is choosing the 00:00 of the following date in his particular timezone.
Because: The possible range of timezones today is from -12:00 to + 14:00 so if we are not adding the timezone information to the date – it means that for 3 different people on the Earth it can mean the 3 different days. That's why preserving the timezone where the day was chosen is really important.
@adamhooper honestly you contradict yourself. Date (in javascript) – is a timestamp, yes it is a number of (mili)seconds from the 01/01/1970 00:00 UTC. And this is the world we live in.
A date has time. And this time is 00:00. This is the rule (at least for this particular library).
We're getting somewhere!
Indeed, I agree that in this particular library, a date has a time. My thesis is: _that's exactly the issue here_.
Because: The possible range of timezones today is from -12:00 to + 14:00 so if we are not adding the timezone information to the date – it means that for 3 different people on the Earth it can mean the 3 different days. That's why preserving the timezone where the day was chosen is really important.
I agree completely with the problem. I'm saying the API shouldn't try to solve it.
The API has to choose _which_ problem its users will face:
Problem A: Alice selects a Date, and Bob sees any of three Dates. [DatePicker does this]
Problem B: Alice selects a Date, and Bob must interpret it in one of three ways. [ISO8601 does this]
The only way to solve problem B is to create problem A. The only way to solve problem A is to create problem B.
ISO8601 (and HTML <input type="date">) opt to expose problem B to their users. That's because Problem B is easy to solve -- both because it costs less code to work around it, and because there are so many tools out there that solve it. Heck, even MUI's own DateTimePicker solves problem B.
I don't agree with these 2 problems. You are choosing the moment of time and not the abstract date
You are choosing the moment of time and not the abstract date
@dmtrKovalenko That isn't what _I'm_ doing. Every time I use a date picker, I am picking an abstract date.
So is the reporter.
So are most users, I suspect.
And that's a miss understanding I am trying to prevent.
A date in a calendar (without time and timezone) is a valid thing. There are many use-cases where it doesn't mark an event. Eg. New Year, January 1st every year. It's exact and cannot be described with time and timezones (as it means 20+ different events). Similarly my birthday is on 9th March and it doesn't matter which country. My driving license will expire sooner if I move to Australia, later if I move to US (from Europe). I feel the quest of changing _the world as it works currently_ over-ambitious.
I'm running into this issue because my test snapshots are failing when validating the rendering of DateTime pickers. I set the value to the Unix epoch; I was hoping that would prevent the mismatch in the snapshot. Despite the timezone being set explicitly in value, DateTimePicker coerces the value into a local time (causing the test to fail in travis-CI).
Looking back at the conversation, it's clear that there are some strong opinions on the proper way to approach this. As programmers, we can accommodate a multitude of opinions at the cost of complexity.
I'd suggest keep the default behavior as it is, but allowing the programmer and/or user decide how to handle timezones.
We could allow the programmer to address timezones by, for instance, allowing a localize option to be specified in the configuration. If true, use the default behavior; if false, interpret the selected date/time as UTC.
We could allow the user to address timezones by letting them pick the timezone instead of assuming they mean local time. This could be achieved by adding a dropdown with a list of timezones to the DateTimePicker, or creating a new DateTimeZonePicker with such a dropdown.
Happy to contribute to development on either approach, and my ears and eyes are open to other suggestions.
I will open an RFC so we move to the native date input API (string). This could both ease adoption and solve this problem.
Most helpful comment
I will open an RFC so we move to the native date input API (string). This could both ease adoption and solve this problem.