角度材料日期选择器:1天前的日期解析UTC问题

Posted

技术标签:

【中文标题】角度材料日期选择器:1天前的日期解析UTC问题【英文标题】:Angular material date picker: Date Parsing UTC problem 1 day before 【发布时间】:2019-10-27 22:39:32 【问题描述】:

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

所以和其他用户一样,我对 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);

【问题讨论】:

在您的 console.log... 转换为 UTC 字符串... w3schools.com/jsref/jsref_toutcstring.asp @AkberIqbal 好的,我现在看到它在 UTC 中的样子,但这对我的问题有什么帮助?当我不手动修复它时它仍然是错误的“脏” 你需要导入MatMomentDateModule而不是MatNativeDateModule才能工作 provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: useUtc: true 【参考方案1】:

回复your own incorrect Answer…

UTC 晚上 11 点 = 次日 00:00+01:00(同一时刻)

1999 年 12 月 24 日星期三 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit) 但在请求中是

1999-12-23T23:00:00.000Z

这两个字符串代表相同的值。它们是观看同一时刻的两种方式。一个是 UTC 晚上 11 点,另一个是 UTC 前一个小时,所以它代表第二天的第一刻。在长达 24 小时的一天中,在 23 日晚上 11 点增加一个小时,这将超过午夜的钟声,直到 24 日的第一刻。

解析

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

您的输入字符串采用标准 ISO 8601 格式。 java.time 类在解析/生成字符串时默认使用这些标准格式。所以不需要指定格式模式。

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

必须将某个时刻(日期、时间和分配的时区或与 UTC 的偏移量)保存到类似于标准 SQL 类型 TIMESTAMP WITH TIME ZONE 的类型的数据库列中。类似于TIMESTAMP WITHOUT TIME ZONE 类型的列将是错误 数据类型。

要写入您的数据库,我们需要从Instant 的基本构建块类切换到更灵活的OffsetDateTime 类。 JDBC 4.2 及更高版本需要在 JDBC 驱动程序中支持 OffsetDateTime 类,而对 Instant 的支持是可选的。

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

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

myPreparedStatement.setObject( … , odt ) ;

检索。

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

这已经在 Stack Overflow 上讨论过很多次了。因此,搜索以了解更多信息。请在发帖前彻底搜索 Stack Overflow。

LocalDate

如果您的目标只是跟踪一个日期,并且您并不真正关心时间和时区,那么您应该在 Java 中使用 LocalDate,在标准 SQL 中使用 DATE 列。

【讨论】:

以我的结构,它甚至不可能像这样。我只是使用我的 repo 来保存给定的实体和我的所有值。而这只是一个约会。最后它对我有用并且没有发现任何问题,如果我不使用 JPA 和 Spring 并不真正需要的preparedStatements,我将再次使用它 这是一个更好的尝试还是一个正确的尝试:this.dateOfBirth = new Timestamp(ZonedDateTime.of(dob, ZoneId.of("UTC")).toInstant().toEpochMilli());【参考方案2】:

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

例如

(默认):2019-06-11T19:00:00.000Z 等于(通过 UTCString):2019 年 6 月 11 日星期二 19:00:00 GMT 等于(通过 LocaleString):6/12/2019, 12:00:00 AM 等于(按 LocaleTimeString):12:00:00 AM

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

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

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

【讨论】:

如果我想在Angular中再次显示它是不一样的。它保存在我的数据库中,使用 JPA 作为解析日期,只有 1999-12-23 没有时间戳或只有 00:00,所以当我想显示它时它是错误的。 在保存到数据库之前,如果这样做:let someVar = new Date(parsedDate)... 然后,在你的数据库中插入这个someVar;你可以分叉我的 stackblitz 来告诉我你正在经历什么......然后我或社区中的其他人可以轻松地提供帮助 对不起,我无法关注你。 parsedDate 是什么?我的意思是我应该如何解析以及解析什么?我只想像我在问题中所说的那样保存 dateOfBirth 并能够在用户的个人资料中再次显示它。也许我的问题更多在于我的 Java 后端解析。 stackblitz.com/edit/… 如果您在此处输入我的日期,您将看到我作为后端的 JSON 输出得到的确切内容。我将其格式化以便能够使用DateTimeFormatterLocalDate.parse 将其保存到我的数据库中,也许我还需要使用LocalDateTime 即使在你的 stackblitz 中,Date 对象也有相同的日期......表面上的格式可以改变......检查这个分叉版本:stackblitz.com/edit/… 我认为我们实际上在谈论不同的事情。我不知道你想告诉我什么。我知道日期总是相同的,只是时区不同。最后是我作为请求得到的 1999-12-23T23:00:00.000Z。当我将它保存在我的数据库中并稍后检索它并显示它(就像我一样)时,longDate 代表 1999-12-23 出了什么问题。我还发现我的 lastUpdated 日期+时间是错误的。我几分钟前编辑了一些东西,上面写着:13. June 2019 11:48:58 但它是 13:48:58【参考方案3】:

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

要将日期直接保存到我的 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

【讨论】:

不正确。您的格式模式忽略了末尾的 Z,这表示 UTC,发音为“Zulu”。因此,您不必要地丢弃了有价值的信息。您的输入应该被解析为Instant 而不是LocalDateTimeLocalDateTime不能代表片刻,正如其 Javadoc 中所解释的那样。请参阅my Answer 中更正的代码示例。

以上是关于角度材料日期选择器:1天前的日期解析UTC问题的主要内容,如果未能解决你的问题,请参考以下文章

角度材料日期时间选择器更改显示日期时间的格式

角度材料日期选择器中的日期不正确

角度材料日期选择器最小和最大日期验证消息

角度材料日期选择器错误呈现

如何从角度材料日期选择器获取当前时间?

如何自定义角度材料日期选择器?