tl;博士
我如何确定时间永远是正确的?
如果不可能,那么这里有一个解决方法。这假设您知道这些不良数据的发送者打算使用的时区。
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" )
)
或者,使用Instant,根据定义,它始终采用 UTC。
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 中的时刻,除非您有特定原因使用时区(例如向用户演示)。所以从你的ZonedDateTime 中提取一个Instant。
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 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。