Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DatePicker][TimePicker] Does not correctly accept UTC Dates. #4538

Closed
brucedlukens opened this issue Jun 21, 2016 · 25 comments
Closed

[DatePicker][TimePicker] Does not correctly accept UTC Dates. #4538

brucedlukens opened this issue Jun 21, 2016 · 25 comments
Labels
component: date picker This is the name of the generic UI component, not the React module! component: time picker This is the name of the generic UI component, not the React module! v0.x

Comments

@brucedlukens
Copy link

Problem description

The Date and Time Pickers do not allow you to select the date/time in UTC (or any other timezone), it always defaults to local time.

Steps to reproduce

Create a Date/Time Picker, there is no prop to change the timezone.

Versions

  • Material-UI: 0.15.0
  • React: 15.1.0
  • Browser: All
@mbrookes
Copy link
Member

@brucedlukens This sounds like a feature request rather than an issue. Could you explain your use case in a bit more detail?

@brucedlukens
Copy link
Author

Sure, basically what I'm doing is I have a "Date Time Picker" input in our app. This "Date Time Picker" provides the user with a disabled text field showing the currently selected date/time in a specific format, in UTC (everything in the system must be UTC). I have 2 buttons where the user can select a new date, and another button where they can select a new time. Since there's no option in the Date and Time Pickers to set a timezone, there's obviously a difference between the text field that displays UTC date/time and the pickers. I would think displaying UTC in the pickers would be a pretty common use case, especially since a Javascript Date object has the functionality to convert to UTC very easily.

@mbrookes
Copy link
Member

mbrookes commented Jun 22, 2016

So, the TimePicker currently returns a Date object with the selected time in the user's timezone, but you want to be able to use that Date object as if it were UTC when displaying it your other text field? So if a user selects say 4:30pm, it gets treated as 4:30pm UTC, regardless of their timezone?

@SunJang
Copy link

SunJang commented Jun 23, 2016

I have a similar problem.

In following picture, "10:55pm" is shown in the text field.

2016-06-23 2 02 09
If I click the field it shows 10:55pm which is expected.

2016-06-23 2 03 01

On the other hand, If I change value to "12:55pm" and click "Ok" button, then it suddenly shows 9:55pm.

2016-06-23 2 03 11

2016-06-23 2 03 20

this is log message from onChanged handler. (ignore "year","month","day". I only need "hour" and "minute")

2016-06-23 2 11 24

Finally, If I click the field again, then it show 9:55pm
2016-06-23 2 12 20

I think it is inconsistent.

v.0.15.0, react, redux-form

@brucedlukens
Copy link
Author

@mbrookes My issue is similar to @SunJang. Essentially what I'm saying is that the date and time pickers will ALWAYS show the user's current time zone, I believe that it should display the time zone that we pass in. So if I want the user to choose a UTC date/time, it should use UTC time when they select the date/time, not their local time zone.

@mbrookes
Copy link
Member

@SunJang your issue sounds like a bug, but I wasn't able to reproduce it after setting my timezone to CST and following your screenshots. If you can reliably reproduce this with 0.15.1, please open a new issue if there isn't one already.

@brucedlukens I understand what you're asking for, but something like Moment Timezone may be your best bet for now.

@vorlov
Copy link

vorlov commented Jun 29, 2016

Same Issue
if I pass Tue, 28 Jun 2016 00:00:00 GMT to datepicker and my timezone is -9 GMT it will show
Mon, 27 Jun 2016 15:00:00-09:00 GMT

@brucedlukens
Copy link
Author

@mbrookes I'm not sure what Moment Timezone would do for me. I don't have an issue getting the correct timezone and formatting it from a date object. The issue I have is just that the Date/Time Pickers do not support anything beyond local timezone. This seems like an oversight in the implementation of the components. I already have workarounds in place but they aren't what anyone would call "correct".

@vorlov
Copy link

vorlov commented Jun 29, 2016

@brucedlukens could you please share your workarounds?

@brucedlukens
Copy link
Author

@vorlov I just create a second date object that is offset by the time zone, so the "local time" is the same as whatever it would be UTC. In the unlikely event that a user changes time zones while they're using the application it will obviously be incorrect but I don't think that's really a high risk.

@mbrookes
Copy link
Member

mbrookes commented Jul 2, 2016

This seems like an oversight in the implementation of the components.

It's an oversight in the implementation of JavaScript - that's what Moment Timezone would help with, but it sounds like you already have a solution.

@brucedlukens
Copy link
Author

Just to update this issue, Moment timezone does not seem to solve this issue. It is definitely an issue in the implementation of the DatePicker and TimePicker. I may submit a pull request in the future that fixes this problem, but the basic issue in the code is this: no matter what date object gets passed in, the date and time pickers convert it to local time. The doesn't allow the user to pick the dates/times in anything except local time. If the pickers accepted a timezone prop (what I'd like to do in the pull request when I get around to it), the pickers could base the Date/Time off of that timezone instead of local time.

@mpontikes mpontikes mentioned this issue Aug 5, 2016
13 tasks
@aahan96
Copy link
Contributor

aahan96 commented Aug 11, 2016

@brucedlukens Are you working on the PR?

@jayalfredprufrock
Copy link

Man just hit this today...the datepicker is surely beautiful but could use some love under the hood...

@oliviertassinari oliviertassinari added the component: date picker This is the name of the generic UI component, not the React module! label Oct 19, 2016
@oliviertassinari oliviertassinari added component: time picker This is the name of the generic UI component, not the React module! and removed component: time picker This is the name of the generic UI component, not the React module! labels Nov 15, 2016
@clayne11
Copy link

This is a pretty major issue. The component needs a way to pass in UTC date / times so that we can decide as application authors how to interpret the date / time.

@ds0nt
Copy link

ds0nt commented Aug 3, 2017

I agree. For our usecase, we show reports in the user's organization timezone. I beleive it is not a rare use case at all.

  1. User selects dates for report (need start of day timestamp, and another end of day timestamp)
  2. Convert dates to timestamps
  3. fetches report based on those timestamps.

Unfortunately this simple workflow is made quite difficult because:

  1. Let's say the user's account is in CEST, but his computer is in EST (in the case of a system a concept of organizations, this would make sense)
  2. If I pick the date June 15th, 2017, it will return me the EST time for the start of that day.
  3. Now I have to actually change the date that has been returned, by calculating the offsets in between these two timezones, and then adding (or subtracting) the offset, depending on if it's the value prop, or the callback fn prop.

It's not as simple as just doing a timezone conversion. This is actually modifying the point in time that is returned, and then doing a timezone conversion (for display). And also doing the reverse, to populate the value prop from the parent component.

This would be so much easier with a customizable timezone prop. This logic is not easy to implement, and requires wrapping the DatePicker with crazy logic. I think the use-case would be fairly common in any b2b, or organizational application.

I ended up here because I figured it would actually be easier for me to add the timezone prop myself, than doing all these conversions on the fly (it's not easy to think about, debug, or get right (at least for me))

I will admit I was surprised when there wasn't a timezone prop, for a datetime component, not that my surprisedness matters :p.

@ds0nt
Copy link

ds0nt commented Aug 3, 2017

Any help would be appreciated, as I'm banging my head against the wall in order to get these date's to math right. :)

@ds0nt
Copy link

ds0nt commented Aug 3, 2017

As far as I know, moment.js doesn't have a function like: ModifyByTimezoneOffset(tz1, tz2) function. So it's crazy nutsness to figure out how to implement :).

@clayne11
Copy link

clayne11 commented Aug 3, 2017

You can use moment-timezone to handle timezone offsets.

@ds0nt
Copy link

ds0nt commented Aug 3, 2017

I think I've gotten it to work with the following code

Wrap the component with a component like the following (where TZ is the custom timezone):

    shiftPickerDateToTZDate(pickerDate) {
        let pickerOffset = pickerDate.getTimezoneOffset()
        let utcDate = new Date()
        utcDate.setTime(pickerDate.getTime() - pickerOffset * 60000)

        let tzOffset = moment.tz(pickerDate, TZ).utcOffset()
        let tzDate = new Date()
        tzDate.setTime(utcDate.getTime() - tzOffset * 60000)
        return tzDate
    }

    shiftTzDateToPickerDate(tzDate) {
        let tzUTCOffset = moment.tz(tzDate, TZ).utcOffset()
        let utcDate = new Date()
        utcDate.setTime(tzDate.getTime() + tzUTCOffset * 60000)

        let pickerDate = new Date()
        let pickerOffset = pickerDate.getTimezoneOffset()
        pickerDate.setTime(utcDate.getTime() + pickerOffset * 60000)

        return pickerDate
    }
   // ...
             

                <DatePicker
                    value={this.shiftTzDateToPickerDate(this.props.start)}
                    onChange={(ev, date) => this.setStartDate(this.shiftPickerDateToTZDate(date))}
                    // ...
                />

For all y'all that struggle get confuzzled with timezone math like me.

@oliviertassinari
Copy link
Member

Closing for #4787

@SharudAgarwal
Copy link

I think I've gotten it to work with the following code

Wrap the component with a component like the following (where TZ is the custom timezone):

    shiftPickerDateToTZDate(pickerDate) {
        let pickerOffset = pickerDate.getTimezoneOffset()
        let utcDate = new Date()
        utcDate.setTime(pickerDate.getTime() - pickerOffset * 60000)

        let tzOffset = moment.tz(pickerDate, TZ).utcOffset()
        let tzDate = new Date()
        tzDate.setTime(utcDate.getTime() - tzOffset * 60000)
        return tzDate
    }

    shiftTzDateToPickerDate(tzDate) {
        let tzUTCOffset = moment.tz(tzDate, TZ).utcOffset()
        let utcDate = new Date()
        utcDate.setTime(tzDate.getTime() + tzUTCOffset * 60000)

        let pickerDate = new Date()
        let pickerOffset = pickerDate.getTimezoneOffset()
        pickerDate.setTime(utcDate.getTime() + pickerOffset * 60000)

        return pickerDate
    }
   // ...
             

                <DatePicker
                    value={this.shiftTzDateToPickerDate(this.props.start)}
                    onChange={(ev, date) => this.setStartDate(this.shiftPickerDateToTZDate(date))}
                    // ...
                />

For all y'all that struggle get confuzzled with timezone math like me.

Had to make a few edits. There were some cases where a Date function was used on a moment object and vice-versa. Once those were fixed, it worked great!! Thanks!!

import moment from "moment-timezone";

export const shiftPickerDateToTimezoneDate = (pickerDate, timezone) => {
  let pickerOffset = moment(pickerDate).utcOffset();
  let utcDate = new Date();
  utcDate.setTime(moment(pickerDate).valueOf() + pickerOffset * 60000);

  let tzOffset = moment.tz(pickerDate, timezone).utcOffset();
  let tzDate = new Date();
  tzDate.setTime(utcDate.getTime() - tzOffset * 60000);
  
  return tzDate;
};

export const shiftTimezoneDateToPickerDate = (tzDate, timezone) => {
  let tzUtcOffset = moment.tz(tzDate, timezone).utcOffset();
  let utcDate = new Date();
  utcDate.setTime(moment(tzDate).valueOf() + tzUtcOffset * 60000);

  let pickerDate = new Date();
  let pickerOffset = pickerDate.getTimezoneOffset();
  pickerDate.setTime(utcDate.getTime() + pickerOffset * 60000);

  return pickerDate;
};

@deeptikanta
Copy link

@mbrookes
I am sending data as Tue Aug 20 2019 10:56:00 GMT-0700 (Pacific Daylight Time) but it is displaying
Mon Aug 19 2019.Any help would be appreciated.
thank you

@mbrookes
Copy link
Member

@deeptikanta v0 is no longer supported.

@mui mui locked as off-topic and limited conversation to collaborators Aug 20, 2019
@mbrookes
Copy link
Member

Not strictly off-topic, but GitHub doesn't have a better choice for locking out-of date issues.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
component: date picker This is the name of the generic UI component, not the React module! component: time picker This is the name of the generic UI component, not the React module! v0.x
Projects
None yet
Development

No branches or pull requests