4

I am using the react DateTimePicker widget and want to store date in ISO irregardless of the timezone and DST. The current setup of the localisation is en-GB.

I have tried suggestions from the following posts and gone through the documentation of moment.js Converting date UTC gives wrong date

moment.utc('Wed Sep 08 1999 00:00:00 GMT+0100').toJSON()

But the end result was but the result was 1999-09-07T23:00:00.000Z thus not storing the correct input date.

I am now formatting the date with the following formatDate function however when I changed my timezone on my machine to Chile, the date 1999-09-07 GMT-0200 becomes 1999-09-08T00:00:00.000Z.

const formatDate = (v) => {
  console.log(v)
  const date = moment(v).format('YYYY-MM-DD')
  console.log(moment.utc(date).toJSON())
  return moment.utc(date).toJSON()
}

Is there something that I am missing?

Yuvi
  • 528
  • 8
  • 18
  • Always provide formatting string when constructing moment objects. This will significantly decrease errors. – alpakyol Jul 18 '19 at 13:25
  • "1999-09-07T23:00:00.000Z" is the UTC equivalent of "Wed Sep 08 1999 00:00:00 GMT+0100", why do you think it's wrong (and 1999-09-07 GMT-0200 is 1999-09-08T00:00:00.000Z)? Perhaps you mean you want ISO 8601 with the local timezone? – RobG Jul 18 '19 at 22:31
  • The default formatting for moment.js is ISO 8601 with local timezone, so `moment('Wed Sep 08 1999 00:00:00 GMT+0100').format()` produces a string like "1999-09-08T00:00:00+0100" if the host is in timezone +0100. – RobG Jul 18 '19 at 22:55
  • If you want to format a date in a specific timezone, see [*Format date in a specific timezone*](https://stackoverflow.com/questions/15347589/format-date-in-a-specific-timezone). – RobG Jul 18 '19 at 23:05
  • @RobG yes, you are correct with the second statement. I have now similiar approach as you have stated on the last point. Using the moment().utcOffset() to store the correct value of local time in utc – Yuvi Jul 19 '19 at 11:03

1 Answers1

0

I tried different workarounds of this issue but at the end most workable variant I created you can find below.

The main idea of this variant is to show the wanted TZ inside the input field, but display local TZ inside the calendar and timeList.

I would be grateful for your feedback.

Also see: https://github.com/jquense/react-widgets/issues/467 and https://github.com/jquense/react-widgets/issues/515

enter image description here

enter image description here

import React, { useState, useMemo, useCallback } from 'react';
import moment from 'moment-timezone';
import DateTimePickerOrigin from 'react-widgets/lib/DateTimePicker';

const monthShortNames = [
  'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
];

const dayShortNames = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];

const DateTimePicker = (props) => {
  const {
    tz, // <-- tz you want to display, eg 'Etc/UTC'
    min,
    max,
    value,
    onChange,
    ...tail
  } = props;

  const [localTz] = useState(moment.tz.guess());

  const fixedParse = useCallback(
    (dateStr) => (dateStr ? new Date(dateStr) : null),
    [],
  );

  const fixedFormat = 'MMM D, YYYY HH:mm z';

  const fixedTimeFormat = useCallback(
    (date) => {
      if (localTz !== tz) {
        return `${moment.tz(date, localTz).format('HH:mm z')} (${moment.tz(date, tz).format('HH:mm z')})`;
      }
      return moment.tz(date, localTz).format('HH:mm');
    },
    [tz, localTz, fixedFormat],
  );

  const fixedDateFormat = useCallback(
    (date) => String(date.getDate()),
    [],
  );

  const fixedYearFormat = useCallback(
    (date) => String(date.getFullYear()),
    [],
  );

  const fixedMonthFormat = useCallback(
    (date) => monthShortNames[date.getMonth()],
    [],
  );

  const fixedDayFormat = useCallback(
    (date) => dayShortNames[date.getDay()],
    [],
  );

  const fixedMin = useMemo(
    () => (min === null ? undefined : min),
    [min],
  );

  const fixedMax = useMemo(
    () => (max === null ? undefined : max),
    [max],
  );

  return (
    <DateTimePickerOrigin
      {...tail}
      parse={fixedParse}
      format={fixedFormat}
      timeFormat={fixedTimeFormat}
      dateFormat={fixedDateFormat}
      yearFormat={fixedYearFormat}
      monthFormat={fixedMonthFormat}
      dayFormat={fixedDayFormat}
      min={fixedMin}
      max={fixedMax}
      value={value}
      onChange={onChange}
    />
  );
};