在Java SE 8中,添加了一个新包:java.time,它提供了结构良好的API来处理时间和日期。如果熟悉Joda Time,它将很容易掌握。
在Java刚刚发布,也就是版本1.0的时候,对时间和日期仅有的支持就是java.util.Date类。大多数开发者对它的第一印象就是,它根本不代表一个“日期”。实际上,它只是简单的表示一个,从1970-01-01Z开始计时的,精确到毫秒的瞬时点。由于标准的toString()方法,按照JVM的默认时区输出时间和日期,有些开发人员把它误认为是时区敏感的。
在升级Java到1.1期间,Date类被认为是无法修复的。由于这个原因,java.util.Calendar类被添加了进来。悲剧的是,Calendar类并不比java.util.Date好多少。它们面临的部分问题是:
- 可变性。像时间和日期这样的类应该是不可变的。
- 偏移性。Date中的年份是从1900开始的,而月份都是从0开始的。
- 命名。Date不是“日期”,而Calendar也不真实“日历”。
- 格式化。格式化只对Date有用,Calendar则不行。另外,它也不是线程安全的。
JDK1.8提供的日期处理类都是不可变对象,所以是线程安全的。
日期
在新的API中,LocalDate是其中最重要的类之一。它是表示日期的不可变类型,不包含时间和时区。
LocalDate date = LocalDate.of(2016, Month.JANUARY, 24);
int year = date.getYear(); // 2016
Month month = date.getMonth(); // 1月
int dom = date.getDayOfMonth(); // 24
DayOfWeek dow = date.getDayOfWeek(); // 星期天,SUNDAY
int len = date.lengthOfMonth(); // 31 (1月份的天数)
boolean leap = date.isLeapYear(); // true (是闰年)
date = date.withYear(2015); // 2015-01-24
date = date.plusMonths(2); // 2015-03-24
date = date.minusDays(1); // 2015-03-23
// 注意,LocalDate是不可变类型,date.minusDays 返回的不是同一个实例
时间
本地时间,LocalTime。LocalTime是值类型,且跟日期和时区没有关联。当对时间进行加减操作时,以午夜基准,24小时一个周期。因此,20:00加上6小时,结果就是02:00。
LocalTime time = LocalTime.of(20, 30, 00); // 20:30:00
int hour = time.getHour(); // 20
int minute = time.getMinute(); // 30
time = time.withSecond(6); // 20:30:06
time = time.plusMinutes(3); // 20:33:06
时间和日期组合
LocalDateTime类只是LocalDate和LocalTime的简单组合。它表示一个跟时区无关的日期和时间。LocalDateTime可以直接创建,或者组合时间和日期。
LocalDateTime dt1 = LocalDateTime.of(2016, Month.JANUARY, 10, 20, 30);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(20, 30);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime的其他方法跟LocalDate和LocalTime相似。这种相似的方法模式非常有利于API的学习。下面总结了用到的方法前缀:
of: 静态工厂方法,从组成部分中创建实例
from: 静态工厂方法,尝试从相似对象中提取实例。from()方法没有of()方法类型安全
now: 静态工厂方法,用当前时间创建实例
parse: 静态工厂方法,总字符串解析得到对象实例
get: 获取时间日期对象的部分状态
is: 检查关于时间日期对象的描述是否正确
with: 返回一个部分状态改变了的时间日期对象拷贝
plus: 返回一个时间增加了的、时间日期对象拷贝
minus: 返回一个时间减少了的、时间日期对象拷贝
to: 把当前时间日期对象转换成另外一个,可能会损失部分状态
at: 用当前时间日期对象组合另外一个,创建一个更大或更复杂的时间日期对象
format: 提供格式化时间日期对象的能力
时间格式化
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.now();
String string = formatter.format(dateTime);
System.out.println(string); // 2019-01-23 16:20:43
LocalDateTime dateTime1 = LocalDateTime.parse("2016-01-24 15:03:59", formatter);
System.out.println(dateTime1);
时间戳
Instant instant = Instant.now();
System.out.println(System.currentTimeMillis());
System.out.println(instant.toEpochMilli());
System.out.println(instant.atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli());
System.out.println(instant.atZone(ZoneId.of("GMT+08:00")).toInstant().toEpochMilli());
System.out.println(instant.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
各种转换
public static LocalDateTime getDateTimeOfTimestamp(long millis) {
Instant instant = Instant.ofEpochMilli(millis);
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
}
public static long getMilliSeconds(LocalDateTime localDateTime) {
ZoneId zone = ZoneId.systemDefault(); // ZoneId.of("Asia/Shanghai")
Instant instant = localDateTime.atZone(zone).toInstant(); //localDateTime.toInstant(ZoneOffset.of("+8"));
return instant.toEpochMilli();
}
public static Date toDate(LocalDateTime time) {
ZonedDateTime zdt = time.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
public static LocalDateTime toLocalDateTime(Date date) {
Instant instant = date.toInstant();
return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
}