07、日期类

  • 被称为“新纪元”的时间线原点
    • 被设置为穿过伦敦格林尼治皇家天文台的本初子午线所处时区的 1970 年 1 月 1 日的午夜

  • Java 标准库有两套处理日期时间的 API:
    • 一套定义在 java.util 这个包里。
      • 主要包括:Date、Calendar 和 TimeZone 这几个类;
    • 一套新的 API 是在 Java8 引入的,定义在 java.time 这个包里。
      • 主要包括:LocalDateTime、ZonedDateTime、ZoneId 等。

一、时间点 与 时间差


1、java.time.Instant 类(final 修饰)-- 时间点


  • 在 Java 中,Instant 表示时间线上的某个点某个时刻)。
    • 这与 UNIX/POSIX 时间中使用的惯例相同。
    • 从“新纪元”的时间线原点开始,时间按照每天 86400 秒向前或向回度量,精确到纳秒
  • Instant 的值往回可追溯 10 亿年(Instant.MIN)前
    • 对于表示宇宙年龄(大约 135 亿年)来说还差得远,但是对于所有实际应用来说,应该足够了
  • 最大的值 Instant.MAX 是公元 1000000000 年的 12 月 31 日

  • 特点:
    • 基于时间戳:存储从 1970-01-01T00:00:00Z(Unix 纪元)开始的 纳秒 偏移
    • 不可变且线程安全:所有修改操作返回新实例
    • 不包含时区:但可与其他时区类(如: ZonedDateTime)转换。
  • 调用静态方法 Instant.now() 会给出当前的时刻
public static void main(String[] args) {
    // 1. 创建实例
    Instant now = Instant.now();
        // 当前时间: 2025-05-21T03:28:37.983182Z
    System.out.println("当前时间: " + now);

    // 2. 解析字符串
    Instant parsed = Instant.parse("2025-05-31T08:00:00Z");
        // 解析时间: 2025-05-31T08:00:00Z
    System.out.println("解析时间: " + parsed);

    // 3. 时间运算
    Instant future = now.plus(Duration.ofHours(3));
        // 3小时后: 2025-05-21T06:29:17.864303Z
    System.out.println("3小时后: " + future);

    // 4. 转换到 ZonedDateTime
    ZonedDateTime tokyoTime = now.atZone(ZoneId.of("Asia/Shanghai"));
        // 北京时间: 2025-05-21T11:30:37.925976+08:00[Asia/Shanghai]
    System.out.println("北京时间: " + tokyoTime);

    // 5. 比较时间
    boolean isLater = future.isAfter(now);
        // future 是否在 now 之后? true
    System.out.println("future 是否在 now 之后? " + isLater);
}

2、java.time.Duration 类(final 修饰)-- 基于时间单位(秒、纳秒)的时间差


  • Duration 是两个时刻之间的时间量
    • 可以通过调用 toNanos、toMillis、getSeconds、toMinutes、toHours、toDays 来获得 Duration 按照传统单位度量的时间长度
  • 如果想要让计算精确到纳秒级。那么,就需要当心上溢问题
    • long 值可以存储大约 300 年时间对应的纳秒数
  • 如果需要的 Duration 短于这个时间300 年时间)。
    • 那么,可以直接将其转换为纳秒数
  • 如果需要的 Duration 长于这个时间300 年时间)。
    • 让 Duration 对象用一个 long 来存储秒数,用另外一个 int 来存储纳秒数
  • Duration 接口包含了大量用于执行算术运算方法

  • 特点:
    • 不可变且线程安全:所有操作返回新实例
    • 支持正负值:可以表示过去或未来的时间间隔
    • 精确到纳秒:但实际精度取决于输入数据
    • 与时间对象交互:支持对 InstantLocalDateTime 等时间类进行加减操作。
public static void main(String[] args) {
    // 1. 创建 Duration
    Duration duration = Duration.ofHours(1).plusMinutes(30).plusSeconds(15);
        // 自定义 Duration: PT1H30M15S
    System.out.println("自定义 Duration: " + duration);

    // 2. 计算两个时间的间隔
    Instant start = Instant.parse("2025-05-31T08:00:00Z");
    Instant end = Instant.parse("2025-05-31T09:30:15Z");
    Duration between = Duration.between(start, end);
        // 时间间隔: PT1H30M15S
    System.out.println("时间间隔: " + between);

    // 3. 转换为其他单位
        // 总秒数: 5415
    System.out.println("总秒数: " + between.toSeconds());
        // 总毫秒数: 5415000
    System.out.println("总毫秒数: " + between.toMillis());

    // 4. 时间运算
    Duration newDuration = between.plusMinutes(10).minusSeconds(5);
        // 调整后的 Duration: PT1H40M10S
    System.out.println("调整后的 Duration: " + newDuration);

    // 5. 应用到 LocalDateTime
    LocalDateTime ldt = LocalDateTime.of(2025, 5, 31, 8, 0);
    LocalDateTime updatedLdt = ldt.plus(newDuration);
        // 加 Duration 后的时间: 2025-05-31T09:40:10
    System.out.println("加 Duration 后的时间: " + updatedLdt);
}

二、本地日期


  • 在 Java 中有两种人类时间,本地日期(时间)时区时间

  • 本地日期(时间):包含当天的日期当天的时间,但是与时区信息 没有任何关联。
    • 1903 年 6 月 14 日就是一个本地日期的示例。
      + lambda 演算的发明者 Alonzo Church 在这一天诞生。
      • 因为,这个日期既没有当天的时间也没有时区信息。因此,它并不对应精确的时刻

  • 时区时间:1969 年 7 月 16 日 09:32:00 EDT(阿波罗 11 号发射的时刻)是一个时区日期/时间
    • 表示的是时间线上的一个精确的时刻

1、java.time.LocalDate 类(final 修饰)


  • LocalDate 带有年、月、日的日期。
    • 为了构建 LocalDate 对象,可以使用 nowof 静态方法

  • 特点:
    • 不可变且线程安全:所有修改操作返回新实例
    • 无时间和时区:只包含年、月、日
    • 格式化和解析:支持 ISO-8601 标准(如:yyyy-MM-dd)及其他自定义格式
    • 日期运算:支持加减天数、月数、年数,或调整到特定日期(如月末)。
public static void main(String[] args) {
    // 1. 创建实例
    LocalDate today = LocalDate.now();
        // 当前日期: 2025-05-21
    System.out.println("当前日期: " + today);

    // 2. 解析字符串
    LocalDate parsedDate = LocalDate.parse("2025-12-25");
        // 圣诞节: 2025-12-25
    System.out.println("圣诞节: " + parsedDate);

    // 3. 日期运算
    LocalDate nextWeek = today.plusWeeks(1);
        // 一周后: 2025-05-28
    System.out.println("一周后: " + nextWeek);

    // 4. 调整到当月最后一天
    LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
        // 本月最后一天: 2025-05-31
    System.out.println("本月最后一天: " + lastDay);

    // 5. 计算日期差
    long daysToChristmas = ChronoUnit.DAYS.between(today, parsedDate);
        // 距离圣诞节还有 218 天
    System.out.println("距离圣诞节还有 " + daysToChristmas + " 天");

    // 6. 格式化输出
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE");
    String formatted = parsedDate.format(formatter);
        // 格式化后的圣诞节: 2025年12月25日 星期四
    System.out.println("格式化后的圣诞节: " + formatted);
}

2、java.time.Period 类(final 修饰)-- 基于日期单位(年、月、日)的时间差


  • 对于 Instant 来说,两个 Instant 之间时长是 Duration。
  • 对于本地日期(LocalDate)来说,两个 LocalDate 之间时长是 Period。
    • 它表示的是流逝的年、月、日数量

  • 特点:
    • 不可变且线程安全:所有操作返回新实例。
    • 基于日历单位:以年、月、日为单位,而非精确的时间(如:秒、纳秒)。
    • 支持格式化和解析:遵循 ISO-8601 标准(如: P1Y2M3D)。
    • 与日期类交互:可对 LocalDate 进行加减操作
public static void main(String[] args) {
    // 1. 计算两个日期之间的时段
    LocalDate birthday = LocalDate.of(1990, 5, 20);
    LocalDate today = LocalDate.now();
    Period age = Period.between(birthday, today);
        // 年龄: 35岁 0个月 1天
    System.out.println("年龄: " + age.getYears() + "岁 " + age.getMonths() + "个月 " + age.getDays() + "天");

    // 2. 创建并操作时段
        // 1年6个月10天
    Period projectPeriod = Period.of(1, 6, 10);
        // 增加 2 个月
    Period extendedPeriod = projectPeriod.plusMonths(2);
        // 延长后的项目周期: P1Y8M10D
    System.out.println("延长后的项目周期: " + extendedPeriod);

    // 3. 应用时段到日期
    LocalDate projectStart = LocalDate.of(2024, 1, 1);
    LocalDate projectEnd = projectStart.plus(extendedPeriod);
        // 项目结束日期: 2025-09-11
    System.out.println("项目结束日期: " + projectEnd);

    // 4. 解析字符串
    Period parsed = Period.parse("P3Y4M");
        // 解析结果: P3Y4M
    System.out.println("解析结果: " + parsed);

    // 5. 精度问题
    LocalDate localDate = LocalDate.of(2024, 01, 31);
        // 2024-02-29  (闰年)
    System.out.println(localDate.plusMonths(1));
        // 2024-01-31
    System.out.println(localDate);
    localDate = LocalDate.of(2025, 01, 31);
        // 2025-02-28  (平年)
    System.out.println(localDate.plusMonths(1));
        // 2025-01-31
    System.out.println(localDate);

    // 6.获取某天是星期几
        // 星期五
    System.out.println(localDate.getDayOfWeek().getValue());
}

三、日期调整器(java.time.temporal.TemporalAdjusters)

  • TemporalAdjusters 类提供了大量用于常见调整的静态方法
    • 用于对日期进行复杂的调整操作
      • 如:找到当月的最后一天下一个周一每个月的第一个星期二等)。
public static void main(String[] args) {
    LocalDate date = LocalDate.of(2025, 5, 15);

    // 基础调整示例
        // 当月第一天: 2025-05-01
    System.out.println("当月第一天: " + date.with(TemporalAdjusters.firstDayOfMonth()));
        // 下一个周一: 2025-05-19
    System.out.println("下一个周一: " + date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)));

    // 复杂调整示例
    LocalDate thirdTuesday = date.with(
            TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.TUESDAY)
    );
        // 当月第三个星期二: 2025-05-20
    System.out.println("当月第三个星期二: " + thirdTuesday);

    // 自定义调整器示例(跳过周末)
    TemporalAdjuster addBusinessDays = temporal -> {
        LocalDate current = LocalDate.from(temporal);
        int daysToAdd = 10;
        while (daysToAdd > 0) {
            current = current.plusDays(1);
            if (current.getDayOfWeek() != DayOfWeek.SATURDAY && current.getDayOfWeek() != DayOfWeek.SUNDAY) {
                daysToAdd--;
            }
        }
        return temporal.with(current);
    };
    LocalDate result = date.with(addBusinessDays);
    // 加 10 个工作日: 2025-05-29 (假设无节假日)
    System.out.println("加 10 个工作日: " + result);
}

四、本地时间(java.time.LocalTime 类)


  • LocalTime 表示当日时刻(如:15:30:00)。可以用 now 或 of 方法创建其实例:
    • plus 和 minus 操作是按照一天 24 小时循环操作的。
  • 常量时间
    • LocalTime.MIN(00:00)
    • LocalTime.MAX(23:59:59.999999999)
    • LocalTime.NOON(12:00)。

  • 特性:
    • 不可变且线程安全:所有修改操作返回新实例
    • 无日期和时区:仅包含 时、分、秒、纳秒
    • 格式化和解析:支持 ISO-8601 格式(如:HH:mm:ss.SSS)及自定义格式
    • 时间运算:支持加减 小时、分钟、秒、纳秒,或调整特定时间
public static void main(String[] args) {
    // 1. 创建实例
    LocalTime now = LocalTime.now();
        // 当前时间: 14:22:08.070697
    System.out.println("当前时间: " + now);
    // 指定时、分、秒、纳秒创建
    LocalTime time1 = LocalTime.of(14, 30);
        // 14:30
    System.out.println(time1);
    LocalTime time2 = LocalTime.of(14, 30, 45);
        // 14:30:45
    System.out.println(time2);
    LocalTime time3 = LocalTime.of(14, 30, 45, 123_000_000);
        // 14:30:45.123
    System.out.println(time3);

    int hour = now.getHour();          // 小时(0-23)
    int minute = now.getMinute();      // 分钟(0-59)
    int second = now.getSecond();      // 秒(0-59)
    int nano = now.getNano();          // 纳秒(0-999,999,999)


    // 2. 解析字符串
    LocalTime parsed = LocalTime.parse("09:15:30");
        // 解析时间: 09:15:30
    System.out.println("解析时间: " + parsed);
        // 解析字符串(默认 ISO 格式)
    LocalTime parsedTime = LocalTime.parse("14:30:45.123");
        // 解析时间: 14:30:45.123
    System.out.println("解析时间: " + parsedTime);

    // 3. 时间运算
        // 添加 2 小时,减 30 分钟,加 0.5 秒。
    LocalTime meetingTime = parsed.plusHours(2).minusMinutes(15).plusNanos(500_000_000);
        // 会议时间: 会议时间: 11:00:30.500
    System.out.println("会议时间: " + meetingTime);

    // 使用 ChronoUnit 指定单位
        // 加1小时
    LocalTime nextHour = now.plus(1, ChronoUnit.HOURS);

    // 调整到特定时间(如整点)
    LocalTime adjusted = now.withHour(15).withMinute(0).withSecond(0);
        // 15:00:00.516488
    System.out.println(adjusted);

    // 4. 比较时间
    LocalTime deadline = LocalTime.of(17, 0);
        // 是否超过截止时间? false
    System.out.println("是否超过截止时间? " + now.isAfter(deadline));

    // 5. 格式化输出
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
    String formatted = meetingTime.format(formatter);
        // 格式化会议时间: 11:00 上午
    System.out.println("格式化会议时间: " + formatted);

    // 自定义格式化为字符串
    formatter = DateTimeFormatter.ofPattern("HH时mm分ss秒");
    formatted = now.format(formatter);
    // 14时32分35秒
    System.out.println(formatted);
}

五、本地日期时间(java.time.LocalDateTime 类)


  • 表示 不含时区日期时间(如 “2025-05-31T14:30:45.123”),精确到纳秒
    • 它结合了 LocalDate 和 LocalTime。
    • 适用于需要同时处理日期时间的场景(如:日志记录、计划任务等)。
  • 特点:
    • 不可变且线程安全:所有修改操作返回新实例
    • 无时区信息:仅包含日期时间,依赖系统默认时区显式指定日期时间字段
    • 精确到纳秒:支持高精度时间操作。
    • 格式化和解析:兼容 ISO 格式(如:yyyy-MM-ddTHH:mm:ss.SSS)及自定义格式
      • yyyy是年份,MM是月份,dd是日期,EEEE是星期全称。
      • HH是 24 小时制小时,hh是 12 小时制小时,mm是分钟,ss是秒,SSS 是毫秒。
    • 与日期/时间类互操作:可与 LocalDateLocalTimeZonedDateTime无缝转换
public static void main(String[] args) {
    // 1. 创建实例
    LocalDateTime now = LocalDateTime.now();
        // 当前时间: 2025-05-21T15:17:09.366350
    System.out.println("当前时间: " + now);
    now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    // 2025-05-21T15:17:09.368194
    System.out.println(now);

    LocalDateTime dateTime = LocalDateTime.of(2025, Month.OCTOBER, 12, 15, 30, 45);
        // 2025-10-12T15:30:45
    System.out.println(dateTime);

    // 2. 解析字符串
    LocalDateTime parsed = LocalDateTime.parse("2025-12-25T08:30");
        // 解析时间: 2025-12-25T08:30
    System.out.println("解析时间: " + parsed);

    // 3. 时间运算
    LocalDateTime nextWeek = now.plusWeeks(1);
        // 一周后: 2025-05-28T15:13:42.289273
    System.out.println("一周后: " + nextWeek);

    // 4. 调整到当月最后一天的午夜
    LocalDateTime lastDay = now.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MIDNIGHT);
        // 当月最后一天的午夜: 2025-05-31T00:00
    System.out.println("当月最后一天的午夜: " + lastDay);

    // 5. 格式化输出
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
    String formatted = parsed.format(formatter);
        // 格式化结果: 2025年12月25日 08:30
    System.out.println("格式化结果: " + formatted);
}

六、时区时间(java.time.ZonedDateTime


  • 互联网编码分配管理机构 (Internet Assigned Numbers Authority,IANA)保存着一个数据库。
    • 里面存储着世界上所有已知的时区(www.iana.org/time-zones)。
      • 它每年会更新数次,其中许多更新是为了处理夏令时变更规则
    • Java 使用了 IANA 数据库
  • 每个时区都有一个ID(如:America/New_York、Europe/Berlin、Asia/Shanghai)。
    • 要想找出所有可用的时区,可以调用 Zoneld.getAvailableZonelds
  • 给定一个时区 ID,静态方法 Zoneld.of(id) 可以产生一个 Zoneld 对象
ZoneId.of("Asia/Shanghai")

  • 用于表示 不含时区完整日期时间(如:2024-05-31T15:30:45+08:00[Asia/Shanghai])。
    • 它结合了 LocalDateTime 和 ZoneId,能够精确处理跨时区日期时间操作。
    • 适用于需要明确时区信息 的场景(如:国际会议、航班时刻表等)。
  • 特点:
    • 不可变且线程安全:所有操作返回新实例
    • 包含时区和偏移量:基于 ZoneIdZoneOffset,支持夏令时自动调整
    • 精确到纳秒:支持高精度时间操作。
    • 格式化和解析
      • 兼容 ISO 标准格式(如:yyyy-MM-ddTHH:mm:ss.SSSXXX[VV])及自定义格式
    • 与 UTC 转换:可转换为 Instant(时间戳),便于跨系统交互
public static void main(String[] args) {
    // 1. 创建实例
    ZonedDateTime nowShanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        // 上海当前时间: 2025-05-21T15:31:56.229683+08:00[Asia/Shanghai]
    System.out.println("上海当前时间: " + nowShanghai);

    // 2. 转换时区
    ZonedDateTime nowNewYork = nowShanghai.withZoneSameInstant(ZoneId.of("America/New_York"));
        // 同一时刻的纽约时间: 2025-05-21T03:31:56.229683-04:00[America/New_York]
    System.out.println("同一时刻的纽约时间: " + nowNewYork);

    // 3. 时间运算:加 2 天并调整到下一个周一
    ZonedDateTime adjusted = nowShanghai.plusDays(2).with(TemporalAdjusters.next(DayOfWeek.MONDAY));
        // 调整后的时间: 2025-05-26T15:31:56.229683+08:00[Asia/Shanghai]
    System.out.println("调整后的时间: " + adjusted);

    // 4. 格式化与解析
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z");
    String formatted = adjusted.format(formatter);
        // 格式化结果: 2025-05-26 15:31 CST
    System.out.println("格式化结果: " + formatted);

    ZonedDateTime parsed = ZonedDateTime.parse("2025-06-03 10:30 CST",
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"));
    // 解析后的时间: 2025-06-03T10:30-05:00[America/Chicago]
    System.out.println("解析后的时间: " + parsed);
}

七、java.util.Date


  • Java 早期版本中用于表示日期和时间的类。
    • 存储自 1970 年 1 月 1 日 00:00:00 GMT(纪元(epoch)时间) 以来的毫秒数
public Date() {
    this(System.currentTimeMillis());
}
  • 特点:
    • 时间精度:毫秒级精度。
    • 时区问题Date 内部不存储时区信息,但 toString() 使用 JVM 默认时区格式化。
    • 可变性:对象创建后可通过 setTime() 修改时间值可能导致 线程安全问题
      • 多线程环境下需同步改用 不可变类(如:java.time.Instant)。
    • 过时方法:如 getYear()setMonth() 已废弃,建议避免使用。
public class DateExample {
    public static void main(String[] args) throws InterruptedException {
        // 1、创建 Date 对象
            // 当前时间
        Date now = new Date();
            // 当前时间: Wed May 21 17:18:59 CST 2025
        System.out.println("当前时间: " + now);

        // 通过毫秒数创建(示例:2023-10-05 00:00:00)
            // 纪元毫秒数
        long epochMillis = 1696435200000L;
        Date specificDate = new Date(System.currentTimeMillis());
            // 指定时间: Wed May 21 17:19:00 CST 2025
        System.out.println("指定时间: " + specificDate);

            // 错误用法(已废弃)
            // 年份参数为 2025-1900=123,月份从 0 开始(9 表示十月)
        Date date = new Date(125, 9, 5);
            // Sun Oct 05 00:00:00 CST 2025
        System.out.println(date);

        // 2、比较日期
        Date date1 = new Date();
            // 当前时间 +1 秒
        Date date2 = new Date(date1.getTime() + 1000);
            // date1 是否在 date2 之前: true
        System.out.println("date1 是否在 date2 之前: " + date1.before(date2));
            // date1 和 date2 的比较结果: -1
        System.out.println("date1 和 date2 的比较结果: " + date1.compareTo(date2));

        // 3、计算时间差
        long start = System.currentTimeMillis();
            // 模拟耗时操作
        Thread.sleep(2000);
        long end = System.currentTimeMillis();

        Date startDate = new Date(start);
        Date endDate = new Date(end);
        long duration = endDate.getTime() - startDate.getTime();
            // 2000
        System.out.println("耗时(毫秒): " + duration);
    }
}

八、java.util.Calendar


  • Java 中用于处理日期时间抽象类,提供对日期字段(年、月、日、小时等)的灵活操作。
    • Java 8 后推荐使用 java.time API(如:LocalDateTime)。

  • CalendarDate比:
    • 多了一个可以做简单的日期时间运算的功能。
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

public class CalendarExample {
    public static void main(String[] args) {
        // 1. 创建实例并设置日期
        Calendar calendar = Calendar.getInstance();
        calendar.set(2025, Calendar.OCTOBER, 5, 15, 30, 0);

            // 指定时区(如 "America/New_York")
        Calendar calendarNY = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));

        // 2. 获取字段值
            // 年: 2025
        System.out.println("年: " + calendar.get(Calendar.YEAR));
            // 月: 10 (需 +1)
        System.out.println("月: " + (calendar.get(Calendar.MONTH) + 1));

        // 3. 增加 10 天
        calendar.add(Calendar.DAY_OF_MONTH, 10);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            // 加 10 天后: 2025-10-15
        System.out.println("加 10 天后: " + sdf.format(calendar.getTime()));

        // 4. 比较日期
        Calendar other = Calendar.getInstance();
        other.set(2025, Calendar.DECEMBER, 31);
        boolean isBefore = calendar.before(other);
            // 是否在 2025-12-31 之前? true
        System.out.println("是否在 2025-12-31 之前? " + isBefore);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值