Выбор даты: сдвиг часового пояса

Проблема

Если я выбираю дату в <DateInput>, дата и время находятся в часовом поясе пользователя (например, 2017-07-01T00:00:00.000+02:00), но когда они отправляются на сервер, они преобразуются в UTC и, таким образом, заканчивается как 2017-06-30T22:00:00.000Z, на один день позже.

Базовая серверная часть не знает о часовом поясе пользователя и не может сдвинуть даты назад, поэтому после удаления времени получается неправильная дата.

Что бы решить мою проблему

Любой из этих вариантов будет работать нормально:

  1. Datetime отправляется с часовым поясом пользователя.
  2. Дата и время отправляются как наивные (= без часового пояса).
  3. Дата, выбранная пользователем, уже считается UTC.
  4. Отправляется только дата (2017-07-01) вместо даты и времени в формате ISO.

Что я пробовал

  • Я просмотрел DateInput документацию Admin-on-rest и не нашел любой вариант изменить поведение.
  • Я просмотрел соответствующую документацию Material-UI и единственный подходящий вариант кажется DateTimeFormat, но, несмотря на пару попыток, я ничего не добился.
  • Я проверил другие ответы, например Material UI Time Picker UTC, но не могу понять, как применить предлагаемое решение.

person tmt    schedule 05.07.2017    source источник


Ответы (4)


Дата и время пользовательского интерфейса материала возвращают локальные значения. Вы можете написать функцию в их функции onChange, чтобы переформатировать дату в любое значение.

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

Затем вы можете сбросить время, которое вы отправляете на сервер, используя простую функцию, подобную приведенной ниже, для преобразования местного времени в UTC.

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

(для показа)

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

Это весь код компонента для моего компа.

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>
    )
  }
}

При этом у вас есть 2 варианта.

1) вы можете напрямую подключить это к редуксу и снимать действия, когда оба выбраны.

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

2) Напишите свой собственный компонент ввода

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

person kunal pareek    schedule 05.07.2017
comment
Спасибо за ваш вклад. Это похоже на движение в правильном направлении. Однако я не понимаю, как применить это преобразование в Admin-on-rest. Должен признать, что это совсем другая сфера деятельности, чем я обычно занимаюсь. - person tmt; 05.07.2017
comment
Редактирование моего ответа - person kunal pareek; 05.07.2017
comment
В конце концов я остановился на другом подходе, но еще раз спасибо за ваши ценные предложения. - person tmt; 06.07.2017

Самый простой вариант — установить пользовательскую функцию в качестве атрибута parse атрибута <DateInput>, которая позволяет манипулировать значением перед входом в хранилище Redux.

const dateString = v => {
  if (isNaN(v)) return;

  let parsedDate = new Date(v);
  let adjustedDate = new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000);

  return adjustedDate;
};

<DateInput source="your_date" parse={dateString} />

Это описано в Admin-on-rest документацию, а дополнительную информацию можно найти в документации по форме Redux на странице Крючки жизненного цикла ценности.

ПРИМЕЧАНИЕ: кредит принадлежит kunal pareek за его предложение о том, как исправить смещение часового пояса. в другом расширенном ответе.

ОБНОВЛЕНИЕ: это может работать неправильно в текущей версии (по состоянию на 08/2018) службы Admin-on-rest. См. ответ от Томаша Мадейского и/или проверьте документацию, в которой проблема была .

person tmt    schedule 06.07.2017
comment
Это почти работает, проверьте мой ответ здесь - person Tomasz Madeyski; 03.08.2018

Что ж, поскольку редактирование не должно изменять намерение автора, и это слишком долго для редактирования, я добавляю свой собственный ответ на основе tmt ответ:

Вариант использования части parse в порядке, но сама функция неисправна (см. мои комментарии в коде, чтобы понять, почему):

const dateString = v => {
  /* 
  * since v parameter coming is a string then you cannot use isNan 
  * undefined is also coming as a parameter so !v is good equivalent
  */
  if (!v) return; 

  let parsedDate = new Date(v);
  let adjustedDate = new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000);

  /*
  * since parameter coming in is a string you need to pass string further, that's why I added toISOString() call
  */
  return adjustedDate.toISOString();
};

<DateInput source="your_date" parse={dateString} />
person Tomasz Madeyski    schedule 03.08.2018
comment
Судя по всему, произошли изменения, и проблема была решена в Admin-on-rest. Теперь они предлагают собственное решение: marmelab.com/admin-on-rest/Inputs. .html#датаввод - person tmt; 03.08.2018

Просто установите момент для определенного часового пояса и инициализируйте средство выбора пользовательского интерфейса материала с помощью момента, и да-да.. это сработает..

import moment from 'moment-timezone';
import MomentUtils from '@date-io/moment';
import {DatePicker, MuiPickersUtilsProvider} from '@material-ui/pickers';

useEffect(()=>{
    moment.tz.setDefault('Asia/Kolkata');
},[])

<MuiPickersUtilsProvider libInstance={moment} utils={MomentUtils}>
               <DatePicker
                  variant='inline'
                  autoOk
                  label=''
                  disableToolbar
                  disableFuture
                  format='MM/DD/YYYY'
                  value={selectedDay}
                  onChange={(day) => {
                    const date = moment(day).format('MM/DD/YYYY');
                    setCurrentDay(date);
                    handleDayChange(day);
                  }}
                  InputProps={{
                    disableUnderline: true,
                    style: {
                      backgroundColor: 'white',
                      paddingLeft: 10,
                      borderRadius: 5,
                      width: 160,
                    },
                    endAdornment: (
                      <InputAdornment position='end'>
                        <CalendarTodayIcon style={{paddingRight: 8}} />
                      </InputAdornment>
                    ),
                  }}
                />
              </MuiPickersUtilsProvider>
person Saquib Ajaz    schedule 18.06.2021