Date picker: timezone shift

tmt picture tmt · Jul 5, 2017 · Viewed 11.2k times · Source

Problem

If I select a date in a <DateInput>, the datetime is in the user's timezone (e.g. 2017-07-01T00:00:00.000+02:00) but when it's sent to the server, it's transformed to UTC and thus ends up as 2017-06-30T22:00:00.000Z, one day behind.

The underlaying back-end is unaware of the user's timezone and cannot shift the dates back so after stripping the time it ends up with wrong date.

What would solve my problem

Any of these options would work fine:

  1. The datetime is sent with the user's timezone.
  2. The datetime is sent as naive (=without timezone).
  3. The date selected by the user is already treated as UTC.
  4. A date only (2017-07-01) instead of the ISO datetime is sent.

What I tried

  • I looked at Admin-on-rest's DateInput documentation and haven't found any option to alter the behaviour.
  • I looked at the related Material-UI's documentation and the only somewhat relevant option appears to be DateTimeFormat but despite a couple of tries I got nowhere.
  • I checked other answers, e.g Material UI Time Picker UTC but cannot figure out how to apply the suggested solution.

Answer

kunal pareek picture kunal pareek · Jul 5, 2017

Material UI date and timepickers return the local values. You can write a function in their onChange function to reformat the date to any value.

  handleTimeInput = (event, time) => {
    this.setState({
      time: time
    })
  }

You can then reset the time you send to server by using a simple function like the one below to convert local time to UTC

export default (date) => {
  return new Date(date.getTime() + date.getTimezoneOffset()*60000)
}

(for displaying)

export default (date) => {
  const tempDate = new Date(date)
  return new Date(tempDate.getTime() - tempDate.getTimezoneOffset()*60000)
}

This is the entire component code for my comp.

class DateTimePicker extends Component {
  constructor(props) {
    super(props);
    this.state = {
      date: null,
      time: null
    };
  }
  handleDateInput = (event, date) => {
    this.setState({
      date: date
    })
  }
  handleTimeInput = (event, time) => {
    this.setState({
      time: time
    })
  }
  componentDidUpdate(prevProps, prevState) {
    const {setSchedule, channel} = this.props
    if (prevState.date !== this.state.date || prevState.time !== this.state.time) {
      if (this.state.date && this.state.time) {
        setSchedule(convertISTtoUTC(concatenateDateAndTime(this.state.date, this.state.time)), channel)
      }
    }
  }
  render() {
    return (
      <div >
          <DatePicker autoOk={true} onChange={this.handleDateInput} />
          <TimePicker autoOk={true} pedantic={true} onChange={this.handleTimeInput} />
      </div>
    )
  }
}

With this you have 2 options.

1) you can hook this to redux directly and shoot actions when both are selected.

https://marmelab.com/admin-on-rest/Actions.html

2) Write your own Input component

https://marmelab.com/admin-on-rest/Inputs.html#writing-your-own-input-component