在工作中经常会碰到需要对时间进行处理的情况。SimpleDateFormat被大量使用于处理时间格式化过程,由于时间转换过程遇到的多线程并发的使用场景并不多见,所以很难发现在该类的隐患,事实上,该类并非是线程安全的,在多线程使用format()和parse()方法时可能会遇到问题。
也就是说,使用同一个SimpleDateFormat实例,开若干线程做日期转换操作,得到的结果可能并不准确。
在这种背景下, 我们在大数据分析等多种高并发场景下, 应该寻找一个线程安全的替代品——Joda-time。
首先先介绍以下joda-time的基本用法:
创建Joda-Time对象
先介绍以下这个类库常用的类:
ReadableInstant
表示时间上的不可变瞬间的 Joda 类都属于这个类的子类,即不可修改的时间。
其中的两个子类分别为 DateTime 和 DateMidnight:
DateTime:这是最常用的一个类。它以毫秒级的精度封装时间上的某个瞬间时刻。
创建DateTime 时如果没有提供 Chronology(年表) 或 DateTimeZone(时区),Joda将使用 ISOChronology(默认)和DateTimeZone(来自系统设置,即运行代码的机器所在的时区)
可以使用多种方式构建 DateTime 对象(如下图)。
(1)无参构造函数(获得系统的时间)
val date = new DateTime()
(2)Joda 可以使您精确地控制创建 DateTime 对象的方式,该对象表示时间上的某个特定的瞬间。
val dateTime = new DateTime(
2000, //year
1, // month
1, // day
0, // hour
0, // minute
0, // second
0 // milliseconds
)
(3)通过时间戳创建
val date = new DateTime(1553677539000l)
(4)Joda 支持使用许多其他对象作为构造函数的参数,用于创建 DateTime:
// 使用已有的Calendar类
var dateTime = new DateTime(calendar)
// 使用另外一个 Joda DateTime
dateTime = new DateTime(anotherDateTime)
// 使用字符串 (必须按照以下格式)
var timeString = "2019-03-26T13:30:00-06:00"
dateTime = new DateTime(timeString)
timeString = "2019-03-26"
dateTime = new DateTime(timeString)
DateMidnight:这个类封装某个时区(通常为默认时区)在特定年/月/日的凌晨12点的时刻。
它基本上类似于 DateTime,不同之处在于时间部分总是为与该对象关联的特定 DateTimeZone 时区的凌晨12点。
ReadablePartial
这是一个不可变的局部时间片段(例如,有时您比较关心年/月/日,或者一天中的时间,甚至是一周中的某天)。用于处理这种时间片段的两个有用类分别为 LocalDate 和 LocalTime:
LocalDate:该类封装了一个年/月/日的组合。当时区变得不重要时,使用它存储日期将非常方便。
例如,某个特定对象的出生日期 可能为 1999 年 4 月 16 日,在保存所有业务值的同时不会了解有关此日期的任何其他信息(比如这是一周中的星期几,或者这个人出生地所在的时区)。在这种情况下,应当使用 LocalDate。
//只需要年月日的时候
val localDate = new LocalDate(2018, 9, 6)
LocalTime:这个类封装一天中的某个时间,当时区不重要的情况下,可以使用这个类来只存储一天当中的某个时间。
例如,晚上 11:52 可能是一天当中的一个重要时刻(比如,一个 cron 任务将启动,它将备份文件系统的某个部分),
但是这个时间并没有特定于某一天,因此我不需要了解有关这一时刻的其他信息。
//只需要时分秒毫秒的时候
val localTime = new LocalTime(13, 30, 26, 0) // 1:30:26PM
Joda可以很轻易的转换成 Java Date 对象和Calendar对象 。 (通过 dateTime.getMillis()得到时间戳作为介质)
日期计算
val dt = new DateTime()
//昨天
val yesterday = dt.minusDays(1)
//明天
val tomorrow = dt.plusDays(1)
//1个月前
val before1month = dt.minusMonths(1)
//3个月后
val after3month = dt.plusMonths(3)
//2年前
val before2year = dt.minusYears(2)
//5年后
val after5year = dt.plusYears(5)
日期格式化
joda中格式化日期十分简单, 只需要调用 toString()方法, 指定格式化格式就可以了。
val dateTime = DateTime.now
dateTime.toString("MM/dd/yyyy hh:mm:ss.SSSa")
dateTime.toString("dd-MM-yyyy HH:mm:ss")
dateTime.toString("EEEE dd MMMM, yyyy HH:mm:ssa")
dateTime.toString("MM/dd/yyyy HH:mm ZZZZ")
dateTime.toString("MM/dd/yyyy HH:mm Z")
09/06/2018 02:30:00.000PM
06-Sep-2018 14:30:00
Sunday 06 September, 2018 14:30:00PM
09/06/2018 14:30 America/Chicago
09/06/2018 14:30 -0500