Java 日期和时间

⚡ 智能摘要

Java 日期和时间处理结合了传统的 java.util.Date 和 SimpleDateFormat 类以及在 中引入的现代 java.time API。 Java 8,为开发人员提供不可变、线程安全的类型,以实现精确的日历工作。

  • 🕒 旧日期: java.util.Date 类以毫秒为单位存储一个时间点,并通过 toString 方法打印一个默认字符串。
  • 🔤 图案字母: SimpleDateFormat 使用 yyyy、MM、dd、HH、mm、ss 和 zzz 等字母来定义自定义输出格式。
  • 比较: compareTo 方法返回一个负数、零或正数,用于按时间顺序对两个 Date 实例进行排序。
  • 🆕 现代 API: java.time 中的 LocalDate、LocalDateTime、ZonedDateTime 和 Instant 用不可变对象替换了 Date 和 Calendar。
  • 🧵 螺纹安全: DateTimeFormatter 是不可变的,可以安全地共享,而 SimpleDateFormat 需要同步或 ThreadLocal 包装器。
  • 🔁 互操作性: 使用 Date.toInstant、Date.from 和 Calendar.toInstant 辅助函数在旧 API 和新 API 之间进行转换。

Java 日期和时间

处理日期和时间 Java 这是构建调度逻辑、记录时间戳、生成报告和审计跟踪的常规步骤。在编写任何代码之前,了解构成日期的参数很有帮助:

  • (两位数或四位数)
  • (可以是两位数字、月份的前三个字母,或者完整的月份名称)。
  • 日期 (实际的日期)。
  • (给定日期的星期名称,例如星期日、星期一、星期二)。

Java 它还公开了描述时间线上某一时刻的许多其他参数,包括时代、一年中的第几周、时区和偏移量。以下各节将通过可运行的示例逐一介绍这些参数。

如何在 Java

显示日期 Java

Java 提供了一个 Date 在课堂上 java.util 该类存储精确到毫秒的特定时间点,并提供用于打印、比较和格式化该时间点的辅助方法。要获取当前日期和时间,请使用其默认构造函数实例化该类:

import java.util.Date;

class Date_Ex1 {
    public static void main(String args[]) {
        // Instantiate a Date object by invoking its constructor
        Date objDate = new Date();
        // Display the Date and Time using toString()
        System.out.println(objDate.toString());
    }
}

输出:

Wed Nov 29 06:36:22 UTC 2017

示例以默认的 toString 格式打印日期。要以不同的格式显示同一时间点,首先需要了解格式化字母。 Java 用途。

SimpleDateFormat:解析和格式化日期

SimpleDateFormat 上课 java.text 转换一个 Date 将对象转换为格式化字符串,并将格式化字符串解析回对象。 Date它使用与日期或时间部分对应的模式字母。下表列出了所有支持的字母:

博客日期或时间组件例如:
G纪元指示符AD
y年份2026
M月份七月或Jul或07
w一年中的第几周27
W月中的周数2
D一年中的某一天189
d月中天数10
F每月中的星期几2
E星期几名称周二或周二
u星期几(1 = Monday(7 = 星期日)1
a上午或下午标记PM
H一天中的小时数(0-23)0
k一天中的小时数(1-24)24
K上午或下午时间(0-11)0
h上午或下午时间(1-12)12
m一小时内的分钟数30
s分钟内第二55
S毫秒978
z时区(名称)太平洋标准时间;PST;GMT-08:00
Z时区(RFC 822 偏移量)-0800
X时区(ISO 8601 偏移量)-08 或 -0800 或 -08:00

您无需记住整张表格。每次创建新图案时,只需参考它即可。

如何使用 SimpleDateFormat

SimpleDateFormat 是的子类 DateFormat 这样,您就可以使用上面的模式字母来格式化和解析日期。组合这些字母即可描述您想要生成或读取的确切字符串。

例如:

  1. 日期格式要求: 2012.10.23 20:20:45 太平洋标准时间合适的格式模式是 yyyy.MM.dd HH:mm:ss zzz.
  2. 日期格式要求: 09:30:00 AM 23-May-2012合适的格式模式是 hh:mm:ss a dd-MMM-yyyy.

提示: 手表字母大小写。大写 M 月份,小写 m 很小。大写 H 是一个 24 小时制时钟,小写 h 这是一个12小时制时钟。将它们混淆会产生无声的错误输出。

以下程序会打印两次当前日期,一次使用默认格式,一次使用 SimpleDateFormat 模式:

import java.text.SimpleDateFormat;
import java.util.Date;

class TestDates_Format {
    public static void main(String args[]) {
        // Current system date and time is assigned to objDate
        Date objDate = new Date();
        System.out.println(objDate);

        // Build the target pattern and format the date
        String strDateFormat = "hh:mm:ss a dd-MMM-yyyy";
        SimpleDateFormat objSDF = new SimpleDateFormat(strDateFormat);
        System.out.println(objSDF.format(objDate));
    }
}

输出:

Wed Nov 29 06:31:41 UTC 2017
06:31:41 AM 29-Nov-2017

比较日期示例

比较日期示例

订购两件商品最可靠的方法是 Date 对象是 compareTo() 该方法会在调用者时间早于目标时间时返回负整数,在两个时间点相同时返回零,在调用者时间晚于目标时间时返回正整数。以下代码片段解析两个日期字符串并打印它们的相对顺序:

import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;

class TestDates_Compare {
    public static void main(String args[]) throws ParseException {

        SimpleDateFormat objSDF = new SimpleDateFormat("dd-MM-yyyy");
        Date dt_1 = objSDF.parse("20-08-1981");
        Date dt_2 = objSDF.parse("12-10-2012");

        System.out.println("Date1 : " + objSDF.format(dt_1));
        System.out.println("Date2 : " + objSDF.format(dt_2));

        // compareTo returns > 0 when dt_1 is after dt_2
        if (dt_1.compareTo(dt_2) > 0) {
            System.out.println("Date 1 occurs after Date 2");
        }
        // compareTo returns < 0 when dt_1 is before dt_2
        else if (dt_1.compareTo(dt_2) < 0) {
            System.out.println("Date 1 occurs before Date 2");
        }
        // compareTo returns 0 when both dates are equal
        else {
            System.out.println("Both are the same date");
        }
    }
}

输出:

Date1 : 20-08-1981
Date2 : 12-10-2012
Date 1 occurs before Date 2

注意: 原始代码使用了以下模式 dd-mm-yyyy小写字母 mm 字母代表分钟,而不是月份,因此解析器默默地生成了错误的月份值。上面的修复方法使用了大写字母。 MM 月份的字母是正确的模式字母。

Java 8 现代日期和时间 API (java.time)

Java 8 引入了 java.time 该软件包是一个重新设计的 API,其灵感来源于 Joda-Time 库。新的类是不可变的、线程安全的,并将人类日历与机器时间线分离。最常见的类型有:

  • LocalDate 日期不包含时间或时区(例如,生日)。
  • LocalTime 适用于没有日期或时区的挂钟时间。
  • LocalDateTime 指定日期和时间,不指定时区。
  • ZonedDateTime 指定日期和时间以及特定时区。
  • OffsetDateTime 日期和时间具有 UTC 偏移量,但没有指定时区。
  • Instant 机器时间戳,从 UNIX 纪元开始计算。
  • DurationPeriod 适用于基于时间和日期的金额。

以下示例使用现代 API 打印当前日期、时间、时间戳和时区:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.Instant;
import java.time.ZoneId;

class ModernDateDemo {
    public static void main(String args[]) {
        LocalDate today = LocalDate.now();
        LocalDateTime now = LocalDateTime.now();
        ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
        Instant epoch = Instant.now();

        System.out.println("LocalDate     : " + today);
        System.out.println("LocalDateTime : " + now);
        System.out.println("ZonedDateTime : " + zoned);
        System.out.println("Instant       : " + epoch);
    }
}

每种类型都有工厂方法(now, of, parse)和不可变操作(plusDays, minusHours, withYear返回一个新值,而不是改变原始对象。

DateTimeFormatter:在现代语言中格式化日期 Java

DateTimeFormatter in java.time.format 取代 SimpleDateFormat它使用相同的模式字母样式,是不可变的,并且可以安全地在线程间共享而无需额外的同步。该类还公开了现成的常量,例如: ISO_LOCAL_DATEISO_ZONED_DATE_TIME 适用于标准格式。

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

class FormatterDemo {
    public static void main(String args[]) {
        LocalDateTime now = LocalDateTime.now();

        DateTimeFormatter iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        DateTimeFormatter human = DateTimeFormatter.ofPattern("hh:mm:ss a dd-MMM-yyyy");

        System.out.println(now.format(iso));
        System.out.println(now.format(human));

        LocalDateTime parsed = LocalDateTime.parse("2026-07-04T09:30:00", iso);
        System.out.println("Parsed back : " + parsed);
    }
}

要使用现代 API 比较两个日期,请调用 isBefore, isAfterisEqual这些方法比……读起来更清晰。 compareTo 并与每个人合作 java.time 类。

螺纹安全与最佳实践 Java 日期和时间

以上皆是 java.util.DateSimpleDateFormat 早于现代并发 Java每种方法都有其已知的缺陷。请遵循以下实践,以确保生产代码中的日期处理正确无误:

  • 优先使用 java.time 而不是 Date 和 Calendar。 新类型是不可变的,并且是线程安全的,它们可以更准确地模拟领域(没有时间的日期,没有时区的时间等等)。
  • 不要在不同线程间共享 SimpleDateFormat。 该类存储内部解析状态,并且不是线程安全的。请将其包装在一个…… ThreadLocal同步实例,或切换到 DateTimeFormatter.
  • 务必指定语言环境。 月份和日期名称会随着 JVM 默认区域设置而变化,这使得诸如此类的模式变得复杂。 MMMEEE 不具备可移植性。请显式传递区域设置(例如, Locale.ENGLISH).
  • 务必明确说明时区。 LocalDateTime 没有区域,所以转换为 ZonedDateTime or Instant 在需要精确把握时机的时候,特别是对于日志、数据库列和网络有效载荷而言。
  • 请谨慎地在两个 API 之间进行转换。 绝大部分储备使用 date.toInstant(), Date.from(instant)calendar.toInstant().atZone(zone) 处于传统代码和现代代码的交界处。
  • 以UTC格式存储时间戳。 坚持不懈 transmit 日期以 UTC 为准( Instant 或者 ISO 8601 字符串)并在显示时转换为用户区域。

常见问题

调用 java.time 包中的 LocalDateTime.now() 可获取系统时区的日期和时间,调用 LocalDate.now() 则仅获取日期。在旧版本中…… Java使用 new java.util.Date() 获取当前时间。

java.util.Date 表示自 Unix 纪元以来的毫秒数,并且是可变的。LocalDate 表示不包含时间和时区的日历日期,是不可变的,并且是线程安全的,它提供了更清晰的方法,例如 plusDays 和 isBefore。

不行。SimpleDateFormat 会维护可变的解析状态,在线程间共享时会产生错误的结果。请将其封装在 ThreadLocal 中,或在实例上进行同步,或者改用不可变且安全的 DateTimeFormatter。

请传递完整的字符串“yyyy-MM-dd”。大写字母 MM 代表月份,小写字母 mm 代表分钟。如需完整的日期和时间,请使用“yyyy-MM-dd HH:mm:ss”。请务必指定区域设置,以确保月份名称的一致性。

对于旧版 Date 类型,可以使用 compareTo 方法,它会返回一个负数、零或正数整数。对于 java.time 类型,可以对 LocalDate 或 Instant 类型调用 isBefore、isAfter 或 isEqual 方法。这两种方法都遵循时间顺序。

调用 `date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()`。要回溯到之前的日期时间,请使用 `Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant())`。如果需要精确到特定时间,请明确指定时区。

基于日志和文档语料库训练的AI模型能够从样本字符串中推断出可能的模式,并返回匹配的DateTimeFormatter模式。机器学习分类器还能标记出诸如“月-日”和“日-月”顺序等模糊字段。

是的。GitHub Copilot 会建议 DateTimeFormatter 模式、ZonedDateTime 转换以及 JUnit 测试是否具有区域设置相关的输出。 Rev在提交之前,请查看每个建议的正确模式字母、区域设置和时区。

总结一下这篇文章: