【问题标题】:Possible bug in Java 8 SimpleDateFormat? [duplicate]Java 8 SimpleDateFormat 中可能存在的错误? [复制]
【发布时间】:2020-05-07 15:58:14
【问题描述】:

这里是Java 8,我有以下代码:

public class PossibleBug {

  public static void main(String[] args) {
    new PossibleBug().run();
  }

  public void run() {

    buildDate("20181205");

  }

  public Date buildDate(final String yyyyMmDd) throws ParseException {
    TimeZone expectedTz = TimeZone.getTimeZone("America/New_York");
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    sdf.setTimeZone(expectedTz);

    TimeZone actualTz = sdf.getTimeZone();

    Date answer = sdf.parse(yyyyMmDd);
    return answer;
  }

}

非常基本的东西:

  1. 创建一个 SimpleDateFormat 并将其时区设置为 EST
  2. 使用 SDF 解析日期字符串
  3. 结果应该也是 EST 中的日期

不过在运行时,看看调试器的结果:

这怎么可能?!?! sdf.parse(yyyyMmDd) 正在返回一个 GMT 格式的日期。是我遗漏了什么还是SimpleDateFormat 中的错误?

我能够调用 buildDate 并从不同的类中运行它,它似乎工作正常:

【问题讨论】:

  • 您已明确标记为 [java-8],因此不要使用DateSimpleDateFormat。使用来自java.time 的类。
  • 这对我来说很好用。输出为Wed Dec 05 00:00:00 EST 2018。所以你的环境有问题。
  • 05.12.2018 00:00 解释为在 America/New_York 在您的示例中打印为 GMT 05:00。
  • 我建议你不要使用TimeZoneSimpleDateFormatDate。这些类设计糟糕且过时,SimpleDateFormat 特别是出了名的麻烦。而是使用ZoneIdLocalDateDateTimeFormatter,均来自java.time, the modern Java date and time API

标签: java java-8 simpledateformat


【解决方案1】:

documentation 说:“此解析操作使用日历生成日期。在解析之前清除日历的所有日期时间字段,并且日历的日期时间字段的默认值用于任何丢失日期-时间信息。例如,如果解析操作没有给出年份值,则解析的 Date 的年份值为 1970 年和 GregorianCalendar。TimeZone 值可能会被覆盖,具体取决于给定的模式和时区值在文本中。之前通过调用 setTimeZone 设置的任何 TimeZone 值可能需要恢复以进行进一步操作。"

简而言之,SimpleDateFormat 是一个格式化程序/解析器,而不是执行时区转换的实用程序。如果要解析的字符串中没有 TZ,则从 Calendar 获取默认值。

考虑一下如果您调用setTimeZone, 然后解析一个实际上包含时区本身的字符串会发生什么?您希望会发生什么?

另外,请注意Date 不包含时区。它特别定义为自 1970 年 1 月 1 日 00:00 UTC 以来的毫秒数。库函数在需要时应用时区(例如在转换为 String 时),如果您未指定时区,您将获得默认时区。您看到 GMT 是因为您的默认时区是 GMT,或者因为您的 IDE 始终在 GMT 中显示 Date 对象,并且说他必须拥有 EST 的人将默认时区设置为 EST。

在您的情况下,您正在解析一个根本不包含时区的字符串。事实上,它甚至不包含时间。使用Date 处理,呃,日期(我意识到这很令人困惑,我的意思是没有时间的日期),可能会导致错误,尤其是当您的默认时区不是 UTC/GMT 时。我推荐使用LocalDateLocalDate.parse 方法。

【讨论】:

    【解决方案2】:

    如果您查看the Java-Doc for SimpleDateFormat.parse(),您会发现 TimeZone 可能被覆盖:

    TimeZone 值可能会被覆盖,具体取决于text 中的给定模式和时区值。之前通过调用setTimeZone 设置的任何TimeZone 值可能需要恢复以进行进一步操作。

    【讨论】:

    • 感谢您的建议!
    【解决方案3】:

    Date 不存储时区。它本质上只是一个 long 的包装器,在 epoch 之后存储毫秒。

    当您打印它时(或当您的调试器调用 toString() 方法来获取要显示的字符串表示时),将使用您的 JVM 的默认时区,而不管它是如何创建的。

    Date,尽管有这个名字,但它并没有模拟日期:它是一个即时时间。

    鉴于您的输入是"20181205",请不要使用Date:使用java.time 中的类,例如java.time.LocalDate

    【讨论】:

    • 是的。 .
    • 您是否在该代码中设置了默认时区?
    • @hotmeatballsoup 该答案与您的问题代码相同,它在解析日期时使用时区。它绝不表示生成的 Date 对象具有时区。因此,在您的情况下,您的调试器以不同的方式格式化日期。这可能很奇怪,但与您的应用程序完全无关。
    • 您正在将日期字符串转换为 Date 对象,这实际上是一个瞬间。同样,当您将这个名为Date 的瞬间格式化为实际的日期/时间字符串时,将再次使用时区。正如你所看到的那样,它会有所不同。格式化为 EST 时,同一时刻变为 Wed Dec 05 00:00:00,而格式化为 GMT 时变为 Wed Dec 05 05:00:00
    • @hotmeatballsoup 看看TimeZone.getDefault() 在这两个地方是什么。 可能没有设置它,但也许是别的东西(这就是可变全局状态的问题)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    相关资源
    最近更新 更多