在一个项目中需要获取数据库中某天0时0分0秒到第二天0时0分0秒之间的记录,一开始想到的是之间使用Date对象的getTime()方法获得当前的微秒数,然后截掉小时、分钟和微秒得到该日期的0时0分0秒,代码如下:
1 | final long MICROSECONDS_PER_DAY = 24 * 60 * 60 * 1000; |
一切看上去都很reasonable,日期也是对的,不过时间并不是0时0分0秒,而是8时0分0秒
1 | System.out.println(today); //Wed Jun 01 08:00:00 CST 2016 |
为了搞清楚问题出在哪里,对Java中与date、time相关概念做了一次梳理。
时区(TimeZone)
时区(Time Zone)是地球上的区域使用同一个时间的定义,1884年在华盛顿召开国际经度会议时,为了克服时间上的混乱,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1-12区,西1-12区。每个时区横跨经度15度,时间正好是1小时。最后的东、西第12区各跨经度7.5度,以东、西经180度为界。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时,相邻两个时区的时间相差1小时。例如,中国东8区的时间总比泰国东7区的时间早1小时,而比日本东9区的时间迟1小时。因此,出国旅行的人,必须随时调整自己的手表,才能和当地时间相一致。凡向西走,每过一个时区,就要把表拨慢1小时(比如2点拨到1点);凡向东走,每过一个时区,就要把表拨快1小时(比如1点拨到2点)。并且规定英国(格林尼治天文台旧址)为本初子午线,即零时(24时)经线。在中国采用首都北京所在地东八区的时间为全国统一使用时间。
Java中的TimeZone对应现实世界中的时区,可以使用getTimeZone(String ID)获得某个时区,例如使用TimeZone.getTimeZone(“Asia/Shanghai”)可以得到中国标准时间的时区,使用TimeZone.getDefault()可以得到系统标准时区。
Date
在Java 7中,Date类除了Date(long milliseconds)和Date()两个构造器外,其它构造器都废弃了。这两个构造器都基于从1970年1月1日00:00:00GMT算起的微秒数,默认构造器以系统当前的微秒数构造对象。
1 | //Date类的构造器 |
Date对象常用 after(Date when)、before(Date when)和compareTo(Date anotherDate)这三个比较函数,这些比较都是基于上述的微秒数的。另外的一个常用函数getTime()用来获取Date对象的微秒数。
值得一提的是Date对象的toString()方法,这个方法在将Date转化为String过程中,会将微秒数表示的格林尼治时间转化到系统的默认时区时间。这就不难解释前文中System.out.println(today)会输出Wed Jun 01 08:00:00 CST 2016
了:Date today = new Date(curMillis - curMillis % MICROSECONDS_PER_DAY)
得到的是格林威治时间的零点,在输出时根据当前时区(东八区)加了8个小时。
Calendar
Calendar是Java提供的一个日期时间类,相对于Date类中废弃的大部分构造器和函数,Calendar类提供了更多的功能。通常使用Calendar now = Calendar.getInstance();
获取当前日期和时间。Calendar也是基于与1970年1月1日 00:00:00的毫秒数计时,getInstance()方法中也使用了System.curentMilliseconds()作为当前毫秒数。
可以通过Calendar对象的getTime()方法获得对应的Date对象,使用getTimeInMillis()方法获得对应的毫秒数。Calendar对象可以通过setTime()方法设置日期和时间。
DataFormat
Date可以按照指定的格式输出对应的字符串,也可以将字符串表示的日期和时间转化为Date对象,它们之间转换的桥梁是DateFormat,常用的DateFormat是SimpleDataFormat。
1 | DateFormat dateFormat = new SimapleDateFormat("yyyy-MM-dd HH:mm:ss"); |
1 | DateFormat dateFormat = new SimpleDateFormat("yy-M-d hh:mm:s"); |
值得注意的是:Calendar.HOUR表示12小时制,对应DateFormat格式小时位置的”hh”;Calendar.HOUR_OF_DAY表示24小时制,对应DateFormat格式小时位置的”HH”。Calendar.HOUR的表示范围是1-12,Calendar.HOUR_OF_DAY的表示范围是0-23。如果通过Calendar.HOUR设置小时为0点,并通过设置为”hh”的DateFormat打印,小时位将显示12.
通过以上分析,需要对日期和时间进行操作时,Calendar提供了最为完善的支持,有些操作只能通过Calendar进行,例如涉及到地域和时区的操作等。Date一般用于在数据库、前端等场合对日期时间进行传输,类似于Java中日期和时间的DTO对象。Calendar与Date之间、Date与String之间可以进行非常方便的转换。
针对本文开头提出的需求,可以通过Calendar对象获取前一天的第一刻和最后一刻。
1 | public Date getFirstMoment(Date date) { |