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

Java 8 新特性:Date-Time API

csdh11 2025-03-12 13:40 2 浏览

Java 8 引入的 Date-Time API (java.time 包) 为日期和时间处理带来了显著的改进,解决了 java.util.Date 类的许多痛点:


  1. 非线程安全
  2. 时区处理麻烦
  3. 格式化和时间计算繁琐
  4. 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。


本文将详细讲解 Java 8 新的 Date-Time API,并通过源码和示例代码对比 Java 8 之前的实现方式,深入剖析其设计的用意和目的。


java.time 主要类


java.util.Date 既包含日期又包含时间,而 java.time 将它们进行了分离:


  • LocalDateTime:日期和时间,格式为 yyyy-MM-ddTHH:mm:ss.SSS
  • LocalDate:仅日期,格式为 yyyy-MM-dd
  • LocalTime:仅时间,格式为 HH:mm:ss


格式化


Java 8 之前:


java


public void oldFormat(){
    Date now = new Date();
    //格式化 yyyy-MM-dd
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    String date  = sdf.format(now);
    System.out.println(String.format("date format : %s", date));

    //格式化 HH:mm:ss
    SimpleDateFormat sdft = new SimpleDateFormat("HH:mm:ss");
    String time = sdft.format(now);
    System.out.println(String.format("time format : %s", time));

    //格式化 yyyy-MM-dd HH:mm:ss
    SimpleDateFormat sdfdt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String datetime = sdfdt.format(now);
    System.out.println(String.format("dateTime format : %s", datetime));
}



Java 8 之后:


java


public void newFormat(){
    //格式化 yyyy-MM-dd
    LocalDate date = LocalDate.now();
    System.out.println(String.format("date format : %s", date));

    //格式化 HH:mm:ss
    LocalTime time = LocalTime.now().withNano(0);
    System.out.println(String.format("time format : %s", time));

    //格式化 yyyy-MM-dd HH:mm:ss
    LocalDateTime dateTime = LocalDateTime.now();
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String dateTimeStr = dateTime.format(dateTimeFormatter);
    System.out.println(String.format("dateTime format : %s", dateTimeStr));
}



字符串转日期格式


Java 8 之前:


java


//已弃用
Date date = new Date("2021-01-26");
//替换为
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = sdf.parse("2021-01-26");



Java 8 之后:


java


LocalDate date = LocalDate.of(2021, 1, 26);
LocalDate.parse("2021-01-26");

LocalDateTime dateTime = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
LocalDateTime.parse("2021-01-26T12:12:22");

LocalTime time = LocalTime.of(12, 12, 22);
LocalTime.parse("12:12:22");



Java 8 之前 的转换需要借助 SimpleDateFormat 类,而 Java 8 之后 只需要使用 LocalDate、LocalTime、LocalDateTime 的 of 或 parse 方法。


日期计算


以下以计算 一周后的日期 为例,其他单位(年、月、日、时等)类似。这些单位都在
java.time.temporal.ChronoUnit 枚举中定义。


Java 8 之前:


java


public void afterDay(){
    //一周后的日期
    SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
    Calendar ca = Calendar.getInstance();
    ca.add(Calendar.DATE, 7);
    Date d = ca.getTime();
    String after = formatDate.format(d);
    System.out.println("一周后日期:" + after);

    //算两个日期间隔多少天,计算间隔多少年,多少月方法类似
    String dates1 = "2021-12-23";
    String dates2 = "2021-02-26";
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    Date date1 = format.parse(dates1);
    Date date2 = format.parse(dates2);
    int day = (int) ((date1.getTime() - date2.getTime()) / (1000 * 3600 * 24));
    System.out.println(dates1 + "和" + dates2 + "相差" + day + "天");
}



Java 8 之后:


java


public void pushWeek(){
    //一周后的日期
    LocalDate localDate = LocalDate.now();
    //方法1
    LocalDate after = localDate.plus(1, ChronoUnit.WEEKS);
    //方法2
    LocalDate after2 = localDate.plusWeeks(1);
    System.out.println("一周后日期:" + after);

    //算两个日期间隔多少天,计算间隔多少年,多少月
    LocalDate date1 = LocalDate.parse("2021-02-26");
    LocalDate date2 = LocalDate.parse("2021-12-23");
    Period period = Period.between(date1, date2);
    System.out.println("date1 到 date2 相隔:" + period.getYears() + "年" + period.getMonths() + "月" + period.getDays() + "天");

    //获取总天数
    long day = date2.toEpochDay() - date1.toEpochDay();
    System.out.println(date1 + "和" + date2 + "相差" + day + "天");
}



获取指定日期


获取特定一个日期,如本月最后一天或第一天。


Java 8 之前:


java


public void getDay() {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    //获取当前月第一天:
    Calendar c = Calendar.getInstance();
    c.set(Calendar.DAY_OF_MONTH, 1);
    String first = format.format(c.getTime());
    System.out.println("first day:" + first);

    //获取当前月最后一天
    Calendar ca = Calendar.getInstance();
    ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
    String last = format.format(ca.getTime());
    System.out.println("last day:" + last);

    //当年最后一天
    Calendar currCal = Calendar.getInstance();
    Calendar calendar = Calendar.getInstance();
    calendar.clear();
    calendar.set(Calendar.YEAR, currCal.get(Calendar.YEAR));
    calendar.roll(Calendar.DAY_OF_YEAR, -1);
    Date time = calendar.getTime();
    System.out.println("last day:" + format.format(time));
}



Java 8 之后:


java


public void getDayNew() {
    LocalDate today = LocalDate.now();
    //获取当前月第一天:
    LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
    //获取当前月最后一天:
    LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
    //获取下一天:
    LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
    //获取当年最后一天:
    LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
    //2021年最后一个周日
    LocalDate lastSundayOf2021 = LocalDate.parse("2021-12-31").with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
}




java.time.temporal.TemporalAdjusters 提供了很多便捷的方法来获取特定日期。


JDBC 和 Java 8


现在 JDBC 时间类型和 Java 8 时间类型的对应关系是:


  1. Date ---> LocalDate
  2. Time ---> LocalTime
  3. Timestamp ---> LocalDateTime


而之前统统对应 Date 类型。


时区


java.util.Date 对象实际上存储的是 1970 年 1 月 1 日 0 点(GMT)至 Date 对象所表示时刻所经过的毫秒数。因此,它记录的毫秒数与时区无关,但在使用上需要转换成当地时间,这就涉及到了时间的国际化。java.util.Date 本身并不支持国际化,需要借助 TimeZone。


java


//北京时间
Date date = new Date();
SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//北京时区
bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
System.out.println("毫秒数:" + date.getTime() + ", 北京时间:" + bjSdf.format(date));

//东京时区
SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));
System.out.println("毫秒数:" + date.getTime() + ", 东京时间:" + tokyoSdf.format(date));

//直接打印会自动转成当前时区时间
System.out.println(date);



在新特性中引入了 java.time.ZonedDateTime 来表示带时区的时间。它可以看成是 LocalDateTime + ZoneId。


java


//当前时区时间
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("当前时区时间: " + zonedDateTime);

//东京时间
ZoneId zoneId = ZoneId.of(ZoneId.SHORT_IDS.get("JST"));
ZonedDateTime tokyoTime = zonedDateTime.withZoneSameInstant(zoneId);
System.out.println("东京时间: " + tokyoTime);

// ZonedDateTime 转 LocalDateTime
LocalDateTime localDateTime = tokyoTime.toLocalDateTime();
System.out.println("东京时间转当地时间: " + localDateTime);

//LocalDateTime 转 ZonedDateTime
ZonedDateTime localZoned = localDateTime.atZone(ZoneId.systemDefault());
System.out.println("本地时区时间: " + localZoned);



java.time 包与 Joda-Time 的对比


Java 8 引入的 java.time 包是对日期和时间处理的一次重大改进,它借鉴了许多 Joda-Time 的设计理念,并在此基础上进行了优化。那么,java.time 包与 Joda-Time 相比有哪些优势和劣势呢?


1. 设计理念和 API 风格


Joda-Time


  • 设计理念:Joda-Time 旨在提供一个更直观和更强大的日期时间处理库,弥补 java.util.Date 和 java.util.Calendar 的不足。
  • API 风格:Joda-Time 的 API 风格非常直观,类名和方法名都非常清晰易懂。例如,DateTime 类表示日期和时间,LocalDate 类表示仅日期。


Java 8 java.time


  • 设计理念:java.time 包是基于 JSR 310 的规范实现,吸收了 Joda-Time 的优点,并在此基础上进行了优化和改进。
  • API 风格:java.time 包的 API 风格与 Joda-Time 类似,但在命名和结构上更加规范。例如,LocalDateTime 表示日期和时间,LocalDate 表示仅日期,LocalTime 表示仅时间。


2. 集成和兼容性


Joda-Time


  • 集成:Joda-Time 是一个独立的第三方库,需要手动添加依赖。
  • 兼容性:Joda-Time 与 Java SE 8 之前的版本兼容,适用于所有版本的 Java。


Java 8 java.time


  • 集成:java.time 包是 Java SE 8 的一部分,无需额外添加依赖。
  • 兼容性:java.time 包仅适用于 Java SE 8 及以上版本,对于 Java 8 之前的版本,需要额外的兼容性库(如 ThreeTen-Backport)。


3. 性能和线程安全


Joda-Time


  • 性能:Joda-Time 的性能相对较好,但在某些操作上可能不如 java.time 包高效。
  • 线程安全:Joda-Time 的大多数类是不可变的,因此是线程安全的。


Java 8 java.time


  • 性能:java.time 包在设计时考虑了性能优化,尤其是在日期计算和格式化等高频操作上性能更优。
  • 线程安全:java.time 包中的所有类都是不可变的,因此也是线程安全的。


4. 功能和扩展性


Joda-Time


  • 功能:Joda-Time 提供了丰富的功能,支持各种日期时间操作、格式化、解析、时区转换等。
  • 扩展性:Joda-Time 提供了一些扩展功能,例如自定义时间单位和字段。


Java 8 java.time


  • 功能:java.time 包提供了与 Joda-Time 类似的功能,并在某些方面进行了增强。例如,提供了更丰富的日期调整器(TemporalAdjuster)和更强大的时间量(Duration 和 Period)。
  • 扩展性:java.time 包也提供了良好的扩展性,允许用户自定义时间单位和字段。


示例对比


Joda-Time 示例


java


import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class JodaTimeExample {
    public static void main(String[] args) {
        // 当前时间
        DateTime now = DateTime.now();
        System.out.println("当前时间: " + now);

        // 格式化
        DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDate = now.toString(formatter);
        System.out.println("格式化后的时间: " + formattedDate);

        // 解析
        DateTime parsedDate = DateTime.parse("2021-01-26 12:12:22", formatter);
        System.out.println("解析后的时间: " + parsedDate);

        // 日期计算
        DateTime oneWeekLater = now.plusWeeks(1);
        System.out.println("一周后的时间: " + oneWeekLater);
    }
}



Java 8 java.time 示例


java


import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class JavaTimeExample {
    public static void main(String[] args) {
        // 当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println("当前时间: " + now);

        // 格式化
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDate = now.format(formatter);
        System.out.println("格式化后的时间: " + formattedDate);

        // 解析
        LocalDateTime parsedDate = LocalDateTime.parse("2021-01-26 12:12:22", formatter);
        System.out.println("解析后的时间: " + parsedDate);

        // 日期计算
        LocalDateTime oneWeekLater = now.plusWeeks(1);
        System.out.println("一周后的时间: " + oneWeekLater);
    }
}



总结


优势


  1. 集成:java.time 包是 Java 8 的一部分,无需额外依赖,集成更方便。
  2. 性能:java.time 包在性能上进行了优化,尤其是在高频日期时间操作上。
  3. 线程安全:java.time 包中的所有类都是不可变的,因此线程安全。
  4. 标准化:java.time 包是基于 JSR 310 的规范实现,更加标准化。


劣势


  1. 兼容性:java.time 包仅适用于 Java SE 8 及以上版本,对于 Java 8 之前的版本需要额外的兼容性库。
  2. 学习成本:对于熟悉 Joda-Time 的开发者来说,迁移到 java.time 包可能需要一定的学习成本。


使用 java.time 包中的类处理时间间隔和持续时间


在 Java 8 的 java.time 包中,处理时间间隔和持续时间有两个主要的类:Duration 和 Period。Duration 用于表示基于时间的间隔(以秒和纳秒为单位),而 Period 用于表示基于日期的间隔(以年、月、日为单位)。


1. Duration 类


Duration 类用于表示两个时间点之间的时间间隔,精确到秒和纳秒。它常用于计算精确的时间差,如秒、分钟、小时等。


示例代码


import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class DurationExample {
    public static void main(String[] args) {
        // 当前时间
        LocalDateTime startTime = LocalDateTime.now();
        System.out.println("开始时间: " + startTime);

        // 模拟一个耗时操作
        try {
            Thread.sleep(3000); // 休眠3秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 结束时间
        LocalDateTime endTime = LocalDateTime.now();
        System.out.println("结束时间: " + endTime);

        // 计算时间间隔
        Duration duration = Duration.between(startTime, endTime);
        System.out.println("时间间隔: " + duration.getSeconds() + " 秒 " + duration.getNano() + " 纳秒");

        // 其他常用方法
        System.out.println("时间间隔(分钟): " + duration.toMinutes());
        System.out.println("时间间隔(毫秒): " + duration.toMillis());

        // 使用ChronoUnit计算时间间隔
        long secondsBetween = ChronoUnit.SECONDS.between(startTime, endTime);
        System.out.println("时间间隔(ChronoUnit): " + secondsBetween + " 秒");
    }
}



2. Period 类


Period 类用于表示两个日期之间的时间间隔,精确到年、月、日。它常用于计算日期间的差异,如天数、月数、年数等。


示例代码


java


import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class PeriodExample {
    public static void main(String[] args) {
        // 当前日期
        LocalDate startDate = LocalDate.now();
        System.out.println("开始日期: " + startDate);

        // 目标日期
        LocalDate endDate = startDate.plusYears(1).plusMonths(2).plusDays(3);
        System.out.println("结束日期: " + endDate);

        // 计算日期间隔
        Period period = Period.between(startDate, endDate);
        System.out.println("日期间隔: " + period.getYears() + " 年 " + period.getMonths() + " 月 " + period.getDays() + " 天");

        // 其他常用方法
        System.out.println("总月数: " + period.toTotalMonths());

        // 使用ChronoUnit计算日期间隔
        long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
        System.out.println("日期间隔(ChronoUnit): " + daysBetween + " 天");
    }
}



选择 Duration 和 Period 的适用场景


选择 Duration 的场景


  • 需要精确到秒和纳秒的时间计算。
  • 计时操作,如测量代码执行时间、任务的持续时间等。
  • 时区无关的时间计算,如两个时间点之间的间隔。


选择 Period 的场景


  • 需要基于年、月、日的日期计算。
  • 计算两个日期之间的年、月、日差异。
  • 日历相关操作,如计算某个日期之后的某年某月某日。


比较两个日期之间的差异


示例代码


java


import java.time.LocalDate;
import java.time.Period;

public class PeriodDifferenceExample {
    public static void main(String[] args) {
        // 两个日期
        LocalDate startDate = LocalDate.of(2020, 1, 1);
        LocalDate endDate = LocalDate.of(2021, 6, 15);

        // 计算日期间隔
        Period period = Period.between(startDate, endDate);
        System.out.println("日期间隔: " + period.getYears() + " 年 " + period.getMonths() + " 月 " + period.getDays() + " 天");
    }
}



使用 Duration 进行时区无关的时间计算


示例代码


java


import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.ZoneId;

public class ZonedDateTimeExample {
    public static void main(String[] args) {
        // 纽约时间
        ZonedDateTime startDateTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println("纽约开始时间: " + startDateTime);

        // 东京时间
        ZonedDateTime endDateTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
        System.out.println("东京结束时间: " + endDateTime);

        // 计算时间间隔
        Duration duration = Duration.between(startDateTime, endDateTime);
        System.out.println("时间间隔: " + duration.toHours() + " 小时");
    }
}



使用 ChronoUnit 枚举


ChronoUnit 枚举提供了一系列用于时间单位的常量,支持基于时间和日期的计算。


示例代码


java


import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class ChronoUnitExample {
    public static void main(String[] args) {
        // 当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println("当前时间: " + now);

        // 计算一周后的时间
        LocalDateTime oneWeekLater = now.plus(1, ChronoUnit.WEEKS);
        System.out.println("一周后的时间: " + oneWeekLater);

        // 计算一年前的日期
        LocalDate oneYearAgo = LocalDate.now().minus(1, ChronoUnit.YEARS);
        System.out.println("一年前的日期: " + oneYearAgo);

        // 计算两个时间点之间的小时数
        long hoursBetween = ChronoUnit.HOURS.between(now, oneWeekLater);
        System.out.println("时间间隔(小时): " + hoursBetween + " 小时");
    }
}



总结


使用 Java 8 java.time 包中的 Duration 和 Period 类可以方便地处理时间间隔和持续时间。Duration 用于表示基于时间的间隔,如秒、分钟、小时等;而 Period 用于表示基于日期的间隔,如年、月、日等。此外,ChronoUnit 枚举提供了一系列用于时间单位的常量,支持基于时间和日期的计算。

相关推荐

最具标志性的电影服饰

在1933年的电影《晚宴》中,一袭白色斜裁缎质礼服和一头染成银灰色的卷发将珍·哈露变成了片中孤独骄傲的凯蒂·帕卡德(KittyPackard)。这条白色礼服风靡一时,与它类似的款式统称为”珍·哈露式...

呼吸系统及其他相关疾病英文词汇

allergic/alls:rd3ik/a.过敏的同根alergyn.辻敏症allergenn.变应原(引起过敏反应的物质)...

itch 一周游戏汇:8月19日-8月25日(上)

...

python实现微信群消息自动转发简明教程

基本思路是,用Python模拟微信网页版登陆,接收到群里消息后,对文本、图片、分享等各类消息类型分别处理,并转发至其他群。主要源头是使用itchat,itchat是一个开源的微信个人号接口,pyt...

python实践——如何批量向好友发送消息,亲测有效!

今天为大家介绍如何像指定好友或微信群批量,一共两个Python模块,第一个是itchat,...

用python分析你的朋友圈,很好玩~

设计喵的内心OS:我一脸懵逼点进来,一脸懵逼走出去,你说了什么?事情是这么来的,我看到有朋友在做朋友圈的电子书,也想自己研究一下,发现原来有个itchart这个微信第三方API可以读取微信数据,抱着好...

探索Python技术在电脑端自动化中:从群发消息到智能朋友圈秒赞

一、技术革新与基础库的安装首先,确保Python环境已如诗如画般配置完毕,随后安装那些将赋予我们魔力的第三方库:...

使用Python实现微信电脑端自动化:群发消息、朋友圈点赞与秒赞

一、技术准备与基础库安装首先,确保Python环境已正确配置,随后安装必要的第三方库:...

Python 查看微信撤回的消息(完整代码)

看到了一个基于python的微信开源库:itchat,玩了一天,做了一个程序,把私聊撤回的信息可以收集起来并发送到个人微信的文件传输助手,包括:...

用 Python 玩转微信就是这么简单

前言wxpy在itchat的基础上,通过大量接口优化提升了模块的易用性,并进行丰富的功能扩展。用来干啥一些常见的场景控制路由器、智能家居等具有开放接口的玩意儿...

itch 一周游戏汇:2月17日-2月23日(下)

...

Python开源项目合集(第三方平台)

wechat-python-sdk-wechat-python-sdk微信公众平台Python开发包http://wechat-python-sdk.readthedocs.org/,非官方...

78行Python代码帮你复现微信撤回消息!

来源:悟空智能科技本文约700字,建议阅读5分钟。本文基于python的微信开源库itchat,教你如何收集私聊撤回的信息。...

女朋友老喜欢撤回消息?看我如何利用Python识破她的心理

一、pipInstallitchat既然都用python解决问题了,第一步当然是明确用什么库啦,直接执行pipinstallitchat:...

打游戏老是被女朋友骚扰怎么办?教你用Python智能回复消息

Python中itchat模块对于操作微信来说是个福音,今天,我们就用Python实现微信自动回复,同时调用图灵机器人,实现智能回复。具体代码如下:#导入...