日期:2019-05-20
时间:05:20:13
日期时间:2019-05-20 05:20:13(中间必须有空格)
以GMT或者UTC加时区偏移表示,例如:GMT+08:00或者UTC+08:00表示东八区。
GMT和UTC可以认为基本是等价的,只是UTC使用更精确的原子钟计时,每隔几年会有一个闰秒,我们在开发程序的时候可以忽略两者的误差,因为计算机的时钟在联网的时候会自动与时间服务器同步时间。
从Java 8开始,java.time包提供了新的日期和时间API,主要涉及的类型有:
本地日期和时间:LocalDateTime
、LocalDate
、LocalTime
;
带时区的日期和时间:ZonedDateTime
;
时刻:Instant
;
时区:ZoneId
、ZoneOffset
;
时间间隔:Duration
。
以及一套新的用于取代SimpleDateFormat的格式化类型DateTimeFormatter。
表示一个本地日期和时间:
import java.time.*;
public class Main {
public static void main(String[] args) {
//由于每行代码执行需要时间,结果可能有毫秒级误差
LocalDate d = LocalDate.now(); //当前日期
LocalTime t = LocalTime.now(); //当前时间
LocalDateTime dt = LocalDateTime.now(); //当前日期和时间
//统一时间转换,无毫秒级误差
LocalDateTime dt1 = LocalDateTime.now(); //当前日期和时间
LocalDate d1 = dt.toLocalDate(); //转换到当前日期
LocalTime t1 = dt.toLocalTime(); //转换到当前时间
//指定日期和时间可以使用of方法
LocalDate d2 = LocalDate.of(2020, 05, 20);
LocalTime t2 = LocalTime.of(05, 20, 13);
LocalDateTime dt2 = LocalDateTime.of(2020, 05, 20, 05, 20, 13);
LocalDateTime dt3 = LocalDateTime.of(d2, t2); //等同于dt2的结果
//按照国际通用格式打印日期时间(ISO8601标准)
System.out.println(d);
System.out.println(t);
System.out.println(dt);
System.out.println(d1);
System.out.println(t1);
System.out.println(dt1);
System.out.println(dt2);
System.out.println(dt3);
}
}
输出结果:
2020-08-01
14:12:06.957
2020-08-01T14:12:06.957
2020-08-01
14:12:06.957
2020-08-01T14:12:06.957
2020-05-20T05:20:13
2020-05-20T05:20:13
如果要自定义输出的格式,或者要把一个非ISO8601格式的字符串解析成LocalDateTime,可以使用新的DateTimeFormatter。
import java.time.*;
import java.time.format.*;
public class Main {
public static void main(String[] args) {
// 自定义格式化:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dtf.format(LocalDateTime.now()));
// 用自定义格式解析:
LocalDateTime dt2 = LocalDateTime.parse("2020/05/20 13:14:18", dtf);
System.out.println(dt2);
// 自定义时间加5天减3小时:
LocalDateTime dt3 = dt2.plusDays(5).minusHours(3);
System.out.println(dt3);
// 自定义时间减1月
LocalDateTime dt4 = dt2.minusMonths(1);
System.out.println(dt4);
}
}
输出结果:
2020/08/01 14:28:17
2020-05-20T13:14:18
2020-05-25T10:14:18
2020-04-20T13:14:18
对日期和时间进行调整则使用withXxx()方法,例如:withHour(15)
会把10:11:12
变为15:11:12
调整年:withYear()
调整月:withMonth()
调整日:withDayOfMonth()
调整时:withHour()
调整分:withMinute()
调整秒:withSecond()
import java.time.*;
public class Main {
public static void main(String[] args) {
// 用自定义格式解析:
LocalDateTime dt2 = LocalDateTime.of(2020,05,20,13,14,18);
System.out.println(dt2);
// 修改自定义时间,日期变为31日
LocalDateTime dt5 = dt2.withDayOfMonth(31);
System.out.println(dt5);
// 修改自定义时间,月份变为9
LocalDateTime dt6 = dt2.withMonth(9);
System.out.println(dt6);
}
}
输出结果:
2020-05-20T13:14:18
2020-05-31T13:14:18
2020-09-20T13:14:18
其他时间计算,使用LocalDateTime的with()方法
import java.time.*;
import java.time.temporal.*;
public class Main {
public static void main(String[] args) {
// 本月第一天0:00时刻
LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
System.out.println(firstDay);
// 本月最后1天
LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println(lastDay);
// 下月第1天
LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(nextMonthFirstDay);
// 本月第1个周一
LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println(firstWeekday);
}
}
输出结果:
2020-08-01T00:00
2020-08-31
2020-09-01
2020-08-03
Duration表示两个时刻之间的时间间隔。另一个类似的Period表示两个日期之间的天数
import java.time.*;
public class Main {
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
LocalDateTime end = LocalDateTime.of(2020, 1, 9, 19, 25, 30);
Duration d = Duration.between(start, end); //两个时刻之间的时间间隔
System.out.println(d);
Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
System.out.println(p); //两个日期之间的天数
}
}
输出结果:
PT1235H10M30S //国际通用格式,表示1235小时10分钟30秒
P1M21D //国际通用格式,表示1个月21天
ZonedDateTime表示一个带时区的日期和时间。可以简单地把ZonedDateTime理解成LocalDateTime加ZoneId。ZoneId是java.time引入的新的时区类,注意和旧的java.util.TimeZone区别。
import java.time.*;
public class Main {
public static void main(String[] args) {
//默认时区
ZonedDateTime zbj = ZonedDateTime.now();
//用指定时区获取当前时间
ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println(zbj);
System.out.println(zny);
}
}
输出结果:
虽然它们时区不同,但表示的时间都是同一时刻(毫秒数不同是执行语句时的时间差)
2020-08-01T14:49:58.767+08:00[Asia/Shanghai]
2020-08-01T02:49:58.771-04:00[America/New_York]
要转换时区,首先需要有一个ZonedDateTime对象,然后,通过withZoneSameInstant()将关联时区转换到另一个时区,转换后日期和时间都会相应调整。
实例:北京时间转换为纽约时间
import java.time.*;
public class Main {
public static void main(String[] args) {
//以中国时区获取当前时间
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
//转换为纽约时间
ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println(zbj);
System.out.println(zny);
}
}
输出结果:
2020-08-01T15:12:13.839+08:00[Asia/Shanghai]
2020-08-01T03:12:13.839-04:00[America/New_York]
对ZonedDateTime或LocalDateTime进行格式化,需要使用DateTimeFormatter类。DateTimeFormatter可以通过格式化字符串和Locale对日期和时间进行定制输出。
import java.time.*;
import java.time.format.*;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.now();
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");
System.out.println(formatter.format(zdt));
var zhFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
System.out.println(zhFormatter.format(zdt));
var usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
System.out.println(usFormatter.format(zdt));
}
}
输出结果:
2020-08-01T07:19 GMT
2020 8月 01 周六 07:19
Sat, August/01/2020 07:19
Instant表示高精度时间戳,它可以和ZonedDateTime以及long互相转换。
import java.time.*;
public class Main {
public static void main(String[] args) {
//当前时间戳
Instant now = Instant.now();
System.out.println(now.getEpochSecond()); //秒级时间戳
System.out.println(now.toEpochMilli()); //毫秒级时间戳
//给时间戳附加时区,转换为带时区时间
long a = now.getEpochSecond(); //这里必须是秒级时间戳
Instant ins = Instant.ofEpochSecond(a);
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
System.out.println(zdt);
}
}
输出结果:
1596267222
1596267222396
2020-08-01T15:33:42+08:00[Asia/Shanghai]