计算机程序的思维逻辑 (33) - Joda-Time
本系列文章经补充和完善,已修订整理成书《Java编程的逻辑》,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http://item.jd.com/12299018.html
Joda-Time
上节介绍了JDK API中的日期和时间类,我们提到了JDK API的一些不足,并提到,实践中有一个广泛使用的日期和时间类库,Joda-Time,本节我们就来介绍Joda-Time。俗话说,工欲善其事,必先利其器,Joda-Time就是操作日期和时间的一把利器。
Joda-Time的官网是http://www.joda.org/joda-time/。它的基本概念和工作原理与上节介绍的是类似的,比如说,都有时刻和年历的概念,都有时区和Locale的概念,主要工作,都是在毫秒和年月日等年历信息之间进行相互转换。
Joda-Time的主要类和Java API的类也有一个粗略的对应关系:
Joda-Time |
Java API |
说明 |
Instant | Date | 时刻 |
DateTime | Calendar | 年历 |
DateTimeZone | TimeZone | 时区 |
DateTimeFormatter | DateFormat | 格式化 |
需要说明的是,这只是一个非常粗略的对应,并不严谨,Joda-Time也还有非常多的其他类。
虽然基本概念是类似的,但API的设计却有很大不同,Joda-Time的API更容易理解和使用,代码也更为简洁,下面我们会通过例子来说明。
另外,与Date/Calendar的设计有一个很大的不同,Joda-Time中的主要类都被设计为了不可变类,我们之前介绍过不可变类,包装类/String都是不可变类,不可变类有一个很大的优点,那就是简单、线程安全,所有看似的修改操作都是通过创建新对象来实现的。
本文并不打算全面介绍Joda-Time的每个类,相反,我们主要通过一些例子来说明其基本用法,体会其方便和强大,同时,学习其API的设计理念。
创建对象
新建一个DateTime对象,表示当前日期和时间:
DateTime dt = new DateTime();
新建一个DateTime对象,给定年月日时分秒等信息:
//2016-08-18 15:20
DateTime dt = new DateTime(2016,8,18,15,20); //2016-08-18 15:20:47
DateTime dt2 = new DateTime(2016,8,18,15,20,47); //2016-08-18 15:20:47.345
DateTime dt3 = new DateTime(2016,8,18,15,20,47,345);
获取日历信息
与Calendar不同,DateTime为每个日历字段都提供了单独的方法,取值的范围也都是符合常识的,易于理解和使用,来看代码:
//2016-08-18 15:20:47.345
DateTime dt = new DateTime(2016,8,18,15,20,47,345);
System.out.println("year: "+dt.getYear());
System.out.println("month: "+dt.getMonthOfYear());
System.out.println("day: "+dt.getDayOfMonth());
System.out.println("hour: "+dt.getHourOfDay());
System.out.println("minute: "+dt.getMinuteOfHour());
System.out.println("second: "+dt.getSecondOfMinute());
System.out.println("millisecond: " +dt.getMillisOfSecond());
System.out.println("day_of_week: " +dt.getDayOfWeek());
输出为:
year: 2016
month: 8
day: 18
hour: 15
minute: 20
second: 47
millisecond: 345
day_of_week: 4
每个字段的输出都符合常识,且保持一致,都是从1开始,比如dayOfWeek,周四就是4, 易于理解。
格式化
Java API中,格式化必须使用一个DateFormat对象,而Joda-Time中,DateTime自己就有一个toString方法,可以接受一个pattern参数,看例子:
//2016-08-18 14:20:45.345
DateTime dt = new DateTime(2016,8,18,14,20,45,345);
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss"));
输出为:
2016-08-18 14:20:45
Joda-Time也有与DateFormat类似的类,看代码:
DateTime dt = new DateTime(2016,8,18,14,20);
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
System.out.println(formatter.print(dt));
输出为:
2016-08-18 14:20
这里有两个类,一个是DateTimeFormat,另一个是DateTimeFormatter。DateTimeFormatter是具体的格式化类,提供了print方法将DateTime转换为字符串。DateTimeFormat是一个工厂类,专门生成具体的格式化类,除了forPattern方法,它还有一些别的工厂方法,本文就不介绍了。
程序设计的一个基本思维是关注点分离,程序一般总是比较复杂的,涉及方方面面,解决的思路就是分解,将复杂的事情尽量分解为不同的方面,或者说关注点,各个关注点之间耦合度要尽量低。
具体来说,对应到Java,每个类应该只关注一点。上面的例子中,因为生成DateTimeFormatter的方式比较多,就将生成DateTimeFormatter这个事单独拿了出来,就有了工厂类DateTimeFormat,只关注生产DateTimeFormatter,Joda-Time中还有别的工厂类,比如ISODateTimeFormat,工厂类是一种常见的设计模式。
除了将DateTime转换为字符串,DateTimeFormatter还可以将字符串转化为DateTime,代码如下:
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
DateTime dt = formatter.parseDateTime("2016-08-18 14:20");
与上节介绍的格式化类不同,Joda-Time的DateTimeFormatter是线程安全的,可以安全的被多个线程共享。
设置和修改时间
上节介绍Calendar时提到,修改时期和时间有两种方式,一种是直接设置绝对值,另一种是在现有值的基础上进行相对增减操作,DateTime也支持这两种方式。
不过,需要注意的是,DateTime是不可变类,修改操作是通过创建并返回新对象来实现的,原对象本身不会变。
我们来看一些例子。
调整时间为下午3点20
DateTime dt = new DateTime();
dt = dt.withHourOfDay(15).withMinuteOfHour(20);
DateTime有很多withXXX方法来设置绝对时间。DateTime中非常方便的一点是,方法的返回值是修改后的DateTime对象,可以接着进行下一个方法调用,这样,代码就非常简洁,也非常容易阅读,这种一种流行的设计风格,称为流畅接口 (Fluent Interface),相比之下,使用Calendar,就必须要写多行代码,比较臃肿,下面我们会看到更多例子。
另外,注意需要将最后的返回值赋值给dt,否则dt的值不会变。
三小时五分钟后
DateTime dt = new DateTime().plusHours(3).plusMinutes(5);
DateTime有很多plusXXX和minusXXX方法,用于相对增加和减少时间。
今天0点
DateTime dt = new DateTime().withMillisOfDay(0);
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss.SSS"));
当前时间为2016-08-18,所以输出为
2016-08-18 00:00:00.000
withMillisOfDay直接设置当天毫秒信息,会同时将时分秒等信息进行修改。
下周二上午10点整
DateTime dt = new DateTime().plusWeeks(1).withDayOfWeek(2)
.withMillisOfDay(0).withHourOfDay(10);
明天最后一刻
DateTime dt = new DateTime().plusDays(1).millisOfDay().withMaximumValue();
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss.SSS"));
当前时间为2016-08-18,所以输出为
2016-08-19 23:59:59.999
这里说明一下,plusDays(1)容易理解,设为第二天。millisOfDay()的返回值比较特别,它是一个属性,具体类为DateTime的一个内部类Property,这个属性代表当天毫秒信息,这个属性有一些方法,可以接着对日期进行修改,withMaximumValue就是将该属性的值设为最大值。
这样,代码是不是非常简洁?除了millisOfDay,DateTime还有很多类似属性。我们来看更多的例子。
本月最后一天最后一刻
DateTime dt = new DateTime().dayOfMonth().withMaximumValue().millisOfDay().withMaximumValue();
下个月第一个周一的下午5点整
DateTime dt = new DateTime().plusMonths(1).dayOfMonth().withMinimumValue()
.plusDays(6).withDayOfWeek(1).withMillisOfDay(0).withHourOfDay(17);
我们稍微解释下:
new DateTime().plusMonths(1).dayOfMonth().withMinimumValue()
将时间设为了下个月的第一天。.plusDays(6).withDayOfWeek(1)将时间设为第一个周一。
时间段的计算
JDK API中没有关于时间段计算的类,而Joda-Time包含丰富的表示时间段和用于时间段计算的方法,我们来看一些例子。
计算两个时间之间的差
Joda-Time有一个类,Period,表示按日历信息的时间段,看代码:
DateTime start = new DateTime(2016,8,18,10,58);
DateTime end = new DateTime(2016,9,19,12,3);
Period period = new Period(start,end);
System.out.println(period.getMonths()+"月"+period.getDays()+"天"
+period.getHours()+"小时"+period.getMinutes()+"分");
输出为:
1月1天1小时5分
只要给定起止时间,Period就可以自动计算出来,两个时间之间有多少月、多少天、多少小时等。
如果只关心一共有多少天,或者一共有多少周呢?Joda-Time有专门的类,比如Years用于年,Days用于日,Minutes用于分钟,来看一些例子。
根据生日计算年龄
年龄只关心年,可以使用Years,看代码:
DateTime born = new DateTime(1990,11,20,12,30);
int age = Years.yearsBetween(born, DateTime.now()).getYears();
计算迟到分钟数
假定早上9点是上班时间,过了9点算迟到,迟到要统计迟到的分钟数,怎么计算呢?看代码:
int lateMinutes = Minutes.minutesBetween(
DateTime.now().withMillisOfDay(0).withHourOfDay(9),
DateTime.now()).getMinutes();
单独的日期和时间类
我们一直在用DateTime表示完整的日期和时间,但在年龄的例子中,只需要关心日期,在迟到的例子中,只需要关心时间,Joda-Time分别有单独的日期类LocalDate和时间类LocalTime。
使用LocalDate计算年龄
LocalDate born = new LocalDate(1990,11,20);
int age = Years.yearsBetween(born, LocalDate.now()).getYears();
使用LocalTime计算迟到时间
int lateMinutes = Minutes.minutesBetween(
new LocalTime(9,0),
LocalTime.now()).getMinutes();
LocalDate和LocalTime可以与DateTime进行相互转换,比如:
DateTime dt = new DateTime(1990,11,20,12,30);
LocalDate date = dt.toLocalDate();
LocalTime time = dt.toLocalTime();
DateTime newDt = DateTime.now().withDate(date).withTime(time);
与JDK API的互操作
Joda-Time中的类可以方便的与JDK中的类进行相互转换。
JDK -> Joda
Date、Calendar可以方便的转换为DateTime对象:
DateTime dt = new DateTime(new Date());
DateTime dt2 = new DateTime(Calendar.getInstance());
也可以方便的转换为LocalDate和LocalTime对象:
LocalDate.fromDateFields(new Date());
LocalDate.fromCalendarFields(Calendar.getInstance());
LocalTime.fromDateFields(new Date());
LocalTime.fromCalendarFields(Calendar.getInstance());
Joda -> JDK
DateTime对象也可以方便的转换为JDK对象:
DateTime dt = new DateTime();
Date date = dt.toDate();
Calendar calendar = dt.toCalendar(Locale.CHINA);
LocalDate也可以转换为Date对象:
LocalDate localDate = new LocalDate(2016,8,18);
Date date = localDate.toDate();
小结
本节介绍了Joda-Time,一个方便和强大的日期和时间类库,本文并未全面介绍,主要是通过一些例子展示了其基本用法。
我们也介绍了Joda-Time之所以易用的一些设计思维,比如,关注点分离,为方便操作,提供单独的功能明确的类和方法,设计API为流畅接口,设计为不可变类,使用工厂类等。
下一节,我们来讨论一个有趣的话题,那就是随机。
----------------
未完待续,查看最新文章,敬请关注微信公众号“老马说编程”(扫描下方二维码),从入门到高级,深入浅出,老马和你一起探索Java编程及计算机技术的本质。用心写作,原创文章,保留所有版权。
计算机程序的思维逻辑 (33) - Joda-Time的更多相关文章
- 计算机程序的思维逻辑 (8) - char的真正含义
看似简单的char 通过前两节,我们应该对字符和文本的编码和乱码有了一个清晰的认识,但前两节都是与编程语言无关的,我们还是不知道怎么在程序中处理字符和文本. 本节讨论在Java中进行字符处理的基础 - ...
- 计算机程序的思维逻辑 (29) - 剖析String
上节介绍了单个字符的封装类Character,本节介绍字符串类.字符串操作大概是计算机程序中最常见的操作了,Java中表示字符串的类是String,本节就来详细介绍String. 字符串的基本使用是比 ...
- 计算机程序的思维逻辑 (64) - 常见文件类型处理: 属性文件/CSV/EXCEL/HTML/压缩文件
对于处理文件,我们介绍了流的方式,57节介绍了字节流,58节介绍了字符流,同时,也介绍了比较底层的操作文件的方式,60节介绍了随机读写文件,61节介绍了内存映射文件,我们也介绍了对象的序列化/反序列化 ...
- 计算机程序的思维逻辑 (31) - 剖析Arrays
数组是存储多个同类型元素的基本数据结构,数组中的元素在内存连续存放,可以通过数组下标直接定位任意元素,相比我们在后续章节介绍的其他容器,效率非常高. 数组操作是计算机程序中的常见基本操作,Java中有 ...
- 计算机程序的思维逻辑 (73) - 并发容器 - 写时拷贝的List和Set
本节以及接下来的几节,我们探讨Java并发包中的容器类.本节先介绍两个简单的类CopyOnWriteArrayList和CopyOnWriteArraySet,讨论它们的用法和实现原理.它们的用法比较 ...
- 计算机程序的思维逻辑 (30) - 剖析StringBuilder
上节介绍了String,提到如果字符串修改操作比较频繁,应该采用StringBuilder和StringBuffer类,这两个类的方法基本是完全一样的,它们的实现代码也几乎一样,唯一的不同就在于,St ...
- 计算机程序的思维逻辑 (38) - 剖析ArrayList
从本节开始,我们探讨Java中的容器类,所谓容器,顾名思义就是容纳其他数据的,计算机课程中有一门课叫数据结构,可以粗略对应于Java中的容器类,我们不会介绍所有数据结构的内容,但会介绍Java中的主要 ...
- 计算机程序的思维逻辑 (40) - 剖析HashMap
前面两节介绍了ArrayList和LinkedList,它们的一个共同特点是,查找元素的效率都比较低,都需要逐个进行比较,本节介绍HashMap,它的查找效率则要高的多,HashMap是什么?怎么用? ...
- 计算机程序的思维逻辑 (48) - 剖析ArrayDeque
前面我们介绍了队列Queue的两个实现类LinkedList和PriorityQueue,LinkedList还实现了双端队列接口Deque,Java容器类中还有一个双端队列的实现类ArrayDequ ...
随机推荐
- Linux 内核概述 - Linux Kernel
Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...
- Chrome出了个小bug:论如何在Chrome下劫持原生只读对象
Chrome出了个小bug:论如何在Chrome下劫持原生只读对象 概述 众所周知,虽然JavaScript是个很灵活的语言,浏览器里很多原生的方法都可以随意覆盖或者重写,比如alert.但是为了保证 ...
- 在Ubuntu下搭建ASP.NET 5开发环境
在Ubuntu下搭建ASP.NET 5开发环境 0x00 写在前面的废话 年底这段时间实在太忙了,各种事情都凑在这个时候,没时间去学习自己感兴趣的东西,所以博客也好就没写了.最近工作上有个小功能要做成 ...
- 玩转spring boot——结合JPA事务
接着上篇 一.准备工作 修改pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=&q ...
- WebGIS中等值线前端生成绘制简析
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 等值线是GIS制图中常见的功能,一般有两种思路:一种是先进行插 ...
- MediatorPattern(中介者模式)
/** * 中介者模式 * @author TMAC-J * 研究了这么多设计模式,觉得无非就是几点: * 1.若两个类有耦合关系,设立一个中间类,处理两个类的关系,把两个类的耦合降低 * 2.面向接 ...
- Postman - 功能强大的 API 接口请求调试和管理工具
Postman 是一款功能强大的的 Chrome 应用,可以便捷的调试接口.前端开发人员在开发或者调试 Web 程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的 Fi ...
- iOS 后台处理
iOS 后台处理的常见用途 1.进入后台时候删除资源:应用处于挂起状态的时候所占用的资源越少,该应用被iOS终止的风险就越低.通过从内存中清理那些易于重新创建的资源,可以增加应用驻留内存的机会,因此可 ...
- NDK开发_笔记0
自谷歌搜索退出中国以来,谷歌对全球第二大市场中国的态度一直保持冷淡.可是北京时间12月8日,谷歌2016开发者大会在北京召开,同时专门针对中国的谷歌开发者网站已经上线:https://develope ...
- (转) 将ASP.NET Core应用程序部署至生产环境中(CentOS7)
原文链接: http://www.cnblogs.com/ants/p/5732337.html 阅读目录 环境说明 准备你的ASP.NET Core应用程序 安装CentOS7 安装.NET Cor ...