安全的SimpleDateFormat解析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安全的SimpleDateFormat解析相关的知识,希望对你有一定的参考价值。

我有一小段代码,它从响应本身解析响应生成时间,并将其转换为日期以供将来使用。它是这样的:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Date responseTime = sdf.parse(RStime);

它几乎像一个魅力。确切地说,它有99.9%的时间工作,除了一种情况:当毫秒部分为000时,服务器根本不会附加.000毫秒,因此我们遇到了问题。

现在,根据SimpleDateFormat docs,如果解析失败,该函数返回null。但是,我可能误解了它,因为它只是抛出异常。

我是Java和try-catch机制的新手,所以有谁能提供一个优雅的良好实践解决方案来处理这种情况?

谢谢!

答案

java.time

String rsTime = "2018-04-09T10:47:16.999-02:00";
OffsetDateTime responseTime = OffsetDateTime.parse(rsTime);
System.out.println("Parsed date and time: " + responseTime);

此代码段的输出是:

解析日期和时间:2018-04-09T10:47:16.999-02:00

对于省略了000毫秒的版本,它也可以正常工作:

String rsTime = "2018-04-09T10:47:16-02:00";

解析日期和时间:2018-04-09T10:47:16-02:00

你使用的课程,SimpleDateFormatDate,设计很差,很久了(前者特别臭名昭着)。因此,不仅在这种特殊情况下,我建议使用java.time,即现代Java日期和时间API。但是,服务器中的字符串采用ISO 8601格式,而OffsetDateTime和其他类java.time将此格式解析为默认格式,即没有任何显式格式化程序,这使得任务变得非常容易。此外,在标准中,小数秒是可选的,这就是解析字符串的两个变体而没有任何问题的原因。 OffsetDateTime还从它的toString方法打印ISO 8601,这就是为什么在这两种情况下都会打印与解析的字符串相同的字符串。

只有在你不可或缺地需要一个传统API的老式Date对象的情况下,你现在无法改变,转换如下:

Instant responseInstant = responseTime.toInstant();
Date oldfashionedDateObject = Date.from(responseInstant);
System.out.println("Converted to old-fashioned Date: " + oldfashionedDateObject);

我在欧洲/哥本哈根时区的电脑输出是:

转换为老式日期:Mon Apr 09 14:47:16 CEST 2018

链接:Oracle tutorial: Date Time解释如何使用java.time。

另一答案

根据SimpleDateFormat doc,你提到了parse方法:

public Date parse(String text, ParsePosition pos)

抛出:

NullPointerException - 如果text或pos为null。

因此,一种选择是捕获该异常并在catch中执行您需要的操作,例如:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try 
  Date responseTime = sdf.parse(RStime, position);
 catch (NullPointerException e) 
  e.printStackTrace();
  //... Do extra stuff if needed

或者来自DateFormat的继承方法:

public Date parse(String source)

抛出:

ParseException - 如果无法解析指定字符串的开头。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try 
  Date responseTime = sdf.parse(RStime);
 catch (ParseException e) 
  e.printStackTrace();
  //... Do extra stuff if needed

另一答案

这实际上是一种特殊的情况吗?如果不是那么你可能不应该在这种情况下使用例外。在我看来,时间可以以.000ms结束是正常的。在这种情况下,您可以检查字符串是否包含.(点),如果不包含.000到最后。

if(!RStime.contains("."))
    RStime+=".000";

编辑:我忘了时间字符串中的时区。你可能需要一些更复杂的东西。这样的事情应该这样做:

if(!RStime.contains("."))
    String firstPart = RStime.substring(0, 21);
    String secondPart = RStime.substring(21);
    RStime = firstPart + ".000" + secondPart;

另一答案

您可以检查点,然后使用第一种或第二种格式:

String timeString = "2018-04-09T10:47:16.999-02:00";
//String timeString = "2018-04-09T10:47:16-02:00";
String format = timeString.contains(".") ? "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" : "yyyy-MM-dd'T'HH:mm:ssXXX";
Date responseTime = new SimpleDateFormat(format).parse(timeString);
System.out.println("responseTime: " + responseTime);

如果您注释掉第一行并在第二行注释并再次运行它,它将打印出来:

responseTime: Mon Apr 09 14:47:16 CEST 2018

顺便说说:

  • Java 7(显然你使用的版本)返回一个java.text.ParseException: Unparseable date: "2018-04-09T10:47:16-02:00"
  • 自Java 8以来支持Optionals。

以上是关于安全的SimpleDateFormat解析的主要内容,如果未能解决你的问题,请参考以下文章

SimpleDateFormat,Calendar 线程非安全的问题

(转)关于SimpleDateFormat安全的时间格式化线程安全问题

SimpleDateFormat时间格式化存在线程安全问题

深入理解Java:SimpleDateFormat安全的时间格式化 ;

深入理解Java:SimpleDateFormat安全的时间格式化

并发条件下SimpleDateFormat线程不安全及解决方案