jdk8时间API

发布时间 2023-04-21 01:41:32作者: or追梦者
参考: https://juejin.im/post/6844904170240245774

jdk8以前

之前与时间相关的API 是非线程安全的,设计很差且不在同一个包中,时区处理麻烦
多线程并发操作会出现问题
public class TestSimpleDateFormat {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //规定格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return sdf.parse("2020-10-24");
            }
        };
        //创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        //用list承载结果
        List<Future<Date>> futures = new ArrayList<>();
        //添加20个任务
        for (int i = 0; i < 20; i++) {
            futures.add(pool.submit(task));
        }
                //关闭线程池
        pool.shutdown();
        //遍历结果
        for (Future<Date> future:
             futures) {
            System.out.println(future.get());
        }
    }
}

 

jdk8之前的解决方案:使用ThreadLocal

每个线程操作的是缓存在自己工作空间的副本值
public class ThreadLocalUse {
    private static final ThreadLocal<DateFormat> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<DateFormat>(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };
    public static Date convert(String source) throws ParseException {
        return DATE_FORMAT_THREAD_LOCAL.get().parse(source);
    }
    /**
     * @description Removes the current thread's value for this thread-local variable.
     */
    public static void remove(){
        DATE_FORMAT_THREAD_LOCAL.remove();
    }
}

ThreadLocal方式

public class OldResolver {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //ThreadLocal 为每个线程分配一个SimpleDateFormat 来解决线程安全问题
        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                Date obtain = ThreadLocalUse.convert("2020-10-24");
                //及时清理 防止内存泄漏
                ThreadLocalUse.remove();
                return obtain;
            }
        };
        //创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        //用list承载结果
        List<Future<Date>> futures = new ArrayList<>();
        //添加20个任务
        for (int i = 0; i < 20; i++) {
            futures.add(pool.submit(task));
        }
        pool.shutdown();
        //遍历结果
        for (Future<Date> future:
                futures) {
            System.out.println(future.get());
        }
    }
}

 

jdk8以后

LocalDate(日期) 2020-10-2

LocalDateTime(日期和时间) 2020-10-25T09:17:50.482

LocalTime(时间)09:45:41.952

都是final修饰的不可变对象

有final修饰——实例是不可变对象

 

java.time
Class LocalDateTime
• java.lang.Object
• java.time.LocalDateTime
    • All Implemented Interfaces:
Serializable, Comparable<ChronoLocalDateTime<?>>, ChronoLocalDateTime<LocalDate>, Temporal, TemporalAccessor, TemporalAdjuster

来自 <https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html> 
LocalDateTime 可以直接指定: LocalDateTime.of(2020, 10, 25, 16, 24, 30);
提供直接获取年月日的api:如getYear
    @Test
    public void test1() {
        //获取当前日期时间
        LocalDateTime dt1 = LocalDateTime.now();
        LocalDate date = LocalDate.now();
        LocalTime time = LocalTime.now();
        System.out.println(dt1);//2020-10-25T09:17:50.482
        System.out.println(date);//2020-10-2
        System.out.println(time);//09:45:41.952
        //指定日期时间
        LocalDateTime dt2 = LocalDateTime.of(2020, 10, 25, 16, 24, 30);
        System.out.println(dt2);//2020-10-25T16:24:30
        //加法 plus 运算
        LocalDateTime dt3 = dt2.plusYears(10);
        System.out.println(dt3);//2030-10-25T16:24:30
        System.out.println(dt2.plusDays(10));//2020-11-04T16:24:30
        //获取时间
        System.out.println(dt1.getYear());//年   2020
        System.out.println(dt1.getMonth());//月(英文)  OCTOBER
        System.out.println(dt1.getMonthValue());//月(数字) 10
        System.out.println(dt1.getDayOfMonth());//日  25
        System.out.println(dt1.getDayOfWeek());//星期  SUNDAY
        System.out.println(dt1.getDayOfWeek().getValue());//星期(数字)  7
        System.out.println(dt1.getHour());//时   9
        System.out.println(dt1.getMinute());//分  42
        System.out.println(dt1.getSecond());//秒   45
    }

 

Instant 时间戳

使用Unix元年1970年1月1日00:00:00所经历的毫秒
重写了toString
    @Override
    public String toString() {
        return DateTimeFormatter.ISO_INSTANT.format(this);
    }

 

@Test
public void test2() {
    Instant ins = Instant.now();//默认使用UTC时区
    System.out.println(ins);//2020-10-25T01:47:37.815Z
    //使用时区偏移量
    OffsetDateTime offsetDateTime = ins.atOffset(ZoneOffset.ofHours(8));
    System.out.println(offsetDateTime);//2020-10-25T09:51:04.041+08:00
    //获取纳秒
    System.out.println(ins.getNano());
    //获取历元后指定偏移秒的瞬间  即用秒表示的时间戳
    Instant ins2 = Instant.ofEpochSecond(5);
    System.out.println(ins2);//1970-01-01T00:00:05Z
}

 

Clock时钟

@Test
public void test3() {
    //获取时钟,用最佳可用系统时钟返回当前时刻,并用UTC时区转换日期和时间
    Clock utc = Clock.systemUTC();
    System.out.println(utc.getZone());//Z
    System.out.println(utc.instant());//2020-10-25T12:16:48.798Z
    //获取一个时钟,返回的是系统时区表示的当前瞬间,并用默认时区转换日期和时间
    Clock clock = Clock.systemDefaultZone();
    System.out.println(clock);//SystemClock[Asia/Shanghai]
    System.out.println(clock.getZone());//Asia/Shanghai 获取时区
    System.out.println(clock.millis());//1603627665781 毫秒时间戳
    //通过Clock获取Instant  注意Instant默认使用的UTC时区
    Instant instant = clock.instant();//返回clock记录的当前时刻
    System.out.println(instant);//2020-10-25T12:21:40.364Z 因为Instant 默认使用UTC时区,所以Asia的日期和时间被转换了
    System.out.println(instant.toEpochMilli());//1603627665782 毫秒时间戳  和上面一样的
    //通过Clock获取LocalDateTime
    LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, clock.getZone());
    System.out.println(localDateTime);//2020-10-25T20:21:40.364
    //自定义Clock的创建  滴答间隔为3秒  逢3进1  通常时钟的滴答间隔为1s
    Clock tick = Clock.tick(clock, Duration.ofSeconds(3));//滴答时间间隔为3秒钟
    for (int i = 0; i < 10; i++) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(clock.instant().atZone(ZoneId.of("Asia/Shanghai"))
                + "\t对比自定义时钟\t"
                + tick.instant().atZone(ZoneId.of("Asia/Shanghai")));
    }
    /*
    2020-10-25T20:43:42.456+08:00[Asia/Shanghai]    对比自定义时钟 2020-10-25T20:43:42+08:00[Asia/Shanghai]
    2020-10-25T20:43:43.462+08:00[Asia/Shanghai]    对比自定义时钟 2020-10-25T20:43:42+08:00[Asia/Shanghai]
    2020-10-25T20:43:44.468+08:00[Asia/Shanghai]    对比自定义时钟 2020-10-25T20:43:42+08:00[Asia/Shanghai]
    2020-10-25T20:43:45.469+08:00[Asia/Shanghai]    对比自定义时钟 2020-10-25T20:43:45+08:00[Asia/Shanghai]
     */
}

 

Duration时间段

用于计算两个时间的间隔

Duration.getSeconds()时间差转为秒
Duration.getSNacos()时间差转为纳秒
Duration.get(Temproral unit) 转化为指定单时间单元表示形式

 

 

ChronoUnit中是细时间颗粒,更常用
 
 
@Test
public void test4() {
    // 时间的间隔
    Instant ins1 = Instant.now();
    System.out.println("-------------");
    try {
        Thread.sleep(2000);
    } catch (Exception e) {
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();
    Duration duration = Duration.between(ins1, ins2);
    System.out.println("间隔:" + duration);//间隔:PT2S
    System.out.println(duration.getSeconds());//间隔:2 (s)
    System.out.println("----------");
    /*
     日期的间隔
     */
    LocalDate date = LocalDate.now();//2020-10-25
    LocalDate date2 = LocalDate.of(2011, 1, 1);
    Period period = Period.between(date, date2);
    System.out.println(period.getYears());//-9  九年
    System.out.println(period.getMonths());//-9  九个月
    System.out.println(period.getDays());//-24  24天
}

 

TemporalAdjuster校正器 可自定义

@Test
public void test5() {
    LocalDateTime dateTime = LocalDateTime.now();
    System.out.println(dateTime);//2020-10-25T10:57:11.455
    //将day较正为 10号
    LocalDateTime dateTime1 = dateTime.withDayOfMonth(10);
    System.out.println(dateTime1);//2020-10-10T10:57:11.455
    //自定义校正,下一个工作日
    LocalDateTime localDateTime = dateTime.with(date -> {
        //需要强转
        LocalDateTime original = (LocalDateTime) date;
        //获取星期
        DayOfWeek week = original.getDayOfWeek();
        if (week.equals(DayOfWeek.FRIDAY)) {
            //周五 加上 3天 就是工作日
            return original.plusDays(3);
        } else if (week.equals(DayOfWeek.SATURDAY)) {
            //周六 要加上 2天
            return original.plusDays(2);
        } else {
            //其它  都只需要加上一天
            return original.plusDays(1);
        }
    });
    System.out.println(localDateTime);//2020-10-26T11:14:51.058 因为是周日所以只需加上一天
}

 

 

DateTimeFormatter:格式化器 格式化和解析日期或者时间

LocalDateTime等自带格式化与解析方法

@Test
public void test6() {
    //使用已有日期时间格式
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE;
    //LocalDateTime now()默认的是当前系统的时区
    LocalDateTime dateTime = LocalDateTime.now();
    System.out.println(dateTime);//2020-10-25T11:25:22.070
    String format = dateTime.format(dateTimeFormatter);
    System.out.println(format);//2020-10-25
    String format1 = dateTimeFormatter.format(dateTime);
    System.out.println(format1);//2020-10-25
    //格式化器格式化日期时间,日期时间调用格式化器都可以完成时间的转换,正反过来都行
    //自定义日期时间格式
    DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
    //格式化
    String custom = dateTime.format(ofPattern);//2020年10月25日 11:58:41
    System.out.println(custom);
    //解析
    //直接用格式化器解析 结果不易阅读 如果需要 可使用LocalDateTime的form方法转为LocalDateTime等
    TemporalAccessor parse = ofPattern.parse(custom);
    System.out.println(parse);//{},ISO resolved to 2020-10-25T11:58:41
    LocalDateTime parseResult = LocalDateTime.from(parse);//转化成LocalDateTime
    //将得到的TemporalAccessor转化为LocalDateTime:2020-10-25T12:16:30
    System.out.println("将得到的TemporalAccessor转化为LocalDateTime:" + parseResult);
    ////LocalDateTime也提供了解析方法  也是调用格式化器
    //实际执行的 parse(text, DateTimeFormatter.ISO_LOCAL_DATE_TIME) 默认的是系统格式
    //2020年10月25日 11:35:56 用默认的会报错 解析不了
    //LocalDateTime parse1 = LocalDateTime.parse(custom);
    //添加指定的格式化器就可以  比起直接用格式化器解析得到TemporalAccessor再调用form()转化为LocalDateTime简单点
    LocalDateTime parse1 = LocalDateTime.parse(custom, ofPattern);
    System.out.println(parse1);
}

 

ZonedDate  ZonedTime  ZonedDateTime:带时区的时间或日期

@Test
public void test7() {
    //指定时区的LocalDateTime 本地日期时间
    LocalDateTime dateTime = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(dateTime);//2020-10-25T17:43:31.643
    //指定时区的ZonedDateTime  时区日期时间
    ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("US/Pacific"));
    System.out.println(zonedDateTime);//2020-10-25T02:44:48.612-07:00[US/Pacific]
    Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
    System.out.println(availableZoneIds);//Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8, Africa/Nairobi....
}