文章目录
介绍
日期和时间的两套API
一:Date
支持版本及以上
介绍
Date类说明
date常用的用法
自定义时间格式-SimpleDateFormat
SimpleDateFormat线程不安全原因及解决方案
SimpleDateFormat线程为什么是线程不安全的呢?
验证SimpleDateFormat线程不安全
解决方案
解决方案1:不要定义为static变量,使用局部变量
解决方案2:加锁:synchronized锁和Lock锁
解决方案3:使用ThreadLocal方式
解决方案4:使用DateTimeFormatter代替SimpleDateFormat
解决方案5:使用FastDateFormat 替换SimpleDateFormat
FastDateFormat源码分析
实践
问题
二:Calendar
支持版本及以上
介绍
Calendar类说明
Calendar常用的用法
Calendar的跨年问题和解决方案
问题
分析原因
解决方案
问题
三:LocalDateTime
支持版本及以上
介绍
LocalDateTime类说明
LocalDateTime常用的用法
获取当前日期和时间
获取指定日期和时间
日期时间的加减法及修改
LocalDateTime和Date相互转化
Date转LocalDateTime
LocalDateTime转Date
线程安全
四:ZonedDateTime
支持版本及以上
介绍
ZonedDateTime类说明
ZonedDateTime常用的用法
获取当前时间+带时区+时区转换
LocalDateTime+ZoneId变ZonedDateTime
五:DateTimeFormatter
支持版本及以上
介绍
DateTimeFormatter类说明
DateTimeFormatter常用的用法
DateTimeFormatter的坑
1、在正常配置按照标准格式的字符串日期,是能够正常转换的。如果月,日,时,分,秒在不足两位的情况需要补0,否则的话会转换失败,抛出异常。
2、YYYY和DD谨慎使用
六:Instant
支持版本及以上
介绍
Instant类说明
Instant常用的用法
Instant是没有时区的,但是Instant加上时区后,可以转化为ZonedDateTime
long型时间戳转Instant
Instant的坑
推荐相关文章
hutool日期时间系列文章
其他
介绍
本篇文章主要介绍java源码中提供了哪些日期和时间的类
日期和时间的两套API
java提供了两套处理日期和时间的API
1、旧的API,放在java.util 这个包下的:比较常用的有Date和Calendar等
2、新的API是java 8新引入的,放在java.time 这个包下的:LocalDateTime,ZonedDateTime,DateTimeFormatter和Instant等
为什么会有两套日期时间API,这个是有历史原因的,旧的API是jdk刚开始就提供的,随着版本的升级,逐渐发现原先的api不满足需要,暴露了一些问题,所以在java 8 这个版本中,重新引入新API。
这两套API都要了解,为什么呢?
因为java 8 发布时间是2014年,很多之前的系统还是沿用旧的API,所以这两套API都要了解,同时还要掌握两套API相互转化的技术。
一:Date
支持版本及以上
JDK1.0
介绍
Date类说明
Date类负责时间的表示,在计算机中,时间的表示是一个较大的概念,现有的系统基本都是利用从1970.1.1 00:00:00 到当前时间的毫秒数进行计时,这个时间称为epoch(时间戳)
public class SimpleDateFormatDemoTest { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、创建线程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、为线程池分配任务 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、关闭线程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { String dateString = simpleDateFormat.format(new Date()); try { Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失败 "); } } } }
public class SimpleDateFormatDemoTest1 { public static void main(String[] args) { //1、创建线程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、为线程池分配任务 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、关闭线程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = simpleDateFormat.format(new Date()); try { Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失败 "); } } } }
public class SimpleDateFormatDemoTest2 { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、创建线程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、为线程池分配任务 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、关闭线程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { synchronized (simpleDateFormat){ String dateString = simpleDateFormat.format(new Date()); Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2)); } } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失败 "); } } } }
public class SimpleDateFormatDemoTest3 { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static Lock lock = new ReentrantLock(); public static void main(String[] args) { //1、创建线程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、为线程池分配任务 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、关闭线程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { lock.lock(); String dateString = simpleDateFormat.format(new Date()); Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失败 "); }finally { lock.unlock(); } } } }
public class SimpleDateFormatDemoTest4 { private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(){ @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static void main(String[] args) { //1、创建线程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、为线程池分配任务 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、关闭线程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { String dateString = threadLocal.get().format(new Date()); Date parseDate = threadLocal.get().parse(dateString); String dateString2 = threadLocal.get().format(parseDate); System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失败 "); }finally { //避免内存泄漏,使用完threadLocal后要调用remove方法清除数据 threadLocal.remove(); } } } }
public class DateTimeFormatterDemoTest5 { private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、创建线程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、为线程池分配任务 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、关闭线程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { String dateString = dateTimeFormatter.format(LocalDateTime.now()); TemporalAccessor temporalAccessor = dateTimeFormatter.parse(dateString); String dateString2 = dateTimeFormatter.format(temporalAccessor); System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { e.printStackTrace(); System.out.println(Thread.currentThread().getName()+" 格式化失败 "); } } } }
public class FastDateFormatDemo6 { private static FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、创建线程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、为线程池分配任务 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、关闭线程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { String dateString = fastDateFormat.format(new Date()); Date parseDate = fastDateFormat.parse(dateString); String dateString2 = fastDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { e.printStackTrace(); System.out.println(Thread.currentThread().getName()+" 格式化失败 "); } } } }
在JDK中会把前一年末尾的几天判定为下一年的第一周,因此上面程序的结果是1
解决方案
使用Calendar类 get(Calendar.YEAR)获取年份
问题
1、读取月份时,要+1
2、返回的星期是从周日开始计算,周日为1,1~7表示星期
3、Calendar的跨年问题,获取年份要用c.get(Calendar.YEAR),不要用c.getWeekYear();
4、获取指定时间是一年中的第几周时,调用cl.get(Calendar.WEEK_OF_YEAR),要注意跨年问题,跨年的那一周,获取的值为1。离跨年最近的那周为52。
三:LocalDateTime
支持版本及以上
jdk8
介绍
LocalDateTime类说明
表示当前日期时间,相当于:yyyy-MM-ddTHH:mm:ss
LocalDateTime常用的用法
获取当前日期和时间
LocalDateTime currentTime = LocalDateTime.now(); // 当前日期和时间 System.out.println("------------------时间的加减法及修改-----------------------"); //3.LocalDateTime的加减法包含了LocalDate和LocalTime的所有加减,上面说过,这里就只做简单介绍 System.out.println("3.当前时间:" + currentTime); System.out.println("3.当前时间加5年:" + currentTime.plusYears(5)); System.out.println("3.当前时间加2个月:" + currentTime.plusMonths(2)); System.out.println("3.当前时间减2天:" + currentTime.minusDays(2)); System.out.println("3.当前时间减5个小时:" + currentTime.minusHours(5)); System.out.println("3.当前时间加5分钟:" + currentTime.plusMinutes(5)); System.out.println("3.当前时间加20秒:" + currentTime.plusSeconds(20)); //还可以灵活运用比如:向后加一年,向前减一天,向后加2个小时,向前减5分钟,可以进行连写 System.out.println("3.同时修改(向后加一年,向前减一天,向后加2个小时,向前减5分钟):" + currentTime.plusYears(1).minusDays(1).plusHours(2).minusMinutes(5)); System.out.println("3.修改年为2025年:" + currentTime.withYear(2025)); System.out.println("3.修改月为12月:" + currentTime.withMonth(12)); System.out.println("3.修改日为27日:" + currentTime.withDayOfMonth(27)); System.out.println("3.修改小时为12:" + currentTime.withHour(12)); System.out.println("3.修改分钟为12:" + currentTime.withMinute(12)); System.out.println("3.修改秒为12:" + currentTime.withSecond(12));
System.out.println("------------------方法一:分步写-----------------------"); //实例化一个时间对象 Date date = new Date(); //返回表示时间轴上同一点的瞬间作为日期对象 Instant instant = date.toInstant(); //获取系统默认时区 ZoneId zoneId = ZoneId.systemDefault(); //根据时区获取带时区的日期和时间 ZonedDateTime zonedDateTime = instant.atZone(zoneId); //转化为LocalDateTime LocalDateTime localDateTime = zonedDateTime.toLocalDateTime(); System.out.println("方法一:原Date = " + date); System.out.println("方法一:转化后的LocalDateTime = " + localDateTime); System.out.println("------------------方法二:一步到位(推荐使用)-----------------------"); //实例化一个时间对象 Date todayDate = new Date(); //Instant.ofEpochMilli(long l)使用1970-01-01T00:00:00Z的纪元中的毫秒来获取Instant的实例 LocalDateTime ldt = Instant.ofEpochMilli(todayDate.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime(); System.out.println("方法二:原Date = " + todayDate); System.out.println("方法二:转化后的LocalDateTime = " + ldt);
Java’s DateTimeFormatter pattern “YYYY” gives you the week-based-year, (by default, ISO-8601 standard) the year of the Thursday of that week.
推荐相关文章
hutool日期时间系列文章
1DateUtil(时间工具类)-当前时间和当前时间戳
2DateUtil(时间工具类)-常用的时间类型Date,DateTime,Calendar和TemporalAccessor(LocalDateTime)转换
3DateUtil(时间工具类)-获取日期的各种内容
4DateUtil(时间工具类)-格式化时间
5DateUtil(时间工具类)-解析被格式化的时间
6DateUtil(时间工具类)-时间偏移量获取
7DateUtil(时间工具类)-日期计算
8ChineseDate(农历日期工具类)
9LocalDateTimeUtil(JDK8+中的{@link LocalDateTime} 工具类封装)
10TemporalAccessorUtil{@link TemporalAccessor} 工具类封装
其他
要探索JDK的核心底层源码,那必须掌握native用法