Hacker0x01 / react-datepicker

A simple and reusable datepicker component for React
https://reactdatepicker.com/
MIT License
7.96k stars 2.23k forks source link

Date Selected is One Day Off #1018

Open YOO629 opened 7 years ago

YOO629 commented 7 years ago

Hi, I'm having an interesting issue with my usage of the datepicker component.

My code is pretty basic, it looks like this:

<ReactDatePicker
  onChange={(val) => this.handleChange(val)}
  selected={this.state.currentDate}
/>

and my handler fn

  handleChange(momentDate) {
    console.log(momentDate.toISOString())
    this.setState({currentDate: momentDate});
  }

The issue that I see is that when I select a date from the dropdown, the visual aspects of the component appear to be working fine -- I select 09/10 (sept 10th) and that's what I see in the input field.

However, in my console.log() in my handler function, I see that when I select 09/10, the output is 2017-09-09T21:00:00.000Z. momentDate.toISOString() is ultimately what I'm sending to the DB to be saved, so it's problematic that it's one day off.

I'm not sure what to make of this. I was expecting to see 2017-09-10T21:00:00.000Z. It looks like momentDate is localized, so if I'm in a UTC + 3 timezone and choose 09/10 00:00:00, I'm actually choosing 09/09 21:00:00 (my hypothesis). Even if I set the utcOffset prop to 0 it looks like the localization is still happening.

Am I doing something wrong? Is there a way for me to just kind of...turn off the localization? I'm using the component purely to just pick a day in the calendar. I don't care what timezone they're in, if they pick 09/10, they pick 09/10.

Thanks!

nwkeeley commented 6 years ago

Also experiencing this issue.

I open it up, select date its off by 1 day. I then open it up and select any other subsequent day and its correct.

xarxziux commented 6 years ago

I've run into this problem too. This seems to appear in release 0.44.0. From my tests, 0.43.0 behaves correctly and reports the date selected. Release 0.44.0 reports the day prior to the day selected.

From reading through the commits, it's likely that the problem is somewhere in commit a95cd86, but I've not been able to identify the exact cause.

xarxziux commented 6 years ago

Scrap that last comment. Release 0.43.0 worked fine on a test project, but it's now showing the same error on my main project. The hunt continues...

xarxziux commented 6 years ago

From further tests, I can actually turn this bug on in a test project using release 0.41.0 just by setting the startDate to null in the constructor function. Setting it to moment() turns the bug off again.

Release 0.40.0 doesn't seem to have this problem.

romanveryovkin commented 6 years ago

The same issue for me as well. Do anybody have some workarounds?

RobMaple commented 6 years ago

I've just run into a similar sounding issue to this although on closer inspection it seems to be relating to when daylight's savings time ends at the end of October. Selecting a date after this point initially causes the date picker to be one day behind, selecting a date a second time seems to fix this. The 'fix' seems to relate to the moment object having no utc offset the second time - why this is though I'm not sure.

To get around this i'm currently setting the 'utcOffset' prop on the datpicker component to 0 -- this prevents the initial selection from shaowing as the day previous. I'm wondering if this is actually a bug with react-datepicker though or more to do with how dates are handled and manipulated within my app.

aij commented 6 years ago

@RobMaple Are you able to reproduce it with the examples at https://hacker0x01.github.io/react-datepicker/ ?

DST ends on Nov 5 for me, but selecting days before and after seems to be working. (I tried clearing and not clearing at various points, but it didn't seem to make a difference.)

RobMaple commented 6 years ago

@aij The example worked fine although when I updated the version of react-datepicker to 0.55 (the same as in my project) the issue returned. Just updated it to 0.56 however and all looks to be working correctly again 😀

aniruddhashevle commented 6 years ago

This issue is there in v0.58!

mwickett commented 6 years ago

We're seeing this issue on 0.61 as well. With some testing, it looks like it only happens in a GMT negative timezone (WEST of GMT). Setting my machine's timezone to a positive value returns a proper date.

Setting the utcOffset prop to 0 doesn't change anything.

swellmar commented 6 years ago

@mwickett @RobMaple Where do you find this utcOffset prob? I can find it on 100 different places

tastypackets commented 6 years ago

Using v0.39 it corrects after selecting a second date. For example select 12/20/17 and it will be 12/19/17, then select 12/22/17 and it will be 12/22/17.

Initializing it with a date like @xarxziux mentioned does work.

Edit:

I just undated to v0.64 and it is now working properly even with defaulting to null.

guyzmo commented 6 years ago

I'm having that exact same bug, and I just understood what's wrong after chasing my own tail for hours. As I'm only interested in dates, in the date selector, the handled value is a Date object with a time set to midnight.

As the selection of the date is in current local time (I'm in CET time), and the storage is in UTC, local time gets shifted by one hour backward when stored. That makes selection of 2018-02-20 with a default to midnight getting stored as 2018-02-19T23:00:00.000Z.

And because in my code I was doing a Q&D conversion from Datetime to string by splitting over T, I was setting the value back to the wrong date.

Solution, in my case? use momentsjs e-ve-ry-whe-re!

azza85 commented 6 years ago

I had the same issue,, with the date selected going back a day. I fixed the issue for me by adding

selected={null}

The reason I had it blank was if I had it set as

moment()

it was displaying today rather than my placeholder text

alexjjseppala commented 6 years ago

The dates in my app's database were in UTC time with the ISOString format with a "Z" at the end (ie: 2011-10-05T14:48:00.000Z) and I was converting them to Moments as such: Moment("2011-10-05T14:48:00.000Z") but this was using my local UTC offset, causing them to appear one day off.

Creating my dates like this: Moment.utc("2011-10-05T14:48:00.000Z") fixed my issue

ref: https://maggiepint.com/2016/05/14/moment-js-shows-the-wrong-date/

omerdn1 commented 6 years ago

Still having this issue in 1.4.1, couldn't find a proper solution

jimmytb commented 6 years ago

Having the same problem

evolve2k commented 6 years ago

Edit: 17 October - Workaround helps a bit but doesn't solve the issue - see https://github.com/Hacker0x01/react-datepicker/issues/1018#issuecomment-430493313

Workaround

Explicitly add a utcOffset paramater and set it to zero.

It seem the system uses your local utfOffset by default causing a difference in time.

The 'fix' seems to relate to the moment object having no utc offset the second time but using your local system offset the first time.

Example applying the workaround:

<DatePicker
    utcOffset={0}
    dateFormat="DD-MMM HH:mm"
    todayButton="Today in Puerto Rico"
    onChange={this.handleChange}
/>

https://reactdatepicker.com/#example-12

This worked for me. Thanks to @RobMaple

Definence commented 6 years ago

I have the same problem!!

olanMbakop commented 5 years ago

The problem still exists in ^1.6.0

evolve2k commented 5 years ago

Updated since I listed my 'Workaround' post on 7 June, seems it's working only when the local time is on the same day as UTC. For me in Australia, this means that half the day it works and the other half of the day it's off-by-one. This is when the user has the datepicker control open and selects a date with the mouse the first time (eg when the date is empty).

Once there is a date present the control works correctly for future selections of dates.

klijakub commented 5 years ago

I have the same issue

fathurhidayat3 commented 5 years ago

Still exists and mine has 7 hours behind. I realize utcOffset props was removed in #1527 #1581 . I tried to set locale using registerLocale, setDefaultLocale and locale props to "id" but nothing happened.

jesseseligman commented 5 years ago

Still having this issue...

jesseseligman commented 5 years ago

Wrapping the string in a moment object and then calling .toDate() seemed to fix this for me. Something like: selected={moment("2019-02-07").toDate()}

madhukosuri commented 5 years ago

Still having this issue...

cturkdogan commented 5 years ago

I had this issue when I used the dateFormat as "yyyy/MM/dd" When I changed it as below the issue is resolved; showTimeSelect timeFormat="HH:mm" dateFormat="yyyy/MM/dd HH:mm"

tdubrova commented 5 years ago

@cturkdogan thank you, it helps :)

guillecro commented 5 years ago

I am still having this problem, yeah it has something to do with the UTC offset

shahab65 commented 5 years ago

I had the exact same issue. the first time was one day off the next times it worked correctly. but it wasn't anything wrong with react-datepicker. the problem was in my code when I was trying to get date part this way:

  setSelectedDateInForm = (setFieldValue, name, date) => 
    const onDayOff =date.toISOString().split('T')[0];//wrong
    setFieldValue(name, onDayOff);
  };

setFieldValue is from formik library. don't worry about it. I solved my problem by using the format method from the date-fns library this way:

import {format} from 'date-fns'

  setSelectedDateInForm = (setFieldValue, name, date) => {
    const myDate = format(date,"YYYY-MM-DD").split('T')[0];//right
    setFieldValue(name, myDate);
  };
JivkoJelev91 commented 4 years ago

moment(myDate).toISOString(true) Only this works for me :(

iloveip commented 4 years ago

I had the same issue and I fixed it by adding this to my constructor:

const date = new Date();
const isoDate = new Date(date.getTime() - (date.getTimezoneOffset() * 60000));
const [startDate, setStartDate] = useState(isoDate);

And in my component I have:

<DatePicker
  ...
  selected={startDate}
  onChange={value => {
    setStartDate(value)
  }}
/>

See this SO answer for more information.

sachinkaria commented 4 years ago

still having this issue, were there any updates without setting an initial date?

evolve2k commented 4 years ago

Hi folks, ok this one has been open for over 2 years; I'm seeing a couple of ways forward. With 140+ open issues and a single maintainer on the project, its fair to say the poor dev is likely overloaded. I think we're going to need to help this along ourselves. (Yes I'm asking you who's reading this now :P).

Plan

  1. We ask @martijnrusschen very nicely to please give this issue some attention, its an off by one error afterall.. so hard hard can it be? [Probably actually annoyingly hard].

  2. We attempt some groundwork ourselves to help solve this. Starting with reviewing the tests and creating a failing test/tests that isolate the issue.

Anyone willing to have a poke around and report back on what they find?

There are alot of tests, I had a quick look but couldn't narrow down which file to start in. Any ideas?

FatehAK commented 4 years ago

This works: const offsetDate = new Date(selected.getTime() - (selected.getTimezoneOffset() * 60000));

Just put it in onChange() handler

Ayobamigit commented 4 years ago

Hi everyone, Please is there any way to resolve this without setting an initial value?

Chococoin commented 4 years ago

I didn't understand the given solution of @FatehAk, so I've thrived by myself until finding this workaround.

I've got two datePicker component in the definition of my form component <DatePicker name="dob" placeholderText="Date of Birth" dateFormat="dd/MM/yyyy" selected={this.state.dob} onChange={(date) => this.handleChange({ target: { name: "dob", value: date }})} />

and

<DatePicker name="expire_date" placeholderText="Expiration Date" dateFormat="dd/MM/yyyy" selected={this.state.dob} onChange={(date) => this.handleChange({ target: { name: "expire_date", value: date }})} />

In my handlerChange I've tweak like this.

handleChange(event) { let fieldName = event.target.name; let fieldVal = event.target.value; if(fieldName === "dob" || fieldName === "expire_date") { .....fieldVal = new Date(fieldVal.setHours(fieldVal.getHours() - fieldVal.getTimezoneOffset() / 15))); } this.setState({ [fieldName]: fieldVal }); }

Then I realized that it is almost the same workaround.

Why "getTimezoneOffset() / 15" instead of "getTimezoneOffset() / 60"?

Because "In a region where there is no summer time and time zone policy hardly changes, you can partially implement it using getTimezoneOffset() to convert the data. ", hence using 60 / 4 give me more margin.

pelagaggi commented 4 years ago

This works: const offsetDate = new Date(selected.getTime() - (selected.getTimezoneOffset() * 60000));

Just put it in onChange() handler

It worked here

<DatePicker
...
onChange={
    (selected) =>{
     let AdjusteddateValue= (new Date(selected.getTime() - (selected.getTimezoneOffset() * 60000)));
  }}
/>
mustafa-taheri commented 4 years ago

This works: const offsetDate = new Date(selected.getTime() - (selected.getTimezoneOffset() * 60000));

Just put it in onChange() handler

Thanx, It worked

yogendrajs commented 3 years ago

This works: const offsetDate = new Date(selected.getTime() - (selected.getTimezoneOffset() * 60000));

Just put it in onChange() handler

Still having the issue with this

tonypangs commented 3 years ago

Still having this issue on Nov 2020

christianalares commented 3 years ago

Still having this issue on Nov 2020

Same here!

mildavw commented 3 years ago

Thanks to the super-awesome documentation page, this bug appears easy to repro:

  1. Set the timezone on your machine to something west of GMT
  2. Visit https://reactdatepicker.com/#example-custom-date-format
  3. Change line 2 to const [startDate, setStartDate] = useState(new Date('2020-12-25'));
  4. See the date displayed is 2020/12/24

Added*

This is not exactly the same as the first selected date being 1 day off, but I'm guessing it's related.

Also, note that if you're setting an initial date as above, two workarounds are to use Date(2020,12,25) or Date('2020-12-25T00:00')

sam-amistoso commented 3 years ago

I hope it will be fixed.

So far this is the workaround I applied for using the moment time-zone. <DatePicker ... selected={(value)=>new Date(moment.tz(value, 'SPECIFIED TIME ZONE'))} onChange={(selected) => new Date(moment.tz(selected, 'SPECIFIED TIME ZONE'));} />

samuelcastro commented 3 years ago

I'm having the exactly same issue.

danielsequeira commented 3 years ago

I'm having the same issue, specially for timezones east of GMT, the solutions proposed above don't solve it.

ajGingrich commented 3 years ago

I'm having a similar issue but with regards to maxDate. I want the maxDate to be the 5 of January for example but if I set maxDate={new Date('2021-01-05')} the actual Date object has a date of Jan 4th in my time zone so I'm one day off.

The calendar should really be in UTC by default

mildavw commented 3 years ago

I thought one of the workarounds above solved it, but I had a few users using iPhones report that they were still seeing it. Here's a sample component that appears to work on all devices, east and west of the prime meridian. It receives and supplies a date in the YYYY-MM-DD format. In practice I make the input type='hidden'.

function AttendanceDatePicker({date}) {
  const [y,m,d] = date.split('-')
  const initialDate = new Date(y,m-1,d)

  const [selectedDate, setSelectedDate] = useState(initialDate)

  const formatDate = (date) => {
    if (!date) {return ''}

    const [y,m,d] = [date.getFullYear(), date.getMonth(), date.getDate()]
    return(`${y}-${m+1}-${d}`)
  }

  return(
    <>
      <DatePicker
        selected={selectedDate}
        onChange={setSelectedDate}
        inline
      />
      <input name='date' value={formatDate(selectedDate)}/>
    </>
  )
}

export default AttendanceDatePicker
swilcox21 commented 3 years ago

I BELIEVE THE ISSUE IS TIME ZONE RELATED gmt is the standard time zone used which is 5 hours ahead so if its past 7pm it will show the next day

omarsherifincorta commented 3 years ago

I solved the problem when I took into consideration the time zone.

My time zone is GMT+2 so when I used 2021-04-17T23:00:00.000Z, it rendered as April 18th. But when I put 2021-04-17T16:00:00.000Z, it rendered correctly April 17th.