聊一聊 Java 8 前最牛逼的时间工具库 Joda-Time
bigegpt 2024-10-25 10:23 11 浏览
一、简介
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 存在多个设计问题。
问题之一是 Date 和SimpleDateFormatter 类不是线程安全的。为了解决这个问题,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();
Period 和 Duration之间的主要区别 在于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()
当我们需要表示特定时区的日期或时间时,可以使用任何类LocalTime、LocalDate、LocalDateTime、DateTime并在构造函数中指定 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 中的时间类库 (毕竟是官方的)。
相关推荐
- 当Frida来“敲”门(frida是什么)
-
0x1渗透测试瓶颈目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。而现在挖...
- 服务端性能测试实战3-性能测试脚本开发
-
前言在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:阶段一:了解各个接口的入参、出参,使用Python代码模拟前端...
- Springboot整合Apache Ftpserver拓展功能及业务讲解(三)
-
今日分享每天分享技术实战干货,技术在于积累和收藏,希望可以帮助到您,同时也希望获得您的支持和关注。架构开源地址:https://gitee.com/msxyspringboot整合Ftpserver参...
- Linux和Windows下:Python Crypto模块安装方式区别
-
一、Linux环境下:fromCrypto.SignatureimportPKCS1_v1_5如果导包报错:ImportError:Nomodulenamed'Crypt...
- Python 3 加密简介(python des加密解密)
-
Python3的标准库中是没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto和cryptography上,我...
- 怎样从零开始编译一个魔兽世界开源服务端Windows
-
第二章:编译和安装我是艾西,上期我们讲述到编译一个魔兽世界开源服务端环境准备,那么今天跟大家聊聊怎么编译和安装我们直接进入正题(上一章没有看到的小伙伴可以点我主页查看)编译服务端:在D盘新建一个文件夹...
- 附1-Conda部署安装及基本使用(conda安装教程)
-
Windows环境安装安装介质下载下载地址:https://www.anaconda.com/products/individual安装Anaconda安装时,选择自定义安装,选择自定义安装路径:配置...
- 如何配置全世界最小的 MySQL 服务器
-
配置全世界最小的MySQL服务器——如何在一块IntelEdison为控制板上安装一个MySQL服务器。介绍在我最近的一篇博文中,物联网,消息以及MySQL,我展示了如果Partic...
- 如何使用Github Action来自动化编译PolarDB-PG数据库
-
随着PolarDB在国产数据库领域荣膺桂冠并持续获得广泛认可,越来越多的学生和技术爱好者开始关注并涉足这款由阿里巴巴集团倾力打造且性能卓越的关系型云原生数据库。有很多同学想要上手尝试,却卡在了编译数据...
- 面向NDK开发者的Android 7.0变更(ndk android.mk)
-
订阅Google官方微信公众号:谷歌开发者。与谷歌一起创造未来!受Android平台其他改进的影响,为了方便加载本机代码,AndroidM和N中的动态链接器对编写整洁且跨平台兼容的本机...
- 信创改造--人大金仓(Kingbase)数据库安装、备份恢复的问题纪要
-
问题一:在安装KingbaseES时,安装用户对于安装路径需有“读”、“写”、“执行”的权限。在Linux系统中,需要以非root用户执行安装程序,且该用户要有标准的home目录,您可...
- OpenSSH 安全漏洞,修补操作一手掌握
-
1.漏洞概述近日,国家信息安全漏洞库(CNNVD)收到关于OpenSSH安全漏洞(CNNVD-202407-017、CVE-2024-6387)情况的报送。攻击者可以利用该漏洞在无需认证的情况下,通...
- Linux:lsof命令详解(linux lsof命令详解)
-
介绍欢迎来到这篇博客。在这篇博客中,我们将学习Unix/Linux系统上的lsof命令行工具。命令行工具是您使用CLI(命令行界面)而不是GUI(图形用户界面)运行的程序或工具。lsoflsof代表&...
- 幻隐说固态第一期:固态硬盘接口类别
-
前排声明所有信息来源于网络收集,如有错误请评论区指出更正。废话不多说,目前固态硬盘接口按速度由慢到快分有这几类:SATA、mSATA、SATAExpress、PCI-E、m.2、u.2。下面我们来...
- 新品轰炸 影驰SSD多款产品登Computex
-
分享泡泡网SSD固态硬盘频道6月6日台北电脑展作为全球第二、亚洲最大的3C/IT产业链专业展,吸引了众多IT厂商和全球各地媒体的热烈关注,全球存储新势力—影驰,也积极参与其中,为广大玩家朋友带来了...
- 一周热门
- 最近发表
-
- 当Frida来“敲”门(frida是什么)
- 服务端性能测试实战3-性能测试脚本开发
- Springboot整合Apache Ftpserver拓展功能及业务讲解(三)
- Linux和Windows下:Python Crypto模块安装方式区别
- Python 3 加密简介(python des加密解密)
- 怎样从零开始编译一个魔兽世界开源服务端Windows
- 附1-Conda部署安装及基本使用(conda安装教程)
- 如何配置全世界最小的 MySQL 服务器
- 如何使用Github Action来自动化编译PolarDB-PG数据库
- 面向NDK开发者的Android 7.0变更(ndk android.mk)
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- libcrypto.so (74)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)