开发中时间变换问题汇总

各个时间类型概念及转换

1. 数据库中的时间类型

1.1 DATE类型

DATE只表示日期,检索以YYYY-MM-DD的格式显示,范围是1000-01-01到9999-12-31。因为没有时分秒,所以会损失时间精度。

1.2 DATETIME类型

DATETIME表示了日期和时间,检索以YYYY-MM-DD HH:MM:SS格式显示。

时间范围是 “1000-00-00 00:00:00” 到 “9999-12-31 23:59:59”。

1.3 TIME类型

TIME只表示时间,检索以HH:MM:SS格式显示,范围是00:00:00到23:59:59

1.4 TIMESTAMP类型

TIMESTAMP似于linux系统 中的unix timestamp,是一个记录了从格林威治时间 1970 年 01 月 01 日 00 时 00 分 00 秒(北京时间 1970 年 01 月 01 日 08 时 00 分 00 秒)起至现在的总秒数

此外还可以精确到毫秒微秒,timestamp(3)指毫秒,timestamp(6)微秒。

支持的时间范围是 “1970-01-01 00:00:01” 到 “2038-01-19 03:14:07”。

TIMESTAMPDATETIME表示格式一样两者的不同点如下

  • 当使用TIMESTAMP的时候,数据有更新的时候这个字段自动更新为当前时间,所以可以作为lastmodify使用,这个变化是默认设置,如果想使时间不更新可以设置DEFAULT CURRENT_TIMESTAMP
  • TIMESTAMP的范围不能早于1970或者晚于2037,超过这个时间范围的话为0
  • TIMESTAMP存储的时候是转为UTC存储的,获取的时候根据客户端所在时区进行展示
  • timestamp占4个字节datetime占8个字节

2. Java中的时间类型

2.1 yyyy-MM-dd HH:mm:ss代表的意义

字符 意义 时间
yyyy 2018
MM`` 01
dd 16
hh 12小时制 03
HH 24小时制 15
mm 59
ss 59
SSS 毫米 333

2.2 java.util.Date类

Javajava.util.Date类是Java最初的时间类之一。今天该类的大部分方法已不推荐使用,取而代之的是java.util.Calendar类。

@Test
public void testDateTime(){
    Date date = new Date();
    date;            //  默认使用当前的系统时间:Tue Oct 06 18:56:37 CST 2020
    date.getTime();  //  获取当前对象距离1970年1月1日0时0分0秒的毫秒数:1601981797012
    SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");  // 用来格式化时间
    SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    dateFormat1.format(date); // 返回的是字符串格式:2020-10-06
    dateFormat2.format(date); // 返回的是字符串格式:2020-10-06 18:56:37
}

2.2 java.sql中的时间类型

java.sql包中有三种时间类型:

Date:表示日期。如:2018-08-26,只有年月日。

Time:表示时间。如:11:52:08,只有时分秒。

Timestamp:表示时间戳。如:2018-08-26 11:52:08.896,有年月日时分秒,以及毫秒。

这三种时间类型都是继承于java.util.Date类,所以sql中的时间类型是对父类的引用,不需要进行转换

public class Date extends java.util.Date{}
public class Time extends java.util.Date {}
public class Timestamp extends java.util.Date {}

java.sql包下的Date、Time、TimeStamp三个类的构造器都需要一个long类型的参数,表示毫秒值。

java.util.Date d = new java.util.Date();
//时间的long型数据
long longtime = d.getTime();

//Date日期: 2020-10-06
java.sql.Date date = new java.sql.Date(longtime);

//Time时间: 19:06:45
java.sql.Time time = new java.sql.Time(longtime);

//Timestamp时间戳,有年月日时分秒,以及毫秒 : 2020-10-06 19:06:45.603
java.sql.Timestamp timestamp = new java.sql.Timestamp(longtime);

2.3 java.time

Java的 Date,Calendar类型与前端和数据库交互起来较为麻烦,而且Date类线程不安全等诸多弊端。

在Java8时推出了线程安全、简易、高可靠的time包。并且数据库中也支持LocalDateTime类型,在数据存储时候使时间变得简单。

time包中有三个重要的时间类型:年月日时分秒毫秒类型LocalDateTime、年月日类型LocalDate、时分秒毫秒类型LocalTime和数据库中的时间类型一致。

简单使用

  1. 获取当前时间
LocalDate.now();    	// 2020-10-06
LocalTime.now();    	// 19:13:49.696 时分秒毫秒
LocalDateTime.now();    // 2020-10-06T19:13:49.696
  1. 时间格式化:使用DateTimeFormatter是线程安全的,而SimpleDateFormat是线程不安全的。
// 2020-10-06T19:21:39.254
LocalDateTime now = LocalDateTime.now();

// 年月日: 2020-10-06
String format = now.format(DateTimeFormatter.ISO_LOCAL_DATE);   

// 时分秒毫秒 : 2020-10-06
String format1 = now.format(DateTimeFormatter.ISO_TIME);        

// 2020-10-06 19:21:39
String format2 = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));   

2.4 Date与LocalDateTime转换

参考:https://www.cnblogs.com/huanshilang/p/12013386.html

/**
  * 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;
}

2.5 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 createdTime;

方式二:

LocalDateTime字段指定格式化日期的方式返回给前端 在LocalDateTime字段上添加

@JsonFormat(shape = JsonFormat.Shape.STRING, 
            pattern = "yyyy-MM-dd HH:mm:ss", 
            timezone = "GMT+8")
protected LocalDateTime createdTime;

前端传入的日期进行格式化LocalDateTime字段上添加

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

2.6 MybatisPlus操作时间数据类型

假如数据库中的时间类型为datetime,那么使用高版本MybatisPlus生成器生成的实体类的时间类型为LocalDateTime

例如:

// 数据库中的时间类型
`created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '文件创建时间',

// java中的时间类型
private LocalDateTime createdTime;

BUG:当使用MP操作数据库进行数据查询时,如果druid数据库连接池的版本低于1.1.18将会因为时间格式不匹配而报错

Error attempting to get column 'create_time' from result set. 
Cause: java.sql.SQLFeatureNotSupported

解决:druid数据库连接池升级到1.1.18版本以上即可正常操作,不需要转换实体类中的时间类型为Date

原文地址:https://www.cnblogs.com/code-duck/p/13775491.html