Jodd 时间库

来源:
三产
最后修订:
2016年07月14日 00:00:00
 1092

JDateTime 是一个优雅的,对开发者友好的时间日期处理类,并且也一种非常精巧的跟踪日期和时间的一种方式。它使用一种非常清晰并且被广泛证明的算法去进行时间的操作。每个被JDK中Calendar深深虐过的人都将发...

JDateTime 是一个优雅的,对开发者友好的时间日期处理类,并且也一种非常精巧的跟踪日期和时间的一种方式。它使用一种非常清晰并且被广泛证明的算法去进行时间的操作。每个被JDK中Calendar深深虐过的人都将发现使用JDateTime是多么美妙的一种体验。

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有较好的容错性,比如JDateTime jdt = new JDateTime(“2016-1-32”);会生成2016-02-01并不会报错。

先简单看一下如何创建一个Date:

使用构造函数进行JDateTime的初始化
import jodd.datetime.JDateTime;
import jodd.datetime.format.JdtFormat;

/**
 * 拿客
 * www.coderknock.com
 * QQ群:213732117
 * 创建时间:2016年07月14日
 * 描述:JDateTime
 */
public class JoddDate {
    public static void main(String[] args) {        
        JDateTime jdt = new JDateTime();            // 当前的日期以及时间
        jdt.getFormat();//获取格式化字符串,默认是 YYYY-MM-DD hh:mm:ss.mss
        jdt.setFormat("YYYY年MM月DD日 hh时mm分ss秒 mss毫秒");//设置格式化字符串
        System.out.println(jdt);                   //JDateTime重写了toString方法,使用了JdtFormatter来格式化输出字符串
        jdt = new JDateTime(2012, 12, 21);        // 2012年12月21日
        System.out.println(jdt.toString());
        jdt = new JDateTime(System.currentTimeMillis());    // 根据当前系统毫秒说来获取时间
        System.out.println(jdt.toString("MM/DD/YYYY hh:mm:ss mss"));//toString还有一个JdtFormat做完参数的方法,但是不太方便
        jdt = new JDateTime(2012, 12, 21, 11, 54, 22, 124); // 设置日期以及时、分、秒、毫秒
        System.out.println(jdt);
        jdt = new JDateTime("2012-12-21 11:54:22.124");     // 使用格式化字符串设置时间
        System.out.println(jdt);
        jdt = new JDateTime("12/21/2012", "MM/DD/YYYY");    // 使用自己定义的时间字符串设置时间
        System.out.println(jdt);
    }
}

输出:

2016年07月14日 09时49分56秒 204毫秒
2012-12-21 00:00:00.000
2016-07-14 09:49:56.240
2012-12-21 11:54:22.124
2012-12-21 11:54:22.124
2012-12-21 00:00:00.000
使用方法进行JDateTime的时间设置
import jodd.datetime.JDateTime;
import jodd.datetime.format.JdtFormat;

/**
 * 拿客
 * www.coderknock.com
 * QQ群:213732117
 * 创建时间:2016年07月14日
 * 描述:JDateTime
 */
public class JoddDate {
    public static void main(String[] args) {
        JDateTime jdt = new JDateTime();
        System.out.println(jdt);
        jdt.set(2012, 12, 21, 11, 54, 22, 124);  // 设置日期和时间
        System.out.println(jdt);
        jdt.set(2012, 12, 21);                   // 设置日期 这种方法会默认时间为零点
        System.out.println(jdt);
        jdt.setDate(2012, 12, 21);               // 另一种设置日期的方法 这种方法会默认时间为零点
        System.out.println(jdt);
        jdt.setCurrentTime();                    // 设置为当前日期和时间
        System.out.println(jdt);
        jdt.setYear(1973);                       // 改变年(也有单独改变月、日的方法,大家自己尝试)
        System.out.println(jdt);
        jdt.setHour(22);                         // 改变小时(也有单独改变分、毫秒的方法,大家自己尝试)
        System.out.println(jdt);
        jdt.setTime(18, 00, 12, 853);             // 设置时间
        System.out.println(jdt);

    }
}

输出结果:

2016-07-14 10:31:31.380
2012-12-21 11:54:22.124
2012-12-21 00:00:00.000
2012-12-21 00:00:00.000
2016-07-14 10:31:31.412
1973-07-14 10:31:31.412
1973-07-14 22:31:31.412
1973-07-14 18:00:12.853

JDateTime提供了尽可能多的方法来满足开发的需求,并且提供了不同参数的重载方法,基本上你想要的方法都可以找到。 JDateTime提供了日期时间中各个部分的get方法,可以很方便的获取到想要的内容。在JDateTime中天和月是从1开始计数的,所以,JDateTime.JNUARY 是1。 JDateTime还提供几个方法来获取Java中常用的时间格式: convertToDate java.util.Date convertToCalendar java.util.Calendar convertToSqlDate java.sql.Date convertToSqlTime java.sql.Time convertToSqlTimestamp java.sql.Timestamp 同样可以从java中的时间直接转成JDateTime JDateTime(java.util.Date) JDateTime(java.util.calendar) setDateTime(java.util.Date) setDateTime(java.util.calendar) 官方文档中提出的以下方法我在3.7.1版本中测试时已经找不到了

    jdt.convertToGregorianCalendar();
    jdt.convertTo(GregorianCalendar.class);  // generic way of conversion
    jdt.loadFrom(gregCalInstance);       // load time data from GregorianCalendar
    jdt.storeTo(gregCalInstance);        // store time data to GregorianCalendar

时间计算

JDateTime 提供了尽可能多的方法来满足开发的需求,并且提供了不同参数的重载方法在当前的日期/时间上加减指定的时间。它可以仅仅改变单个部分的日期/时间信息也可以一次改变多个部分。

JDateTime中加操作使用add方法开头(有很多很细的操作的方法大家可以仔细看看API)减使用sub(有很多很细的操作的方法大家可以仔细看看API)。

import jodd.datetime.JDateTime;

/**
 * 拿客
 * www.coderknock.com
 * QQ群:213732117
 * 创建时间:2016年07月14日
 * 描述:JDateTime
 */
public class JoddDate {
    public static void main(String[] args) {
        JDateTime jdt = new JDateTime("2016-7-14 11:54:22.124");
        System.out.println(jdt);
        jdt.add(1, 2, 3, 4, 5, 6, 7);  // 增加一年两个月三个小时......
        System.out.println(jdt);
        jdt.add(4, 2, 0);              // 增加四年两个月
        System.out.println(jdt);
        jdt.addMonth(-120);            // 减少120个月
        System.out.println(jdt);
        jdt.subYear(1);                // 减少一年
        System.out.println(jdt);
        jdt.addHour(1234);             // 增加1234小时
        System.out.println(jdt);
    }
}

输出结果:

2016-07-14 11:54:22.124
2017-09-17 15:59:28.131
2021-11-17 15:59:28.131
2011-11-17 15:59:28.131
2010-11-17 15:59:28.131
2011-01-08 01:59:28.131

由于性能的原因,最好是通过一个方法调用按照顺序一次性改变多个时间部分,比如,如果同时要在当前时间上加一定数量的months,minutes和秒,通过add(0, months, 0, 0, minutes, seconds, 0); 一次性改变而不是调用三个单独的方法去改变,这样速度更快。 由于每月天数的不同,对于月的增减需要格外注意:

import jodd.datetime.JDateTime;

/**
 * 拿客
 * www.coderknock.com
 * QQ群:213732117
 * 创建时间:2016年07月14日
 * 描述:JDateTime
 */
public class JoddDate {
    public static void main(String[] args) {
        JDateTime jdt = new JDateTime("2016-1-31");
        System.out.println(jdt);
        jdt.addMonth(1);
        System.out.println(jdt);
        jdt = new JDateTime("2016-1-31");
        //设置MonthFix为flase之后增加一个月就相当于是增加31天(忽略月份天数的不同)
        jdt.setMonthFix(false);//MonthFix默认是true  JDateTimeDefault.monthFix
        jdt.addMonth(1);
        System.out.println(jdt);
    }
}

输出结果:

2016-01-31 00:00:00.000
2016-02-29 00:00:00.000
2016-03-02 00:00:00.000

时间对比

JDateTime中提供了多种方法来进行时间的对比(有很多重载,这块比较简单,不做演示)

compareTo
compareDateTo
isAfter
isBefore
isAfterDate
isBeforeDate
daysBetween
daysBetween
equalsDate
equalsTime

周期

通过JDateTime 可以非常容易的去计算时间周期。通过两个时间相减接口得到的结果去计算周期时间十分的方便。但是如果要包括小时,分钟,秒和毫秒,更简单并且快速的方式是使用Period类。

import jodd.datetime.JDateTime;
import jodd.datetime.Period;

/**
 * 拿客
 * www.coderknock.com
 * QQ群:213732117
 * 创建时间:2016年07月14日
 * 描述:JDateTime
 */
public class JoddDate {
    public static void main(String[] args) {
        JDateTime jdt=new JDateTime("2015-7-14");
        JDateTime jdtTow=new JDateTime("2016-7-14");
        Period period=new Period(jdt,jdtTow);
        System.out.println(period.getDays()); //获取两个时间中天数部分的差
        System.out.println(period.getHours()); //获取两个时间中小时部分的差(只包括显示部分而并不是总差的小时数)
        System.out.println(period.getMilliseconds());//获取两个时间中毫秒部分的差(只包括显示部分而并不是总差的小时数)
        System.out.println(period.getMinutes());//获取两个时间中分钟部分的差(只包括显示部分而并不是总差的小时数)
        System.out.println(period.getSeconds());//获取两个时间中秒部分的差(只包括显示部分而并不是总差的小时数)

        JDateTime newjdt=new JDateTime("2015-7-14 14:23:53.123");
        JDateTime newjdtTow=new JDateTime("2016-7-14 16:10:22.23");
        Period newperiod=new Period(newjdt,newjdtTow);
        System.out.println(newperiod.getDays()); //获取两个时间中天数部分的差
        System.out.println(newperiod.getHours()); //获取两个时间中小时部分的差(只包括显示部分而并不是总差的小时数)
        System.out.println(newperiod.getMilliseconds());//获取两个时间中毫秒部分的差(只包括显示部分而并不是总差的小时数)
        System.out.println(newperiod.getMinutes());//获取两个时间中分钟部分的差(只包括显示部分而并不是总差的小时数)
        System.out.println(newperiod.getSeconds());//获取两个时间中秒部分的差(只包括显示部分而并不是总差的小时数)
    }
}

请认真分析一下输出的结果:

366
0
0
0
0
366
1
900
46
28

如果newjdtTow=new JDateTime(“2016-7-14 15:10:22.23”);那么小时差为0,因为15:10和14:23分直接差的小时数不够一个小时,同理如果newjdtTow=new JDateTime(“2016-7-14 14:10:22.23”);那么天数差会为365。

字符串转换

JDateTime支持的解析模式(支持的较少,但是已经个人觉得够用):

|模式|是否可以解析?|值| |:—-:|:—-:|:—-:| |YYYY|yes|year| |MM|yes|month| |DD|yes|day of month| |D| |day of week| |MML| |month name long| |MMS| |month name short| |DL| |day of week name long| |DS| |day of week name short| |hh|yes|hour| |mm|yes|minute| |ss|yes|seconds| |mss|yes|milliseconds| |DDD| |day of year| |WW| |week of year| |WWW| |week of year with 'W' prefix| |W| |week of month| |E| |era (AD or BC)| |TZL| |time zone name long| |TZS| |time zone name short| 在上面的示例中,实际对字符串的转换已经有一些讲解,下面我们就列出官方的示例,供大家扩展学习:

import jodd.datetime.JDateTime;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

/**
 * 拿客
 * www.coderknock.com
 * QQ群:213732117
 * 创建时间:2016年07月14日
 * 描述:JDateTime
 */
public class JoddDate {
    public static void main(String[] args) {
        JDateTime jdt = new JDateTime(1975, 1, 1);
        System.out.println(jdt.toString());                     // "1975-01-01 00:00:00.000"
        System.out.println(jdt.toString("YYYY.MM.DD"));         // "1975.01.01"
        System.out.println(jdt.toString("MM: MML (MMS)"));      // "01: January (Jan)"
        System.out.println(jdt.toString("DD is D: DL (DS)"));   // "01 is 3: Wednesday (Wed)"
        System.out.println(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");
        System.out.println(jdt);
        jdt.parse("2003-11-23");
        System.out.println(jdt);                // 2003-11-23 00:00:00.000
        jdt.parse("01.01.1975", "DD.MM.YYYY");
        System.out.println(jdt);  // 1975-01-01
        jdt.parse("2001-01-31", "YYYY-MM-***");
        System.out.println(jdt); // 2001-01-01, since day is not parsed
        DateFormat df = new SimpleDateFormat();
        System.out.println(df.format(jdt.convertToDate()));         // date formatter
    }
}

上面示例中对官网给出的部分已经不可用的示例进行了删减 输出结果

1975-01-01 00:00:00.000
1975.01.01
01: 一月 (一月)
01 is 3: 星期三 (星期三)
' is a sign, 1 is a week number and W is a letter
2003-11-24 23:18:38.173
2003-11-23 00:00:00.000
1975-01-01 00:00:00.000
2001-01-01 00:00:00.000
01-1-1 上午12:00

本地化

通过在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仅仅当加减时间时,最后一点,它可以设置一个无效的时间(比如说不存在的时间)。

与Java中时间处理库性能的对比: