Angular material date picker: Date Parsing UTC problem 1 day before

六眼飞鱼酱① 提交于 2020-01-14 14:36:18

问题


I know there are several threads about this and i read them all but couldn't fix this for days. I might found a solution but it seems to be do dirty for me.

So as other users I have the same problem with the datePicker. For me it's the Angular Material Datepicker mat-datepicker

When I log the value I get the correct result:

Wed Dec 24 1999 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit)

but in the request it is

1999-12-23T23:00:00.000Z

What I tried yet:

I've added { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } } to my component and to my app.module.ts. That does not make any difference for me.

My dirty solution (before sending the request):

 let newDate= new Date(this.personalForm.get("dateOfBirth").value);
 newDate.setMinutes(newDate.getMinutes() - newDate.getTimezoneOffset());

When I am doing this the console logs:

Wed Dec 24 1999 01:00:00 GMT+0100 (Mitteleuropäische Normalzeit)

and the request is right:

1997-12-24T00:00:00.000Z

But if somebody now would be from a different timezone like GMT-0100 this would again not work. How to fix this correctly?

I also change the adapter dynamically if it's necessary to know:

 this._adapter.setLocale(this.translateService.currentLang);

回答1:


The value which is picked and displayed is the same... it is the different formats of the date-time which show us a different result (where the date also changes)!!

For e.g:

  • (by default): 2019-06-11T19:00:00.000Z
  • is equal to (by UTCString): Tue, 11 Jun 2019 19:00:00 GMT
  • is equal to (by LocaleString): 6/12/2019, 12:00:00 AM
  • is equal to (by LocaleTimeString): 12:00:00 AM

We don't need to convert it, because the date object contains the same exact time.

This stackblitz will further show the difference a format can make on the surface but the date is the same underneath; If you have questions/comments, you can fork this stackblitz, make changes and post a comment against this answer, i'll try to clarify.

relevant TS:

import {Component} from '@angular/core';

/** @title Basic datepicker */
@Component({
  selector: 'datepicker-overview-example',
  templateUrl: 'datepicker-overview-example.html',
  styleUrls: ['datepicker-overview-example.css'],
})
export class DatepickerOverviewExample {
  planModel: any = {start_time: new Date() };
  constructor(){}

  dateChanged(evt){
    let selectedDate = new Date(evt);
    console.log("by default:", selectedDate);
    console.log("by UTCString:", selectedDate.toUTCString());
    console.log("by LocaleString:", selectedDate.toLocaleString());
    console.log("by LocaleTimeString:", selectedDate.toLocaleTimeString());
  }

}

relevant HTML:

<mat-form-field>
  <input matInput [matDatepicker]="picker" placeholder="Choose a date"
  [(ngModel)]="planModel.start_time"
  (ngModelChange)='dateChanged($event)'
  >
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker></mat-datepicker>
</mat-form-field>

<hr/>

<p>By default: {{planModel.start_time}} </p>
<p>Medium date: {{planModel.start_time | date:'medium'}} </p>
<p>Short date: {{planModel.start_time | date:'short'}} </p>
<p>Short time: {{planModel.start_time | date: 'shortTime' }} </p>
<p>Medium time: {{planModel.start_time | date: 'mediumTime' }} </p>
<p>Long time: {{planModel.start_time | date: 'longTime' }} </p>
<p>Full time: {{planModel.start_time | date: 'fullTime' }} </p>



回答2:


At the end the problem was in my backend and needed to understand what is going on.

To save the Date right into my Postgres Database:

     DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH);
     LocalDateTime lu = LocalDateTime.parse(lastUpdated, inputFormatter);
     LocalDateTime dob = LocalDateTime.parse(dateOfBirth, inputFormatter);

     this.dateOfBirth = Date.from(dob.atZone(ZoneOffset.UTC).toInstant());
     this.lastUpdated = Date.from(lu.atZone(ZoneOffset.UTC).toInstant());

Before I had

 this.dateOfBirth = Date.from(dob.atZone(ZoneId.systemDefault()).toInstant()); 

which was wrong. I just receive the request like it's given by Angular and Java saves it right into the database.

1999-12-23T23:00:00.000Z 

turns into

1997-12-24 00:00:00



回答3:


In response to your own incorrect Answer…

11 PM UTC = 00:00+01:00 the next day (same moment)

Wed Dec 24 1999 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit) but in the request it is

1999-12-23T23:00:00.000Z

These two strings represent the same value. They are two ways of viewing the same moment. One is 11 PM at UTC, the other is an hour ahead of UTC, so it represents the first moment of the next day. Adding one hour to 11 PM on the 23rd in a 24-hour long day takes past the stroke of midnight, to the first moment of the 24th.

Parsing

Parse your input string as an Instant. The Z on the end means UTC and is pronounced “Zulu”. An Instant represent a moment in UTC, always UTC, by definition.

Your input string is in standard ISO 8601 format. The java.time classes use these standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.

Instant instant = Instant.parse( "1999-12-23T23:00:00.000Z" ) ;

A moment (a date, a time-of-day, and an assigned time zone or offset-from-UTC) must be saved into a database column of a type akin to the standard-SQL type TIMESTAMP WITH TIME ZONE. A column of a type akin to TIMESTAMP WITHOUT TIME ZONE would be the wrong data type.

To write to your database, we need to switch from the basic building-block class of Instant to the more flexible OffsetDateTime class. Support for the OffsetDateTime class in JDBC drivers is required by JDBC 4.2 and later, while Instant support is optional.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 

Write to database via a PreparedStatement with a placeholder ?.

myPreparedStatement.setObject( … , odt ) ;

Retrieval.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

This has been discussed many many times on Stack Overflow. So search to learn more. And please search Stack Overflow thoroughly before posting.

LocalDate

If your goal is to track just a date, and you don’t really care about time-of-day and time zone, you should be using LocalDate in Java and DATE column in standard SQL.




回答4:


Haha, my dirty solution for this was this method, which does it's job for what it is, since I just wanted to send the date back as a string without any other information, which would be mapped to a datetime object in the backend and saved...

public setDate(date: any): string {
    const chosenDate = new Date(date);
    return `${chosenDate.getMonth() + 1}/${chosenDate.getDate()}/${chosenDate.getFullYear()}`;
}

and then set the object value in the html

<mat-form-field appearance="outline">
    <mat-label>Birthdate</mat-label>
    <input matInput [matDatepicker]="birthdatePicker" placeholder="Birthdate" 
        (dateChange)="user.birthdate = setDate($event.value)">
    <mat-datepicker-toggle matSuffix [for]="birthdatePicker"></mat-datepicker-toggle>
    <mat-datepicker #birthdatePicker></mat-datepicker>
    <mat-error>error</mat-error>
</mat-form-field>


来源:https://stackoverflow.com/questions/56578738/angular-material-date-picker-date-parsing-utc-problem-1-day-before

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!