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 不能进行解析。