Angular material 日期选择器:1 天前的日期解析 UTC 问题

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

我知道有几个关于这个的话题,我都阅读了它们,但好几天都无法解决这个问题。我可能找到了解决方案,但它似乎对我来说很脏。

因此,与其他用户一样,我对 datePicker 也有同样的问题。对我来说是 Angular Material Datepicker mat-datepicker

当我记录值时,我得到了正确的结果:

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

但在请求中是

1999-12-23T23:00:00.000Z

我尝试过的:

我已将 { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } } 添加到我的 component 和我的 app.module.ts。这对我来说没有任何区别。

我的肮脏解决方案(在发送请求之前):

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

当我这样做时,控制台记录:

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

请求正确:

1997-12-24T00:00:00.000Z

但是如果有人现在来自不同的时区,比如 GMT-0100,这将再次不起作用。如何正确解决这个问题?

如果有必要知道,我也会动态更改适配器:

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

选择和显示的值是相同的... date-time 的不同格式向我们显示了不同的结果(日期也发生了变化)!!

例如:

  • (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

我们不需要转换它,因为日期对象包含相同的精确时间。

This stackblitz 将进一步显示格式在表面上可以产生的差异,但在下面的日期是相同的;如果你有 questions/comments,你可以分叉这个 stackblitz,进行更改并 post 对这个答案发表评论,我会尽力澄清。

相关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());
  }

}

相关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>

最后问题出在我的后端,需要了解发生了什么。

要将日期保存到我的 Postgres 数据库中:

     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());

之前

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

这是错误的。我刚刚收到 Angular 发出的请求,Java 将其直接保存到数据库中。

1999-12-23T23:00:00.000Z 

变成

1997-12-24 00:00:00

回应

11 PM UTC = 00:00+01:00 次日(同一时刻)

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

1999-12-23T23:00:00.000Z

这两个字符串表示相同的值。它们是查看同一时刻的两种方式。一个是 UTC 晚上 11 点,另一个比 UTC 早一个小时,所以它代表第二天的第一时刻。在24小时的漫长一天中,23日晚上11点加一小时,过了午夜,到24日的第一刻。

正在解析

将您的输入字符串解析为 Instant。末尾的 Z 表示 UTC,发音为“Zulu”。根据定义,Instant 表示 UTC 中的时刻,始终是 UTC。

您输入的字符串采用标准 ISO 8601 格式。当 parsing/generating 字符串时,java.time classes 默认使用这些标准格式。因此无需指定格式模式。

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

必须将时刻(日期、time-of-day 和分配的时区或 offset-from-UTC)保存到类似于 standard-SQL 类型的数据库列中 TIMESTAMP WITH TIME ZONE。类似于 TIMESTAMP WITHOUT TIME ZONE 类型的列将是 错误的 数据类型。

要写入您的数据库,我们需要从 Instant 的基本 building-block class 切换到更灵活的 OffsetDateTime class。 JDBC 4.2 及更高版本需要 JDBC 驱动程序支持 OffsetDateTime class,而 Instant 支持是可选的。

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

通过带有占位符 ?PreparedStatement 写入数据库。

myPreparedStatement.setObject( … , odt ) ;

检索。

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

这已经在 Stack Overflow 上讨论过很多次了。所以搜索以了解更多信息。并且请在发布前彻底搜索 Stack Overflow。

LocalDate

如果您的目标只是跟踪一个日期,并且您并不真正关心 time-of-day 和时区,您应该在 Java 和 [= 中使用 LocalDate 26=] 标准中的列 SQL。