1 测试代码如下:
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public static void main(String[] args) {
String date = "07-31-2017 7:32:06 PM";
DateTimeFormatter builder = DateTimeFormat.forPattern("MM-dd-yyyy h:mm:ss a");
System.out.println(builder.parseDateTime(date));
}
运行,抛出如下异常:
Exception in thread "main" java.lang.IllegalArgumentException: Invalid format: "07-31-2017 7:32:06 PM" is malformed at "PM"
at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:945)
at com.cardniu.loanremind.push.Entry.main(Entry.java:31)
2 尝试解决
参考这篇文章,https://stackoverflow.com/questions/32045626/joda-hhmm-am-is-malformed-at-am/,改成如下代码:
public static void main(String[] args) {
String date = "07-31-2017 7:32:06 PM";
//withLocale(Locale.ENGLISH)是新增代码
DateTimeFormatter builder = DateTimeFormat.forPattern("MM-dd-yyyy h:mm:ss a").withLocale(Locale.ENGLISH);
System.out.println(builder.parseDateTime(date));
}
问题解决,但是很可惜,这篇文章只给出了解决方案,但没有解释原因,于是我就调试源代码,来找到背后的原因.
3 源码调试.
根据抛出的异常,我们首先查看DateTimeFormatter类的parseDateTime方法源码,如下:
首先简单解释下1出代码,创建一个DateTimeParserBucket对象,代码如下:
请特别留意?号出的代码,在后面揭秘原因时,要用到
关于DateTimeParserBocket类的解释,如下:
简单分析,会发现是由于newPos的值为负数时,导致程序有异常的,于是我们接着查看org.joda.time.format.DateTimeFormatterBuilder.Composite.parseInto方法的源码:
1处的DateTimeParserBucket类不再啰嗦,关于InternalParser接口的parseInto方法解释如下:
对3处的代码理解:就是利用各种InternalParser的实现类对”07-31-2017 7:32:06 PM”的每部分(年,月,日,时,分,秒)进行解析,保存到bucket中,那InternalParser的实现类又有那些了?如下图:
其中2处的org.joda.time.format.DateTimeFormatterBuilder.Composite这种解析实现类我们在前面使用过,因为我们的异常出现在对”PM”的解析上,而对”PM”使用的解析实现类为1处的org.joda.time.format.DateTimeFormatterBuilder.TextField实现类,来让我们分析下该解析类的parseInto方法:
1处代码:从bucket中获得保存的Locale(想起来在前面提到的?处的代码了吗).
2处代码:根据不同的Locale往validValues中put诸如(“上午”,true),(“下午”,true)这类键值对(当Locale为CHINESE时),若Locale.ENGLISH,put诸如(“am”,true),(“AM”,true),(“pm”,true),(“PM”,true)等值.
3处代码是关键,请看下图:
此时真相大白于天下, validValues不存在key为”PM”的值,导致程序返回异常,
原因在于创建bucket对象时使用的是Locale.CHINESE(此时的Locale.getDefault()返回值).validValues中put诸如(“上午”,true),(“下午”,true)这类键值对
原因找到了,修改起来就很容易,那就是在创建DateTimeFormatter对象实例时指定Locale为ENGLISH.
1通过以上分析,我们可以明白解析”07-31-2017 7:32:06 pM”中的”pM”是肯定不成功的,就算你设置Locale为ENGLISH,也不行,因为不支持这种key值.
2 这是自己第一次通过分析源码来找到异常背后的原因,而不只是停留在把问题解决就够了的层面,做到知其然更要知其所以然.