其他系统会在他们所在的时区为我们发送时间戳。如果在其他系统中在云中运行,则需要+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 zoneoffset-from-UTC指示符。
交换日期时间值时,请使用标准的ISO 8601格式。
在Java中仅使用java.time类。切勿使用DateTimestampCalendar等。
提示:在发送之前(通常来说),请将其他区域的值调整为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.Timestampjava.util.Date。根据JSR 310的使用,与最早的Java版本捆绑在一起的所有日期时间类现在都是旧的。仅使用现代的java.time类。

数据类型错误

您使用了错误的数据类型。要跟踪时刻,即时间轴上的特定点,您必须具有时区或UTC偏移量。 LocalDateTime类完全是在此处使用的错误类。该类故意缺少区域或偏移的任何概念。因此,这与您想要的相反。

要跟踪时刻,请使用InstantOffsetDateTimeZonedDateTime

java - 如何将时间戳转换为日期-LMLPHP

如果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/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4个字母的缩写,例如ESTIST,因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。

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.DateCalendarSimpleDateFormat

要了解更多信息,请参见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 8Java SE 9Java SE 10Java SE 11和更高版本-具有捆绑实现的标准Java API的一部分。


Java 9添加了一些次要功能和修复。

Java SE 6Java 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添加内容的试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

关于java - 如何将时间戳转换为日期,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55493431/

10-11 05:58