• 好用java库(一):java date/time api:jodatime


    基于java的项目,最大的一个好处是有很多开源,优秀的第三方jar包拿过来直接使用,但是引用第三方包时一定要小心审核,确认包的作者或组织的权威性,以免未知的第三方包对项目的性能,安全和正确性的影响。作为一个java coder,有些包你不得不了解下,它们真的可以帮你在项目中节省很多时间去写自己的utils包,况且成熟的社区维护的第三方工具包比自己动手写的专业多了。所以我想写个系列性的文章,介绍下平时在用,而且值得推荐给大家的一些好的第三方jar包。

    时间,日期的处理在应用项目中是经常要用到的一块,如果你还是个稍微追求感觉的程序员,应该早就受不了jdk中java.util.Date这个类,莫名奇妙的构造方法,再加上一堆的deprecated方法,让你觉得为何不把这个类给取消算了。举个例子,现在实例化一个日期对象(2013-1-6):

    1 Date today = new Date(2013,1,6);

    不好意思,其实返回的日期是:3913-2-6,有病吧......

    我们也经常要用到一些时间变换处理的地方,如返回这个月的最后一个星期二,当月的最后一天,每个月的第一天的凌晨三点进行批处理任务,这个时候我们一般会用jdk中的Calendar 。虽然Calendar的出现改善了一些情况,但是为了处理一些时间变换时写出各种DAY_OF_WEEK,DAY_OF_MONTH,MONTH....和add,set,roll之间的组合调用时,这种不直观的api仍然使我们容易犯糊涂。

     Joda-Time 是一个非常优秀的时间日期处理工具,正是它的强大易用性,作者好像成了JSR310的Leader,下面我们就来大概介绍一下里面强大的api。

    实例化日期/时间对象及操作对象

    初始化:

    DateTime dt = new DateTime(2013,1,7,12,23,58,874);

    改变对象:

    dt = dt.withDayOfMonth(20);//设置成20号
    dt = dt.minusDays(2);//前面两天
    dt = dt.plusWeeks(1);//后面一个星期

    注意:「DateTime」对象是不可变对象(Immutable),所以每次操作改变对象时实质上是返回的一个全新对象。

    字段属性:

    DateTime dt2 = new DateTime();
    Property prop = dt.dayOfMonth();//得到字段属性
    int i = prop.getDifference(dt2);//两个日期之间相差几天

    格式化(format)和解析(parse)

    「格式化和解析日期/时间」这种需求在时间处理中大家都会碰到,在标准jdk中使用的是SimpleDateFormat,但是大家都知道这个玩艺用起来真是爱恨交加。首先SimpleDateFormat的实例化是重量级的,这就会促使我们用单例模式使用它,可是这个类又是「线程不安全的」,所以要synchronized调用。

    joda-time对这类需求支持很强大:

    DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS");
    String s = fmt.print(dt);
    dt = fmt.parseDateTime("2013-01-07 22:39:13 782");

    如果你不想每次格式化输出的时候都要去关联一个DateTimeFormatter,可以直接这样:

    DateTime dt = new DateTime();
    String str = dt.toString("yyyy-MM-dd HH:mm:ss SSS");

    这里面的原理其实就是在调用toString的时候把传进去的format string参数生成一个DateTimeFormatter,然后再格式化输出。你可能考虑担心每次都会实例化DateTimeFormatter对象会影响性能,其实内部已经做了缓存了。

    private static final Map<String, DateTimeFormatter> cPatternedCache = new HashMap<String, DateTimeFormatter>(7);

     如果你以为joda-time只有这些对相对jdk改善的功能,那么太小看它了,且看里面一些jdk没有的高级api功能。

    Interval/Duration/Period

     它们三个之间很有相似性,都有“时间间隔”的意思,我们先看一段代码,再讲下它们之间的差异性。

            DateTime start = new DateTime(2011,12,2,15,33);//开始时间
            DateTime end = new DateTime(2013,3,1,12,34);//结束时间
            Interval inteval = new Interval(start,end);
            Duration duration = new Duration(start,end);
            Period period = inteval.toPeriod();
            
            boolean between = inteval.contains(new DateTime(2013,2,1,12,34));//判断指定时间在这段时间间隔里: True
            long millSeconds = duration.getMillis();//得到两个时间相差的豪秒数:39301260000
            long millSeconds1 = period.getMillis();//得到两个时间相差的豪秒数:0

    上面的三个返回结果中,第一个好理解,「2013-2-1 12:34」是中「2011-12-2 15:33」和「2013-3-1 12:34」之间的一个时间。第二个返回这两个时间相差的毫秒数(39301260000),也比较好理解。可是第三个结果返回 0 ,就比较晕了,其实Interval,Duration,Period都有「时间间隔」的意思在里面,但是这里定义三个类肯定是有三种语义在里面的,要理解这三者的区别,只要搞清楚这三个不同的类里包含的「信息」就知道差异了。

    • Interval   : 它包含了「开始时间」和「结束时间」信息,包括各自的时区。所以可以根据Interval对象得到「开始时间」和「结束时间」这两个信息。
    • Duration : 它没有任何「开始时间」和「结束时间」的概念,只是保存了一个具体的时间间隔,也就是一个以毫秒为单元的整数。
    • Period     : 它和「Duration」差不多意思,也是没有包含「开始时间」和「结束时间」的信息,但是它和「Duration」不同的是,它所指的时间间隔不是以毫秒为单元,而是「年」,「月」,「天」,「小时」,「分」,「秒」,「毫秒」为相对单元。举个例子:「Duration」就像是“我比你大30天”的语义,而「Period」就是“我大你一个月”。它们两种语义是不同的,“大你30天”是个绝对值,而“大你一个月”就是相对的,因为「一个月」可能是30天,也可能是31天,也可能会碰到2月分的28天,所以说是相对的。

    再看上面代码中的输出结果,就知道为什么duration和period调用getMillis会出现不同的结果了:duration.getMillis返回的是绝对相差毫秒数,而period.getMillis返回0是因为「开始时间」和「结束时间」间隔的相对时间是:1年2个月3个星期5天21个小时1分钟。

      

  • 相关阅读:
    [luogu] P1440 求m区间内的最小值
    [NOI2014]起床困难综合症
    [SDOI2009]地图复原
    [USACO08JAN] Cow Contest
    【洛谷P5049】旅行(数据加强版)
    【NOIP2015】真题回顾
    【NOIP2014】真题回顾
    【UVA11987】Almost Union-Find
    【UVA11988】破损的键盘
    【UVA11134】传说中的车
  • 原文地址:https://www.cnblogs.com/jcli/p/2844262.html
Copyright © 2020-2023  润新知