Java跟踪日期和时间JDateTime
译者:阿邱 原文链接
JDateTime 是一个优雅的,开发者友好的类并且也一种非常精巧的跟踪日期和时间的一种方式。它使用一种非常清晰并且被广泛证明的算法去进行时间的操作。每个被JDK中Calendar深深虐过的人都将发现这是多麽好的一剂良药。
Julian day
Julian day 或者 Julian day number(JDN)表示距离格林威治时间公元前 4713年 1月1日 星期一正午12点有多少个整数天。Julian Date(JD)则表示距离同样的时间过了多少天(有小数位的天)。JDs 是被推荐的方式当在天文学上表示时间时。
Julian Day number 可以被认为是一种简单的日历:日期是他的整数位,时间是它的小数位。这样它可以非常方便的被引用,计算或者转换。实际上JD 的设计之美正是它可以非常容易的去计算时间的范围进而去根据时间滚动日期,仅仅通过简单的数学加减法。JD允许通过简单的减法去计算两个历史日期之间的时间差。
Julian Day 系统被天文学家引进用来在两个不同的日历中去统一不同的历史年代表时提供统一的的日期系统。
除了零点的选取和名字的与众不同外,Julian Day 和Julian Date 不是直接的关联到Julian Calendar,尽管它可以从将一个日历的任何日期转换到另一个日历。
精度
JDateTime 内部使用JD去表示当前的日期和时间,它根据被证明并很好地测试过的天文学算法去做所有的计算。JDateTime 在所有的计算中提供精确到1毫秒的精度。
日期和时间设置
JDateTime 可以通过多种方式创建:通过制定想要的日期和时间,或者通过传入一个JDK中日期时间相关类的引用,或者通过指定系统的毫秒数等等。
JDateTime 能够通过指定日期和时间数据来创建,一旦被创建,日期和时间可以通过不同的方式来改变,可以通过完整的日期/时间信息来改变(等同于创建新的JDateInstance)或者仅仅改变一部分(仅仅 天或者分钟…)
使用构造函数:
JDateTime jdt = new JDateTime(); // current date and time jdt = new JDateTime(2012, 12, 21): // 21st December 2012, midnight jdt = new JDateTime(System.currentTimeMillis()); // current date and time jdt = new JDateTime(2012, 12, 21, 11, 54, 22, 124); // 21.Dec.2012,11:54:22.124 jdt = new JDateTime("2012-12-21 11:54:22.124"); // -//- jdt = new JDateTime("12/21/2012", "MM/DD/YYYY"); // 21st Dec. 2012, midnight
使用方法:
JDateTime jdt = new JDateTime(); // current date and time jdt.set(2012, 12, 21, 11, 54, 22, 124); // 21st December 2012, 11:54:22.124 jdt.set(2012, 12, 21); // 21st December 2012, midnight jdt.setDate(2012, 12, 21); // change just date to 21st Dec. 2012 jdt.setCurrentTime(); // set current date and time jdt.setYear(1973); // change the year jdt.setHour(22); // change the hour jdt.setTime(18, 00 12, 853); // change just time
读取时间和日期
当然,它可以通过JDateTime读取时间和日期信息,每个部分:年,月,时等等都有对应的getter方法,天和月是从1开始计数的,所以,JDateTime.JNUARY 是1.
除了读取单个日期时间信息单个部分的方法,这里还有须有方便的方法去返回日期和时间信息。
getDateTimeStamp()内部使用JDateTIme返回DateTimeStamp 实例,这个类一次性返回了所有的日期/时间信息。
getJulianDate()内部使用JDateTIme返回JulianDateStamp实例。JulianDateStamp持有当前JD的信息。由于小数位的精度问题,JD 信息通过两个单独的字段进行存储:integer 和 fraction(JD=integer+fraction). 你也就可以得到一个Double类型值得JD,但是它不精确(忽略掉时间),由于当integer部分非常大的时候会导致fraction部分精度丢失。
时间计算
JDateTime 提供了多个方法在当前的日期/时间上加减指定的时间。它可以仅仅改变单个部分的日期/时间信息或者一次改变多个部分。
jdt.add(1, 2, 3, 4, 5, 6, 7); // add 1 year, 2 months, 3 days, 4 hours... jdt.add(4, 2, 0); // add 4 years and 2 months jdt.addMonth(-120); // go back 120 months jdt.subYear(1); // go back one year jdt.addHour(1234); // add 1234 hours
由于性能的原因,最好是通过一个方法调用按照顺序一次性改变多个时间部分,比如,如果同时要在当前时间上加一定数量的months,minutes和秒,通过add(0, months, 0, 0, minutes, seconds, 0); 一次性改变而不是调用三个单独的方法去改变将使速度更快。
由于每个月的长度是变化的月份的增加是不可靠的,这里有两种方式可以被考虑当进行月份增加的时候,下面的例子将显示什么:从2003-01-31加一个月后的时间是什么?
JDateTime 有一个monthFix的标志去表示加法后的月份,当这个标志关闭时,一个月的天数被近似的认为是31天,所以结果是:2003-01-31 + 0-1-0 = 2003-03-03.
当monthFix的标志是关闭的时候(默认也是这样),月份的长度会被忽略,在这种情况下,添加一个月总是得到的是下一个月:2003-01-31 + 0-1-0 = 2003-02-28.
因为月份天数的补丁导致添加月份是不可靠的,所以最佳实践是用添加天数来代替。
周期
通过JDateTime 可以非常容易的去计算周期时间,仅仅通过两个时间相减接口得到去计算周期时间。但是如果要包括小时,分钟,秒和毫秒,更简单并且快速的方式是使用Period类。
转换
JDateTime 可以 非常容易的在其他时间相关的类之间进行转换:GregorianCalendar, java.util.Date, java.sql.Date, Timestamp,DateTimeStamp
不仅如此,他可以从存在的实例设置日期和时间,而不是创建一个新的。
Calendar c = jdt.convertToCalendar(); jdt.convertToGregorianCalendar(); jdt.convertTo(GregorianCalendar.class); // generic way of conversion jdt = new JDateTime(gregCalInstance); // create from GregorianCalendar jdt.loadFrom(gregCalInstance); // load time data from GregorianCalendar jdt.storeTo(gregCalInstance); // store time data to GregorianCalendar
不仅如此,根据自己需要定义自定义的转换也是非常容易的。
字符串转换
特别注意的是日期/时间与字符串之间的转换。这里有两种通用的方式去转换:
通过JDateTime实例进行转换或者通过传递一个JDateTime实例给一些格式化的类。
对所有内部的转换和解析,JDateTime使用所谓的formatters,比如 JdtFormatter的实现。Formatters 遵循简单的协议(接口)仅仅两个方法:定义如何转换为字符串和如何解析字符串为指定的格式。格式通常是一个包含一些模式的字符串模板。因此一个formatter的实例可以用在整个应用中。JDateTime持有formatter和默认的格式用来进行转换和解析。当在转换和解析时没有指定格式则使用默认格式。
假如一个应用程序不得不在多个formatter之间频繁切换,那么你使用JdtFormat来进行转换和解析将更方便-这是一个不可变的类持有formatter-format对。
最后,由于JDateTime可以非常方便的转换为Date,所以你也可以使用JDK的DateFormatter来进行日期时间和字符串的转换。
DefaultFormatter是JDateTime默认使用的格式化类。它在ISO 8601规范的基础上增强了模式,同时对转换和解析-解析支持的模式少一些。
转换的模板字符串除了上诉的文本外还可以包含其他的文本,甚至完整的句子。它可以转义某些模板部分来防止被转换。转义文本通过单引号。任何在引号区域内的两个单引号会北替换为一个单引号,此外,如果一些模式无法被识别它将忽略。
使用例子:
JDateTime jdt = new JDateTime(1975, 1, 1); jdt.toString(); // "1975-01-01 00:00:00.000" jdt.toString("YYYY.MM.DD"); // "1975.01.01" jdt.toString("MM: MML (MMS)"); // "01: January (Jan)" jdt.toString("DD is D: DL (DS)"); // "01 is 3: Wednesday (Wed)" JDateTime jdt = new JDateTime(1968, 9, 30); jdt.toString("'''' is a sign, W is a week number and 'W' is a letter"); // "' is a sign, 5 is a week number and W is a letter" jdt.parse("2003-11-24 23:18:38.173"); jdt.parse("2003-11-23"); // 2003-11-23 00:00:00.000 jdt.parse("01.01.1975", "DD.MM.YYYY"); // 1975-01-01 jdt.parse("2001-01-31", "YYYY-MM-***"); // 2001-01-01, since day is not parsed JDateTime jdt = new JDateTime(); JdtFormatter fmt = new DefaultFormatter(); fmt.convert(jdt, "YYYY-MM.DD"); // external conversion JdtFormat format = new JdtFormat(new DefaultFormatter(), "YYYY+DD+MM"); jdt.toString(format); format.convert(jdt); DateFormat df = new SimpleDateFormat(); df.format(jdt.convertToDate()); // date formatter
本地化
通过在JDateTime中设置locale,可以在结果字符串中返回本地化的名称,比如月份和长短日期的名称,当没有指定locale时,JDateTime使用系统默认的locale。
星期定义
JdateTime提供了两种方式去定义星期,即定义星期的第一天和定义一年的第一个星期(用来周的计数)。
setWeekDefinition(start,must)定义一个星期的起始天和一周的那一天必须在一年中来计算该周属于那一年。
setWeekDefinitionAlt(start, min) 是另一个定义方式来定义一周的起始天和该周属于该年时必须有的最小的天数。
JD供选方案
由于JD的起始时间是如此的久远,JD的数值非常的大和笨重,一个距离现在更近的起始点通常会被用到,比如丢弃掉一些数字位来在保证足够的精度的同时适应有限的计算机内存。
JDateTime 可以将JD在下面的类之间进行转换:
- 简化的 Julian Day (RJD),
- 改良的 Julian Day (MJD), and
- 截短的 Julian Day (TJD), NASA提出的定义
时区和DST(夏时制)
Julian Date,根据定义,是与时区和夏时制无关的。但是,JDateTime支持时区,当时区改变时(并且改变到不同的时区)时间将进行一定的偏移。当JDateTime创建时,系统默认的时区就被使用,当设置新的时区是,当前时间将根据时区的不同而改变,下面的例子表示当前的日本时间:
JDateTime jdt = new JDateTime(); jdt.setTimeZone(TimeZone.getTimeZone("Japan";)); System.out.println(jdt);
此外,可以仅仅去设置时区,而不改变当前的时间。有时在改变时区时会很用,通过changeTimeZone()方法即可。
DST仅仅被部分支持,现在默认情况下DST跟踪是关闭的(标志:trackDST)。当DST跟踪打开时,JDateTime将跟踪DST仅仅当加减时间时,最后一点,它可以设置一个无效的时间(比如说不存在的时间)。
原创文章,转载请注明: 转载自并发编程网 – ifeve.com