参考资料:好好学Java  https://mp.weixin.qq.com/s/Dd_7yUh3lq3TqE2cjsYXvw

JDK8新特性里提供了3个时间类:LocalDate、LocalTime、LocalDateTime

在项目开发中,已经需要对Date类型进行格式,否则可读性很差,格式化Date类型要使用SimpleDateFormat,但SimpleDateFormat是现成不安全的。

1. 为什么需要LocalDate、LocalTime、LocalDateTime

1.1 Date如果不格式化,打印出的日期可读性差

Tue Sep 10 09:34:04 CST 2019

1.2 使用SimpleDateFormat对时间进行格式化,但SimpleDateFormat是线程不安全的。SimpleDateFormat的format方法最终调用代码:

private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length; ) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
}
switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char) count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
}

  calendar是共享变量,并且这个共享变量没有做线程安全控制。当多个线程同时使用相同的SimpleDateFormat对象【如用static修饰的SimpleDateFormat】调用format方法时,多个线程会同时调用calendar.setTime方法,可能一个线程刚设置好time值 另外的一个线程马上把设置的time值给修改了导致返回的格式化时间可能是错误的。

  在多并发情况下使用SimpleDateFormat需格外注意 SimpleDateFormat除了format是线程不安全以外,parse方法也是线程不安全的。parse方法实际调用alb.establish(calendar).getTime()方法来解析,alb.establish(calendar)方法里主要完成了

  a、重置日期对象cal的属性值

  b、使用calb中中属性设置cal

  c、返回设置好的cal对象

  但是这三步不是原子操作。

  多线程并发如何保证线程安全 - 避免线程之间共享一个SimpleDateFormat对象,每个线程使用时都创建一次SimpleDateFormat对象 => 创建和销毁对象的开销大 - 对使用format和parse方法的地方进行加锁 => 线程阻塞性能差 - 使用ThreadLocal保证每个线程最多只创建一次SimpleDateFormat对象 => 较好的方法。Date对时间处理比较麻烦,比如想获取某年、某月、某星期,以及n天以后的时间,如果用Date来处理的话真是太难了,你可能会说Date类不是有getYear、getMonth这些方法吗,获取年月日很Easy,但都被弃用了。

  

2. Java8全新的日期和时间API

2.1 LocalDate

  LocalDate是日期处理类,具体API如下:

// 获取当前日期
LocalDate now = LocalDate.now();
// 设置日期
LocalDate localDate = LocalDate.of(2019, 9, 10);
// 获取年
int year = localDate.getYear(); //结果:2019
int year1 = localDate.get(ChronoField.YEAR); //结果:2019
// 获取月
Month month = localDate.getMonth(); // 结果:SEPTEMBER
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR); //结果:9
// 获取日
int day = localDate.getDayOfMonth(); //结果:10
int day1 = localDate.get(ChronoField.DAY_OF_MONTH); // 结果:10
// 获取星期
DayOfWeek dayOfWeek = localDate.getDayOfWeek(); //结果:TUESDAY
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK); //结果:2

2.2 LocalTime

  LocalTime是时间处理类,具体API如下:

// 获取当前时间
LocalTime now = LocalTime.now();
// 设置时间
LocalTime localTime = LocalTime.of(13, 51, 10);
//获取小时
int hour = localTime.getHour(); // 结果:13
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY); // 结果:13
//获取分
int minute = localTime.getMinute(); // 结果:51
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR); // 结果:51
//获取秒
int second = localTime.getSecond(); // 结果:10
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE); // 结果:10

2.3 LocalDateTime

  LocalDateTime可以设置年月日时分秒,相当于LocalDate + LocalTime

// 获取当前日期时间
LocalDateTime localDateTime = LocalDateTime.now();
// 设置日期
LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate);
// 获取LocalDate
LocalDate localDate2 = localDateTime.toLocalDate();
// 获取LocalTime
LocalTime localTime2 = localDateTime.toLocalTime();

2.4 Instant

// 创建Instant对象
Instant instant = Instant.now();
// 获取秒
long currentSecond = instant.getEpochSecond();
// 获取毫秒
long currentMilli = instant.toEpochMilli();

  如果只是为了获取秒数或者毫秒数,使用System.currentTimeMillis()来得更为方便

2.5 修改LocalDate、LocalTime、LocalDateTime、Instant

  LocalDate、LocalTime、LocalDateTime、Instant为不可变对象,修改这些对象对象会返回一个副本。增加、减少年数、月数、天数等 以LocalDateTime为例。

// 创建日期:2019-09-10 14:46:56
LocalDateTime localDateTime = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
//增加一年
localDateTime = localDateTime.plusYears(1); //结果: 2020-09-10 14:46:56
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS); //结果: 2021-09-10 14:46:56
//减少一个月
localDateTime = localDateTime.minusMonths(1); //结果: 2021-08-10 14:46:56
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS); //结果: 2021-07-10 14:46:56

  通过with修改某些值,年月日时分秒都可以通过with方法设置。

//修改年为2019
localDateTime = localDateTime.withYear(2020);
//修改为2022
localDateTime = localDateTime.with(ChronoField.YEAR, 2022);

  日期计算。比如有些时候想知道这个月的最后一天是几号、下个周末是几号,通过提供的时间和日期API可以很快得到答案 。TemporalAdjusters提供的各种日期时间格式化的静态类,比如firstDayOfYear是当前日期所属年的第一天

LocalDate localDate = LocalDate.now();
LocalDate localDate1 = localDate.with(TemporalAdjusters.firstDayOfYear());

  格式化时间。DateTimeFormatter默认提供了多种格式化方式,如果默认提供的不能满足要求,可以通过DateTimeFormatter的ofPattern方法创建自定义格式化方式

LocalDate localDate = LocalDate.of(2019, 9, 10);
String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
//自定义格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s3 = localDate.format(dateTimeFormatter);

  解析时间。和SimpleDateFormat相比,DateTimeFormatter是线程安全的

LocalDate localDate1 = LocalDate.parse("20190910", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate localDate2 = LocalDate.parse("2019-09-10", DateTimeFormatter.ISO_LOCAL_DATE);

  Date与LocalDateTime转换。

/**
* LocalDateTime转毫秒时间戳
* @param localDateTime LocalDateTime
* @return 时间戳
*/
public static Long localDateTimeToTimestamp(LocalDateTime localDateTime) {
try {
ZoneId zoneId = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zoneId).toInstant();
return instant.toEpochMilli();
} catch (Exception e) {
e.printStackTrace();
}
return null;
} /**
* 时间戳转LocalDateTime
* @param timestamp 时间戳
* @return LocalDateTime
*/
public static LocalDateTime timestampToLocalDateTime(long timestamp) {
try {
Instant instant = Instant.ofEpochMilli(timestamp);
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
} catch (Exception e) {
e.printStackTrace();
}
return null;
} /**
* Date转LocalDateTime
* @param date Date
* @return LocalDateTime
*/
public static LocalDateTime dateToLocalDateTime(Date date) {
try {
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
return instant.atZone(zoneId).toLocalDateTime();
} catch (Exception e) {
e.printStackTrace();
}
return null;
} /**
* LocalDateTime转Date
* @param localDateTime LocalDateTime
* @return Date
*/
public static Date localDateTimeToDate(LocalDateTime localDateTime) {
try {
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = localDateTime.atZone(zoneId);
return Date.from(zdt.toInstant());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

  SpringBoot中应用LocalDateTime

  将LocalDateTime字段以时间戳的方式返回给前端 添加日期转化类

    public class LocalDateTimeConverter extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeNumber(value.toInstant(ZoneOffset.of("+8")).toEpochMilli());
}
}

  并在LocalDateTime字段上添加@JsonSerialize(using = LocalDateTimeConverter.class)注解,如下:

@JsonSerialize(using = LocalDateTimeConverter.class)
protected LocalDateTime gmtModified;

  将LocalDateTime字段以指定格式化日期的方式返回给前端 在LocalDateTime字段上添加@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")注解即可,如下:

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
protected LocalDateTime gmtModified;

  对前端传入的日期进行格式化 在LocalDateTime字段上添加@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")注解即可,如下:

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
protected LocalDateTime gmtModified;

  总结:

  LocalDateTime:Date有的我都有,Date没有的我也有,

 

JDK8的LocalDateTime用法的更多相关文章

  1. LocalDateTime用法(jdk1.8 )

    前言 最近看别人项目源码,发现Java8新的日期时间API很方便强大,所以转载该入门介绍博客,记录一下. 使用新时间日期API的必要性 在java8以前,或许: 当你在做有关时间日期的操作时,你会想到 ...

  2. jdk8中LocalDateTime,LocalDate,LocalTime等日期时间类

    package com.zy.time; import org.junit.Test; import java.time.*; import java.time.format.DateTimeForm ...

  3. java8 :: 用法 (JDK8 双冒号用法)

    https://www.cnblogs.com/tietazhan/p/7486937.html

  4. JDK8的Optional用法

    参考资料:https://www.baeldung.com/java-optional https://mp.weixin.qq.com/s/P2kb4fswb4MHfb0Vut_kZg 1. 描述 ...

  5. jdk8新特性-stream

    一.什么是流stream 1.可理解为高级版本的 Iterator 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的. 2.单向,不可往复 数据只能遍历一次,遍历过一次后即用尽了,就好比 ...

  6. Java8新特性(三)——Optional类、接口方法与新时间日期API

    一.Optional容器类 这是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. 查看结构图可以看到有如下常用方法: of(T)—— ...

  7. java Date equals 的坑

    今天在JDK6上做开发,遇到一个很诡异的问题. Domain中一个实体是Date,称为变量 a, 使用Calendar构造出来的Date,称为变量b, 虽然都是同一天,比如 2016-11-11 00 ...

  8. MySQL的SQL语句——常见报错

    Eclipse连接MySQL数据库 — 8.0版jdbc驱动 键知识 https://blog.csdn.net/jenminzhang/article/details/9816853 [必应翻译] ...

  9. 「日常开发」记一次因使用Date引起的线上BUG处理

    生活中,我们需要掌控自己的时间,减少加班,提高效率:日常开发中,我们需要操作时间API,保证效率.安全.稳定.现在都2020年了,了解如何在JDK8及以后的版本中更好地操控时间就很有必要,尤其是一次线 ...

随机推荐

  1. uniapp,微信小程序中使用 MQTT

    最近在uniapp打包成微信小程序的项目中第一次用到了MQTT.使用比较简单,但是还是遇到了一些问题.在此记录一下. 官方文档:MQTT Github 官方MQTT测试工具:MQTTX.测试工具使用说 ...

  2. java的自定义配置文件统一读取配置类示例

    前言:在我们的日常编程中难免会有些我们自定义的配置,虽然Java中提供了很多的读取配置文件的方法,但是当我们需要修改配置文件的key的时候,就会发现太过散乱了,工作量也会很大,涉及的文件还很多,一不小 ...

  3. How to install chinese input method

    在Ubuntu中安装中文输入法确实比较麻烦,特别是英文版的Ubuntu系统   Ubuntu上的输入法主要有小小输入平台(支持拼音/二笔/五笔等),Fcitx,Ibus,Scim等.其中Scim和Ib ...

  4. 《python编程从入门到实践》2.3字符串

    书籍<python编程从入门到实践> 2.3字符串 知识模块 print()函数,函数名称突出为蓝色,输出括号内的变量或者字符创. 变量名的命名:尽量小写字母加下划线并且具有良好的描述性, ...

  5. 获取字符串指定字符的第n次出现位置

    create function uf_findx (@text nvarchar(max),@find_x varchar(200),@find_n int)returns intasbegin -- ...

  6. 2020年20个Flutter最漂亮的UI库和项目

    Best-Flutter-UI-Templates 地址:https://github.com/mitesh77/Best-Flutter-UI-Templates The History of Ev ...

  7. swagger -- 前后端分离的API接口

    文章目录 一.背景 二.swagger介绍 三.在maven+springboot项目中使用swagger 四.swagger在项目中的好处 五.美化界面 参考链接:5分钟学会swagger配置 参考 ...

  8. Linux内存大页设置

    实际环境中,遇到3次由于内存大页设置参数不合理或者错误,导致系统内存不足,或者数据库内存不足的问题. 按照如下方式,推荐设置大页参考下发设置! 参考HugePages on Oracle Linux ...

  9. 题解 洛谷 P5443 【[APIO2019]桥梁】

    考虑若只有查询操作,那么就可以构造\(Kruskal\)重构树,然后在线询问了,也可以更简单的把询问离线,把询问和边都按权值从大到小排序,然后双指针依次加入对于当前询问合法的边,用并查集维护每个点的答 ...

  10. .net core 自带分布式事务的微服务开源框架JMS

    事务的统一性是微服务的一个重点问题,简洁有效的控制事务,更是程序员所需要的.JMS的诞生,就是为了更简单.更有效的控制事务. 先看一段调用微服务的代码: using (var ms = new JMS ...