NumberFormatException, while parsesing date with SimpleDateFormat.parse()城大时间java.time

2022-09-01 12:42:40

具有创建仅时间日期对象的函数。(为什么需要这是一个很长的故事,在这个上下文中是无关紧要的,但我需要与XML世界中的一些东西进行比较,其中TIME(即仅时间)是一个有效的概念)。

private static final SimpleDateFormat DF_TIMEONLY = new SimpleDateFormat("HH:mm:ss.SSSZ");    

public static Date getCurrentTimeOnly() {

    String onlyTimeStr = DF_TIMEONLY.format(new Date());  // line #5
    Date  onlyTimeDt = null;
    try {
        onlyTimeDt = DF_TIMEONLY.parse(onlyTimeStr);  // line #8
    } catch (ParseException ex) { 
        // can never happen (you would think!)
    }
    return onlyTimeDt;
}

在Java中可能至少有几种其他方法可以创建纯时间日期(或者更准确地说,日期部分是1970-01-01),但我的问题真的不是关于这一点的。

我的问题是,这段代码在生产环境中运行很长时间后,开始随机将NumberFormatException放在第8行。从技术上讲,我会说这应该是不可能的,对吧?

以下是来自上述代码段的随机NumberFormatExceptions的摘录:

java.lang.NumberFormatException: multiple points
java.lang.NumberFormatException: For input string: ".11331133EE22"
java.lang.NumberFormatException: For input string: "880044E.3880044"
java.lang.NumberFormatException: For input string: "880044E.3880044E3"

首先,我希望我们能同意,正式地说这应该是不可能的?代码使用与输出相同的格式 (),然后使用输入。如果您不同意这应该是不可能的,请告诉我。DF_TIMEONLY

我无法在独立环境中重新生成问题。当JVM运行很长时间(>1周)时,问题似乎就来了。我找不到问题的模式,即夏令时/冬令时,AM / PM等。该错误是零星的,这意味着前一分钟它将抛出NumberFormatException,下一分钟它将运行良好。

我怀疑在JVM甚至CPU的某个地方都存在某种算术故障。上述例外表明涉及浮点数,但我不明白它们来自哪里。据我所知,Java的Date对象是一个包装器,它保存自纪元以来的millis数量。long

我猜正在发生的事情是,在第5行创建了一个意外的字符串,所以问题真的在这里,而不是在第8行。onlyTimeStr

下面是一个完整的堆栈跟踪示例:

java.lang.NumberFormatException: For input string: "880044E.3880044E3"
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1241)
    at java.lang.Double.parseDouble(Double.java:540)
    at java.text.DigitList.getDouble(DigitList.java:168)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2086)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
    at java.text.DateFormat.parse(DateFormat.java:355)
    at org.mannmann.zip.Tanker.getCurrentTimeOnly(Tanker.java:746)

环境:Java 7


答案 1

可能的原因是这不是线程安全的,并且您从多个线程引用它。虽然难证明(并且几乎难以测试),但有一些证据表明情况确实如此:SimpleDateFormat

  1. .11331133EE22- 注意一切都是双倍的
  2. 880044E.3880044E3- 这里相同

您可能至少有两个线程交错。我一直在想,我以为它试图处理科学记数法(1E10等),但它可能是时区的一部分。E

值得庆幸的是,(格式化)基本修复很简单:

private static final String FORMAT_STRING = "HH:mm:ss.SSSZ";    

public static Date getCurrentTimeOnly() {

    SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_STRING);

    String onlyTimeStr = formatter.format(new Date());
    return formatter.parse(onlyTimeStr);
}

你也可以在这里做一些其他的事情,但有一些注意事项:

1 - 如果时区是 UTC(或任何没有 DST 的时区),这是微不足道的

public static Date getCurrentTimeOnly() {

    Date time = new Date();

    time.setTime(time.getTime() % (24 * 60 * 60 * 1000));

    return time;
}

2 - 测试此方法时会遇到麻烦,因为您无法安全地暂停时钟(可以更改时区/区域设置)。为了更好地处理Java中的日期/时间,请使用类似JodaTime的东西。请注意,没有附加时区,但仅返回以整数小时为单位的偏移量(并且有些区域不在小时上);为了安全起见,您需要返回一个(具有完整时区),或者只是返回没有它的东西:LocalTimeDateCalendar

// This method is now more testable.  Note this is only safe for non-DST zones
public static Calendar getCurrentTimeOnly() {

    Calendar cal = new Calendar();

    // DateTimeUtils is part of JodaTime, and is a class allowing you to pause time!
    cal.setTimeInMillis(DateTimeUtils.currentTimeMillis() % (24 * 60 * 60 * 1000));

    return cal;
}

答案 2

城大时间

仅供参考,Joda-Time 2.3库提供了一个明确用于您的目的的类,仅限时间,没有任何日期:LocalTime。而且,它是线程安全的不可变实例)。似乎比手动处理麻烦的java.util.Date类要好得多。

LocalTime localTime = new LocalTime();

转储到控制台...

System.out.println( "localTime: " + localTime );

运行时...

localTime: 16:26:28.065

java.time

Java 8 带来了新的 java.time 包,灵感来自 JSR 310 定义的 Joda-Time。

在java.time中,你会发现一个类似于Joda-Time中的LocalTime类。