170804、使用Joda-Time优雅的处理日期时间
简介
在Java中处理日期和时间是很常见的需求,基础的工具类就是我们熟悉的Date和Calendar,然而这些工具类的api使用并不是很方便和强大,于是就诞生了Joda-Time这个专门处理日期时间的库。
由于Joda-Time很优秀,在Java 8出现前的很长时间内成为Java中日期时间处理的事实标准,用来弥补JDK的不足。在Java 8中引入的java.time
包是一组新的处理日期时间的API,遵守JSR 310
。值得一提的是,Joda-Time的作者Stephen Colebourne
和Oracle一起共同参与了这些API的设计和实现。
值得注意的是,Java 8中的java.time
包中提供的API和Joda-Time并不完全相同。比如,在Joda-Time中常用的Interval
(用来表示一对DateTime),在JSR 310
中并不支持。因此,另一个名叫Threeten的第三方库用来弥补Java 8的不足。Threeten翻译成中文就是310的意思,表示这个库与JSR 310
有关。它的作者同样是Joda-Time的作者Stephen Colebourne
。
Threeten主要提供两种发行包:ThreeTen-Backport和ThreeTen-Extra。前者的目的在于对Java 6和Java 7的项目提供Java 8的date-time类的支持;后者的目的在于为Java 8的date-time类提供额外的增强功能(比如:Interval
等)。
由于刚接触Joda-Time,并且目前的工作环境还未涉及到Java 8。因此,关于Java 8的date-time和Threeten的API,将在以后合适的时候介绍。这篇文章关注Joda-Time的使用。
总之,作为一种解决某一问题领域的工具库,我认为有以下几个方面值得关注:
- 功能是否全面,以能够满足生产需要,并用它解决这个问题领域中的绝大多数的问题
- 是否是主流工具。用的人越多,意味着该库经受了更多生产实践的验证,效率安全等方面都已被证明是可靠的
- 自己是否已经熟练掌握。会的多不如会的精,如果能够用一个工具快速熟练可靠地解决问题,在时间成本有限的情况下,就不用刻意追求学习其它可替代的库
引入MAVEN依赖
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.2</version>
</dependency>
核心类介绍
下面介绍5个最常用的date-time类:
- Instant - 不可变的类,用来表示时间轴上一个瞬时的点
- DateTime - 不可变的类,用来替换JDK的Calendar类
- LocalDate - 不可变的类,表示一个本地的日期,而不包含时间部分(没有时区信息)
- LocalTime - 不可变的类,表示一个本地的时间,而不包含日期部分(没有时区信息)
- LocalDateTime - 不可变的类,表示一个本地的日期-时间(没有时区信息)
注意:不可变的类,表明了正如Java的String类型一样,其对象是不可变的。即,不论对它进行怎样的改变操作,返回的对象都是新对象。
Instant比较适合用来表示一个事件发生的时间戳。不用去关心它使用的日历系统或者是所在的时区。
DateTime的主要目的是替换JDK中的Calendar类,用来处理那些时区信息比较重要的场景。
LocalDate比较适合表示出生日期这样的类型,因为不关心这一天中的时间部分。
LocalTime适合表示一个商店的每天开门/关门时间,因为不用关心日期部分。
DateTime类
作为Joda-Time很重要的一个类,详细地看一下它的用法。
构造一个DateTime实例
如果查看Java Doc,会发现DateTime有很多构造方法。这是为了使用者能够很方便的由各种表示日期时间的对象构造出DateTime实例。下面介绍一些常用的构造方法:
- DateTime():这个无参的构造方法会创建一个在当前系统所在时区的当前时间,精确到毫秒
- DateTime(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour, int secondOfMinute):这个构造方法方便快速地构造一个指定的时间,这里精确到秒,类似地其它构造方法也可以传入毫秒。
- DateTime(long instant):这个构造方法创建出来的实例,是通过一个long类型的时间戳,它表示这个时间戳距
1970-01-01T00:00:00Z
的毫秒数。使用默认的时区。 - DateTime(Object instant):这个构造方法可以通过一个Object对象构造一个实例。这个Object对象可以是这些类型:ReadableInstant, String, Calendar和Date。其中String的格式需要是
ISO8601
格式,详见:ISODateTimeFormat.dateTimeParser()
下面举几个例子:
DateTime dateTime1 = new DateTime();
System.out.println(dateTime1); // out: 2016-02-26T16:02:57.582+08:00
DateTime dateTime2 = new DateTime(2016,2,14,0,0,0);
System.out.println(dateTime2); // out: 2016-02-14T00:00:00.000+08:00
DateTime dateTime3 = new DateTime(1456473917004L);
System.out.println(dateTime3); // out: 2016-02-26T16:05:17.004+08:00
DateTime dateTime4 = new DateTime(new Date());
System.out.println(dateTime4); // out: 2016-02-26T16:07:59.970+08:00
DateTime dateTime5 = new DateTime("2016-02-15T00:00:00.000+08:00");
System.out.println(dateTime5); // out: 2016-02-15T00:00:00.000+08:00
访问DateTime实例
当你有一个DateTime实例的时候,就可以调用它的各种方法,获取需要的信息。
- with开头的方法(比如:withYear):用来设置DateTime实例到某个时间,因为DateTime是不可变对象,所以没有提供setter方法可供使用,with方法也没有改变原有的对象,而是返回了设置后的一个副本对象。下面这个例子,将2000-02-29的年份设置为1997。值得注意的是,因为1997年没有2月29日,所以自动转为了28日。
DateTime dateTime2000Year = new DateTime(2000,2,29,0,0,0);
System.out.println(dateTime2000Year); // out: 2000-02-29T00:00:00.000+08:00
DateTime dateTime1997Year =
dateTime2000Year.withYear(1997);
System.out.println(dateTime1997Year); // out: 1997-02-28T00:00:00.000+08:00 - plus/minus开头的方法(比如:plusDay, minusMonths):用来返回在DateTime实例上增加或减少一段时间后的实例。下面的例子:在当前的时刻加1天,得到了明天这个时刻的时间;在当前的时刻减1个月,得到了上个月这个时刻的时间。
DateTime now = new DateTime();
System.out.println(now); // out: 2016-02-26T16:27:58.818+08:00
DateTime tomorrow = now.plusDays(1);
System.out.println(tomorrow); // out: 2016-02-27T16:27:58.818+08:00
DateTime lastMonth = now.minusMonths(1);
System.out.println(lastMonth); // out: 2016-01-26T16:27:58.818+08:00注意,在增减时间的时候,想象成自己在翻日历,所有的计算都将符合历法,由Joda-Time自动完成,不会出现非法的日期(比如:3月31日加一个月后,并不会出现4月31日)。
- 返回Property的方法:Property是DateTime中的属性,保存了一些有用的信息。Property对象中的一些方法在这里一并介绍。下面的例子展示了,我们可以通过不同Property中get开头的方法获取一些有用的信息:
DateTime now = new DateTime(); // 2016-02-26T16:51:28.749+08:00
now.monthOfYear().getAsText(); // February
now.monthOfYear().getAsText(Locale.KOREAN); // 2월
now.dayOfWeek().getAsShortText(); // Fri
now.dayOfWeek().getAsShortText(Locale.CHINESE); // 星期五有时我们需要对一个DateTime的某些属性进行置0操作。比如,我想得到当天的0点时刻。那么就需要用到Property中round开头的方法(roundFloorCopy)。如下面的例子所示:
DateTime now = new DateTime(); // 2016-02-26T16:51:28.749+08:00
now.dayOfWeek().roundCeilingCopy(); // 2016-02-27T00:00:00.000+08:00
now.dayOfWeek().roundFloorCopy(); // 2016-02-26T00:00:00.000+08:00
now.minuteOfDay().roundFloorCopy(); // 2016-02-26T16:51:00.000+08:00
now.secondOfMinute().roundFloorCopy(); // 2016-02-26T16:51:28.000+08:00 - 其它:还有许多其它方法(比如dateTime.year().isLeap()来判断是不是闰年)。它们的详细含义,请参照Java Doc,现查现用,用需求驱动学习。
日历系统和时区
Joda-Time默认使用的是ISO的日历系统,而ISO的日历系统是世界上公历的事实标准。然而,值得注意的是,ISO日历系统在表示1583年之前的历史时间是不精确的。
Joda-Time默认使用的是JDK的时区设置。如果需要的话,这个默认值是可以被覆盖的。
Joda-Time使用可插拔的机制来设计日历系统,而JDK则是使用子类的设计,比如GregorianCalendar。下面的代码,通过调用一个工厂方法获得Chronology的实现:
Chronology coptic = CopticChronology.getInstance();
时区是作为chronology的一部分来被实现的。下面的代码获得一个Joda-Time chronology在东京的时区:
DateTimeZone zone = DateTimeZone.forID("Asia/Tokyo");
Chronology gregorianJuian = GJChronology.getInstance(zone);
Interval和Period
Joda-Time为时间段的表示提供了支持。
- Interval:它保存了一个开始时刻和一个结束时刻,因此能够表示一段时间,并进行这段时间的相应操作
- Period:它保存了一段时间,比如:6个月,3天,7小时这样的概念。可以直接创建Period,或者从Interval对象构建。
- Duration:它保存了一个精确的毫秒数。同样地,可以直接创建Duration,也可以从Interval对象构建。
虽然,这三个类都用来表示时间段,但是在用途上来说还是有一些差别。请看下面的例子:
DateTime dt = new DateTime(2005, 3, 26, 12, 0, 0, 0);
DateTime plusPeriod = dt.plus(Period.days(1));
DateTime plusDuration = dt.plus(new Duration(24L*60L*60L*1000L));
因为当时那个地区执行夏令时的原因,在添加一个Period的时候会添加23个小时。而添加一个Duration,则会精确地添加24个小时,而不考虑历法。所以,Period和Duration的差别不但体现在精度上,也同样体现在语义上。因为,有时候按照有些地区的历法 1天 不等于 24小时。
结语
这篇文章参考了Joda-Time的官方文档:Quick Start,并加上了自己的理解。
涉及到更多的需求和用法(比如“日期时间的格式化”等),可以参考官方文档:User Guide。
作者:JohnShen
链接:http://www.jianshu.com/p/efdeda608780
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
170804、使用Joda-Time优雅的处理日期时间的更多相关文章
- 使用Joda-Time优雅的处理日期时间(转)
简介 在Java中处理日期和时间是很常见的需求,基础的工具类就是我们熟悉的Date和Calendar,然而这些工具类的api使用并不是很方便和强大,于是就诞生了Joda-Time这个专门处理日期时间的 ...
- 一文告诉你Java日期时间API到底有多烂
前言 你好,我是A哥(YourBatman). 好看的代码,千篇一律!难看的代码,卧槽卧槽~其实没有什么代码是"史上最烂"的,要有也只有"史上更烂". 日期是商 ...
- 【转】JAVA 8 日期/时间(Date Time)API指南
前言 本来想写下Java 8的日期/时间API,发现已经有篇不错的文章了,那就直接转载吧~ PS:主要内容没变,做了部分修改. 原文链接: journaldev 翻译: ImportNew.com - ...
- Java8学习笔记(九)--日期/时间(Date Time)API指南
Java 8日期/时间( Date/Time)API是开发人员最受追捧的变化之一,Java从一开始就没有对日期时间处理的一致性方法,因此日期/时间API也是除Java核心API以外另一项倍受欢迎的内容 ...
- DB2 日期时间函数
db2日期时间函数 (DATE(TRIM(CHAR(DT#11Y))||'-'||TRIM(CHAR(DT#11M))||'-'||TRIM(CHAR(DT#11D))) BETWEEN DATE(' ...
- Java-小技巧-004-jdk时间,jdk8时间,joda,calendar,获取当前时间前一周、前一月、前一年的时间
1.推荐使用java8 localdate等 线程安全 支持较好 地址 2.joda 一.简述 查看SampleDateFormat源码,叙述有: * Date formats are not syn ...
- [转] Java8 日期/时间(Date Time)API指南
[From] http://www.importnew.com/14140.html Java 8日期/时间( Date/Time)API是开发人员最受追捧的变化之一,Java从一开始就没有对日期时间 ...
- JDK1.8日期时间库学习
这周在阅读<阿里巴巴Java开发手册>时,在并发处理一节的日期处理中,其强调SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果 定义为 stati ...
- 都9012了,Java8中的日期时间API你还没有掌握?
一,Java8日期时间API产生的前因后果 1.1 为什么要重新定义一套日期时间API 操作不方便:java中最初的Date不能直接对指定字段进行加减操作也不支持国际化,后来新增了Calendar,但 ...
随机推荐
- 关于Cocos2d-x节点和精灵节点的坐标、位置以及大小的设置
1.cocos2d-X中的坐标(0,0),就是运行框的左下角位置,所以运行框看起来就是一个第一象限. 2.节点的锚点就是我们setPosition所设定的位置,默认锚点是在节点的中心,也就是setPo ...
- apt-get强制使用Ipv4
sudo apt-get -o Acquire::ForceIPv4=true update 永久解决办法: 创建文件 /etc/apt/apt.conf.d/99force-ipv4 加入代码: A ...
- 标识符的长度应当符合“min-length && max-information”原则
标识符的长度应当符合“min-length && max-information”原则. 几十年前老 ANSI C 规定名字不准超过 6 个字符,现今的 C++/C 不再有此限制.一 ...
- 使用ffmpeg获取视频流后如何封装存储成mp4文件
int main(int argc,char *argv[]) 02 { 03 AVFormatContext *pFormatCtx; 04 int i,videoStream; 05 AVC ...
- (转)Tiny210v2( S5PV210 ) 平台下 FIMD 对应 的 framebuffer 驱动中,关于 video buffer 的理解
原文:http://www.arm9home.net/read.php?tid-25938.html 管理提醒: 本帖被 xoom 执行加亮操作(2012-12-13) 如之前所说,一直想知道显示数据 ...
- Ubuntu server版上使用命令行操作VPNclient
Ubuntu server版上使用命令行操作VPNclient VPN,虚拟专用网络,这个技术还是非常有用的.近期笔者參与的项目中就使用上了VPN,大概情况是这种.有两个开发团队,在异地,代码服务器在 ...
- myslq的索引类型为MyISAM和BDB的表:复合索引下的自增长
本文源自:http://www.himigame.com/mysql/781.html 3.6.9. 使用AUTO_INCREMENT 可以通过AUTO_INCREMENT属性为新的行产生唯一的标识: ...
- JQuery------jQuery.parseHTML()的使用方法
代码: $(document).ready(function () { var data = jQuery.parseHTML("<p>你好</p>"); ...
- Effective C++ Item 16 Use the same form in corresponding uses of new and delete
1. When you created an array and want to return the memory to system. You need to explicitly add [] ...
- ZooKeeper(七)-- ZK原生API实现分布式锁
一.使用场景 在分布式应用,往往存在多个进程提供同一服务.这些进程有可能在相同的机器上,也有可能分布在不同的机器上. 如果这些进程共享了一些资源,可能就需要分布式锁来锁定对这些资源的访问. 二.实现分 ...