解析日期时的NumberFormatException

 十点半的 发布于 2023-02-04 20:44

有一个创建仅限时间的Date对象的函数.(为什么这是必需的是一个长篇故事,在这种情况下是无关紧要的,但我需要与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),但我的问题实际上并非如此.

我的问题是这段代码在生产运行很长时间后开始在#8行上随机抛出NumberFormatException.从技术上讲,我会说这应该是不可能的,对吧?

以下是来自上面一段代码的随机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"

首先,我希望我们能够正式认为这应该是不可能的吗?代码使用相同的format(DF_TIMEONLY)作为输出然后输入.如果你不同意它应该是不可能的,请告诉我.

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

我怀疑在JVM或者甚至在CPU中某处存在某种算术故障.上述例外情况表明涉及浮点数,但我没有看到它们来自何处.据我所知,Java的Date对象是一个包装器,long它包含了自纪元以来的millis数.

我猜测正在发生的事情是onlyTimeStr在第5行中创建了一个意外的字符串,所以这个问题确实存在于这里而不是第8行.

以下是完整堆栈跟踪的示例:

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不是线程安全的事实,并且您从多个线程引用它.虽然极难证明(并且难以测试),但有证据表明情况如此:

      .11331133EE22 - 注意一切都变了一倍

      880044E.3880044E3 - 同样在这里

    您可能至少有两个线程交错.将E被扔我,我想它试图处理科学记数法(1E10等),但它是对可能部分时区.

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

    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之类的东西.请注意,LocalTime没有附加时区,但Date只返回整数小时的偏移量(并且小时内没有区域); 为了安全起见,你需要返回一个Calendar(带有完整的时区),或者只返回没有它的东西:

    // 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;
    }
    

    2023-02-04 20:45 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有