Java对日期、日历及时间的处理一直以来都饱受诟病,比如java.util.Date和java.util.Calendar类易用性差,不支持时区,非线程安全;还有用于格式化日期的类DateFormat也是非线程安全的等问题。
Java8引入的新的一系列API,对时间日期的处理提供了更好的支持,清楚的定义了时间日期的一些概念,比如说,瞬时时间(Instant),持续时间(duration),日期(date),时间(time),时区(time-zone)以及时间段(Period)。
Java8常用的日期和时间类包含LocalDate、LocalTime、Instant、Duration、Period、LocalDateTime以及ZonedDateTime等。
LocalDate:不包含时间的日期,比如2019-10-14。可以用来存储生日,周年纪念日,入职日期等。
LocalTime:与LocalDate想对照,它是不包含日期的时间。
LocalDateTime:包含了日期及时间,没有偏移信息(时区)。
ZonedDateTime:包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的。
Instant:时间戳,与System.currentTimeMillis()类似。
Duration:表示一个时间段。
Period:用来表示以年月日来衡量一个时间段。
另外,还有新的日期解析格式化类DateTimeFormatter
。
Java8中关于时间日期的API有以下关键点:
LocalDate类内只包含日期,不包含具体时间
。只需要表示日期而不包含时间,就可以使用它。
// 只获取日期
LocalDate today = LocalDate.now();
System.out.println(today);
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
System.out.printf("Year : %d Month : %d day : %d \t %n", year, month, day);
同时,还可以通过LocalDate获取日期是月份的第几天、周的第几天,月份的天数,是否为闰年等。看下面的代码是不是非常方便。
LocalDate today = LocalDate.now();
// 月份中的第几天
int dayOfMonth = today.getDayOfMonth();
// 一周的第几天
DayOfWeek dayOfWeek = today.getDayOfWeek();
// 月份的天数
int length = today.lengthOfMonth();
// 是否为闰年
boolean leapYear = today.isLeapYear();
上面通过now获取LocalDate对象,也可以通过静态方法of()或parse创建任意一个日期。再也不用像之前一样年只能从1900年开始,月必须从0开始等。
LocalDate oneDay = LocalDate.of(2019,10,1);
System.out.println(oneDay);
LocalDate重写了equals方法,让日期的比较也变得简单了。
// 定义任意日期
LocalDate oneDay = LocalDate.of(2019, 10, 1);
System.out.println(oneDay);
// 定义任意比较
LocalDate anyDay = LocalDate.of(2019, 10, 1);
System.out.println(oneDay.equals(anyDay));
同时,针对日期还可延伸出MonthDay或YearMonth类,顾名思义,只包含月天或年月。同样适用于equals方法来比较。
boolean notBefore = LocalDate.parse("2019-10-01").isBefore(LocalDate.parse("2019-10-02"));
boolean isAfter = LocalDate.parse("2019-10-01").isAfter(LocalDate.parse("2019-10-02"));
对日期进行前一天后一天或前一个月的加减也变得十分方便。
LocalDate tomorrowDay = LocalDate.now().plusDays(1);
LocalDate nextMonth = LocalDate.now().plusMonths(1);
between内的参数类型可以是:LocalDateTime、LocalDate、LocalTime。
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration = Duration.between(from, to);
// 区间统计换算
// 总天数
long days = duration.toDays();
// 小时数
long hours = duration.toHours();
// 分钟数
long minutes = duration.toMinutes();
// 秒数
long seconds = duration.getSeconds();
// 毫秒数
long milliSeconds = duration.toMillis();
// 纳秒数
long nanoSeconds = duration.toNanos();
LocalDate parseDay = LocalDate.parse("2019-10-01");
System.out.println(parseDay);
LocalTime和LocalDate类似,区别在于LocalDate不包含具体时间,而LocalTime包含具体时间。
HH:mm:ss.nnn
,这里nnn是纳秒*同样可以使用now或of方法来获得对象。
//获取当天时间 HH:mm:ss.nnn
LocalTime localTime = LocalTime.now();
// of方式创建时间对象
LocalTime oneTime = LocalTime.of(10,10,10);
// ofSecondOfDay方式创建时间对象
LocalTime localTime = LocalTime.ofSecondOfDay(seconds);
LocalDate类似它也拥有parse(字符串转时间对象)、isBefore(比较两个时间大小)、获取时间单元(时分秒)等方法,就不再赘述。
还有一个在实战中查询日期区间时我们经常定义的“23:59:59.99”常量再也不用自己定义了。
// 23:59:59.999999999
LocalTime maxTime = LocalTime.MAX;
// 00:00
LocalTime minTime = LocalTime.MIN;
between内的参数类型可以是:LocalDateTime、LocalDate、LocalTime。
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration = Duration.between(from, to);
// 区间统计换算
// 总天数
long days = duration.toDays();
// 小时数
long hours = duration.toHours();
// 分钟数
long minutes = duration.toMinutes();
// 秒数
long seconds = duration.getSeconds();
// 毫秒数
long milliSeconds = duration.toMillis();
// 纳秒数
long nanoSeconds = duration.toNanos();
LocalDateTime表示日期和时间组合。
可以通过of()方法直接创建,也可以调用LocalDate的atTime()方法或LocalTime的atDate()方法将LocalDate或LocalTime合并成一个LocalDateTime。
LocalDateTime now = LocalDateTime.now();
LocalDateTime oneTime = LocalDateTime.of(2019,10,14,10,12,12);
// 拼接日期
LocalTime.now().atDate(LocalDate.now());
LocalDateTime与LocalDate和LocalTime之间可以相互转化。其他日期增减等操作与上面的类似。
```java
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration = Duration.between(from, to);
// 区间统计换算
// 总天数
long days = duration.toDays();
// 小时数
long hours = duration.toHours();
// 分钟数
long minutes = duration.toMinutes();
// 秒数
long seconds = duration.getSeconds();
// 毫秒数
long milliSeconds = duration.toMillis();
// 纳秒数
long nanoSeconds = duration.toNanos();
//获取一天的开始时间,2017,7,22 00:00
public static LocalDateTime getDayStart(LocalDateTime time) {
return time.withHour(0)
.withMinute(0)
.withSecond(0)
.withNano(0);
}
//获取一天的结束时间,2017,7,22 23:59:59.999999999
public static LocalDateTime getDayEnd(LocalDateTime time) {
return time.withHour(23)
.withMinute(59)
.withSecond(59)
.withNano(999999999);
}
Instant用于一个时间戳,与System.currentTimeMillis()类似,但Instant可以精确到纳秒(Nano-Second)。
Instant除了可以使用now()方法创建,还可以通过ofEpochSecond方法创建。
Instant now = Instant.now();
// 其中ofEpochSecond第一个参数表示秒,第二个参数表示纳秒。整体表示:从1970-01-01 00:00:00开始后的365天100纳秒的时间点
long tiemstamp = Instant.ofEpochSecond(365 * 24 * 60, 100);
时间段
Duration的内部实现与Instant类似,但Duration表示时间段。
通过between方法创建:
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration = Duration.between(from, to);
Duration对象还可以通过of()方法创建,该方法参数为时间段长度和时间单位:
// 7天
Duration duration1 = Duration.of(7, ChronoUnit.DAYS);
// 60秒
Duration duration2 = Duration.of(60, ChronoUnit.SECONDS);
between内的参数类型可以是:LocalDateTime、LocalDate、LocalTime。
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration = Duration.between(from, to);
// 区间统计换算
// 总天数
long days = duration.toDays();
// 小时数
long hours = duration.toHours();
// 分钟数
long minutes = duration.toMinutes();
// 秒数
long seconds = duration.getSeconds();
// 毫秒数
long milliSeconds = duration.toMillis();
// 纳秒数
long nanoSeconds = duration.toNanos();
日期段
Period与Duration类似,获取一个时间段,只不过单位为年月日
。
也可以通过of方法和between方法创建,between方法接收的参数为LocalDate。
Period period = Period.of(1, 10, 25);
Period period1 = Period.between(LocalDate.now(), LocalDate.now().plusYears(1));
ZonedDateTime类,用于处理带时区的日期和时间。ZoneId表示不同的时区。大约有40不同的时区。
//获取所有时区集合:
Set allZoneIds = ZoneId.getAvailableZoneIds();
//创建时区:
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
//把LocalDateTime转换成特定的时区:
ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), zoneId);
另外和时区一起使用的类是OffsetDateTime类,OffsetDateTime是不变的,表示date-time偏移,存储所有日期和时间字段,精确至纳秒,从UTC/Greenwich计算偏移。
Java8对日期的格式化操作非常简单,首先看到上面的类大多都提供了parse方法,可以直接通过解析字符串得到对应的对象。
LocalDateTime dateTime = LocalDateTime.now();
String str = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(str);
str = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println(str);
public class DateTimePatternConstant {
public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_PATTERN = "yyyy-MM-dd";
public static final String TIME_PATTERN = "HH:mm:ss";
}
public class DateTimeAndStringConvertTest {
public static void main(String[] args) {
String startDateStr = "2021-08-15 09:09:09";
String endDateStr = "2021-08-15 10:10:10";
String date = "2021-08-15";
String time = "01:01:01";
String dateTimePattern = DateTimePatternConstant.DATE_TIME_PATTERN;
String datePattern = DateTimePatternConstant.DATE_PATTERN;
String timePattern = DateTimePatternConstant.TIME_PATTERN;
// 日期时间与字符串相互转换示例
// dateTime_str_convert(startDateStr, dateTimePattern);
// 日期与字符串相互转换示例
date_string_convert(date, datePattern);
// 时间与字符串相互转换示例同理
}
private static void date_string_convert(String date, String datePattern) {
// 字符串-->日期对象
LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern(datePattern));
// 日期对象-->指定格式的字符串
String format = localDate.format(DateTimeFormatter.ofPattern(datePattern));
// 2021-08-15
System.out.println(format);
}
private static void dateTime_str_convert(String startDateStr, String pattern) {
// Exception in thread "main" java.time.format.DateTimeParseException: Text '2021-08-15' could not be parsed at index 10
//LocalDateTime localDate = LocalDateTime.parse(date);
// 字符串-->日期时间对象
LocalDateTime localDateTime = LocalDateTime.parse(startDateStr, DateTimeFormatter.ofPattern(pattern));
// 日期时间对象-->指定格式的字符串
String format = localDateTime.format(DateTimeFormatter.ofPattern(pattern)));
// 2021-08-15 09:09:09
System.out.println(format);
}
}
LocalDateTime 转 Date(亲测有效):
LocalDateTime localDateTime = LocalDateTime.now();
Date date = Date.from(localDateTime.atZone( ZoneId.systemDefault()).toInstant());
Date 转 LocalDateTime:
Date startDate=new Date();
LocalDateTime localDateTime = startDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
13位的时间戳。
将timestamp转为LocalDateTime:
public LocalDateTime timestamToDatetime(long timestamp){
Instant instant = Instant.ofEpochMilli(timestamp);
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
将LocalDataTime转为timestamp:
public long datatimeToTimestamp(LocalDateTime ldt){
long timestamp = ldt.toInstant(ZoneOffset.of("+8")).toEpochMilli();
return timestamp;
}
我在网上还找到了另一个将datetime转为时间戳的方法:
ZoneId zone = ZoneId.systemDefault();
long timestamp = ldt.atZone(zone).toInstant().toEpochMilli();
Java8的时间转为时间戳的大概的思路就是LocalDateTime先转为Instant,设置时区,然后转timestamp。
略。
参考文章: