public static void main(String[] args) throws ParseException {String test = "2018-12-06";System.out.println("转换之前日期:"+test);SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");Date date = sdf.parse(test);System.out.println("转换之后日期:"+new SimpleDateFormat("yyyy-MM-dd").format(date));}

请问以上代码的执行是否会抛ParseException异常?如果不抛异常,那么执行结果是?

不卖关子了,答案是不会抛异常,而且能够将日期转换出来,但是这个日期明显穿越了!

我们接下来看看SimpleDateFormat是如何带领我们穿越时空的。这个parse方法到底在搞什么飞机!

先说一下SimpleDateFormat日期匹配的一个简单流程,先有一个大致印象。

  1. SimpleDateFormat初始化会调用initialize方法,该方法会对传入的日期格式进行重新编码并放入compiledPattern数组中。
  2. 调用parse方法进行日期匹配,通过循环第1步编码好的compiledPattern数组,分别匹配对应的年月日,然后存入CalendarBuilder类的实例对象calb中。
  3. 最后一步是调用CalendarBuilder类的实例对象calb的getTime方法返回Date对象。

接下来我们来详细看看parse方法的详细代码

@Overridepublic Date parse(String text, ParsePosition pos){checkNegativeNumberExpression();int start = pos.index;int oldStart = start;int textLength = text.length();boolean[] ambiguousYear = {false};CalendarBuilder calb = new CalendarBuilder();for (int i = 0; i < compiledPattern.length; ) {int tag = compiledPattern[i] >>> 8;int count = compiledPattern[i++] & 0xff;if (count == 255) {count = compiledPattern[i++] << 16;count |= compiledPattern[i++];}switch (tag) {case TAG_QUOTE_ASCII_CHAR:if (start >= textLength || text.charAt(start) != (char)count) {pos.index = oldStart;pos.errorIndex = start;return null;}start++;break;case TAG_QUOTE_CHARS:while (count-- > 0) {if (start >= textLength || text.charAt(start) != compiledPattern[i++]) {pos.index = oldStart;pos.errorIndex = start;return null;}start++;}break;default:// Peek the next pattern to determine if we need to// obey the number of pattern letters for// parsing. It's required when parsing contiguous// digit text (e.g., "20010704") with a pattern which// has no delimiters between fields, like "yyyyMMdd".boolean obeyCount = false;// In Arabic, a minus sign for a negative number is put after// the number. Even in another locale, a minus sign can be// put after a number using DateFormat.setNumberFormat().// If both the minus sign and the field-delimiter are '-',// subParse() needs to determine whether a '-' after a number// in the given text is a delimiter or is a minus sign for the// preceding number. We give subParse() a clue based on the// information in compiledPattern.boolean useFollowingMinusSignAsDelimiter = false;if (i < compiledPattern.length) {int nextTag = compiledPattern[i] >>> 8;if (!(nextTag == TAG_QUOTE_ASCII_CHAR ||nextTag == TAG_QUOTE_CHARS)) {obeyCount = true;}if (hasFollowingMinusSign &&(nextTag == TAG_QUOTE_ASCII_CHAR ||nextTag == TAG_QUOTE_CHARS)) {int c;if (nextTag == TAG_QUOTE_ASCII_CHAR) {c = compiledPattern[i] & 0xff;} else {c = compiledPattern[i+1];}if (c == minusSign) {useFollowingMinusSignAsDelimiter = true;}}}start = subParse(text, start, tag, count, obeyCount,ambiguousYear, pos,useFollowingMinusSignAsDelimiter, calb);if (start < 0) {pos.index = oldStart;return null;}}}// At this point the fields of Calendar have been set.  Calendar// will fill in default values for missing fields when the time// is computed.pos.index = start;Date parsedDate;try {parsedDate = calb.establish(calendar).getTime();// If the year value is ambiguous,// then the two-digit year == the default start yearif (ambiguousYear[0]) {if (parsedDate.before(defaultCenturyStart)) {parsedDate = calb.addYear(100).establish(calendar).getTime();}}}// An IllegalArgumentException will be thrown by Calendar.getTime()// if any fields are out of range, e.g., MONTH == 17.catch (IllegalArgumentException e) {pos.errorIndex = start;pos.index = oldStart;return null;}return parsedDate;}

checkNegativeNumberExpression方法里面的内容就不赘述了,重点看循环里面的内容。

1.循环compiledPattern数组,compiledPattern里面存的是之前initialize初始化方法编码好的日期格式。先对compiledPattern[i]进行高8位和低8位的判断,决定对日期截取的字符个数count。接着对匹配模式的一个switch判断,如果是数字模式会进入default,这里有两个关键参数: obeyCount和useFollowingMinusSignAsDelimiter(注:通过源码注释,obeyCount的作用可以理解为作为一个纯数字的匹配模式标记。useFollowingMinusSignAsDelimiter通过注释的解释是后面的subParse方法需要将数字后面的-(负号/减号)标记出来。)

2. subParse方法将对日期字符串进行截取,并转换成CalendarBuilder类的实例calb的日期参数(调用calb.set(field, value),这个方法设置的是日历字段值的参数)。问题就出在这个方法里面。

第一次循环,截取年份,text的值是"2018-12-06",count=4。起始下标start=0截取出来是2018。

第二次循环,截取月份,text的值还是"2018-12-06",count=2,起始下标start=4,那么截取出来的数字是-1(这里的-号不是作为分隔符,而是被认为了数字的正负符号!)。

第三次循环,截取日期,text的值还是"2018-12-06"count=2,起始下标start=6,截取出来的数字是2-(也就是正数2)。

到这里,疑问已经渐渐明朗~

3.最后调用calb.establish(calendar).getTime()方法构造日期对象。getTime方法里面实际是调用了Calendar类的computeTime方法,这个方法会根据第2步中设置的日历字段的值来返回一个日期的毫秒值。

总结:

1.为什么不抛异常?例如对于-2来说,这在Number里面代表的是负数的2(-被当做负号),在转换Number的时候能够正常返回,只有当Number格式化返回null的时候,才会抛出java.text.ParseException异常,比如将日期改为2018~12-06

2.平时在使用SimpleDateFormat格式化日期的时候,除了严格控制日期的格式,另外还要注意时区,不然SimpleDateFormat还有可能再次带你穿越时空哦。

转载于:https://my.oschina.net/u/2601381/blog/3032934

SimpleDateFormat的时空穿越之旅相关推荐

  1. 时尚穿越html5游戏,时尚穿越之旅

    时尚穿越之旅是一款橙光穿越类游戏.普通服装设计师苏墨雪突然穿越成一个绝世倾城的女子,如何在高冷的王爷,温柔的书生,挥金如土却视其为之珍宝的大少爷,以及一切都是迷的魔教教主之间回转,又如何以现代的设计能 ...

  2. 南澳.西冲-东冲穿越之旅

    南澳.西冲-东冲穿越之旅 这是我第一次游后记日记,不仅仅是因为这次旅途本身的惊险刺激,而是有更多的感慨和经验,让我情不自禁的把它写出来和其他人一起分享. 西冲,东冲位于深圳东南大鹏半岛的最南端,沿海岸 ...

  3. 时空人文之旅(一)古代人民眼中的“宇宙”——写在第九届中国卫星导航学术年会之际

    时空人文之旅(一)古代人民眼中的"宇宙"--写在第九届中国卫星导航学术年会之际 粟卫民  http://blog.csdn.net/suen/ 日期:2018-5-24 作者也开通 ...

  4. 视差滚动:零基础css代码实现时空穿越效果

    先贴出最终效果: 上面的图片中,底层为冬季大树图片,上层为春季大树图片. 上层元素滚动之间,春季图片的位置却没有变化,感觉像是上层元素的滚动使底层元素的冬季大树图片实现"时空穿越" ...

  5. 量子计算机时空穿越,美俄顶尖专家发现:量子世界时间可以停止,未来穿越时空能做到?...

    原标题:美俄顶尖专家发现:量子世界时间可以停止,未来穿越时空能做到? ​趣味探索讯 不管我们是在消磨时光,还是在努力学习,不管我们是醒着,还是在睡觉,时间齿轮始终无法停止,光阴之箭继续一分一秒地流逝, ...

  6. 量子计算机时空穿越,科学界沸腾!俄美两国科学家竟让时光倒流,未来真能时空穿越?...

    原标题:科学界沸腾!俄美两国科学家竟让时光倒流,未来真能时空穿越? 趣味探索讯 不论你是在工作,还是在休息,不论你是在吃饭,还是在睡觉,时间都在"滴答,滴答"地流逝,而我们也会因为 ...

  7. 量子计算机时空穿越,科学家用量子计算机模拟时间机器 人类能否回到过去?...

    时间旅行一直是一个让人着迷的事情,像最近火爆的<复仇者联盟4>就涉及了时间旅行的概念.在理论上伟大的爱因斯坦将人类带入了量子物理时代,他的相对论也印证了时间旅行是有可能的,这也让无数的科学 ...

  8. 01、开启时空克隆之旅 C++三维视频融合实战系列(时空克隆)

    几年来,在数字孪生的概念指引下,各行各业的三维可视化项目蓬勃发展,政府也实时提出了打造实景三维中国的目标. 相比于数字孪生,时空克隆有什么不同吗?单从字面意思来讲,什么是孪生?孪生就是我们俗称的双胞胎 ...

  9. 时空穿越!谷歌利用众包老照片还原儿时3D街景,浏览器即可体验

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 [导读]对许多人来说,凝视一张城市的旧照片,可以唤起怀旧和好奇的感觉.你有没有想过在20世纪40年代漫 ...

最新文章

  1. 建立注册DLL和反注册DLL文件的快捷方式
  2. 科目三电子路考哪些情况会被评判不合格
  3. 从另一个角度看大数据量处理利器:布隆过滤器
  4. 年逾九十院士潘际銮:身背千亿科研价值身居斗室
  5. The Rise of Worse Is Better 论文-学习笔记
  6. android+usb转串口+唯一id,Android平台3G模块驱动移植-USB转串口
  7. WebRTC之gn与ninja(十三)
  8. mysql时间设计模式_java 23种设计模式及具体例子 收藏有时间慢慢看
  9. BZOJ1064[NOI2008] 假面舞会
  10. Java数组:随机排序
  11. Python常用模块-20个常用模块总结
  12. congratulation的用法_congratulation用法详解
  13. postman接口测试之断言+参数化
  14. day python calss08 深浅copy
  15. java面试题2019最新
  16. 程序员成长之路(一)
  17. matlab绘制星座图,怎么弄星座图:systemview 信号星座图怎么画
  18. 笔记-项目沟通管理-沟通方法和方式
  19. SWIFT,国际清算与数字人民币
  20. iOS开发键盘设置,IOS7深灰色键盘

热门文章

  1. WOW技术-----1, 模型的高光
  2. android webview 铺满_统编三年级上《铺满金色巴掌的水泥路》学习笔记
  3. java学习到了瓶颈期,要怎么样才能进阶?
  4. 【推荐算法】点击率预估模型(CTR) 快速入门(赋源码)
  5. 斯坦福命名实体识别(Stanford Named Entity Recognizer)
  6. 阿里妈妈2018的DIN DIEN
  7. DIEA ,Ecplise 配置谷歌代码风格 Google Java Style
  8. PHP8编译swoole,php安装swoole
  9. pgdac和unidac插入效率究竟差多少?
  10. 群组测试(Group testing)介绍