JDK1.8新特性--新时间与日期API

本文深入探讨了Java中传统和现代的时间与日期API,包括SimpleDateFormat的线程安全性问题及解决方案,新时间与日期API的优点,本地时间与时间戳的使用,时间校正器的功能,以及时间日期格式化与时区处理的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、传统的时间与日期API

传统的时间与日期API最大的缺点就是线程不安全
看如下代码:

public class TestSimpleDateformat {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List<Future<Date>> futureList =new ArrayList<>();
        Callable<Date> task = new MyCallable();
        for (int i = 0; i < 10; i++) {
            futureList.add(executorService.submit(task));
        }
        futureList.forEach((f)-> {
            try {
                System.out.println(f.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        executorService.shutdown();
    }
}
class MyCallable implements Callable<Date>{
    private static  final SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
    @Override
    public Date call() throws Exception {
        return  sdf.parse("2020-2-1");
    }
}

在这里插入图片描述
我看可以看到出现会出现错误,SimpleDateFormat继承了DateFormat,在DateFormat中定义了一个protected属性的 Calendar类的对象:calendar。只是因为Calendar累的概念复杂,牵扯到时区与本地化等等,Jdk的实现中使用了成员变量来传递参数,这就造成在多线程的时候会出现错误。
传统最常用的解决办法就是使用ThreadLocal线程局部变量来解决
如下代码:

public class TestSimpleDateformat {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List<Future<Date>> futureList =new ArrayList<>();
        Callable<Date> task = new MyCallable();
        for (int i = 0; i < 10; i++) {
            futureList.add(executorService.submit(task));
        }
        futureList.forEach((f)-> {
            try {
                System.out.println(f.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        executorService.shutdown();
    }
}
class MyCallable implements Callable<Date>{
    private static final ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };

    @Override
    public Date call() throws Exception {
        return  threadLocal.get().parse("2020-2-1");
    }
}

在这里插入图片描述
程序输出结果正确。

二、新时间与日期API

public class TestSimpleDateformat {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List<Future<LocalDate>> futureList =new ArrayList<>();
        Callable<LocalDate> task = new MyCallable();
        for (int i = 0; i < 10; i++) {
            futureList.add(executorService.submit(task));
        }
        futureList.forEach((f)-> {
            try {
                System.out.println(f.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        executorService.shutdown();
    }
}
class MyCallable implements Callable<LocalDate>{
    private static final DateTimeFormatter dtf =DateTimeFormatter.ISO_LOCAL_DATE;

    @Override
    public LocalDate call() throws Exception {
        return  LocalDate.parse("2020-02-01",dtf);
    }
}

程序结果:
在这里插入图片描述
可以看到无论有多少个线程在执行,都创建了一个新的LocalDate实例,所有线程安全。

三、本地时间与时间戳

使用LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

public class TestLocalDateTime {
    /*
     * 1.LocalDate 本地日期
     * 2.LocalTime 本地时间
     * 3.LocalDateTime 本地日期与时间
     *
     * 本测试是使用LocalDateTime为例,另外两个和这个一样
     */
    @Test
    public void test01(){
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        //指定本地时间
        LocalDateTime time = LocalDateTime.of(2020, 2, 1, 15, 33, 16);
        System.out.println(time);

        //日期运算
        //在当前的日期上加8年
        LocalDateTime nowPlus = now.plusYears(8);
        System.out.println(nowPlus);
        //在当前的日期上减8天
        LocalDateTime nowMinus = now.minusDays(8);
        System.out.println(nowMinus);

        //获取时间
        //获取年份
        System.out.println(now.getYear());
        /*
         *获取月份,注意得到月份有两个方法,一个是getMonth(),得到Month这个
         *枚举类,你可以用这个类的getValue()方法得到月份,而getMonthValue()
         *可以直接得到月份
         */
        System.out.println(now.getMonthValue());
        System.out.println(now.getMonth().getValue());
        //得到天数
        System.out.println(now.getDayOfMonth());
        //得到小时
        System.out.println(now.getHour());
        //得到分钟
        System.out.println(now.getMinute());
        //得到秒
        System.out.println(now.getSecond());
    }

    /*
     * Instant:时间戳(以Unix元年:1970年1月1日00:00:00到某个时间之间的毫秒值)
     * 计算机读到的时间
     */
    @Test
    public void test02(){
        /*
         * 笔者当前的时间为2020-02-01T16:01:07.753,而输出结果是2020-02-01T08:01:7.753Z
         * 那为什么会出现这种情况呢?默认获取UTC时区的时间,不知道的读者可以去百度,美国就是使用
         * 的UTC时区,美国与我国时差相差8个小时,那么在2020-02-01T08:01:7.753Z时间上加8个小时
         * 就对于2020-02-01T16:01:07.753
         */
        Instant now = Instant.now();
        System.out.println(now);
        //做偏移量运算检验以下,结果是完全正确的
        OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
        System.out.println(offsetDateTime);

        //转为毫秒值
        System.out.println(now.toEpochMilli());

        //结果1970-01-01T00:01:00Z
        Instant instant = Instant.ofEpochSecond(60);
        System.out.println(instant);
    }

    /*
     * Duration:计算两个“时间”之间的间隔
     * period:计算两个“日期”之间的间隔
     */
    @Test
    public void test03(){
        Instant instant1 = Instant.now();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Instant instant2 = Instant.now();
        Duration between = Duration.between(instant1, instant2);
        //得到两个时间之间相差多少秒
        System.out.println(between.getSeconds());
    }

    /*
     *
     * period:计算两个“日期”之间的间隔
     */
    @Test
    public void test04(){
        LocalDate localDate1 = LocalDate.of(2020,1,1);
        LocalDate localDate2 = LocalDate.now();
        Period between = Period.between(localDate1, localDate2);
        System.out.println(between.getMonths());
    }
}

四、时间校正器

TemporalAdjuster:时间校正器。有时我们可以需要获取例如:将时间调整到“下个周末”等操作。
TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster的实现。

public class TestTemporalAdjuster {
    /*
     * TemporalAdjuster:时间校正器
     */
    @Test
    public void test05(){
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        //把当前时间的天数改为8,如2020-02-01T16:36:46.533改为2020-02-08T16:36:46.533
        LocalDateTime localDateTime = now.withDayOfMonth(8);
        System.out.println(localDateTime);
        //把当前时间调整为下周六
        LocalDateTime with = now.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
        System.out.println(with);
        //自定义:下一个结婚纪念日,今天当作纪念日,再过12*30天就是下一个结婚纪念日
        LocalDateTime time = now.with((t) -> {
            LocalDateTime marryTime = (LocalDateTime) t;
            LocalDateTime nextMarryTime = marryTime.plusDays(12*30);
            return nextMarryTime;
        });
        System.out.println(time);
    }
}

五:时间日期格式化与时区的处理

public class TestDateTimeFormatter {
    /*
     * DateTimeFormatter:格式化时间/日期
     */
    @Test
    public void test01(){
        //使用默认的格式化
        DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now.format(formatter));
        //使用自定义的
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println(now.format(formatter1));
        LocalDateTime parse = LocalDateTime.parse(now.format(formatter1), formatter1);
        System.out.println(parse);
    }
    /*
     * java8中加入了对时区的支持,带时区的时间分别为:
     * ZonedDate、ZonedTime、ZonedDateTime
     * 其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式
     * 例如:Asia/Shanghai等
     * ZoneId:该类中包含了所有的时区信息
     *        getAvailableZonelds():可以获取所有时区信息
     *        of(id):用指定的时区信息获取ZoneId对象
     */
    @Test
    public void test02(){
        //有多少可以支持的时区
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(System.out::println);
    }
    @Test
    public void test03(){
        //指定一个时区构建时间
        LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Rangoon"));
        System.out.println(now);
        //构建带时区的时间
        ZonedDateTime zonedDateTime = now.atZone(ZoneId.of("Asia/Rangoon"));
        System.out.println(zonedDateTime);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值