我们常用的日期格式方式就是使用SimpleDateFormat,SimpleDateFormat在我们系统的里面,经常会用到,比如转换时间戳导出、打印啊等等。
伪代码:
正常我们都会这样使用转换时间
注意:有些是需要的时候创建新实例
如:工具类,创建新的实例
SimpleDateFormat在并发下会出现出现时间不对,线程挂死等等。
见源码:
作者写的注释:
翻译为:
- 日期格式不同步。
- 建议为每个线程创建单独的格式实例。
- 如果多个线程同时访问一种格式,则必须从外部对其进行同步。
我们常用的format方法,我们去看看。
其次底层操作用的是calendar,然后声明的时候如果用static修饰变量,那么calendar变量也是就是一个共享变量,如果simpledateformat被用在n多方法,就你比如导出,需要各种转换的时候,那并发的时候,就会出现线程1执行完calendar.setTime(date),设置了值,这时候挂起的时候,线程2拿到了执行权,线程2也执行了calendar.setTime(date),线程2挂起了,然后线程1取值的时候,被其它竞争线程修改了值,那就会出现时间不对,线程挂死等。
如果需要用到simpledateformat,那如何解决这个问题
这里先要提2个东西
JDK1.8之前,JDK1.8之后
先讲JDK1.8之前
按上述作者的注释
一:JDK1.8之前
1:加一个同步锁
publict Class SimpleDateFormatTest{
public static String formatDate(Date date) {
synchronized (formater) {
return formater.format(date);
}
}
}
一个同步锁搞定,但是我们知道同步锁会影响效率,高并发下会影响性能的
2:ThreadLocal
本地线程 - 解决线程竞争,安全不影响效率
private static ThreadLocal<DateFormat> threadLocalFormater = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static String threadLoaclFormatDate(Date date) {
return threadLocalFormater.get().format(date);
}
二:JDK1.8之后
java.time.format包下的DateTimeFormatter,是JDK1.8加入的日期格式化工具类,引入这个工具类就是解决了SimpleDateFormat的线程安全问题
见源码:
简述:
DateTimeFormatter不单单是不变对象,并也是线程安全的。
见源码:258行,作者的注释
SimpleDateFormat不是线程安全的,使用的时候,只能在方法内部创建新的局部变量。而DateTimeFormatter可以只创建一个实例,到处引用。
1 常用方法:
1.1 实例化方式:
预定义的标准格式;如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
本地化相关的格式;如:ofLocalizedDateTime(FormatStyle.LONG)
自定义的格式;如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
1.2 常用方法
// 自定义格式如:ofPattern("yyyy-MM-dd hh:mm:ss")
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");// 12小时
DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// 24小时
/* 日期类型转换字符串 -- start */
// LocalTime --> String
LocalTime localTime = LocalTime.now();
String format1 = localTime.format(DateTimeFormatter.ISO_TIME); // 15:01:22.42
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm:ss");
String format2 = localTime.format(formatter);
System.out.println("LocalTime --> String:" + format1);
System.out.println("LocalTime --> String:" + format2);
// LocalTime --> String
// localDate --> String
LocalDate localDate1 = LocalDate.now();
String format3 = localDate1.format(DateTimeFormatter.BASIC_ISO_DATE); // yyyyMMdd
String format4 = localDate1.format(DateTimeFormatter.ISO_DATE); // yyyy-MM-dd
System.out.println("LocalDate --> String:" + format3);
System.out.println("LocalDate --> String:" + format4);
// LocalDateTime --> String
LocalDateTime localDateTime = LocalDateTime.now();
String timeStr1 = localDateTime.format(dateTimeFormatter1);
System.out.println("LocalDateTime --> String:" + timeStr1);
String timeStr2 = dateTimeFormatter1.format(LocalDateTime.now());
System.out.println("LocalDateTime --> String:" + timeStr2);
// LocalDateTime --> String
/* 日期类型转换字符串 -- end */
/* 字符串转日期类型 -- start */
// String --> LocalDate
LocalDate localDate2 = LocalDate.parse("2021-05-11 11:32:05",dateTimeFormatter2);
System.out.println("String --> LocalDate:" + localDate2);
LocalDate localDate3 = LocalDate.parse("2021-05-11");
System.out.println("String --> LocalDate:" + localDate3);
DateTimeFormatter pattern1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
System.out.println("String --> LocalDate:" + LocalDate.parse("2021-05-11").format(pattern1));
// String --> LocalDate
// String --> LocalTime
LocalTime localTime2 = LocalTime.parse("07:43:53");
System.out.println("String --> LocalTime:" + localTime2);
// String --> LocalTime
// String -->LocalDateTime
LocalDate localDate4 = LocalDate.parse("2021-05-11 15:30:53",dateTimeFormatter2);
System.out.println("String -->LocalDateTime:" + localDate4);
// String -->LocalDateTime
/* 字符串转日期类型 -- end */
// 解析
// 注意:我的日期格式12小时制,但是我的文本时间是24小时制,启动看会报什么错
// TemporalAccessor temporalAccessor1 = dateTimeFormatter1.parse("2021-05-11 15:32:05");
// System.out.println(temporalAccessor1);
// java.time.format.DateTimeParseException: Text '2021-05-11 15:32:05' could not be parsed:
// Invalid value for ClockHourOfAmPm (valid values 1 - 12): 15
TemporalAccessor temporalAccessor2 = dateTimeFormatter1.parse("2021-05-11 11:32:05");
// {SecondOfMinute=5, MilliOfSecond=0, NanoOfSecond=0, HourOfAmPm=11, MinuteOfHour=32, MicroOfSecond=0},ISO resolved to 2021-05-11
System.out.println(temporalAccessor2);
如果你的JDK是1.8,并且你的simpledateformat没有做线程安全处理,除了上述的解决方案,强烈建议你使用JDK1.8推出的DateTimeFormatter ,如果没有用,别人搞这个干吗,需要注意的是,要使用instant代替Date,LocalDateTime替代Calendar
这边给出一些常用的转换方法
// date与instant的相互转化 -- start
Instant now = Instant.now();
System.out.println("时间戳与北京时间相差8个时区:"+now);
// 我们去查看源码: 272行
// 发现是采用的UTC,如果你需要使用北京时间,则需要增加8个小时
Instant nowUTC = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8));
System.out.println("nowUTC :"+nowUTC );
Date date = Date.from(now);
System.out.println(date);
Instant instant = date.toInstant();
// LocalDate、LocalDateTime 的now()方法使用的是系统默认时区 不存在Instant.now()的时间问题。
// date与instant的相互转化 -- end
// LocalDateTime代替Calendar -- start
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId);
int year = localDateTime.getYear();// 年
int month = localDateTime.getMonthValue();// 月
int dayOfMonth = localDateTime.getDayOfMonth();// 日
int hour = localDateTime.getHour();// 小时
int minute = localDateTime.getMinute();// 分钟
int second = localDateTime.getSecond();// 秒
System.out.println(year);
System.out.println(month);
System.out.println(dayOfMonth);
System.out.println(hour);
System.out.println(minute);
System.out.println(second);
// LocalDateTime代替Calendar -- end
// DateTimeFormatter代替SimpleDateFormat
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// 24小时制
// Date格式化成String -- start
String format = dateTimeFormatter.format(localDateTime);
System.out.println("Date格式化成String:" + format);
// Date格式化成String -- end
// String获得Date -- start
LocalDateTime parse = LocalDateTime.parse(format, dateTimeFormatter);
ZoneOffset offset = OffsetDateTime.now().getOffset();
Instant instant2 = parse.toInstant(offset);
Date date1 = Date.from(instant2);
System.out.println("String获得Date:" + date1);
// String获得Date -- end
见阿里巴巴手册: