Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Day incorrect in datepicker #7167

Closed
aceleghin opened this issue Sep 19, 2017 · 46 comments · Fixed by #7858 or #11336
Closed

Day incorrect in datepicker #7167

aceleghin opened this issue Sep 19, 2017 · 46 comments · Fixed by #7858 or #11336
Assignees

Comments

@aceleghin
Copy link

aceleghin commented Sep 19, 2017

Bug, feature request, or proposal:

When I select a date I see the correct date in the field but, when I save, the datepicker send the day before the date I have selected

here's the code:
<md-form-field> <input mdInput [(ngModel)]="fareCalendar.startDate" name="startDate" [mdDatepicker]="picker" placeholder="startDate"> <md-datepicker-toggle mdSuffix [for]="picker"></md-datepicker-toggle> <md-datepicker #picker></md-datepicker> </md-form-field>

I am using angular 4 and @angular/material": "^2.0.0-beta.10
rtcnm

but the date:
f5z10

How can I send the UTC date

@julianobrasil
Copy link
Contributor

Could you try to reproduce it here, please: https://plnkr.co/edit/8nRKSJ?p=preview ?

@aceleghin
Copy link
Author

I just want that the date I have selected in calendar is already in UTC timezone

@julianobrasil
Copy link
Contributor

julianobrasil commented Sep 19, 2017

That's why I'm asking you to reproduce it in the plunk. For me it's working fine, but it's possible that some bug is happening in your case:

image

If it's just a matter of configs, stackovervlow.com is more suitable than this channel.

@julianobrasil
Copy link
Contributor

julianobrasil commented Sep 19, 2017

The calendar returns a javascript Date object. What you've shown in above message seems like something generated from other source (it looks like a JSON.stringify() result or maybe from a database service). To get back the Javascript Date object, you have to do (really not sure how did you get that result):

fareCalendar.startDate = new Date(fareCalendar.startDate);

@aceleghin
Copy link
Author

If I select th date: 6/9/2017 and print it I see:
Wed Sep 06 2017 00:00:00 GMT+0200

Then I save the date in a body const body = { startDate: this.fareCalendar.startDate };
And make post request.
I am using Http client

@julianobrasil
Copy link
Contributor

Oh, you may be refering to the fact that the UTC time add/subtract your current time offset. Look at the picture below: I'm in a GMT+2h00 zone, so, my UTC value is offset by 2 hours. So, if I set the hours to 23h00 in my Date object, my UTC date get offset to the day after (as in 1h00 am).

image

@aceleghin
Copy link
Author

Yes, I want the date selected is already UTC, I used primeng calendar and there is a property like utc="true" that solve this.
If I select 22/9/2017 I want send: 2017-09-22T00:00:00.000Z

@julianobrasil
Copy link
Contributor

Well... I really don't know if it's planned to support such a utc attribute.

cc: @mmalerba

@mmalerba
Copy link
Contributor

We do plan to add support for different timezones/offsets, but its not there yet. In the mean time you could make a custom DateAdapter that works with UTC dates to accomplish this.

Here is the DateAdapter class you would have to extend:
https://github.com/angular/material2/blob/master/src/lib/core/datetime/date-adapter.ts

Some examples of existing implementations:
https://github.com/angular/material2/blob/master/src/lib/core/datetime/native-date-adapter.ts
https://github.com/angular/material2/blob/master/src/material-moment-adapter/adapter/moment-date-adapter.ts

And some documentation that may help:
https://material.angular.io/components/datepicker/overview
https://blog.angular.io/taking-advantage-of-the-angular-material-datepicker-237e80fa14b3

@we125182
Copy link

@lippomano I have the same request. Did you get any solution about it ? thank you for any suggestions.

@aceleghin
Copy link
Author

@we125182 I write a method that manipulate every date it is not the best practice but it's simpler for me

@adaojunior
Copy link

I'm also getting a incorrect date.

bug-on-datetime

@adaojunior
Copy link

I get this exact same behaviour on https://material.angular.io/components/datepicker/overview
It always adds +1 day after selected.

@mmalerba
Copy link
Contributor

mmalerba commented Oct 3, 2017

@adaojunior if you're seeing the same issue on the docs site its likely some issue related to your locale / time zone. Any additional information you can provide would be useful. If you're using Moment.js in your app (or willing to try it) you could give the MomentDateAdapter a try and see if it resolves your issue. Just add the MatMomentDateModule from @angular/material-moment-adapter at your application root instead of MatNativeDateModule

@adaojunior
Copy link

@mmalerba same issue on the docs, see:
bug-on-angular-datepicker

I'm using MdNativeDateModule on my application.

@npen
Copy link

npen commented Oct 11, 2017

@mmalerba Hello, is it possible to import MatMomentDateModule from @angular/material-moment-adapter when using beta12? I cannot find the way to do it, it's seems to me this module is not yet released?

@mmalerba
Copy link
Contributor

@npen yeah I think it missed the release, you can install it from here in the mean time: https://github.com/angular/material2-moment-adapter-builds

@jt-helsinki
Copy link

jt-helsinki commented Oct 12, 2017

Adding +1 day is a new "feature" with the day picker whilst using the default DateAdapter. I believe it has been introduced in beta 12.

This also happens for me on the material angular site:
https://material.angular.io/components/datepicker/overview

Today is the 12th of October at 22:30. To replicate:

  • Open the datepicker the 11 October is "circled" as today's date.
  • Select the 12th, the 13th is displayed when the picker is closed.
  • When I reopen it, the 12th is selected

I'm using an Aussie time zone. 22:30. When I switch back to Europe, it works as intended despite being on the same day as in Australia.

@mmalerba
Copy link
Contributor

@jt-helsinki Thanks for the detailed info, strangely I am unable to reproduce it using the same settings. I tried 12th of October 2017 at 22:30 in the following time zones (all of the Australian ones listed on my system): Adelaide, Brisbane, Broken Hill, Currie, Darwin, Ecula, Hobart, Lindeman, Lord Howe, Melbourne, Perth, Sydney.

All of them showed the 12th circled and selected the 12th after clicking (on docs site)
date

If anyone knows what I'm doing wrong please let me know...

@jt-helsinki
Copy link

jt-helsinki commented Oct 12, 2017

Looks like you're doing exactly what I was doing.

I notice I'm on a different OS. An unlikely shot, could it be a OS/browser thing?

I'm on:
OSX
Chrome (Version 61.0.3163.100 (Official Build) (64-bit))

Anyone else care to confirm?

The strange thing with this is that the date had not yet flipped to the following day. Europe and Australia were still on the same day.

@julianobrasil
Copy link
Contributor

@jt-helsinki @adaojunior, are you in regions with daylight saving time period currently on?

@phmello
Copy link

phmello commented Oct 12, 2017

Yes!! Sydney!!

@julianobrasil
Copy link
Contributor

julianobrasil commented Oct 12, 2017

And is it a normal DST period, I mean, every year, in October, Sydney is usually in DST...?

@phmello
Copy link

phmello commented Oct 12, 2017

yes, First weekend of Oct to first weekend of April of the next year
Sydney DST

@julianobrasil
Copy link
Contributor

julianobrasil commented Oct 13, 2017

@mmalerba, I've heard of something about ECMA 5.1 and previous versions having DST issues... if this is the problem, it would affect older browsers (that, for sure, is not the case of @jt-helsinki's #7167 (comment)). Look at this test in IE 11 that I couldn't reproduce in Chrome (I think you have already noticed the calendar showing the 31st day of the previous month):

image

In above image, automatic DST detection is activated. If I disable it the problem doesn't happen.

EDIT: take a look at this old article that I've found: https://codeofmatt.com/2013/06/07/javascript-date-type-is-horribly-broken/

Maybe some polyfills are causing the problem.

@jt-helsinki
Copy link

Oddly enough, this started happening when the Aussie daylight savings time ticked over.

Strange because he same problem doesn't happen here in Europe. Then again, maybe it will when the clocks change at the end of the month.

@darkurse
Copy link

darkurse commented Nov 2, 2017

@jt-helsinki , same thing here in Europe when DST kicked in. If DatePicker is not supposed to handle different timezones, shouldn't it ( at least ) handle DST ?

@mmalerba
Copy link
Contributor

mmalerba commented Nov 2, 2017

Indeed it should, hence the open PR to fix it

@ankitraonka
Copy link

@mmalerba shouldnt the datepicker not support timezones

its for selecting only date

if i am selecting date of birthday it shouldnt change according to different time zones

@Silthus
Copy link
Contributor

Silthus commented Apr 30, 2018

I made my self a workaround by overriding the MomentJS DateAdapter compiling the date from UTC and then converting it into the locale date:

import { Inject, Injectable, Optional } from '@angular/core';
import { MAT_DATE_LOCALE } from '@angular/material';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { Moment } from 'moment';
import * as moment from 'moment';

@Injectable()
export class MomentUtcDateAdapter extends MomentDateAdapter {

  constructor(@Optional() @Inject(MAT_DATE_LOCALE) dateLocale: string) {
    super(dateLocale);
  }

  createDate(year: number, month: number, date: number): Moment {
    // Moment.js will create an invalid date if any of the components are out of bounds, but we
    // explicitly check each case so we can throw more descriptive errors.
    if (month < 0 || month > 11) {
      throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`);
    }

    if (date < 1) {
      throw Error(`Invalid date "${date}". Date has to be greater than 0.`);
    }

    let result = moment.utc({ year, month, date }).locale(this.locale);

    // If the result isn't valid, the date must have been out of bounds for this month.
    if (!result.isValid()) {
      throw Error(`Invalid date "${date}" for month with index "${month}".`);
    }

    return result;
  }
}

The important line being:

let result = moment.utc({ year, month, date }).locale(this.locale);

@Laurensvdw
Copy link

@Silthus
Your solution works like a charm. Just had this issue and you commented 18 hours ago. Lucky me! :D

@MichalK6677
Copy link

Silthus' workaround works great.

@mmalerba: Is there a plan to implement something into oficial MomentDateAdapter to avoid workarounds?

@mmalerba
Copy link
Contributor

@MichalK6677 I wasn't aware that the MomentDateAdapter had this issue as well. Do you want to send a PR to fix it?

@Silthus
Copy link
Contributor

Silthus commented May 15, 2018

@mmalerba I can send a PR later this day.

Silthus added a commit to Silthus/material2 that referenced this issue May 15, 2018
Moment DateAdapter should create dates in utc format to avoid time zone
collisions when passing date to servers

Closes angular#7167
Silthus added a commit to Silthus/material2 that referenced this issue Jun 5, 2018
Added MAT_MOMENT_DATE_ADAPTER_OPTIONS with the useUtc: boolean option to
parse dates as utc
The new option defaults to false and will not cause any breaking
changes.

Closes angular#7167
Silthus added a commit to Silthus/material2 that referenced this issue Jun 5, 2018
Describe how to set the MomentDateAdapter to parse dates as utc.
Describe the default behaviour of the MomentDateAdapter.
Add an example on how to configure MAT_MOMENT_DATA_ADAPTER_OPTIONS

Closes angular#7167
@glucena
Copy link

glucena commented Jul 3, 2018

You can change the default behaviour to parse dates as UTC by providing the MAT_MOMENT_DATA_ADAPTER_OPTIONS and setting it to useUtc: true.

@NgModule({
imports: [MatDatepickerModule, MatMomentDateModule],
providers: [
{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
]
})

Check here for more details

@smnbbrv
Copy link

smnbbrv commented Aug 22, 2018

MAT_MOMENT_DATE_ADAPTER_OPTIONS - oh man, that's so awesome!

However, this..., it's not should, it must be default configuration for datepicker.

@raibait
Copy link

raibait commented Oct 1, 2018

Why does datepicker care about the timezones at all? Who would ever need that?

@julianobrasil
Copy link
Contributor

@raibait, in fact, it doesn't care about it. But the underlying objects that currently store the date values usually do (regular javascript Date objects and Moment.js are the most common cases). This is the reason for so many issues related to wrong dates. Building a date adapter and configuring it correctly is important to avoid headaches.

@thw0rted
Copy link

Maybe the problem here is the fact that the datepicker's value is a Date in the first place. Date is a perfectly good(ish) class for representing an absolute instant in real time; it's a terrible representation of a calendar date. This entire thread, dozens of posts, dozens of users complaining, could have been avoided if the default date adapter went to/from ISO 8601 date-only strings. Every date library (and of course the native Date object) understands them, they are exactly as specific as the UI that generated them, and they can be "upgraded" to an instant or range of real time by simply adding "Z" on the end. What I mean to say is, adding context to go from "2018/11/26" to "2018/11/26Z" makes sense, but going the other way -- trying to remove time-zone-ness from the data -- does not.

Of course, I can fix all this by writing my own date adapter, or going out and getting the Moment-based one, but as long as the default behavior is Date-based, you're going to keep seeing people showing up in this thread.

@nugrohotanjo
Copy link

I made my self a workaround by overriding the MomentJS DateAdapter compiling the date from UTC and then converting it into the locale date:

import { Inject, Injectable, Optional } from '@angular/core';
import { MAT_DATE_LOCALE } from '@angular/material';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { Moment } from 'moment';
import * as moment from 'moment';

@Injectable()
export class MomentUtcDateAdapter extends MomentDateAdapter {

  constructor(@Optional() @Inject(MAT_DATE_LOCALE) dateLocale: string) {
    super(dateLocale);
  }

  createDate(year: number, month: number, date: number): Moment {
    // Moment.js will create an invalid date if any of the components are out of bounds, but we
    // explicitly check each case so we can throw more descriptive errors.
    if (month < 0 || month > 11) {
      throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`);
    }

    if (date < 1) {
      throw Error(`Invalid date "${date}". Date has to be greater than 0.`);
    }

    let result = moment.utc({ year, month, date }).locale(this.locale);

    // If the result isn't valid, the date must have been out of bounds for this month.
    if (!result.isValid()) {
      throw Error(`Invalid date "${date}" for month with index "${month}".`);
    }

    return result;
  }
}

The important line being:

let result = moment.utc({ year, month, date }).locale(this.locale);

thanks, it's work very well :)

@Achyuthkodali
Copy link

Achyuthkodali commented Jul 15, 2019

@Silthus the adapter you did works like a charm, but I have a requirement to set the format for the date.
How can I achieve that alongside timezone adapter?

my date format: "DD MMM YYYY" // 15 jul 2019

@christrude
Copy link

christrude commented Sep 9, 2019

I am wondering if adding the useUTC portion to a component's providers instead of the module's providers will not work?

When using the Honolulu timezone (spoofing the browser), we were not able to make moment work, we ended up having to manually remove the timezone offset in order to get the mat-datepicker to choose the correct date:

let newDate= new Date(initialInfo.value); newDate.setMinutes(newDate.getMinutes() + newDate.getTimezoneOffset()); variableValueCtrl.reset(newDate);

When I tried to get it to work with moment it cause a whole host of other issues. However this still causes the selection to be wrong:

Screen Shot 2019-09-09 at 2 10 57 PM

Not looking for a solution, just alerting to the issues

@christrude
Copy link

christrude commented Sep 9, 2019

Stackblitz showing crazy go nuts wonky-ness when you have your browser spoofed to India time:

https://stackblitz.com/edit/angular-trude-datetest

Screen Shot 2019-09-06 at 5 32 37 PM

And in that stack blitz, look at what it selects in the UI:

Screen Shot 2019-09-09 at 2 42 41 PM

So set to the 6th, moment converts to the 5th, mat-datepicker displays the 4th, but selects the 3rd.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Oct 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet