问题描述
我有一个 Spring 应用程序,我从一个 Angular 客户端接收一个字符串形式的日期,我如何将此日期转换为 Java 日期,以下是 TypeScript 日期格式:
TypeScript 日期格式: Mon Jan 04 2021 00:00:00 GMT+0100 (Central European Standard Time)
Java 日期格式: Mon Jan 04 00:00:00 CET 2021
解决方法
java.time
如何将此日期转换为 Java 日期,以下是 打字稿日期格式:
TypeScript 日期格式: Mon Jan 04 2021 00:00:00 GMT+0100 (Central European Standard Time)
java.time
为您提供 DateTimeFormatterBuilder
,您可以使用它定义一个复杂的格式进行解析/格式化,如下所示:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.util.HashSet;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDateTime = "Mon Jan 04 2021 00:00:00 GMT+0100 (Central European Standard Time)";
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("EEE MMM dd uuuu HH:mm:ss")
.appendLiteral(" ")
.appendZoneRegionId()
.appendOffset("+HHmm","Z")
.appendLiteral(" (")
.appendZoneText(TextStyle.FULL)
.appendLiteral(")")
.toFormatter(Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(strDateTime,dtf);
System.out.println(zdt);
}
}
输出:
2021-01-04T00:00+01:00[Europe/Paris]
Java 日期格式: Mon Jan 04 00:00:00 CET 2021
日期时间对象应该存储有关日期、时间、时区等的信息,与格式无关。您可以使用日期时间格式化 API 将日期时间对象格式化为 String
对象,并使用您选择的模式。
- 现代日期时间类型的日期时间格式 API 在包中,
java.time.format
例如java.time.format.DateTimeFormatter
、java.time.format.DateTimeFormatterBuilder
等 - 旧日期时间类型的日期时间格式 API 在包中,
java.text
例如java.text.SimpleDateFormat
、java.text.DateFormat
等
下面给出了如何将 zdt
(上面获得的)格式化为自定义格式:
DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z uuuu",Locale.ENGLISH);
String strDateTimeFormatted = zdt.format(dtfOutput);
System.out.println(strDateTimeFormatted);
输出:
Mon Jan 04 00:00:00 CET 2021
从 Trail: Date Time 了解现代日期时间 API。
如果您想将 zdt
(上面获得的)转换为 java.util.Date
,您可以按如下所示进行:
Date date = Date.from(zdt.toInstant());
System.out.println(date);
关于旧日期时间 API 的说明:
java.util.Date
对象不是像 modern date-time types 那样的真实日期时间对象;相反,它表示从 Epoch of January 1,1970
开始的毫秒数。当您打印 java.util.Date
的对象时,其 toString
方法返回根据此毫秒值计算的日期时间。由于 java.util.Date
没有时区信息,它应用您的 JVM 的时区并显示相同的时区。如果您需要在不同的时区打印日期时间,则需要将时区设置为 SimpleDateFomrat
并从中获取格式化的字符串。
3. java.util
的日期时间 API 及其格式化 API SimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到 modern date-time API。
- 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7。
- 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。
Mon Jan 04 2021 00:00:00 GMT+0100 (Central European Standard Time)
这是一个不明确的时间戳。政治实体和整个世界可以自由地重新定义 CEST 的含义,此时 GMT+0100 意味着一件事,而 CST 意味着另一件事。此外,CEST 对在该日期进行的任何不同于 GMT+0100 的“数学”都有影响。
例如,如果我想知道距该日期 6 个月的日期,那么如果 CST 领先,您需要:
Thu Jul 04 2021 00:00:00 GMT+0200 (Central European Daylight Time)
或者您可能想要那个时间,但要早一个小时 - 另一个悬而未决的问题,对于“在此时间戳中添加 6 个月”这一想法中固有的问题没有明确的答案,因此您必须指定它,因为否则你会得到系统的一些随机猜测,你几乎不希望你的计算机/编程语言像这样猜测 50/50 的概念。
如果 GMT+0100 领先,您最终会得到:
Thu Jul 04 2021 00:00:00 GMT+0100 (Central European Standard Time)
这完全奇怪在整个星球上有零个地方可以打印此时间戳:没有人在 7 月 4 日的 CEST 上,任何地方。这是一个“真实”的时间戳,但实际上却被 0 人使用。
因此,在继续之前,首先您需要问自己,您想要在转换时保留哪些点点滴滴。请注意,有 3 种不同的时间概念:
-
'solarflares' 时间:通常测量为自纪元以来的毫秒数。这缺乏诸如时区之类的人类概念。记录事件发生或将要发生的确切时刻,如果该事件不是人工预约(即预测太阳耀斑何时发生或确实发生,因此称为“太阳耀斑”时间)。在 java 中,最好用
java.time.Instant
表示。 -
“约会”时间:通常用一大袋数字来衡量:年、月、日、小时、分钟、秒、和时区。不是那些
GMT+0100
无用区域之一,而是类似于Europe/Amsterdam
的区域。像Central European Standard Time
这样的东西有点常见,但也是一个非常糟糕的主意。这些名称没有明确的含义,诸如Europe/Amsterdam
之类的东西很可能总是具有明确的含义,这是一个适当的区域。这个想法是:假设你和你的牙医预约了下个月在阿姆斯特丹的约会。这确实看起来像是一种 Solarflares 的交易,但事实并非如此:如果荷兰决定加入不同的时区,那么在您的约会之前的绝对秒数也会随之改变。而太阳耀斑不会仅仅因为某个政治实体如此规定而提前一个小时发生。 -
'闹钟'时间:类似于约会时间,但没有区域信息作为基本信息。如果您将闹钟设置为在早上 8 点叫醒您,然后跳上飞机向西飞了一圈,您希望闹钟仍然在当地时间早上 8 点响起:闹钟响起前的秒数当您跨区域移动时,关闭应该会发生变化。
那么,您的时间价值打算代表什么?
如果是“太阳耀斑”时间。
然后工作是首先将其转换为 java.time.Instant
对象,然后从那里取出它。例如,通过.atZone()
转换回约会时间,然后使用java.time.format.DateTimeFormatter
将其打印为类似的字符串。想象一下输入是在新加坡时间,然后使用此策略输出仍然在 CET 中。另外,我觉得您显示字符串不是因为您需要像这样格式化字符串,而是因为您错误地认为 java 以这种字符串格式存储时间。它没有; java 将约会时间存储为具有年、月、日等字段的对象。将其打印到字符串是分离出来的,并由格式化程序控制。您可以制作格式化程序以任何方式打印它。
可以根据 j.t.Instant
将其解析为 GMT+0100
值,您可以忽略 CEST 尾随文本。
如果是“约会”时间。
然后你需要把它解析出来,这很重要; GMT+0100 不是你想要的,那是一个合法的区域,但不是一个有用的区域(CEST 区域中没有国家使用 GMT+0100。地球上有些地方全年都是 GMT+0100。他们不是,但是,在中欧附近的任何地方)。因此,相关位是除 GMT+0100 之外的所有内容,包括 CEST,这不是标准日期格式,据我所知,“中欧标准时间”不是 java.time 可以识别的。您将需要一个表格,其中包含 typescript 在那时可以生成的所有可能的字符串。
如果是“闹钟时间”
然后你可以在最后一个 :00
之后停止解析并扔掉剩下的。使用正则表达式将其剥离,然后使用 LocalDateTime
方法解析为 LocalDateTime.of(stringValue,formatter)
。很简单。
注意事项
试着让你的 TypeScript 以其他方式打印这些东西。一种很好的方法是将太阳耀斑时间与明确的区域(如 Europe/Amsterdam
,而不是 Central European Standard Time
)结合起来。这更容易重新解析为 3 种不同的时间概念方式中的任何一种。
您可以使用纪元时间在客户端和服务器之间共享时间,这是一种简单的方法。
Typescript 纪元时间管理
const epoch = new Date().getTime();
console.log(epoch);
const date = new Date(epoch);
console.log(date);
结果:
1609801111672
Date Mon Jan 04 2021 22:58:31 GMT+0000 (Western European Standard Time)
使用 java.util.Date 进行 Java 纪元时间管理
long epoch = new Date().getTime();
log.debug( "Epoch: {}",epoch );
Date date = new Date(epoch);
log.debug( "Date: {}",date );
结果
Epoch: 1609801111672
Date: 2021-01-04T22:58:31.672Z