Date
相关信息
Java 中所有的时间类型与操作方式。
Date
java.util.Date
是最常用的时间类型,精度(年-月-日-时-分-秒)。
Date date = new Date();
无参构建函数返回一个时间类型对象,值为当前时间。
不格式化时可读性较差:Tue Sep 10 09:34:04 CST 2019
.
System.currentTimeMillis()
System.currentTimeMillis()
获取系统当前秒数或者毫秒数,返回一个 long 类型。
Calendar
日历类,可以用来对时间进行计算操作。
// 获取一个实例对象
Calendar calendar = Calendar.getInstance();
// 为对象中的时间赋值
calendar.setTime(new Date());
// 修改时间值,正数往后推,负数往前推
calendar.add(calendar.DATE, 2);
calendar.add(calendar.DATE, -1);
// 获取时间值
Date date = calendar.getTime();
System.out.println(date);
LocalDate
LocalDate 是 JDK8 提供的日期类型,精度(年-月-日),提供了较多的 API.
不可变对象,修改这些对象会返回一个副本。
Api | 描述 |
---|---|
now() | 获取当前年月日 |
of() | 构造指定的年月日 |
getYear() | 获取年 |
getMonth() | 获取月 |
getDayOfMonth() | 获取日 |
getDayOfWeek() | 获取星期 |
format() | 时间解析为字符串 |
parse() | 字符串解析为时间 |
// 获取当前日期
LocalDate localDate = LocalDate.now();
// 构造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 9, 10);
// 获取年份
int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
// 获取月份
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
// 获取日
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
// 获取周
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);
// 格式化
String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
// 解析字符串
LocalDate localDate1 = LocalDate.parse("20190910", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate localDate2 = LocalDate.parse("2019-09-10", DateTimeFormatter.ISO_LOCAL_DATE);
// 特殊时间
// firstDayOfYear() 获取今年的第一天
LocalDate localDate1 = localDate.with(firstDayOfYear());
LocalTime
LocalTime 是 JDK8 提供的时间类型,时间类型,精度(时-分-秒),提供了较多的 API.
不可变对象,修改这些对象会返回一个副本。
// 构建对象
LocalTime localTime = LocalTime.of(13, 51, 10);
LocalTime localTime1 = LocalTime.now();
// 获取时
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
// 获取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
// 获取秒
int second = localTime.getSecond();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
LocalDateTime
LocalDateTime 是 JDK8 提供的日期时间类型,精度(年-月-日-时-分-秒),提供了较多的 API.
不可变对象,修改这些对象会返回一个副本。
// 构建对象
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();
// 增加一年
localDateTime = localDateTime.plusYears(1);
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);
// 减少一个月
localDateTime = localDateTime.minusMonths(1);
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);
// 通过with修改某些值
// 修改年为2020
localDateTime = localDateTime.withYear(2020);
// 修改为2022
localDateTime = localDateTime.with(ChronoField.YEAR, 2022);
时间戳
- 添加转化类。
- 在字段上添加注解
@JsonSerialize(using = 转化类.class)
.
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());
}
}
@JsonSerialize(using = LocalDateTimeConverter.class)
protected LocalDateTime gmtModified;
出参格式化
在 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;
Instant
秒数对象。
不可变对象,修改这些对象会返回一个副本。
// 创建Instant对象
Instant instant = Instant.now();
// 获取秒数
long currentSecond = instant.getEpochSecond();
// 获取毫秒数
long currentMilli = instant.toEpochMilli();
日期比较
Date.compareTo()
:经典比较方法,如果两个日期相等,返回 0,如果调用方在参数之后,返回值大于 0,如果调用方在参数之前,则返回值小于 0.
Date.before()
:语义友好的比较方法,调用方在参数之前,返回 true.
Date.after()
:语义友好的比较方法,调用方在参数之后,返回 true.
Date.equals()
:语义友好的比较方法,调用方和参数相等,返回 true.
SimpleDateFormat
java.text.SimpleDateFormat
是线程不安全的时间格式化工具类。
其在构造方法中传入解析方式 pattern
.
解析方式 | |
---|---|
Date and Time Pattern | Result |
"yyyy.MM.dd G 'at' HH:mm:ss z" | 2001.07.04 AD at 12:08:56 PDT |
"EEE, MMM d, ''yy" | Wed, Jul 4, '01 |
"h:mm a" | 12:08 PM |
"hh 'o''clock' a, zzzz" | 12 o'clock PM, Pacific Daylight Time |
"K:mm a, z" | 0:08 PM, PDT |
"yyyyy.MMMMM.dd GGG hh:mm aaa" | 02001.July.04 AD 12:08 PM |
"EEE, d MMM yyyy HH:mm:ss Z" | Wed, 4 Jul 2001 12:08:56 -0700 |
"yyMMddHHmmssZ" | 010704120856-0700 |
"yyyy-MM-dd'T'HH:mm:ss.SSSZ" | 2001-07-04T12:08:56.235-0700 |
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX" | 2001-07-04T12:08:56.235-07:00 |
"YYYY-'W'ww-u" | 2001-W27-3 |
"yyyy-MM-dd HH:mm:ss" | 2022-12-01 15:04:59 |
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 将时间类型解析为字符串
String string = sdf.format(new Date());
// 将字符串解析为时间
Date date = sdf.parse("2021-02-05 08:00:00");
为什么线程不安全
SimpleDateFormat
继承了 DateFormat
,在 DateFormat
中定义了一个 protected
属性的 Calendar
类的对象。因为 Calendar
类概念复杂,牵扯到时区与本地化等,JDK 的实现中使用成员变量来传递参数。
SimpleDateFormat
中的格式化方法不是同步的。建议为每个线程创建单例的实例。在多线程的时候可能会出现错误。在一定负载情况下时,会出现如转化的时间不正确、报错、线程被挂死等。
同步锁
使用同步锁实现线程安全,调用此方法的线程就要 block,并发量大时对性能有一定的影响。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateSyncUtil {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date)throws ParseException{
synchronized(sdf){
return sdf.format(date);
}
}
public static Date parse(String strDate) throws ParseException{
synchronized(sdf){
return sdf.parse(strDate);
}
}
}
ThreadLocal
使用 ThreadLocal
,将共享变量变为独享,线程独享比方法独享在并发环境中能减少创建对象的开销。对性能要求高时推荐使用。
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ConcurrentDateUtil {
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
}
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ThreadLocalDateUtil {
private static final String date_format = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
public static DateFormat getDateFormat() {
DateFormat df = threadLocal.get();
if(df == null){
df = new SimpleDateFormat(date_format);
threadLocal.set(df);
}
return df;
}
public static String formatDate(Date date) throws ParseException {
return getDateFormat().format(date);
}
public static Date parse(String strDate) throws ParseException {
return getDateFormat().parse(strDate);
}
}
DateTimeFormatter
线程安全的 SimpleDateFormat,功能类似。
// DateTimeFormatter 默认提供了多种格式化方式,通过 ofPattern 方法创建自定义格式化方式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s = localDate.format(dateTimeFormatter);
FastDateFormat
Apache commons 中的 FastDateFormat
宣称是既快又线程安全的 SimpleDateFormat
只能对日期进行 format 不能进行解析。