目录
一、传统时间与日期类
在JDK8之前,Java语言自带的日期时间类主要有java.util.Date
、java.util.Calendar
和java.text.SimpleDateFormat
。这些类存在一系列问题,使得在编写代码时需要做出许多不必要的努力:
java.util.Date
类:除了表示当前日期时间以外,其余所有方法都被视为已过时(deprecated),且不支持时区信息,也不是线程安全的。java.util.Calendar
类:虽然是一个比较全面的日期时间处理类,并且可以进行一些精度更高的计算,但由于设计不够合理,使得代码变得冗长和难以维护,同时也不是线程安全的。java.text.SimpleDateFormat
类:虽然可以将日期时间格式化为字符串,但它同样也不是线程安全的。
如今仍有部分公司在使用这些时间与日期类部分内容,所以我们也有必要去了解一下。有的完全过时的内容这里就不在讲解。
1.Date类
Java中的Date
类是一个用于表示特定瞬间(精确到毫秒)的日期和时间的类。尽管从Java 9开始,Date
类被标记为过时(deprecated),并且推荐使用新的日期时间API(如java.time
包下的类),但了解Date
类的常见方法仍然是有价值的。以下是Date
类的一些常见方法:
构造方法
1.Date()
:创建一个表示当前日期和时间的Date
对象(使用系统默认的时区和毫秒计时器)。
import java.util.Date;
public class DateExample {
public static void main(String[] args) {
// 创建一个表示当前日期和时间的Date对象
Date now = new Date();
// 打印当前日期和时间(注意:输出格式依赖于JVM的实现)
System.out.println("当前日期和时间(默认格式):" + now.toString());
}
}
2.Date(long date)
:根据指定的毫秒数(自1970年1月1日00:00:00 GMT以来的毫秒数)创建一个Date
对象。
import java.util.Date;
public class DateExample {
public static void main(String[] args) {
// 定义一个表示特定时间点的毫秒数(例如,2023年1月1日00:00:00 GMT的毫秒数)
// 注意:这里只是一个示例值,实际值需要根据具体日期计算
long specificTimeMillis = 1672483200000L; // 假设这是正确的毫秒数
// 根据指定的毫秒数创建一个Date对象
Date specificDate = new Date(specificTimeMillis);
// 打印特定日期和时间(注意:输出格式依赖于JVM的实现)
System.out.println("特定日期和时间(默认格式):" + specificDate.toString());
}
}
获取日期和时间信息的方法
getTime()
:返回自1970年1月1日00:00:00 GMT以来的毫秒数(即时间戳)。
import java.util.Date;
public class DateExample {
public static void main(String[] args) {
// 创建一个表示当前日期和时间的Date对象
Date now = new Date();
// 获取当前日期和时间的毫秒数(时间戳)
long timestamp = now.getTime();
// 打印时间戳
System.out.println("当前时间戳(毫秒):" + timestamp);
}
}
设置日期和时间信息的方法
setTime(long time):
设置Date
对象表示的日期和时间,使用自1970年1月1日00:00:00 GMT以来的毫秒数。
import java.util.Date;
public class DateExample {
public static void main(String[] args) {
// 创建一个Date对象
Date date = new Date();
// 打印原始日期和时间(默认格式)
System.out.println("原始日期和时间:" + date.toString());
// 定义一个新的时间戳(例如,某个特定时间点的毫秒数)
long newTimeMillis = 1672483200000L; // 假设这是另一个日期的毫秒数
// 设置Date对象表示的新日期和时间
date.setTime(newTimeMillis);
// 打印修改后的日期和时间(默认格式)
System.out.println("修改后的日期和时间:" + date.toString());
}
}
2.Calendar类
Java中的Calendar
类是一个抽象类,用于封装日历信息,如年、月、日、时、分、秒等,并提供操作这些字段的方法。
由于Calendar
是抽象类,因此你不能直接实例化它。相反,你需要使用Calendar
类的一个子类实例,通常是通过调用Calendar
类的getInstance()
静态方法获得的,该方法会返回一个Calendar
对象,该对象是根据默认时区和语言环境初始化的。
主要特点和功能
- 抽象性:
Calendar
是一个抽象类,不能直接实例化。 - 时区和语言环境:
Calendar
提供了操作日历字段的方法,这些字段值基于特定的时区和语言环境。 - 字段值:
Calendar
类使用整数来表示日历字段,如YEAR
、MONTH
、DAY_OF_MONTH
、HOUR_OF_DAY
等。注意,MONTH
字段是从0开始的(0表示1月,11表示12月)。 - 字段操作:提供了获取(
get
)、设置(set
)、增加(add
)和滚动(roll
)日历字段值的方法。 - 不可变性(某种程度上):虽然
Calendar
对象本身是可变的(即,你可以修改其字段值),但一旦你通过getTime()
方法获得了一个Date
对象,那么该Date
对象是不可变的。
常用方法
1. 获取当前日历对象
Calendar
类是一个抽象类,不能直接实例化。通常,我们使用Calendar.getInstance()
方法来获取一个当前日期和时间的Calendar
实例,该实例使用了默认的时区和语言环境。
import java.util.Calendar;
public class CalendarDemo {
public static void main(String[] args) {
// 获取当前日历对象
Calendar calendar = Calendar.getInstance();
System.out.println("当前日历对象:" + calendar.toString()); // 注意:toString()输出可能不直观
}
}
2. 获取日历中的某个信息
你可以使用get(int field)
方法来获取Calendar
对象中的某个字段的值。字段常量(如YEAR
、MONTH
、DAY_OF_MONTH
等)定义在Calendar
类中。
import java.util.Calendar;
public class CalendarDemo {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 获取当前年份
int year = calendar.get(Calendar.YEAR);
System.out.println("当前年份:" + year);
// 获取当前月份(注意:月份是从0开始的)
int month = calendar.get(Calendar.MONTH) + 1;
System.out.println("当前月份:" + month);
// 获取当前日期
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println("当前日期:" + day);
}
}
3. 获取日期对象
你可以使用getTime()
方法从Calendar
对象中获取一个Date
对象,该对象表示Calendar
的时间值。
import java.util.Calendar;
import java.util.Date;
public class CalendarDemo {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 获取Date对象
Date date = calendar.getTime();
System.out.println("当前日期和时间:" + date.toString());
}
}
4. 获取时间毫秒值
Calendar
类提供了getTimeInMillis()
方法来获取当前时间相对于“纪元”(即1970年1月1日00:00:00.000 GMT)的毫秒数。
import java.util.Calendar;
public class CalendarDemo {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 获取时间毫秒值
long timeInMillis = calendar.getTimeInMillis();
System.out.println("当前时间毫秒值:" + timeInMillis);
}
}
5. 修改日历的某个信息
你可以使用set(int field, int value)
方法来修改Calendar
对象中的某个字段的值。
import java.util.Calendar;
public class CalendarDemo {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 修改年份
calendar.set(Calendar.YEAR, 2024);
// 验证修改
System.out.println("修改后的年份:" + calendar.get(Calendar.YEAR));
}
}
6. 为某个信息增加或者减少值
你可以使用add(int field, int amount)
方法来给Calendar
对象中的某个字段增加或减少指定的值。
import java.util.Calendar;
public class CalendarDemo {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 给当前日期增加10天
calendar.add(Calendar.DAY_OF_MONTH, 10);
// 验证增加
System.out.println("10天后的日期:" + calendar.getTime().toString());
// 给当前年份减少1年
calendar.add(Calendar.YEAR, -1);
// 验证减少
System.out.println("减少1年后的年份:" + calendar.get(Calendar.YEAR));
}
}
3.SimpleDateFormat类
SimpleDateFormat
类是 Java 中用于格式化和解析日期的一个非常强大的类。它允许你进行用户自定义的日期-时间格式。这个类继承自 DateFormat
抽象类,并实现了 Serializable
接口,这意呀着它可以被序列化以便在网络中传输或存储到文件系统中。
主要用途
- 格式化日期:将
Date
对象转换成符合特定模式的字符串。 - 解析日期:将符合特定模式的字符串转换成
Date
对象。
构造函数
SimpleDateFormat
类提供了多个构造函数,但最常用的一个接受一个模式字符串作为参数,这个字符串定义了日期的格式。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
模式字符串
模式字符串由字母和数字组成,每个字母代表日期或时间的一部分。以下是一些常用的模式字母:
y
:年M
:月d
:日H
:小时(0-23)m
:分钟s
:秒S
:毫秒
例如,"yyyy-MM-dd HH:mm:ss"
是一个常见的日期时间模式,它表示格式为“年-月-日 时:分:秒”的字符串。
将日期格式化成日期与时间字符串
当你有一个 Date
对象,并希望将其格式化为包含日期和时间的字符串时,你可以这样做:
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatExample {
public static void main(String[] args) {
// 创建一个Date对象,这里以当前日期时间为例
Date date = new Date();
// 创建一个SimpleDateFormat实例,并指定日期时间格式为"yyyy-MM-dd HH:mm:ss"
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 使用format方法将Date对象格式化为字符串
String formattedDateTime = sdf.format(date);
// 输出格式化后的日期时间字符串
System.out.println(formattedDateTime); // 输出类似:2023-04-01 15:30:00
}
}
将时间毫秒值格式化成字符串
时间毫秒值是自1970年1月1日00:00:00 UTC(协调世界时间)以来的毫秒数。要将这个时间毫秒值格式化为字符串,你首先需要将其转换为 Date
对象,然后使用 SimpleDateFormat
进行格式化。但实际上,SimpleDateFormat
的 format
方法也接受一个 long
类型的参数(即时间毫秒值),不过更常见的做法是先转换为 Date
对象,因为这样可以更清晰地表达你的意图。
不过,为了直接展示如何使用毫秒值,这里给出一个简化的例子:
import java.text.SimpleDateFormat;
public class SimpleDateFormatMillisExample {
public static void main(String[] args) {
// 获取当前时间的毫秒值
long millis = System.currentTimeMillis();
// 创建一个SimpleDateFormat实例,并指定日期时间格式为"yyyy-MM-dd HH:mm:ss"
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 直接使用format方法和毫秒值来格式化时间(虽然不常见,但技术上可行)
// 注意:这里实际上是在内部将long类型的毫秒值转换为了Date对象,然后进行了格式化
String formattedDateTime = sdf.format(new Date(millis));
// 输出格式化后的日期时间字符串
System.out.println(formattedDateTime); // 输出类似:2023-04-01 15:30:00
// 或者,更直接地(如果你确定只需要毫秒值),但这样不会进行格式化
// System.out.println(millis); // 这将输出一个长整型数字,不是格式化的日期时间字符串
}
}
在这个例子中,我们首先获取了当前时间的毫秒值,然后创建了一个 SimpleDateFormat
实例来指定我们想要的日期时间格式。尽管 format
方法直接接受一个 long
类型的参数在技术上不是直接可行的(因为 format
方法的重载版本接受的是 Object
类型,但实际上是期望一个 Date
或其子类的实例),但上面的代码通过 new Date(millis)
将毫秒值转换为了 Date
对象,然后进行了格式化。这是处理时间毫秒值时更常见和推荐的做法。
解析日期
解析日期是指将符合特定格式的字符串转换成 Date
对象的过程。在 SimpleDateFormat
中,你可以通过定义日期时间的模式(pattern)来指定这个字符串的格式。
示例
假设你有一个日期字符串 "2023-04-01"
,并且你知道这个字符串是以 "yyyy-MM-dd"
格式表示的(即年-月-日)。你可以使用 SimpleDateFormat
来解析这个字符串到 Date
对象。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatExample {
public static void main(String[] args) {
// 定义日期字符串和日期格式
String dateString = "2023-04-01";
String dateFormat = "yyyy-MM-dd";
// 创建 SimpleDateFormat 实例
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
try {
// 解析字符串到 Date 对象
Date date = sdf.parse(dateString);
// 输出 Date 对象(注意:直接输出 Date 对象可能不是很直观,因为 Date.toString() 使用的是默认格式)
System.out.println(date); // 输出类似于 Sat Apr 01 00:00:00 GMT 2023
// 如果需要按照特定格式输出,可以再次使用 SimpleDateFormat
System.out.println(sdf.format(date)); // 输出 2023-04-01
} catch (ParseException e) {
e.printStackTrace();
// 处理解析异常
}
}
}
要点
- 当使用
parse
方法时,如果字符串与指定的模式不匹配,会抛出ParseException
。因此,你需要处理这个异常。 - 默认的
SimpleDateFormat
是不线程安全的。如果你在多线程环境中使用同一个SimpleDateFormat
实例,可能会遇到并发问题。解决方法是为每个线程创建单独的SimpleDateFormat
实例,或者使用ThreadLocal
来管理它们。 - 当你使用
SimpleDateFormat
格式化或解析日期时,请确保你的模式和字符串或Date
对象相匹配,以避免意外错误。 - Java 8 引入了新的日期和时间 API(位于
java.time
包中),这些API提供了更强大、灵活且线程安全的日期和时间处理能力。如果你正在使用 Java 8 或更高版本,建议考虑使用这些新的API。
注意事项
- 线程安全性:
SimpleDateFormat
不是线程安全的。如果在多线程环境中使用相同的SimpleDateFormat
实例,可能会出现并发问题。一个常见的解决方案是为每个线程创建单独的SimpleDateFormat
实例,或者使用ThreadLocal
来管理实例。 - 时区:默认情况下,
SimpleDateFormat
使用默认的时区和语言环境来格式化或解析日期。如果需要,可以使用setTimeZone(TimeZone zone)
方法来设置时区。 - 模式字符大小写:在模式中,某些字符的大小写是有意义的。例如,
MM
表示月份,而mm
表示分钟。
二、新旧替换
JDK8新增的时间与日期类相比以前的类,带来了显著的变化和改进。以下是对这些新增类及其替代的旧类以及具体变化的详细说明:
一、新增的时间与日期类及其替代的旧类
新增类 | 替代的旧类(或功能) |
---|---|
LocalDate | java.util.Calendar (部分功能) |
LocalTime | java.util.Calendar (部分功能) |
LocalDateTime | java.util.Calendar 、java.util.Date (部分功能) |
Instant |
|
ZonedDateTime | java.util.Calendar (带时区功能) |
DateTimeFormatter | java.text.SimpleDateFormat |
Duration | java.util.concurrent.TimeUnit (部分功能) |
Period | 无直接替代,提供新的时间间隔表示 |
二、具体变化
LocalDate
- 替代了
java.util.Calendar
中处理日期的部分功能,但更加简洁和直观。 - 表示一个具体的日期,如“2024-07-27”,不包含时间信息。
- 提供了丰富的API来处理日期,如加减天数、月份、年份,以及比较日期等。
- 替代了
LocalTime
- 替代了
java.util.Calendar
中处理时间的部分功能。 - 表示一个具体的时间,如“10:29:18”,不包含日期信息。
- 提供了API来处理时间,如加减小时、分钟、秒等。
- 替代了
LocalDateTime
- 整合了
LocalDate
和LocalTime
的功能,表示一个具体的日期和时间,如“2024-07-27T10:29:18”。 - 提供了更全面的API来处理日期和时间,包括加减日期、时间,以及比较等。
- 整合了
Instant
- 替代了
java.util.Date
中时间戳的功能,但更加精确(到纳秒)。 - 表示一个具体的时间点,即时间线上的一个瞬时。
- 提供了与
ZonedDateTime
等类进行转换的API,便于时区处理。
- 替代了
ZonedDateTime
- 替代了
java.util.Calendar
中处理带时区日期时间的部分功能,但更加完善。 - 表示一个具体的带时区的日期时间,如“2024-07-27T10:29:18+08:00[Asia/Shanghai]”。
- 提供了时区转换、加减日期时间等API。
- 替代了
DateTimeFormatter
- 替代了
java.text.SimpleDateFormat
,提供了更加强大和灵活的日期时间格式化功能。 - 可以通过模式字符串来自定义日期时间的格式,也可以解析符合特定格式的字符串为日期时间对象。
- 替代了
Duration
和Period
- 这两个类提供了新的时间间隔表示方式。
Duration
用于表示时间间隔,如“PT1H30M”(1小时30分钟)。Period
用于表示日期间隔,如“P2Y6M”(2年6个月)。
三、总结
JDK8新增的时间与日期类相比以前的类,在以下几个方面有了显著的变化:
- 线程安全性:新类都是不可变的,因此它们是线程安全的。
- 不可变性:一旦创建,新类的对象就不能被修改,这有助于防止意外的修改和并发问题。
- 更清晰的API设计:新类提供了更直观、更易于理解的API,使得日期时间的处理更加简单和方便。
- 更好的国际化支持:新类支持时区处理,并且遵循ISO 8601标准,使得日期时间的表示更加国际化。
- 更精确的时间表示:
Instant
类提供了纳秒级的时间精度,比旧的java.util.Date
类更加精确。
三、JDK8后新时间与日期类
LocalDate
LocalDate
代表没有时区的日期,如年月日。
常见方法:
now()
: 获取当前日期。of(int year, int month, int dayOfMonth)
: 从年月日创建一个LocalDate
。getYear()
: 获取年份。getMonth()
: 获取月份,返回一个Month
枚举。getDayOfMonth()
: 获取月份中的日。plusDays(long days)
: 添加天数。minusDays(long days)
: 减去天数。
import java.time.LocalDate;
public class LocalDateExample {
public static void main(String[] args) {
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("Today's date: " + today);
// 通过年、月、日创建LocalDate实例
LocalDate specificDate = LocalDate.of(2023, 4, 1);
System.out.println("Specific date: " + specificDate);
// 加上天数
LocalDate nextWeek = specificDate.plusDays(7);
System.out.println("Next week: " + nextWeek);
// 减去月份
LocalDate previousMonth = specificDate.minusMonths(1);
System.out.println("Previous month: " + previousMonth);
// 获取年份、月份和月份中的天数
int year = specificDate.getYear();
int month = specificDate.getMonthValue();
int dayOfMonth = specificDate.getDayOfMonth();
System.out.println("Year: " + year + ", Month: " + month + ", Day: " + dayOfMonth);
// 格式化日期
String formattedDate = specificDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println("Formatted date: " + formattedDate);
}
}
LocalTime
LocalTime
代表没有日期的时间,如时分秒。
常见方法:
now()
: 获取当前时间。of(int hour, int minute, int second)
: 从时分秒创建一个LocalTime
。getHour()
: 获取小时。getMinute()
: 获取分钟。getSecond()
: 获取秒。
import java.time.LocalTime;
public class LocalTimeExample {
public static void main(String[] args) {
// 获取当前时间
LocalTime now = LocalTime.now();
System.out.println("Current time: " + now);
// 通过小时、分钟、秒创建LocalTime实例
LocalTime specificTime = LocalTime.of(14, 30, 45);
System.out.println("Specific time: " + specificTime);
// 加上小时
LocalTime later = specificTime.plusHours(2);
System.out.println("Later time: " + later);
// 减去分钟
LocalTime earlier = later.minusMinutes(30);
System.out.println("Earlier time: " + earlier);
// 获取小时、分钟和秒
int hour = specificTime.getHour();
int minute = specificTime.getMinute();
int second = specificTime.getSecond();
System.out.println("Hour: " + hour + ", Minute: " + minute + ", Second: " + second);
// 格式化时间
String formattedTime = specificTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println("Formatted time: " + formattedTime);
}
}
LocalDateTime
LocalDateTime
代表没有时区的日期和时间。
常见方法:
now()
: 获取当前的日期和时间。of(LocalDate date, LocalTime time)
: 从LocalDate
和LocalTime
创建一个LocalDateTime
。getYear()
,getMonth()
,getDayOfMonth()
,getHour()
,getMinute()
,getSecond()
: 获取日期和时间的各个部分。
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
public class LocalDateTimeExample {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("Current date and time: " + now);
LocalDateTime specificDateTime = LocalDateTime.of(LocalDate.of(2024, 7, 27), LocalTime.of(13, 45, 20));
System.out.println("Specific date and time: " + specificDateTime);
int year = now.getYear();
System.out.println("Year: " + year);
int hour = now.getHour();
System.out.println("Hour: " + hour);
}
}
ZoneId
ZoneId
是时区的标识符,它可以是诸如"Europe/Paris"的地理区域标识符,或者是像"UTC"这样的固定偏移量。
常见方法:
systemDefault()
: 获取系统默认时区。of(String zoneId)
: 从字符串创建一个ZoneId
。getRules()
: 获取与此ZoneId
关联的时区规则。normalized()
: 返回规范化形式的ZoneId
。
示例代码:
import java.time.ZoneId;
public class ZoneIdExample {
public static void main(String[] args) {
// 获取系统默认时区
ZoneId systemDefaultZoneId = ZoneId.systemDefault();
System.out.println("System Default ZoneId: " + systemDefaultZoneId);
// 从字符串创建一个ZoneId
ZoneId zoneId = ZoneId.of("Europe/Paris");
System.out.println("ZoneId of Europe/Paris: " + zoneId);
// 获取时区规则
ZoneId zoneRules = zoneId.getRules();
System.out.println("Zone Rules: " + zoneRules);
// 返回规范化形式的ZoneId
ZoneId normalizedZoneId = zoneId.normalized();
System.out.println("Normalized ZoneId: " + normalizedZoneId);
}
}
ZonedDateTime
ZonedDateTime
是一个包含日期和时间的类,同时关联了时区信息。
常见方法:
now()
: 获取当前日期和时间,并附加系统默认时区。now(ZoneId zone)
: 获取指定时区的当前日期和时间。of(LocalDateTime dateTime, ZoneId zone)
: 从LocalDateTime
和ZoneId
创建一个ZonedDateTime
。withZoneSameLocal(ZoneId zone)
: 更改时区,保留本地日期和时间。withZoneSameInstant(ZoneId zone)
: 更改时区,保留瞬间。plusDays(long days)
: 添加天数。minusDays(long days)
: 减去天数。toLocalDate()
: 获取本地日期。toLocalTime()
: 获取本地时间。getZone()
: 获取时区。
import java.time.ZonedDateTime;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class ZonedDateTimeExample {
public static void main(String[] args) {
// 获取系统默认时区的当前日期和时间
ZonedDateTime now = ZonedDateTime.now();
System.out.println("Current date and time with system default timezone: " + now);
// 获取指定时区的当前日期和时间
ZonedDateTime nowInParis = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
System.out.println("Current date and time in Europe/Paris: " + nowInParis);
// 从LocalDateTime和ZoneId创建一个ZonedDateTime
LocalDateTime localDateTime = LocalDateTime.of(2024, 7, 27, 13, 45, 20);
ZonedDateTime specificDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
System.out.println("Specific date and time: " + specificDateTime);
// 更改时区,保留本地日期和时间
ZonedDateTime zonedDateTimeWithLocalTime = specificDateTime.withZoneSameLocal(ZoneId.of("Europe/Paris"));
System.out.println("Specific date and time in Europe/Paris (same local time): " + zonedDateTimeWithLocalTime);
// 更改时区,保留瞬间
ZonedDateTime zonedDateTimeWithSameInstant = specificDateTime.withZoneSameInstant(ZoneId.of("Europe/Paris"));
System.out.println("Specific date and time in Europe/Paris (same instant): " + zonedDateTimeWithSameInstant);
// 添加天数
ZonedDateTime plusDays = specificDateTime.plusDays(1);
System.out.println("Specific date and time plus one day: " + plusDays);
// 减去天数
ZonedDateTime minusDays = specificDateTime.minusDays(1);
System.out.println("Specific date and time minus one day: " + minusDays);
// 获取本地日期
LocalDate localDate = specificDateTime.toLocalDate();
System.out.println("Local date: " + localDate);
// 获取本地时间
LocalTime localTime = specificDateTime.toLocalTime();
System.out.println("Local time: " + localTime);
// 获取时区
ZoneId zone = specificDateTime.getZone();
System.out.println("ZoneId: " + zone);
}
}
Instant
Instant
类在 Java 中表示一个时间线上的一个瞬时点,它是以 Unix 时间戳(即自1970年1月1日UTC以来的秒数)为基础的,但精度更高,可以达到纳秒级别。Instant
是不带时区的,它表示的是全球统一的时间线上的一个点。
Instant 的主要特点
- 精确到纳秒。
- 不带时区信息,仅表示时间线上的一个点。
- 可以用来表示时间戳,非常适合用于系统间的时间交互,因为它消除了时区带来的复杂性。
Instant 的常见方法
now()
: 获取当前时间的Instant
实例。plus(TemporalQuery<? extends Temporal> query, long amount)
: 在Instant
上加上指定的时间量。minus(TemporalQuery<? extends Temporal> query, long amount)
: 从Instant
上减去指定的时间量。atZone(ZoneId zone)
: 将Instant
转换为特定时区的ZonedDateTime
。toEpochMilli()
: 将Instant
转换为自1970年1月1日UTC以来的毫秒数(与 Unix 时间戳相似,但精度稍低)。toEpochSecond()
: 将Instant
转换为自1970年1月1日UTC以来的秒数。
代码示例
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class InstantExample {
public static void main(String[] args) {
// 获取当前时间的 Instant 实例
Instant now = Instant.now();
System.out.println("Current instant: " + now);
// 将 Instant 转换为自1970年1月1日UTC以来的毫秒数
long epochMilli = now.toEpochMilli();
System.out.println("Epoch milli: " + epochMilli);
// 将 Instant 转换为自1970年1月1日UTC以来的秒数
long epochSecond = now.toEpochSecond();
System.out.println("Epoch second: " + epochSecond);
// 加上10秒
Instant tenSecondsLater = now.plusSeconds(10);
System.out.println("Ten seconds later: " + tenSecondsLater);
// 减去5分钟
Instant fiveMinutesEarlier = now.minusMinutes(5);
System.out.println("Five minutes earlier: " + fiveMinutesEarlier);
// 将 Instant 转换为特定时区的 ZonedDateTime
ZonedDateTime zdt = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("Shanghai time: " + zdt);
}
}
DateTimeFormatter
主要特点
- 自定义格式:可以定义几乎任何所需的日期时间格式。
- 解析:将文本(通常是字符串)解析为日期时间对象。
- 格式化:将日期时间对象格式化为文本(字符串)。
- 本地化:支持使用不同的语言环境和地区设置来格式化日期时间。
常见用法
-
预定义格式:
DateTimeFormatter
提供了一些预定义的格式,如ISO_LOCAL_DATE
、ISO_LOCAL_TIME
、ISO_LOCAL_DATE_TIME
等。 -
自定义格式:使用
DateTimeFormatter.ofPattern(String pattern)
方法可以根据指定的模式字符串创建自定义格式化程序。 -
解析和格式化:使用
parse(CharSequence text)
方法将文本解析为日期时间对象,使用format(TemporalQuery<?> query)
方法将日期时间对象格式化为文本。
代码示例
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterExample {
public static void main(String[] args) {
// 定义自定义的日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 使用 LocalDateTime 和 formatter 格式化日期时间
LocalDateTime now = LocalDateTime.now();
String formattedDateTime = now.format(formatter);
System.out.println("Formatted DateTime: " + formattedDateTime);
// 解析字符串为 LocalDateTime
String dateTimeStr = "2023-10-01 15:30:45";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter);
System.out.println("Parsed DateTime: " + parsedDateTime);
// 使用预定义的 ISO 格式
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String isoDateTimeStr = now.format(isoFormatter);
System.out.println("ISO Formatted DateTime: " + isoDateTimeStr);
// 解析 ISO 格式的字符串
LocalDateTime parsedIsoDateTime = LocalDateTime.parse(isoDateTimeStr, isoFormatter);
System.out.println("Parsed ISO DateTime: " + parsedIsoDateTime);
}
}
在这个例子中,我们首先创建了一个自定义的 DateTimeFormatter
实例,用于将 LocalDateTime
对象格式化为 "yyyy-MM-dd HH:mm:ss"
格式的字符串,并展示了如何将这个格式的字符串解析回 LocalDateTime
对象。接着,我们使用了一个预定义的 ISO 格式的 DateTimeFormatter
来演示如何以 ISO 标准格式进行日期时间的格式化和解析。
DateTimeFormatter
是处理日期时间文本表示的强大工具,它支持灵活的格式定义和本地化,相比于SimpleDateFormat,它的线程更安全。
Period
Period
类用于表示基于日历的日期(年、月、日)之间的间隔。它主要用于处理那些与时间无关,仅与日期相关的间隔,比如两个生日之间的年数差异。Period
的字段包括年(years)、月(months)和日(days)。
示例代码
import java.time.LocalDate;
import java.time.Period;
public class PeriodExample {
public static void main(String[] args) {
LocalDate startDate = LocalDate.of(2020, 1, 15);
LocalDate endDate = LocalDate.of(2023, 4, 20);
// 计算两个日期之间的Period
Period period = Period.between(startDate, endDate);
// 输出结果
System.out.println("Years: " + period.getYears());
System.out.println("Months: " + period.getMonths());
System.out.println("Days: " + period.getDays());
// 也可以通过toTotalMonths()获取总月份数
long totalMonths = period.toTotalMonths();
System.out.println("Total Months: " + totalMonths);
}
}
Duration
Duration
类用于表示基于时间的持续时间,精确到纳秒。它主要用于处理那些与时间相关的间隔,比如两个时间点之间的时长。Duration
的内部实现是基于秒和纳秒的,但它提供了方便的方法来以天、小时、分钟、秒等为单位进行操作。
示例代码
import java.time.Duration;
import java.time.Instant;
public class DurationExample {
public static void main(String[] args) {
Instant startTime = Instant.now();
// 模拟一些操作...
try {
Thread.sleep(5000); // 休眠5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant endTime = Instant.now();
// 计算两个时间点之间的Duration
Duration duration = Duration.between(startTime, endTime);
// 输出结果
System.out.println("Seconds: " + duration.getSeconds());
// 获取剩余的纳秒部分
long nano = duration.getNano();
System.out.println("Nano seconds: " + nano);
// 也可以以其他单位输出
System.out.println("Duration in millis: " + duration.toMillis());
// 转换为以天为单位的近似值(注意:这只是一个近似值,因为天的小数部分被忽略了)
long days = duration.toDays();
System.out.println("Approximate days: " + days);
}
}