31

I'm using the react-datepicker to let user select a date. However, right now it uses local time (PDT), but I want to hardcode it to use a specific timezone (PST).

I tried using utcOffset prop but it doesn't seem to be doing anything. Does anyone know how to accomplish this?

0

10 Answers 10

7

For my part I set the defaultTimezone before rendering the React-dates plugin. React-dates will just use the default timezone.

moment.tz.setDefault('America/Los_Angeles');
Sign up to request clarification or add additional context in comments.

2 Comments

Make sure to use import moment from 'moment-timzeone rather than just import moment from 'moment'. I made this mistake and was very confused why it wasn't working.
where to put this moment.tz.setDefault('America/Los_Angeles') I put in my DateInput Component, but it's not working..
6

This works for me:

import React, { ComponentProps } from "react"
import DatePicker from "react-datepicker"
import moment from "moment"

interface Props {
  timezone: string
}

const DatePickerWithTimezone = ({
  selected,
  onChange,
  timezone,
  ...props
}: Props & ComponentProps<typeof DatePicker>) => (
  <DatePicker
    selected={selected ? setLocalZone(selected, timezone) : null}
    onChange={(v, e) => {
      onChange(v ? setOtherZone(v, timezone) : null, e)
    }}
    {...props}
  />
)

const setLocalZone = (date: Date, timezone: string) => {
  const dateWithoutZone = moment
    .tz(date, timezone)
    .format("YYYY-MM-DDTHH:mm:ss.SSS")
  const localZone = moment(dateWithoutZone).format("Z")
  const dateWithLocalZone = [dateWithoutZone, localZone].join("")

  return new Date(dateWithLocalZone)
}

const setOtherZone = (date: Date, timezone: string) => {
  const dateWithoutZone = moment(date).format("YYYY-MM-DDTHH:mm:ss.SSS")
  const otherZone = moment.tz(date, timezone).format("Z")
  const dateWithOtherZone = [dateWithoutZone, otherZone].join("")

  return new Date(dateWithOtherZone)
}

export default DatePickerWithTimezone

1 Comment

Nice! It gets a bit simpler if you use the date-fns-timezone library which provides convertToLocalTime(date, { timeZone: timezone }) and convertToTimeZone(date, { timeZone: timezone }) which work essentially the same way that your setLocalZone and setOtherZone functions do.
5

since datepicker doesn't use moment.js anymore i tried to implement a hacky solution for this issue, assuming the initial value is a string for instance:

export const formatUTC = (dateInt, addOffset = false) => {
    let date = (!dateInt || dateInt.length < 1) ? new Date : new Date(dateInt);
    if (typeof dateInt === "string") {
        return date;
    } else {
        const offset = addOffset ? date.getTimezoneOffset() : -(date.getTimezoneOffset());
        const offsetDate = new Date();
        offsetDate.setTime(date.getTime() + offset * 60000)
        return offsetDate;
    }
}

inside date i call the formatter like this:

selected={formatUTC(this.props.input.value,true)}
                    onChange={date => formatUTC(date)}

1 Comment

this works for me. should have marked as correct
1

I've been thru this, If you decided that you want to just ignore your local offset then you can hardcode the zone. Observation just to give a complete answer: PST will always be -08:00, but if you want for example pacific time, right now is -07:00, in this case, you may want to install 'moment.timezone' then import moment from 'moment-timezone' and just get the current offset with moment.tz('US/Pacific').format('Z')

The code in typescript (I can change it to Javascript if you want):

interface ICalendarInputProps {
  handleChange: (newDate: moment.Moment) => void;
}

const CalendarInput = ({ handleChange }: ICalendarInputProps) => {
  const onChange = (date: Date) => {
    handleChange(moment(`${moment(date).format('YYYY-MM-DDThh:mm:ss')}-08:00`));
    // This is to get the offset from a timezone: handleChange(moment(`${moment(date).format('YYYY-MM-DDThh:mm:ss')}${moment.tz('US/Pacific').format('Z')}`));
  };

  return (<DatePicker onChange={onChange} />);
};

export default CalendarInput;

1 Comment

Just a note.. rather than hh - use HH (24hr time) in the format call - otherwise you will always return time as am. ${moment(date).format('YYYY-MM-DDTHH:mm:ss')}-08:00)
1

This component outputs Date objects set to midnight local-time at the start of the chosen day. This is a problem. If there is a way of configuring this behaviour, I haven't found it.

The only way to stay sane when dealing with dates is to make sure that your dates are always midnight UTC at the start of the date in question. To get this behaviour from react-datepicker, the only thing I've found is to subtract the timezone offset from the output...

interface IProps {
  value: any
  setValue: (value: Date) => void
}

const DayPicker: FC<IProps> = ({ value, setValue, placeholderText = "", minDate = new Date() }) => {
  function correctToUtcThenSet(val: Date) {
    setValue(new Date(val.getTime() - val.getTimezoneOffset() * 60000))
  }
  return <DatePicker
    onChange={correctToUtcThenSet}
    selected={value}
  />
}

1 Comment

sucks this is the way everyone is doing this, Its really the only solution ive found aswell. Feels so hacky and wrong. But if it works it works
0

Other answers didn't work as I'd hoped, and sometimes the dates were off by 1 day because of time zone differences.

This is what I needed:

import DatePicker from 'react-datepicker';

import 'react-datepicker/dist/react-datepicker.css';
import { getEndOfDayUtc, treatLocalDateAsUtcMidnight, treatUtcMidnightAsLocalDate } from '../../../shared/helpers/datetime';

type DatePickerUtcProps = {
  selected: Date | string;
  onChange: any;
  isEndOfDay: boolean;
};

function getSelectedAsLocal(selected: Date | string): Date {
  const selectedDate = typeof selected === 'string' ? new Date(selected) : selected;

  return treatUtcMidnightAsLocalDate(selectedDate);
}

export function DatePickerUtc({ selected, onChange, isEndOfDay, ...datePickerProps }: DatePickerUtcProps): JSX.Element {
  function onChangeAsUtc(local: Date) {
    const utc = treatLocalDateAsUtcMidnight(local);
    const adjusted = isEndOfDay ? getEndOfDayUtc(utc) : utc;
    console.log('onChangeAsUtc', { local, utc, adjusted, isEndOfDay });
    onChange(adjusted);
  }

  return <DatePicker onChange={onChangeAsUtc} selected={getSelectedAsLocal(selected)} {...datePickerProps} />;
}
export function treatLocalDateAsUtcMidnight(localDate: Date): Date {
  const moment = dayjs(localDate).tz('UTC', true); // https://day.js.org/docs/en/plugin/timezone
  const utcMidnight = getStartOfDayUtc(moment.toDate());
  console.log({ localDate, utcMidnight });
  return utcMidnight;
}

export function treatUtcMidnightAsLocalDate(utcMidnight: Date): Date {
  const sliceOfJustTheDatePart = utcMidnight.toISOString().slice(0, 10);
  const localDate = dayjs(sliceOfJustTheDatePart).toDate();

  console.log({ localDate, sliceOfJustTheDatePart, utcMidnight });
  return localDate;
}
From: <DatePickerUtc selected={startDate} onChange={(utcDate: Date) => setStartDate(utcDate)} {...datePickerProps} />
To: <DatePickerUtc selected={endDate} onChange={(utcDate: Date) => setEndDate(utcDate)} {...datePickerPropsEndOfDay} />

Comments

0

I use dayjs and it worked well for me:

import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";

// Initialize dayjs timezone plugin
dayjs.extend(timezone);

interface DatePickerProps {
  date: string | undefined;
  setDate: (date: string | undefined) => void;
}

// Handler for date/time changes
const handleDateTimeChange = React.useCallback(
  (selectedDateTime: Date | null) => {
    const isoFormattedDateTime = selectedDateTime
      ? dayjs(selectedDateTime).format("YYYY-MM-DDTHH:mm:ss.SSS[Z]")
      : undefined;
    setDate(isoFormattedDateTime);
  },
  [setDate]
);

// Convert ISO datetime to local datetime
const convertToLocalDateTime = (isoDateTime: string | undefined): Date | null => {
  if (!isoDateTime) return null;

  // Get browser's timezone offset in seconds
  const browserTimezoneOffset = new Date().getTimezoneOffset() * 60;
  
  // Get user's timezone offset in format like "+01:00"
  const userTimezoneOffset = dayjs()
    .tz(Intl.DateTimeFormat().resolvedOptions().timeZone)
    .format("Z");

  // Convert ISO datetime to local time
  const timestampInSeconds = dayjs(isoDateTime).unix();
  const localTimestampInSeconds = timestampInSeconds + browserTimezoneOffset;

  // Format the local datetime with the user's timezone offset
  const localIsoDateTime = dayjs
    .unix(localTimestampInSeconds)
    .format(`YYYY-MM-DDTHH:mm:ss.SSS[${userTimezoneOffset}]`);

  return dayjs(localIsoDateTime).toDate();
};

// Usage in component
<ReactDatePicker
  selected={convertToLocalDateTime(date)}
  onChange={handleDateTimeChange}
/>

Comments

-1

I also didn't have luck with utcOffset. You could use moment-timezone in your project instead of moment and convert it yourself:

import moment from "moment-timezone";

onDateChange = date => {
  console.log("as is:", date.format("YYYY-MM-DD HH:mm:ss"));
  console.log("PST:", moment(date).tz("America/Los_Angeles").format("YYYY-MM-DD HH:mm:ss"));
};

Sandbox: https://codesandbox.io/s/y2vwj9mwpz

Comments

-1
{isStartPickerVisible && (
  <DateTimePicker
    value={startDate ? moment(startDate).toDate() : new Date()}
    mode="date"
    display={Platform.OS === 'ios' ? 'spinner' : 'default'}
    onChange={(event, selectedDate) => {
      setStartPickerVisible(false);
      if (selectedDate) {
        setStartDate(moment(selectedDate).startOf('day').toISOString());
      }
    }}
  />
)}

{isEndPickerVisible && (
  <DateTimePicker
    value={endDate ? moment(endDate).toDate() : new Date()}
    mode="date"
    display={Platform.OS === 'ios' ? 'spinner' : 'default'}
    onChange={(event, selectedDate) => {
      setEndPickerVisible(false);
      if (selectedDate) {
        setEndDate(moment(selectedDate).startOf('day').toISOString());
      }
    }}
  />
)}

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
-4

Since you're using moment.js, you can try using moment.utc() and subtract hours to pst timezone.

moment.utc().startOf('day').subtract(8, 'hours')

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.