【问题标题】:How convert Timestamp to Date如何将时间戳转换为日期
【发布时间】:2019-08-24 21:08:26
【问题描述】:

其他系统在他们的时区为我们发送时间戳。如果我们在其他系统的云中运行它是 +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')

【问题讨论】:

  • 由于一致性和时区问题,Date 自 Java 1.8 起已弃用。 Timestamp 是什么?它不是 Java 的通用类之一。
  • 您的问题不是转换为日期。您的问题是您的系统创建了一个字符串,您必须在其中猜测时区。最好确保将系统发送的字符串调整为 UTC 并采用标准格式来表明这一点。这些将很容易转换。仅当实际实例不重要时才应使用当地时间。
  • 我建议您既不要使用也不要使用TimestampDate。这些课程设计不佳且早已过时。更强烈的是,您已经在使用java.time, the modern Java date and time API。取而代之的是时间戳使用来自 java.time 的Instant
  • @Deluxxx 将更多详细信息作为对您的问题的编辑而不是作为评论发布。读者不必翻阅评论即可理解问题。

标签: java date timestamp


【解决方案1】:

只是对Basil Bourque’s clever and very informative answer的一个小补充。

  1. 我知道日期是 CET 时区。

对不起,这还不够。中欧时间(CET)是许多欧洲和非洲(!)时区的通用术语,其细节不同。欧洲标准时间的偏移量通常为 +01:00,夏季的偏移量为 +02:00(称为中欧夏令时或 CEST)。非洲的全年都是 +01:00。对于过去的日期,就在几十年前,一些地区使用夏令时 (DST),另一些则没有,一些地区在 +00:00、+01:00 或 +02:00,并且在历史上更早的许多其他偏移量被使用,一般不是整个小时。

未来更糟!有人建议欧盟放弃夏令时,由各成员国决定是使用永久标准时间还是永久夏令时,避免春秋两季的时间调整。关于这件事正在进行一场权力斗争,所以我们不知道它是否会发生,也不知道每个成员国会选择什么。因此,即使您可以从其他系统(例如,Europe/Sarajevo)告诉我您的字符串的确切时区,也没有人知道2019-11-01-00.30.50.000200(从现在起不到 7 个月)是否会位于偏移量 +01: 00 或 +02:00。

链接:European MPs vote to end summer time clock changes BBC 新闻。

【讨论】:

    【解决方案2】:

    tl;博士

    我如何确定时间永远是正确的?

    • 在日期时间输入字符串中包含 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.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/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 中的时刻,除非您有特定原因使用时区(例如向用户演示)。所以从你的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.DateCalendarSimpleDateFormat

    要了解更多信息,请参阅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 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

    【讨论】:

      猜你喜欢
      • 2012-04-10
      • 2016-11-26
      • 2019-04-03
      • 1970-01-01
      • 2020-10-19
      • 1970-01-01
      相关资源
      最近更新 更多