Joda-Time简介(一)

引用文章:

Joda-Time简介

快速入门指南(英文)

官方API(2.9.7)

只有在Java开发中,经历过日期换算的困扰,才会寻求更好的日期格式。在Jdk方式里,我们需要运用Date,SimpleDateFormat,Canledar三个类里的相关方法才能实现日期的换算操作。如果是使用Oracle开发,那可能会把时间的处理放在Sql语句中来避免使用Jdk处理。如:增加(减少)一个月的计算。在平时的项目中,难免会涉及日期的处理。今天就来介绍一个面向 Java™ 平台的易于使用的开源时间/日期库,Joda-Time,它将会在日期处理上给我们带来极大的便利,简化Jdk方式的复杂和冗长。

Joda-Time 令时间和日期值变得易于管理、操作和理解。事实上,易于使用是 Joda 的主要设计目标。其他目标包括可扩展性、完整的特性集以及对多种日历系统的支持。并且 Joda 与 JDK 是百分之百可互操作的,因此您无需替换所有 Java 代码,只需要替换执行日期/时间计算的那部分代码。

Joda 简介及特性

简介

Joda-Time为Java日期和时间类提供具有质量保证的替换。Joda-Time事实上已经成为了Java先前到现在JavaSE8标准的日期和时间库了。开发人员已经被呼吁迁移到java.time(jsr-310)标准。

Joda 和 JDK 互操作性

JDK Calendar 类缺乏可用性,这一点很快就能体会到,而 Joda 弥补了这一不足。Joda 的设计者还做出了一个决定,我认为这是它取得成功的构建:JDK 互操作性。Joda 的类能够生成(但是,正如您将看到的一样,有时会采用一种比较迂回的方式)java.util.Date 的实例(和 Calendar)。这使您能够保留现有的依赖 JDK 的代码,但是又能够使用 Joda 处理复杂的日期/时间计算。

例如以下代码:

Calendar calendar = Calendar.getInstance();
DateTime dateTime = new DateTime(2017, 1, 17, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(20).plusMonths(1).toString("yyyy-MM-dd HH:mm:ss.SSS");
calendar.setTime(dateTime.toDate());

DateTime和Calendar两者可以实现完美转化。除此之外,在后面,将会看到更多的Joda和JDK的互操作性。

特性

  • 本地日期(LocalDate) -包含日期不包含时间
  • 本地时间(LocalTime) -包含时间不包含日期
  • 瞬间(Instant) -一个时间线上的瞬时点
  • 日期时间(DateTime) -包含日期和时间与时区
  • 时区(DateTimeZone) -一个更好的时区
  • 时间跨度(Duration and Period)-时间量
  • 区间(Interval) -两个时刻之间的时间(特定的时间跨度)
  • 一个全面并且灵活的格式化解析器

Joda 关键日期时间概念

Joda 使用以下概念,它们可以应用到任何日期/时间库:

  • 不可变性(Immutability)
  • 瞬间性(Instant)
  • 局部性(Partial)
  • 年表(Chronology)
  • 时区(Time zone)
我将针对 Joda 依次讨论每一个概念。

不可变性

我在本文讨论的 Joda 类具有不可变性,因此它们的实例无法被修改。(不可变类的一个优点就是它们是线程安全的)。我将向您展示的用于处理日期计算的 API 方法全部返回一个对应 Joda 类的新实例,同时保持原始实例不变。当您通过一个 API 方法操作 Joda 类时,您必须捕捉该方法的返回值,因为您正在处理的实例不能被修改。您可能对这种模式很熟悉;比如,这正是 java.lang.String 的各种操作方法的工作方式。

瞬间性

Instant 表示时间上的某个精确的时刻,使用从 epoch(特定的时间:1970-01-01 00:00:00 UTC) 开始计算的毫秒表示。这一定义与 JDK 相同,这就是为什么任何 Joda Instant 子类都可以与 JDK Date 和 Calendar 类兼容的原因。
更通用一点的定义是:一个瞬间 就是指时间线上只出现一次且唯一的一个时间点,并且这种日期结构只能以一种有意义的方式出现一次。

局部性

一个局部时间,正如我将在本文中将其称为局部时间片段一样,它指的是时间的一部分片段。瞬间性指定了与 epoch 相对的时间上的一个精确时刻,与此相反,局部时间片段指的是在时间上可以来回 “移动” 的一个时刻,这样它便可以应用于多个实例。比如,6 月 2 日 可以应用于任意一年的 6 月份(使用 Gregorian 日历)的第二天的任意瞬间。同样,11:06 p.m. 可以应用于任意一年的任意一天,并且每天只能使用一次。即使它们没有指定一个时间上的精确时刻,局部时间片段仍然是有用的。

年表

我喜欢将局部时间片段看作一个重复周期中的一点,这样的话,如果我正在考虑的日期构建可以以一种有意义的方式出现多次(即重复的),那么它就是一个局部时间。
  • ISO
  • GJ (GregorianJulian)
  • Gregorian
  • Julian
  • Buddhist
  • Coptic
  • Ethiopic
  • Islamic
Joda-Time 2.9.7 支持 8 种年表,每一种都可以作为特定日历系统的计算引擎。

时区

时区是值一个相对于英国格林威治(又叫格林尼治)的地理位置,用于计算时间。要了解事件发生的精确时间,还必须知道发生此事件的位置。任何严格的时间计算都必须涉及时区(或相对于 GMT世界时),除非在同一个时区内发生了相对时间计算(即使这样时区也很重要,如果事件对于位于另一个时区的各方存在利益关系的话)。
DateTimeZone 是 Joda 库用于封装位置概念的类。许多日期和时间计算都可以在不涉及时区的情况下完成,但是仍然需要了解 。DateTimeZone 如何影响 Joda 的操作。默认时间,即从运行代码的机器的系统时钟检索到的时间,在大部分情况下被使用。
相关API:Class DateTimeZone

创建 Joda-Time 对象

现在,我将展示在采用该库时会经常遇到的一些 Joda 类,并展示如何创建这些类的实例。

本节中介绍的所有实现都具有若干构造函数,允许您初始化封装的日期/时间。它们可以分为 4 个类别:

  • 使用系统时间。
  • 使用多个字段指定一个瞬间时刻(或局部时间片段),达到这个特定实现所能支持的最细粒度的精确度。
  • 指定一个瞬间时刻(或局部时间片段),以毫秒为单位。
  • 使用另一个对象(例如,java.util.Date,或者是另一个 Joda 对象,或者一个符合格式的String字符串)。

我将在第一个类中介绍这些构造函数: DateTime。当您使用其他 Joda 类的相应构造函数时,也可以使用这里介绍的内容。

ReadableInstant

Joda 通过 ReadableInstant 类实现了瞬间性这一概念。表示时间上的不可变瞬间的 Joda 类都属于这个类的子类。(将这个类命名为 ReadOnlyInstant 可能更好,我认为这才是设计者需要传达的意思)。换句话说,ReadableInstant 表示时间上的某一个不可修改的瞬间)。其中的两个子类分别为 DateTime 和 DateMidnight:
  • DateTime:这是最常用的一个类。它以毫秒级的精度封装时间上的某个瞬间时刻。DateTime 始终与 DateTimeZone 相关,如果您不指定它的话,它将被默认设置为运行代码的机器所在的时区。
可以使用多种方式构建 DateTime 对象。这个构造函数使用系统时间:
DateTime dateTime = new DateTime();
下面的代码使用一些字段值构建了一个 DateTime 对象:
DateTime dateTime = new DateTime(  2017, //year年  1,    // month月  17,  
  // day日  0,    // hour 时(午夜是0)  0,    // minute分  0,    // second秒  0     // milliseconds毫秒);
正如您所见,Joda 可以使您精确地控制创建 DateTime 对象的方式,该对象表示时间上的某个特定的瞬间。每一个 Joda 类都有一个与此类似的构造函数,您在此构造函数中指定 Joda 类可以包含的所有字段。您可以用它快速了解特定类在哪一种粒度级别上操作。
下一个构造函数将指定从 epoch 到某个时刻所经过的毫秒数。它根据 JDK Date 对象的毫秒值创建一个 DateTime 对象,其时间精度用毫秒表示,因为 epoch 与 Joda 是相同的:

java.util.Date jdkDate = new Date();long timeInMillis = jdkDate.getTime();DateTime dateTime = new DateTime(timeInMillis);
并且这个例子与前例类似,唯一不同之处是我在这里将 Date 对象直接传递给构造函数:
java.util.Date jdkDate = new Date();dateTime = new DateTime(jdkDate);
Joda 支持使用许多其他对象作为构造函数的参数,用于创建 DateTime,例如所示:
直接将不同对象传递给 DateTime 的构造函数

// 使用一个 Calendar
java.util.Calendar calendar = Calendar.getInstance();
dateTime = new DateTime(calendar);
// 使用另一个 Joda DateTime
DateTime anotherDateTime = new DateTime();
dateTime = new DateTime(anotherDateTime);
// 使用一个字符串 (必须是符合正确格式的)
String timeString = "2017-01-17T22:30:00-08:00";
dateTime = new DateTime(timeString);
timeString = "2017-01-17";
dateTime = new DateTime(timeString);
注意,如果您准备使用 String(必须经过解析),您必须对其进行精确地格式化。参考 Javadoc,获得有关 Joda 的 ISODateTimeFormat资料

  • DateMidnight:这个类封装某个时区(通常为默认时区)在特定年/月/日的午夜时分的时刻。它基本上类似于 DateTime,不同之处在于时间部分总是为与该对象关联的特定 DateTimeZone 时区的午夜时分。

ReadablePartial

应用程序所需处理的日期问题并不全部都与时间上的某个完整时刻有关,因此您可以处理一个局部时刻。例如,有时您比较关心年/月/日,或者一天中的时间,甚至是一周中的某天。Joda 设计者使用 ReadablePartial 接口捕捉这种表示局部时间的概念,这是一个不可变的局部时间片段。用于处理这种时间片段的两个有用类分别为 LocalDate 和 LocalTime:
  • LocalDate:该类封装了一个年/月/日的组合。当地理位置(即时区)变得不重要时,使用它存储日期将非常方便。例如,某个特定对象的出生日期 可能为 1999 年 4 月 16 日,但是从技术角度来看,在保存所有业务值的同时不会了解有关此日期的任何其他信息(比如这是一周中的星期几,或者这个人出生地所在的时区)。在这种情况下,应当使用 LocalDate。
我们可以直接new一个系统当前日期:
LocalDate localDate = new LocalDate();
也可以通过显式地提供所含的每个字段的值来创建 LocalDate:
LocalDate localDate = new LocalDate(2017, 1, 17);// 2017-1-17
LocalDate 替代了在早期 Joda 版本中使用的 YearMonthDay。
  • LocalTime:这个类封装一天中的某个时间,当地理位置不重要的情况下,可以使用这个类来只存储一天当中的某个时间。
例如,晚上 11:52 可能是一天当中的一个重要时刻(比如,一个 cron(定时) 任务将启动,它将备份文件系统的某个部分),但是这个时间并没有特定于某一天,因此我不需要了解有关这一时刻的其他信息。
实例化一个时间:

LocalTime localTime = new LocalTime();
也可以通过显式地提供所含的每个字段的值来创建 LocalTime:
LocalTime localTime = new LocalTime(22, 36, 21, 0);// 22:36:21 0

时间跨度

了解特定的时刻或是某个局部时间片段将非常有用,但是如果能够表达一段时间跨度的话,通常也很有用。Joda 提供了三个类来简化这个过程。您可以选择用于表示不同跨度的类:
  • Duration:这个类表示一个绝对的精确跨度,使用毫秒为单位。这个类提供的方法可以用于通过标准的数学转换(比如 1分钟 = 60 秒,1 天 = 24 小时),将时间跨度转换为标准单位(比如秒、分和小时)。您只在以下情况使用 Duration 的实例:您希望转换一个时间跨度,但是您并不关心这个时间跨度在何时发生,或者使用毫秒处理时间跨度比较方便。
  • Period:这个类表示与 Duration 相同的概念,但是以人们比较熟悉的单位表示,比如年、月、周。您可以在以下情况使用 Period:您并不关心这段时期必须在何时发生,或者您更关心检索单个字段的能力,这些字段描述由 Period 封装的时间跨度。
  • Interval:这个类表示一个特定的时间跨度,将使用一个明确的时刻界定这段时间跨度的范围。Interval 为半开 区间,这表示由 Interval 封装的时间跨度包括这段时间的起始时刻,但是不包含结束时刻。可以在以下情况使用 Interval:需要表示在时间连续区间中以特定的点开始和结束的一段时间跨度。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值