万字博文教你搞懂java源码的日期和时间相关用法

简介: 万字博文教你搞懂java源码的日期和时间相关用法

image.png

文章目录

介绍

日期和时间的两套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(时间戳)image.pngimage.pngimage.pngimage.pngimage.pngimage.png

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()+" 格式化失败 ");
        }
        }
    }
}

image.png

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()+" 格式化失败 ");
      }
    }
    }
}

image.png

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()+" 格式化失败 ");
      }
    }
    }
}

image.png

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();
      }
    }
    }
}

image.png

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();
      }
    }
    }
}

image.png

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()+" 格式化失败 ");
      }
    }
  }
}

image.png

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()+" 格式化失败 ");
      }
    }
  }
}

image.pngimage.pngimage.pngimage.pngimage.pngimage.pngimage.pngimage.png在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常用的用法

获取当前日期和时间

image.png

    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));

image.png

    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);

image.pngimage.pngimage.pngimage.pngimage.pngimage.png

Java’s DateTimeFormatter pattern “YYYY” gives you the week-based-year, (by default, ISO-8601 standard) the year of the Thursday of that week.

image.pngimage.pngimage.png推荐相关文章

hutool日期时间系列文章

1DateUtil(时间工具类)-当前时间和当前时间戳


2DateUtil(时间工具类)-常用的时间类型Date,DateTime,Calendar和TemporalAccessor(LocalDateTime)转换


3DateUtil(时间工具类)-获取日期的各种内容


4DateUtil(时间工具类)-格式化时间


5DateUtil(时间工具类)-解析被格式化的时间


6DateUtil(时间工具类)-时间偏移量获取


7DateUtil(时间工具类)-日期计算


8ChineseDate(农历日期工具类)


9LocalDateTimeUtil(JDK8+中的{@link LocalDateTime} 工具类封装)


10TemporalAccessorUtil{@link TemporalAccessor} 工具类封装


其他

要探索JDK的核心底层源码,那必须掌握native用法


目录
相关文章
|
1月前
|
Java Apache Maven
Java百项管理之新闻管理系统 熟悉java语法——大学生作业 有源码!!!可运行!!!
文章提供了使用Apache POI库在Java中创建和读取Excel文件的详细代码示例,包括写入数据到Excel和从Excel读取数据的方法。
56 6
Java百项管理之新闻管理系统 熟悉java语法——大学生作业 有源码!!!可运行!!!
|
2月前
|
数据采集 运维 前端开发
【Java】全套云HIS源码包含EMR、LIS (医院信息化建设)
系统技术特点:采用前后端分离架构,前端由Angular、JavaScript开发;后端使用Java语言开发。
70 5
|
3月前
|
Kubernetes jenkins 持续交付
从代码到k8s部署应有尽有系列-java源码之String详解
本文详细介绍了一个基于 `gitlab + jenkins + harbor + k8s` 的自动化部署环境搭建流程。其中,`gitlab` 用于代码托管和 CI,`jenkins` 负责 CD 发布,`harbor` 作为镜像仓库,而 `k8s` 则用于运行服务。文章具体介绍了每项工具的部署步骤,并提供了详细的配置信息和示例代码。此外,还特别指出中间件(如 MySQL、Redis 等)应部署在 K8s 之外,以确保服务稳定性和独立性。通过本文,读者可以学习如何在本地环境中搭建一套完整的自动化部署系统。
69 0
|
4天前
|
运维 自然语言处理 供应链
Java云HIS医院管理系统源码 病案管理、医保业务、门诊、住院、电子病历编辑器
通过门诊的申请,或者直接住院登记,通过”护士工作站“分配患者,完成后,进入医生患者列表,医生对应开具”长期医嘱“和”临时医嘱“,并在电子病历中,记录病情。病人出院时,停止长期医嘱,开具出院医嘱。进入出院审核,审核医嘱与住院通过后,病人结清缴费,完成出院。
20 3
|
9天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
14天前
|
存储 安全 Java
深入理解Java中的FutureTask:用法和原理
【10月更文挑战第28天】`FutureTask` 是 Java 中 `java.util.concurrent` 包下的一个类,实现了 `RunnableFuture` 接口,支持异步计算和结果获取。它可以作为 `Runnable` 被线程执行,同时通过 `Future` 接口获取计算结果。`FutureTask` 可以基于 `Callable` 或 `Runnable` 创建,常用于多线程环境中执行耗时任务,避免阻塞主线程。任务结果可通过 `get` 方法获取,支持阻塞和非阻塞方式。内部使用 AQS 实现同步机制,确保线程安全。
|
12天前
|
移动开发 前端开发 JavaScript
java家政系统成品源码的关键特点和技术应用
家政系统成品源码是已开发完成的家政服务管理软件,支持用户注册、登录、管理个人资料,家政人员信息管理,服务项目分类,订单与预约管理,支付集成,评价与反馈,地图定位等功能。适用于各种规模的家政服务公司,采用uniapp、SpringBoot、MySQL等技术栈,确保高效管理和优质用户体验。
|
1月前
|
JSON 前端开发 Java
震惊!图文并茂——Java后端如何响应不同格式的数据给前端(带源码)
文章介绍了Java后端如何使用Spring Boot框架响应不同格式的数据给前端,包括返回静态页面、数据、HTML代码片段、JSON对象、设置状态码和响应的Header。
119 1
震惊!图文并茂——Java后端如何响应不同格式的数据给前端(带源码)
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
365 37
|
1月前
|
Java API
Java的日期类都是怎么用的
【10月更文挑战第1天】本文介绍了 Java 中处理日期和时间的三个主要类:`java.util.Date`、`java.util.Calendar` 和 `java.time` 包下的新 API。`Date` 类用于表示精确到毫秒的瞬间,可通过时间戳创建或获取当前日期;`Calendar` 抽象类提供丰富的日期操作方法,如获取年月日及时区转换;`java.time` 包中的 `LocalDate`、`LocalTime`、`LocalDateTime` 和 `ZonedDateTime` 等类则提供了更为现代和灵活的日期时间处理方式,支持时区和复杂的时间计算。
35 14