日期时间
Locale
在计算机中,通常使用 Locale
表示一个国家或地区的日期、时间、数字、货币等格式。Locale由语言_国家的字母缩写构成,例如,
zh_CN
表示中文+中国,en_US
表示英文+美国。语言使用小写,国家使用大写。
对于日期来说,不同的Locale,例如,中国和美国的表示方式如下:
- zh_CN:2016-11-30
- en_US:11/30/2016
计算机用Locale在日期、时间、货币和字符串之间进行转换。一个电商网站会根据用户所在的 Local e对用户显示如下:
| 中国用户 | 美国用户 |
---|---|---|
购买价格 | 12000.00 | 12,000.00 |
购买日期 | 2016-11-30 | 11/30/2016 |
Date
、Calendar
、SimpleDateFormat
和 TimeZone
均是 JKD 8 以前的 API,确实不好用,请尽可能放弃他们
JDK 8 该用什么
jdk 8 开始,java.time
包提供了新的一系列日期时间的 API,主要是:
- 本地日期和时间:
LocalDateTime
,LocalDate
,LocalTime
; - 带时区的日期和时间:
ZonedDateTime
; - 时刻:
Instant
; - 时区:
ZoneId
,ZoneOffset
; - 时间间隔:
Duration
。 - 适用于新 API 的 格式化类型:
DateTimeFormatter
LocalDateTime
LocalDate d = LocalDate.now(); // 当前日期
LocalTime t = LocalTime.now(); // 当前时间
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDate d = dt.toLocalDate(); // 转换到当前日期
LocalTime t = dt.toLocalTime(); // 转换到当前时间
// 自定义格式化:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dtf.format(LocalDateTime.now()));
// 用自定义格式解析:
LocalDateTime dt2 = LocalDateTime.parse("2019/11/30 15:16:17", dtf);
System.out.println(dt2);
LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// 加5天减3小时:
LocalDateTime dt2 = dt.plusDays(5).minusHours(3);
System.out.println(dt2); // 2019-10-31T17:30:59
// 减1月:
LocalDateTime dt3 = dt2.minusMonths(1);
System.out.println(dt3); // 2019-09-30T17:30:59
LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// 日期变为31日:
LocalDateTime dt2 = dt.withDayOfMonth(31);
System.out.println(dt2); // 2019-10-31T20:30:59
// 月份变为9:
LocalDateTime dt3 = dt2.withMonth(9); // 把 2019-10-31 的月份调整为9时,日期也自动变为30。
System.out.println(dt3); // 2019-09-30T20:30:59
// 本月第一天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);
// 判断时间前后
LocalDate.now().isBefore(LocalDate.of(2019, 11, 19));
LocalTime.now().isAfter(LocalTime.parse("08:15:00"));
Duration
和 Period
// LocalDateTime 之间的时间差
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); // PT1235H10M30S
// LocalDate 之间的时间差
Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
System.out.println(p); // P1M21D
// 直接创建
Duration d1 = Duration.ofHours(10); // 10 hours
Duration d2 = Duration.parse(“P1DT2H3M”); // 1 day, 2 hours, 3 minutes
ZonedDateTime
// 可以简单地把 ZonedDateTime 理解成 LocalDateTime 加 ZoneId。ZoneId 是 java.time 引入的新的时区类
ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC); // ZoneOffset 有 UTC、MAX、MIN 三个 ZoneId 常量
// 2019-09-15T20:58:18.786182+08:00[Asia/Shanghai]
// 2019-09-15T08:58:18.788860-04:00[America/New_York]
// 转换到 LocalDateTime,丢弃时区信息
LocalDateTime ldt = zdt.toLocalDateTime();
// 时区转换
// 以中国时区获取当前时间:
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 转换为纽约时间:
ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
// 注意时区转换包含了夏令时的计算,这才是时间跨时区转换的正确打开方式,涉及到时区时,千万不要自己计算时差,否则难以正确处理夏令时。
DateTimeFormatter
// DateTimeFormatter 线程安全,不可变;
// SimpleDateFormat 非线程安全,使用的时候,只能在方法内部创建新的局部变量。而 DateTimeFormatter 可以只创建一个实例,到处引用。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm”);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(“E, yyyy-MMMM-dd HH:mm”, Locale.US);
ZonedDateTime zdt = ZonedDateTime.now();
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm’Z’”); // 固定字符可用单引号’表示
formatter.format(zdt);
// 2019-09-15T23:16 GMT+08:00
var zhFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
zhFormatter.format(zdt);
// 2019 9月 15 周日 23:16
var usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
usFormatter.format(zdt);
// Sun, September/15/2019 23:16
Instant
// 用 Instant 代替毫秒数,表示时间戳
now.getEpochSecond(); // 秒
now.toEpochMilli(); // 毫秒
public final class Instant implements … {
private final long seconds; // 秒数
private final int nanos; // 纳秒数
}
// 以指定时间戳创建Instant:
Instant ins = Instant.ofEpochSecond(1568568760);
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault()); // 加上时区就变成 ZonedDateTime, 注意这个 systemDefault 时区
旧API转新API
// Date -> Instant:
Instant ins1 = new Date().toInstant();
// Calendar -> Instant -> ZonedDateTime:
Instant ins2 = Calendar.getInstance().toInstant();
ZonedDateTime zdt = ins2.atZone(calendar.getTimeZone().toZoneId());