角度材料日期选择器: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 输出得到的确切内容。我将其格式化以便能够使用DateTimeFormatter
和LocalDate.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
而不是LocalDateTime
。 LocalDateTime
类不能代表片刻,正如其 Javadoc 中所解释的那样。请参阅my Answer 中更正的代码示例。以上是关于角度材料日期选择器:1天前的日期解析UTC问题的主要内容,如果未能解决你的问题,请参考以下文章