百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

聊一聊 Java 8 前最牛逼的时间工具库 Joda-Time

toyiye 2024-07-08 22:51 11 浏览 0 评论

一、简介

Joda-Time 是 Java 8 发布之前使用最广泛的日期和时间处理库。其目的是提供一个直观的 API 来处理日期和时间,并解决 Java Date/Time API 中存在的设计问题。

随着 Java 8 版本的发布,JDK 核心中引入了该库中实现的中心概念。新的日期和时间 API 可以在java.time包 ( JSR-310 )中找到。

Java 8 发布后,我认为该项目已基本完成,并建议如果可以的话使用 Java 8 API。

2. 为什么使用Joda-Time?

Java 8 之前的日期/时间 API 存在多个设计问题。

问题之一是 DateSimpleDateFormatter 类不是线程安全的。为了解决这个问题,Joda-Time 使用不可变类来处理日期和时间。

Date类并不表示实际的日期,而是指定一个时间点,具有毫秒精度。Date中的年份从1900年开始,而大多数日期操作通常使用从1970年1月1日开始的纪元时间。

此外,Date中的日子、月份和年份偏移是反直觉的。日子从0开始,而月份从1开始。要访问其中的任何一个,我们必须使用Calendar类。Joda-Time提供了一个干净且流畅的API来处理日期和时间。

Joda-Time还支持八种日历系统,而Java只支持两种:公历(java.util.GregorianCalendar)和日本日历(java.util.JapaneseImperialCalendar)。

3. 引入 joda-time 依赖

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10</version>
</dependency>

4. 库概述

Joda-Time使用org.joda.time包中的类 对日期和时间的概念进行建模。

最常用的类如下:

  • LocalDate – 表示没有时间的日期
  • LocalTime – 表示不带时区的时间
  • LocalDateTime – 表示不带时区的日期和时间
  • Instant – 表示从 Java 纪元 1970-01-01T00:00:00Z 开始的精确时间点(以毫秒为单位)
  • Duration – 表示两个时间点之间的持续时间(以毫秒为单位)
  • Period – 与Duration类似,但允许访问日期和时间对象的各个组成部分,例如年、月、日等。
  • Interval – 表示2个瞬时之间的时间间隔

其他重要的特性是日期解析器和格式化程序。这些可以在org.joda.time.format包中找到。

日历系统和时区特定的类可以在org.joda.time.chrono和org.joda.time.tz包中找到。

接下来看一些示例。

5. 表示日期和时间

5.1.当前日期和时间

可以使用 LocalDate 类中的 now() 方法获取 没有时间信息当前日期:

LocalDate currentDate = LocalDate.now();

当我们只需要当前时间,不需要日期信息时,可以使用 LocalTime类:

LocalTime currentTime = LocalTime.now();

要获取当前日期和时间而不考虑时区,可以使用LocalDateTime

LocalDateTime currentDateAndTime = LocalDateTime.now();

可以使用toDateTime()方法获取一个DateTime对象(考虑了时区) 。当不需要时间时,我们可以使用toLocalDate()方法将其转换为LocalDate,当只需要时间时,可以使用toLocalTime()来获取LocalTime对象:

DateTime dateTime = currentDateAndTime.toDateTime();
LocalDate localDate = currentDateAndTime.toLocalDate();
LocalTime localTime = currentDateAndTime.toLocalTime();

上述所有方法都有一个重载方法,接收DateTimeZone对象来指定时区:

LocalDate currentDate = LocalDate.now(DateTimeZone.forID("Asia/ShangHai"));

Joda-Time 还提供了与 Java 日期和时间 API 的集成。构造函数接受java.util.Date对象,并且可以使用 toDate()方法返回java.util.Date对象。

LocalDateTime currentDateTimeFromJavaDate = new LocalDateTime(new Date());
Date currentJavaDate = currentDateTimeFromJavaDate.toDate();

5.2.自定义日期和时间

Joda-Time 提供了几个构造函数来自定义日期和时间。可以指定以下对象:

  • Instant 对象
  • Java Date对象
  • 使用 ISO 格式的日期和时间的字符串表示形式
  • 日期和时间的一部分,如年、月、日、时、分、秒、毫秒
Date oneMinuteAgoDate = new Date(System.currentTimeMillis() - (60 * 1000));
Instant oneMinutesAgoInstant = new Instant(oneMinuteAgoDate);

DateTime customDateTimeFromInstant = new DateTime(oneMinutesAgoInstant);
DateTime customDateTimeFromJavaDate = new DateTime(oneMinuteAgoDate);
DateTime customDateTimeFromString = new DateTime("2018-05-05T10:11:12.123");
DateTime customDateTimeFromParts = new DateTime(2018, 5, 5, 10, 11, 12, 123);

自定义日期和时间的另一种方法是解析ISO 格式的日期和时间

DateTime parsedDateTime = DateTime.parse("2018-05-05T10:11:12.123");

还可以通过自定义DateTimeFormatter来解析日期和时间:

DateTimeFormatter dateTimeFormatter
  = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss");
DateTime parsedDateTimeUsingFormatter
  = DateTime.parse("05/05/2018 10:11:12", dateTimeFormatter);

6. 使用日期和时间

6.1.使用 Instant 类

Instant 表示从 1970-01-01T00:00:00Z 到给定时刻的毫秒数。例如,可以使用默认构造函数或now()方法获取当前时刻:

Instant instant = new Instant();
Instant.now();

要创建一个自定义时刻的Instant,我们可以使用Instant类的构造函数,或者使用EpochMilli()和EpochSecond()方法。

Instant instantFromEpochMilli
  = Instant.ofEpochMilli(milliesFromEpochTime);
Instant instantFromEpocSeconds
  = Instant.ofEpochSecond(secondsFromEpochTime);

或者使用构造函数接收一个表示 ISO 格式的日期和时间的String、Java Date或表示从 1970-01-01T00:00:00Z 开始的毫秒数的long值:

Instant instantFromString
  = new Instant("2018-05-05T10:11:12");
Instant instantFromDate
  = new Instant(oneMinuteAgoDate);
Instant instantFromTimestamp
  = new Instant(System.currentTimeMillis() - (60 * 1000));

当日期和时间表示是字符串时,可以选择使用所需的格式来解析字符串:

Instant parsedInstant
  = Instant.parse("05/05/2018 10:11:12", dateTimeFormatter);

接下来对 Instant 进行使用。

Instant对象比较:

assertTrue(instantNow.compareTo(oneMinuteAgoInstant) > 0);
assertTrue(instantNow.isAfter(oneMinuteAgoInstant));
assertTrue(oneMinuteAgoInstant.isBefore(instantNow));
assertTrue(oneMinuteAgoInstant.isBeforeNow());
assertFalse(oneMinuteAgoInstant.isEqual(instantNow));

另一个很有用的功能是 Instant 可以转换为DateTime对象或 Java Date

DateTime dateTimeFromInstant = instant.toDateTime();
Date javaDateFromInstant = instant.toDate();

当我们需要访问日期和时间的部分内容(例如年份、小时等)时,可以使用 get ()方法并指定 DateTimeField

int year = instant.get(DateTimeFieldType.year());
int month = instant.get(DateTimeFieldType.monthOfYear());
int day = instant.get(DateTimeFieldType.dayOfMonth());
int hour = instant.get(DateTimeFieldType.hourOfDay());

6.2.使用Duration、Period和Interval

Duration表示两个时间点之间的时间(以毫秒为单位),或者可以是两个Instant

long currentTimestamp = System.currentTimeMillis();
long oneHourAgo = currentTimestamp - 24*60*1000;
Duration duration = new Duration(oneHourAgo, currentTimestamp);
Instant.now().plus(duration);

还可以确定 Duration 实际是多少天、小时、分钟、秒或毫秒:

long durationInDays = duration.getStandardDays();
long durationInHours = duration.getStandardHours();
long durationInMinutes = duration.getStandardMinutes();
long durationInSeconds = duration.getStandardSeconds();
long durationInMilli = duration.getMillis();

PeriodDuration之间的主要区别 在于Period是根据其日期和时间组成部分(年、月、小时等)定义的,并不代表确切的毫秒数。使用 Period 时需要考虑时区。

例如,使用Period将1个月加到2月1日上,结果将是3月1日。由于Period库会考虑闰年,所以使用Period可以得到正确的结果。

如果我们使用Duration,结果就会不正确,因为Duration表示固定的时间量,不考虑时间顺序或时区:

Period period = new Period().withMonths(1);
LocalDateTime datePlusPeriod = localDateTime.plus(period);

Interval,顾名思义,表示由两个 Instant 时间点之间的日期和时间间隔:

Interval interval = new Interval(oneMinuteAgoInstant, instantNow);

这个类在我们需要检查两个区间是否有重叠,或者计算它们之间的间隔时非常有用。overlap()方法会在它们有重叠时返回重叠的区间,如果没有重叠则返回null:

Instant startInterval1 = new Instant("2018-05-05T09:00:00.000");
Instant endInterval1 = new Instant("2018-05-05T11:00:00.000");
Interval interval1 = new Interval(startInterval1, endInterval1);
        
Instant startInterval2 = new Instant("2018-05-05T10:00:00.000");
Instant endInterval2 = new Instant("2018-05-05T11:00:00.000");
Interval interval2 = new Interval(startInterval2, endInterval2);

Interval overlappingInterval = interval1.overlap(interval2);

区间的差值可以通过gap()方法计算,而当我们想要知道一个区间的结束是否等于另一个区间的开始时,可以使用abuts()方法。

assertTrue(interval1.abuts(new Interval(
  new Instant("2018-05-05T11:00:00.000"),
  new Instant("2018-05-05T13:00:00.000"))));

6.3.日期和时间操作

一些常见的操作是添加、减去和转换日期和时间。该库为每个类LocalDate、LocalTime、LocalDateTime和DateTime提供了特定的方法。需要注意的是,这些类是不可变的,所以每次方法调用都会创建其类型的新对象。

以LocalDateTime为例,获取当前时刻并尝试改变它的值:

LocalDateTime currentLocalDateTime = LocalDateTime.now();

currentLocalDateTime添加一天,可以使用 plusDays()方法:

LocalDateTime nextDayDateTime = currentLocalDateTime.plusDays(1);

还可以使用plus()方法向currentLocalDateTime添加Period或Duration:

Period oneMonth = new Period().withMonths(1);
LocalDateTime nextMonthDateTime = currentLocalDateTime.plus(oneMonth);

这些方法对于其他日期和时间组件也是类似的,例如,plusYears()用于添加额外的年数,plusSeconds()用于添加更多的秒数等等。

如要从 currentLocalDateTime中减去一天,可以使用minusDays()方法:

LocalDateTime previousDayLocalDateTime
  = currentLocalDateTime.minusDays(1);

此外,除了进行日期和时间的计算,我们还可以使用withHourOfDay()方法来设置日期或时间的各个部分。例如,将小时设置为10可以通过使用withHourOfDay()方法实现。以“with”为前缀的其他方法也可以用于设置日期或时间组件:

LocalDateTime currentDateAtHour10 = currentLocalDateTime
  .withHourOfDay(0)
  .withMinuteOfHour(0)
  .withSecondOfMinute(0)
  .withMillisOfSecond(0);

另一个重要的点是我们可以从日期和时间类类型转换为另一种类型。可以使用库提供的特定方法:

  • toDateTime() – 将LocalDateTime转换为DateTime对象
  • toLocalDate() – 将LocalDateTime转换为LocalDate对象
  • toLocalTime() – 将 LocalDateTime 转换为 LocalTime 对象
  • toDate() – 将LocalDateTime转换为 Java Date对象

7. 使用时区

Joda-Time 使我们可以轻松地处理不同时区并在它们之间进行更改。

Joda-Time 默认使用的时区是从用户的环境变量 `user.timezone` 中获取的。该库API允许我们为每个类或计算单独指定应该使用哪个时区。

当我们在整个应用程序中都使用特定的时区时,那么就可以设置默认的时区:

DateTimeZone.setDefault(DateTimeZone.UTC);

设置完成后,所有日期和时间操作(如果没有特别指定的话)都将以UTC时区表示。

要查看所有可用的时区,可以使用getAvailableIDs()方法:

DateTimeZone.getAvailableIDs()

当我们需要表示特定时区的日期或时间时,可以使用任何类LocalTimeLocalDateLocalDateTimeDateTime并在构造函数中指定 DateTimeZone 对象:

DateTime dateTimeInChicago
  = new DateTime(DateTimeZone.forID("America/Chicago"));
DateTime dateTimeInBucharest
  = new DateTime(DateTimeZone.forID("Europe/Bucharest"));
LocalDateTime localDateTimeInChicago
  = new LocalDateTime(DateTimeZone.forID("America/Chicago"));

此外,在这些类之间进行转换时,可以指定所需的时区。

DateTime convertedDateTime
  = localDateTimeInChicago.toDateTime(DateTimeZone.forID("Europe/Bucharest"));
Date convertedDate
  = localDateTimeInChicago.toDate(TimeZone.getTimeZone("Europe/Bucharest"));

八、结论

Joda-Time 是一个非常好用的时间工具库,在 Java 8 之前被广泛使用,Java 8 引用了它的核心概念,在实际项目中使用时,建议使用 Java 8 中的时间类库 (毕竟是官方的)。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码