其他系统会在他们所在的时区为我们发送时间戳。如果在其他系统中在云中运行,则需要+2小时。本地是好的,因为服务器是相同的时区。我如何确定时间永远正确?
String TIME_STAMP_FORMAT = "yyyy-MM-dd-HH.mm.ss.SSSSSS";
DateTimeFormatter TIME_STAMP_FORMATTER = DateTimeFormatter.ofPattern(TIME_STAMP_FORMAT, Locale.getDefault());
private static Timestamp parseTimestamp(String dateString) {
try {
return Timestamp.valueOf(LocalDateTime.parse(dateString, TIME_STAMP_FORMATTER));
} catch (DateTimeParseException e) {
log.error("Not able to parse timestamp", e);
}
return null;
}
Date afterParse = parseTimestamp('2018-12-31-12.30.50.000200')
最佳答案
tl; dr
我如何确定时间永远正确?
在日期时间输入字符串中包含time zone或offset-from-UTC指示符。
交换日期时间值时,请使用标准的ISO 8601格式。
在Java中仅使用java.time类。切勿使用Date
,Timestamp
,Calendar
等。
提示:在发送之前(通常来说),请将其他区域的值调整为UTC。
如果不可能,那么这里是一种解决方法。假定您知道此不良数据的发送者所预期的时区。
LocalDateTime // Represent a date and time-of-day without the context of a time zone or offset-from-UTC. NOT a moment, NOT a point on the timeline. A meaningless value until you assign a zone/offset.
.parse(
"2018-12-31-12.30.50.000200" , // Avoid such custom formats. Use only ISO 8601 when exchanging date-time values textually.
DateTimeFormatter.ofPattern( "uuuu-MM-dd-HH.mm.ss.SSSSSS" ) // Define formatting pattern to match youre input.
) // Returns a `LocalDateTime` object.
.atZone( // Give meaning to the `LocalDateTime` object by applying a time zone.
ZoneId.of( "Africa/Tunis" ) // Always specify a time zone with `Continent/Region` name, never the 2-4 character pseudo-zones popularly seen in the media.
) // Returns a `ZonedDateTime` object.
.toInstant() // Adjust from a time zone to UTC by extracting an `Instant` object. Same moment, same point on the timeline, different wall-clock time.
参见此code run live at IdeOne.com。
最好避免使用
java.util.Date
类。但是,如果必须与尚未更新为java.time的旧代码进行互操作,则可以进行转换。调用添加到旧类中的新方法,例如Date.from( Instant )
。避免遗留类
切勿使用
java.sql.Timestamp
或java.util.Date
。根据JSR 310的使用,与最早的Java版本捆绑在一起的所有日期时间类现在都是旧的。仅使用现代的java.time类。数据类型错误
您使用了错误的数据类型。要跟踪时刻,即时间轴上的特定点,您必须具有时区或UTC偏移量。
LocalDateTime
类完全是在此处使用的错误类。该类故意缺少区域或偏移的任何概念。因此,这与您想要的相反。要跟踪时刻,请使用
Instant
,OffsetDateTime
或ZonedDateTime
。如果java.time类的方法带有可选的时区(
ZoneId
)或自UTC偏移量(ZoneOffset
)的参数,请考虑所需的参数。始终通过区域/偏移。这样您就不必担心系统管理员如何在运行时设置JVM的当前默认时区。ZonedDateTime.now( // Capture the current moment as seen through the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "Pacific/Auckland" )
)
或者,根据定义,始终使用UTC中的
Instant
。Instant.now() // Capture the current moment in UTC.
以
Continent/Region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用2-4个字母的缩写,例如EST
或IST
,因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。ISO 8601
您的问题尚不清楚,但似乎您正在接收自定义格式的日期时间输入字符串。我建议您教育人们发布有关ISO 8601标准的数据。该标准定义了文本之间在系统之间交换的日期时间值的实用格式。
解析/生成字符串时,java.time类默认使用ISO 8601格式。
解决方法
如果数据发布者正在向您发送诸如
2018-12-31-12.30.50.000200
之类的值以便进行交流,则它们已失败。没有区域或偏移量的日期和时间是无用的,就像在不指示货币的情况下传达一定数量的钱一样。您是否肯定知道此错误数据输入的发送方隐式假定的时区?如果是这样,请应用它,作为对他们的不良实践的笨拙的权宜之计。
假设没有任何区域/偏移量指示,请首先将输入解析为
LocalDateTime
。String input = "2018-12-31-12.30.50.000200" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd-HH.mm.ss.SSSSSS" ) ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
应用
ZoneId
获取ZonedDateTime
对象,从而进行调整以查看该特定区域的人们使用的挂钟时间的时刻。ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = ldt.atZone( ldt ) ;
通常,最好使用UTC中的时刻,除非您有特定的理由使用时区(例如向用户演示)。因此,从您的
Instant
中提取一个ZonedDateTime
。Instant instant = zdt.toInstant() ;
符合ISO 8601的字符串末尾的
Z
表示UTC,并发音为“ Zulu”。参见此code run live at IdeOne.com。
输入:2018-12-31-12.30.50.000200
ldt:2018-12-31T12:30:50.000200
zdt:2018-12-31T12:30:50.000200 + 09:00 [亚洲/东京]
即时:2018-12-31T03:30:50.000200Z
关于java.time
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如
java.util.Date
,Calendar
和SimpleDateFormat
。要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
现在位于Joda-Time中的maintenance mode项目建议迁移到java.time类。
您可以直接与数据库交换java.time对象。使用与JDBC driver或更高版本兼容的JDBC 4.2。不需要字符串,不需要
java.sql.*
类。在哪里获取java.time类?
Java SE 8,Java SE 9,Java SE 10,Java SE 11和更高版本-具有捆绑实现的标准Java API的一部分。
Java 9添加了一些次要功能和修复。
Java SE 6和Java SE 7
大多数java.time功能都被反向移植到ThreeTen-Backport中的Java 6和7。
Android
更高版本的Android捆绑了java.time类的实现。
对于较早的Android(ThreeTenABP项目改编为ThreeTen-Backport(如上所述)。请参见How to use ThreeTenABP…。
ThreeTen-Extra项目使用其他类扩展了java.time。该项目是将来可能向java.time添加内容的试验场。您可能会在这里找到一些有用的类,例如
Interval
,YearWeek
,YearQuarter
和more。关于java - 如何将时间戳转换为日期,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55493431/