- java.util.Date日期格式为:年月日时分秒
- java.sql.Date日期格式为:年月日[只存储日期数据不存储时间数据]
- java.sql.Time日期格式为:时分秒
- java.sql.Timestamp日期格式为:年月日时分秒纳秒(毫微秒)
java.util.Date这个类是java.sql.Date, java.sql.Time, java.slq.Timestamp这三个类的父类
。这三个类对java.util.Date类进行了包装
。
java.sql.Date类屏蔽了java.util.Date类的时间有关的方法(形如:hh:mm:ss),因此,
不可以通过这个类访问时间有关的信息
,比如,如果你通过sqlDate.getHour()方法去访问小时信息,此方法会抛出一个IllegalArgumentException异常。这是因为java.sql.Date在继承java.util.Date类的时候对父类进行了重写,禁用了时间访问的方法
。之所以这么处理,是为了和数据库的Date数据类型相匹配,数据库的Date数据类行只是保存日期有关的字段
。Java.sql.Time类屏蔽了java.util.Date的日期有关的字段(形如:yyyy-MM-dd),因此,
不能通过这个类访问日期有关的信息
,比如:如果你通过sqlTime.getYear()方法去获取年有关的信息,此方法会抛出一个IllegalArgumentException异常。这是因为java.sql.Time在继承java.util.Date类的时候对父类进行了重写,禁用了日期访问的方法。之所以这么处理,是为了和数据库的Time数据类型相匹配,数据库的Time数据类行只是保存时间有关的字段。
Java.sql.Timestamp字段则
对java.util.Date这个类进行了扩充
,它在java.util.Date类的基础上增加了毫秒的时间访问控制,因此,你可以通过getNanos()方法去获取时间的毫微秒数(注意此处获取的时间是以毫微秒为单位的,1秒等于十亿毫微秒),同样的,这也是为了和数据库中的Timestamp数据类型进行匹配。
java.util.Date和java.util.Calendar类有什么关系呢?
- Java.util.Calendar类是java.util.Date类的一个更加深入,更加全面的替代。
Java.util.Calendar类支持java.util.Date的所有功能,此外,Calendar还引入了多语言,多区域的特性,可以根据需要获取不同区域,不同时区的时间,Calendar还增加了比Date更加方便和快捷的许多操作,如获取一年当中的第几个星期,各个月的天数等便捷的方法。
Java.util.Calendar区别与java.util.Date的几个地方也需要注意一下:
首先,Calendar增加了毫秒的时间段,通过它可以获取时间点的毫秒值,而java.util.Date只是精确到秒
。其次,Calendar过去年的时候是当前年份比如:2010,而Date获取年份的时获取到的是当前年份-1900的一个值(2010-1900=110,因此,你调用getYear后过去的值就是110)。最后Calendar是一个抽象类,之所以能够实例化,是因为此处的Calendar充当了一个类似于工厂的作用,在getInstance方法中实例化了Calendar子类GregorianCalendar,并把它返回给用户使用。
- Oracle的Date类型,只需要年月日,选择使用java.sql.Date类型 ·MS
- Sqlserver数据库的DateTime类型,需要年月日时分秒,选择java.sql.Timestamp类型
@DateTimeFormat("yyyy-MM-dd")@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")private Date clDate;
@DateTimeFormat("yyyy-MM-dd")@JSONField(format = "yyyy-MM-dd" )private Date clDate;
注意,基于fastJson的方式一定通过实现WebMvcConfigurer接口的消息转换器配置方法configureMessageConverters来将Fastjson的Http消息转换器加入SpringBoot的Http消息转换器列表,同时还要将fastjson的转换器置于默认的消息转换器,不然还是会被jackson先解析了
,参考文章
我使用easyexcel导出数据的时候,报以下错误:
Can not find ‘Converter’ support class Date.
这是easyexcel没有java.sql.Date的转换器,需要自定义,如下:
public class DateConverter implements Converter<Date> {@Overridepublic Class supportJavaTypeKey() {return Date.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic Date convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {//转换成java.sql.DateLocalDate parse = LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));return Date.valueOf(parse);}@Overridepublic CellData convertToExcelData(Date date, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {return new CellData<>(date.toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));}
}
这里的参考文章
Java8里面新出来了一些API,LocalDate(对应date)、LocalTime(对应time)、LocalDateTime(对应datetime)
非常好用。如果想要在JDBC中,使用Java8的日期LocalDate、LocalDateTime,则必须要求数据库驱动的版本不能低于4.2
Tue Sep 10 09:34:04 CST 2019
使用SimpleDateFormat对时间进行格式化,但SimpleDateFormat是线程不安全的
SimpleDateFormat的format方法最终调用代码:
private StringBuffer format(Date date, StringBuffer toAppendTo,FieldDelegate delegate) {// Convert input date to time field listcalendar.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)方法里主要完成了
- 重置日期对象cal的属性值
- 使用calb中中属性设置cal
- 返回设置好的cal对象,但是这三步不是原子操作
- 避免线程之间共享一个SimpleDateFormat对象,每个线程使用时都创建一次SimpleDateFormat对象 =>创建和销毁对象的开销大
- 对使用format和parse方法的地方进行加锁 => 线程阻塞性能差
- 使用ThreadLocal保证每个线程最多只创建一次SimpleDateFormat对象 => 较好的方法
Date对时间处理比较麻烦,比如想获取某年、某月、某星期,以及n天以后的时间,如果用Date来处理的话真是太难了,你可能会说Date类不是有getYear、getMonth这些方法吗,获取年月日很Easy,但都被弃用。
//获取当前年月日
LocalDate localDate = LocalDate.now();
System.out.println("当前的年月日:"+localDate);
//构造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 9, 10);
System.out.println("指定的年月日:"+localDate1);
LocalDate localDate = LocalDate.now();
int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
System.out.println("当前年:"+year);
System.out.println("当前年:"+year1);
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
System.out.println("当前月:"+month);
System.out.println("当前月:"+month1);
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
System.out.println("当前天:"+day);
System.out.println("当前天:"+day1);
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);
System.out.println("当前星期:"+dayOfWeek);
System.out.println("当前星期:"+dayOfWeek1);
LocalTime localTime = LocalTime.of(13, 51, 10);LocalTime localTime1 = LocalTime.now();System.out.println("当前时间:"+localTime);System.out.println("当前时间:"+localTime1);//获取时分秒//获取小时int hour = localTime.getHour();int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);System.out.println("当前小时:"+hour);System.out.println("当前小时:"+hour1);//获取分int minute = localTime.getMinute();int minute1 =localTime.get(ChronoField.MINUTE_OF_HOUR);System.out.println("当前分钟:"+minute);System.out.println("当前分钟:"+minute1);//获取秒int second = localTime.getMinute();int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);System.out.println("当前秒:"+second);System.out.println("当前秒:"+second1);
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);
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
和SimpleDateFormat相比,DateTimeFormatter是线程安全的
。要注意的是,LocalDateTime无法与时间戳进行转换,因为LocalDateTime没有时区,无法确定某一时刻
。而ZonedDateTime相当于LocalDateTime加时区的组合,它具有时区,可以与long表示的时间戳进行转换。可以自行了解。
遇到一个LocalDateTime的序列化与反序列化的问题,正常情况下只需要在返回前端的实体类加上以下标签即可以Json格式返回正常的时间类型:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
如果不使用该标签,那么返回前端的时间就会显示的不正常,将会以实体类的形式来展示时间。
如果只使用以下格式:
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createTime;
则返回的数据类似于:[2020,5,16,0,0]。
所以一定要两个标签都加上,@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)是反序列特定的日期格式。那么如果将时间进行反序列化之后还要数据显示正常,则需要再加上一个标签。
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
这里参考