日期处理第一篇:优雅好用的Java日期工具类Joda-Time

本文介绍了Joda-Time库在Java开发中的重要性,包括其安装、核心特性如LocalDate、LocalTime、DateTime和时区处理,以及为何在Java8之前它是事实上的标准日期库。还涵盖了如何使用Joda-Time进行日期操作和格式化,以及与Java8的Java.time包的关系。
摘要由CSDN通过智能技术生成

日常开发中,处理时间和日期是很常见的需求。基础的java内置工具类只有Date和Calendar,但是这些工具类的api使用并不是很方便和强大,于是就诞生了Joda-Time这个专门处理日期时间的库。

安装

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

简介

Joda-Time提供了Java日期处理的优雅的替代品,在Java8之前,Joda-Time一直是首选日期处理工具类,以弥补JDK的不足。由于Joda-Time良好的设计和风格,很多核心思想被引入到Java8中,Joda-Time的作者Stephen Colebourne和Oracle一起共同参与了这些API的设计和实现。这里我们主要介绍Joda-Time的使用。

Joda的核心特性

  • LocalDate - 没有时间的日期 如:2024-01-16
  • LocalTime - 没有日期的时间 如:16:40:22
  • Instant - 不可变的类,用来表示时间轴上一个瞬时的点
  • DateTime - 不可变的类,用来替换JDK的Calendar类,包含详细的日期时间和时区信息
  • DateTimeZone - 时区
  • Duration and Period - 时间的度量
  • Interval - 两个时间的间隔
  • 全面且灵活的格式化解析器

为什么要使用Joda-Time

在Java SE 8之前,标准的日期和时间类相对较差。通过直接应对这个问题,Joda-Time 成为了在 Java SE 8 之前的 Java 中事实上的标准日期和时间库。请注意,从 Java SE 8 开始,用户被要求迁移到 java.time(JSR-310),这是 JDK 的核心部分,取代了 Joda-Time 项目。
示例代码参考:

// 判断时间前后
public boolean isAfterPayDay(DateTime datetime) {
  if (datetime.getMonthOfYear() == 2) {   // February is month 2!!
    return datetime.getDayOfMonth() > 26;
  }
  return datetime.getDayOfMonth() > 28;
}

// 距离新年还有多少天
public Days daysToNewYear(LocalDate fromDate) {
  LocalDate newYear = fromDate.plusYears(1).withDayOfYear(1);
  return Days.daysBetween(fromDate, newYear);
}

// 计算给定时间之后的某个时间
public boolean isRentalOverdue(DateTime datetimeRented) {
  Period rentalPeriod = new Period().withDays(2).withHours(12);
  return datetimeRented.plus(rentalPeriod).isBeforeNow();
}

快速开始

其中用到最多的是下面这个五个类:
Instant - 不可变的类,用来表示时间轴上一个瞬时的点
DateTime - 不可变的类,用来替换JDK的Calendar类
LocalDate - 不可变的类,表示一个本地的日期,而不包含时间部分(没有时区信息)
LocalTime - 不可变的类,表示一个本地的时间,而不包含日期部分(没有时区信息)
LocalDateTime - 不可变的类,表示一个本地的日期-时间(没有时区信息)
Instant比较适合用来表示一个事件发生的时间戳。不用去关心它使用的日历系统或者是所在的时区。
DateTime的主要目的是替换JDK中的Calendar类,用来处理那些时区信息比较重要的场景。
LocalDate比较适合表示出生日期这样的类型,因为不关心这一天中的时间部分。
LocalTime适合表示一个商店的每天开门/关门时间,因为不用关心日期部分。

DateTime类

丰富的构造方法

如果你直接看源码,会看到这个类有很多构造方法,方便开发者按照需求快速构造相应的日期对象。
在这里插入图片描述
我么经常使用到的有:

 // 直接获取当前时间的对象
 DateTime dateTime = DateTime.now();
 // 直接按照日期每个字段构造
 DateTime dateTime2 = new DateTime(2024, 01, 18, 09, 30, 00);
 
DateTime dateTime1 = new DateTime();
System.out.println(dateTime1); // out: 2016-02-26T16:02:57.582+08:00

DateTime dateTime2 = new DateTime(2016,2,14,0,0,0);
System.out.println(dateTime2); // out: 2016-02-14T00:00:00.000+08:00

DateTime dateTime3 = new DateTime(1456473917004L);
System.out.println(dateTime3); // out: 2016-02-26T16:05:17.004+08:00

DateTime dateTime4 = new DateTime(new Date());
System.out.println(dateTime4); // out: 2016-02-26T16:07:59.970+08:00

DateTime dateTime5 = new DateTime("2016-02-15T00:00:00.000+08:00");
System.out.println(dateTime5); // out: 2016-02-15T00:00:00.000+08:00
常用的其他方法

当我们有了一个DateTime实例之后,就可以访问他的各种方法了。

  • with开头的方法(比如:withYear):用来设置DateTime实例到某个时间,因为DateTime是不可变对象,所以没有提供setter方法可供使用,with方法也没有改变原有的对象,而是返回了设置后的一个副本对象。下面这个例子,将2000-02-29的年份设置为1997。值得注意的是,因为1997年没有2月29日,所以自动转为了28日。
    在这里插入图片描述
DateTime dateTime2000Year = new DateTime(2000,2,29,0,0,0);
System.out.println(dateTime2000Year); // out: 2000-02-29T00:00:00.000+08:00
DateTime dateTime1997Year = 
        dateTime2000Year.withYear(1997); 
System.out.println(dateTime1997Year); // out: 1997-02-28T00:00:00.000+08:00

plus/minus开头的方法(比如:plusDay, minusMonths):用来返回在DateTime实例上增加或减少一段时间后的实例。下面的例子:在当前的时刻加1天,得到了明天这个时刻的时间;在当前的时刻减1个月,得到了上个月这个时刻的时间。
在这里插入图片描述

DateTime now = new DateTime();
System.out.println(now); // out: 2016-02-26T16:27:58.818+08:00
DateTime tomorrow = now.plusDays(1);
System.out.println(tomorrow); // out: 2016-02-27T16:27:58.818+08:00
DateTime lastMonth = now.minusMonths(1);
System.out.println(lastMonth); // out: 2016-01-26T16:27:58.818+08:00

注意,在增减时间的时候,想象成自己在翻日历,所有的计算都将符合历法,由Joda-Time自动完成,不会出现非法的日期(比如:3月31日加一个月后,并不会出现4月31日)。

  • of比如dayOfYear、dayOfMonth等,获取一个时间的某个日期属性。
    在这里插入图片描述
       // 获取当前时间
       DateTime dateTime=  new DateTime(2024, 01, 18, 22, 30, 00);
	   
	   System.out.println(dateTime.monthOfYear().get()); // 1
	   // 也可以使用getXX直接获取对应的值
       System.out.println(dateTime.getDayOfMonth());  //18
       System.out.println(dateTime.getDayOfYear());   //18
       System.out.println(dateTime.getDayOfWeek());   //

  • 返回Property的方法:Property是DateTime中的属性,保存了一些有用的信息。Property对象中的一些方法在这里一并介绍。下面的例子展示了,我们可以通过不同Property中get开头的方法获取一些有用的信息:
DateTime now = new DateTime(); // 2016-02-26T16:51:28.749+08:00
now.monthOfYear().getAsText(); // February
now.monthOfYear().getAsText(Locale.KOREAN); // 2월
now.dayOfWeek().getAsShortText(); // Fri
now.dayOfWeek().getAsShortText(Locale.CHINESE); // 星期五

有时我们需要对一个DateTime的某些属性进行置0操作。比如,我想得到当天的0点时刻。那么就需要用到Property中round开头的方法(roundFloorCopy)。如下面的例子所示:

DateTime now = new DateTime(); // 2016-02-26T16:51:28.749+08:00
now.dayOfWeek().roundCeilingCopy(); // 2016-02-27T00:00:00.000+08:00
now.dayOfWeek().roundFloorCopy(); // 2016-02-26T00:00:00.000+08:00
now.minuteOfDay().roundFloorCopy(); // 2016-02-26T16:51:00.000+08:00
now.secondOfMinute().roundFloorCopy(); // 2016-02-26T16:51:28.000+08:00
  • 日期格式化:DateTimeFormatter和DateTime.parse()进行日期的格式化展示
        DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
        // 可以用于初始化一个对象
        DateTime dateTime2 = DateTime.parse("2023-11-12 23:32:32", dateTimeFormatter);

		// 也可以将已有的实例按照指定的日期格式输出
        DateTime dateTime3 = DateTime.now();
        System.out.println(dateTime3.toString(dateTimeFormatter));
  • 其它:还有许多其它方法(比如dateTime.year().isLeap()来判断是不是闰年)。它们的详细含义,请参照Java Doc,现查现用,用需求驱动学习。

日历系统和时区

Joda-Time默认使用的是ISO的日历系统,而ISO的日历系统是世界上公历的事实标准。然而,值得注意的是,ISO日历系统在表示1583年之前的历史时间是不精确的。
Joda-Time默认使用的是JDK的时区设置。如果需要的话,这个默认值是可以被覆盖的。
Joda-Time使用可插拔的机制来设计日历系统,而JDK则是使用子类的设计,比如GregorianCalendar。下面的代码,通过调用一个工厂方法获得Chronology的实现:

Chronology coptic = CopticChronology.getInstance();

时区是作为chronology的一部分来被实现的。下面的代码获得一个Joda-Time chronology在东京的时区:

DateTimeZone zone = DateTimeZone.forID("Asia/Tokyo");
Chronology gregorianJuian = GJChronology.getInstance(zone);

Interval和Period

Joda-Time为时间段的表示提供了支持。

  • Interval:它保存了一个开始时刻和一个结束时刻,因此能够表示一段时间,并进行这段时间的相应操作
  • Period:它保存了一段时间,比如:6个月,3天,7小时这样的概念。可以直接创建Period,或者从Interval对象构建。
  • Duration:它保存了一个精确的毫秒数。同样地,可以直接创建Duration,也可以从Interval对象构建。
    虽然,这三个类都用来表示时间段,但是在用途上来说还是有一些差别。请看下面的例子:
DateTime dt = new DateTime(2005, 3, 26, 12, 0, 0, 0);
DateTime plusPeriod = dt.plus(Period.days(1)); 
DateTime plusDuration = dt.plus(new Duration(24L*60L*60L*1000L));

因为当时那个地区执行夏令时的原因,在添加一个Period的时候会添加23个小时。而添加一个Duration,则会精确地添加24个小时,而不考虑历法。所以,Period和Duration的差别不但体现在精度上,也同样体现在语义上。因为,有时候按照有些地区的历法 1天 不等于 24小时。

附录

参考链接:
Joda-Time官网:https://www.joda.org/joda-time/

  • 28
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
package com.hexiang.utils; import java.text.SimpleDateFormat; import java.util.*; public class CalendarUtil { public static void main(String args[]) { System.out.println("First day of week is : " + new SimpleDateFormat("yyyy-MM-dd") .format(getFirstDateByWeek(new Date()))); System.out.println("Last day of week is : " + new SimpleDateFormat("yyyy-MM-dd") .format(getLastDateByWeek(new Date()))); System.out.println("First day of month is : " + new SimpleDateFormat("yyyy-MM-dd") .format(getFirstDateByMonth(new Date()))); System.out.println("Last day of month is : " + new SimpleDateFormat("yyyy-MM-dd") .format(getLastDateByMonth(new Date()))); } /** * 获得所在星期的第一天 */ public static Date getFirstDateByWeek(Date date) { Calendar now = Calendar.getInstance(); now.setTime(date); int today = now.get(Calendar.DAY_OF_WEEK); int first_day_of_week = now.get(Calendar.DATE) + 2 - today; // 星期一 now.set(Calendar.DATE, first_day_of_week); return now.getTime(); } /** * 获得所在星期的最后一天 */ public static Date getLastDateByWeek(Date date) { Calendar now = Calendar.getInstance(); now.setTime(date); int today = now.get(Calendar.DAY_OF_WEEK); int first_day_of_week = now.get(Calendar.DATE) + 2 - today; // 星期一 int last_day_of_week = first_day_of_week + 6; // 星期日 now.set(Calendar.DATE, last_day_of_week); return now.getTime(); } /** * 获得所在月份的最后一天 * @param 当前月份所在的时间 * @return 月份的最后一天 */ public static Date getLastDateByMonth(Date date) { Calendar now = Calendar.getInstance(); now.setTime(date); now.set(Calendar.MONTH, now.get(Calendar.MONTH) + 1); now.set(Calendar.DATE, 1); now.set(Calendar.DATE, now.get(Calendar.DATE) - 1); now.set(Calendar.HOUR, 11); now.set(Calendar.MINUTE, 59); now.set(Calendar.SECOND, 59); return now.getTime(); } /** * 获得所在月份的第一天 * @param 当前月份所在的时间 * @return 月份的第一天 */ public static Date getFirstDateByMonth(Date date) { Calendar now = Calendar.getInstance(); now.setTime(date); now.set(Calendar.DATE, 0); now.set(Calendar.HOUR, 12); now.set(Calendar.MINUTE, 0); now.set(Calendar.SECOND, 0); return now.getTime(); } }
Joda-Time 是一个 Java日期和时间处理类库,它提供了比 Java 标准库更加丰富的 API,使用起来更加方便。 以下是 Joda-Time 的使用方法: 1. 引入 Joda-Time 的依赖 在 Maven 项目中,可以在 `pom.xml` 文件中添加以下依赖: ```xml <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.10.13</version> </dependency> ``` 2. 创建日期和时间对象 Joda-Time 中的日期和时间对象分别为 `LocalDate` 和 `LocalTime`。可以使用静态方法 `now()` 来获取当前日期和时间: ```java LocalDate currentDate = LocalDate.now(); LocalTime currentTime = LocalTime.now(); ``` 也可以通过传入年月日、时分秒等参数来创建指定日期和时间对象: ```java LocalDate date = new LocalDate(2021, 9, 14); LocalTime time = new LocalTime(12, 30, 0); ``` 3. 日期和时间的加减 Joda-Time 中可以方便地对日期和时间进行加减操作。例如,可以通过 `plusDays()` 方法来增加指定的天数: ```java LocalDate newDate = currentDate.plusDays(7); ``` 也可以通过 `minusHours()` 方法来减少指定的小时数: ```java LocalTime newTime = currentTime.minusHours(3); ``` 4. 日期和时间的格式化 Joda-Time 中可以使用 `DateTimeFormatter` 类来进行日期和时间的格式化。例如,可以使用 `ISODateTimeFormat` 来格式化日期和时间: ```java DateTimeFormatter formatter = ISODateTimeFormat.dateTime(); String formattedDate = formatter.print(currentDate); String formattedTime = formatter.print(currentTime); ``` 也可以自定义日期和时间的格式化方式。例如,可以使用 `DateTimeFormat` 来指定格式化方式: ```java DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); String formattedDate = formatter.print(currentDate); String formattedTime = formatter.print(currentTime); ``` 以上就是 Joda-Time 的基本使用方法。需要注意的是,Joda-TimeJava 8 之后已经被废弃,建议使用 Java 8 中提供的 `java.time` 包来处理日期和时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员王小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值