Java8新特性之时间处理总结

本文会详细介绍,Java8中关于时间、日期API的具体使用,以及注意问题,具体内容包括传统时间格式化的线程安全问题及解决方案、LocalDateTime、Duration和Period、时间校正器、时间格式化、时区处理等操作。

1、传统时间格式化的线程安全问题及解决方案

传统时间API的安全问题引入
  • 使用java8之前的传统的关于时间日期的API 会有线程安全问题:
@Test
public void test1() throws ExecutionException, InterruptedException {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    Callable<Date> task = new Callable<Date>() {
        @Override
        public Date call() throws Exception {
            return dateFormat.parse("2020-08-10");
        }
    };

    ExecutorService pool = Executors.newFixedThreadPool(10);

    List<Future<Date>> result = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        result.add(pool.submit(task));
    }
    for (Future<Date> future : result) {
        System.out.println(future.get());
    }
   // 运行结果: java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "E.2101E2"

}

运行结果如下图所示,显示无法正确解析日期字符串:
在这里插入图片描述

解决方案
  • 日期线程安全的解决方案1:使用ThreadLocal来存储、解析转换Date对象
 @Test
    public void test2ThreadSafe() throws ExecutionException, InterruptedException {
        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return DateFormatThreadLocal.covert("2022-08-10");
            }
        };
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        try {

            List<Future<Date>> list = new ArrayList<>();

            for (int i = 0; i < 10; i++) {
                list.add(threadPool.submit(task));
            }
            for (Future<Date> future : list) {
                System.out.println(future.get());
            }
        } finally {
            threadPool.shutdown();
        }
    }
  • 解决方案2:使用Java8的新时期API,用DateTimeFormatter 代替SimpleDateFormat,用LocalDate代替Date
@Test
public void test3() throws ExecutionException, InterruptedException {
    DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    Callable<LocalDate> callable = new Callable<LocalDate>() {
        @Override
        public LocalDate call() throws Exception {
            return LocalDate.parse("2022-08-10",timeFormatter);
        }
    };
    ExecutorService threadPool = Executors.newFixedThreadPool(10);
    List<Future<LocalDate>> list = new ArrayList<>();

    for (int i = 0; i < 10; i++) {
        list.add(threadPool.submit(callable));
    }
    for (Future<LocalDate> future : list) {
        System.out.println(future.get());
    }
    threadPool.shutdown();
}

2、LocalDateTime

  • LocalDateTime在前面解决线程安全问题时,已经使用过,下面进行详细讲解。LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信 息,也不包含与时区相关的信息。 注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
  • 具体API使用如下:
    在这里插入图片描述
  • 代码示例:
    @Test
    public void test1(){
        // 1.输出当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);  // 输出格式形如: 2022-08-10T08:29:08.987

        // 2.将指定时间格式化
        LocalDateTime dateTime1 = LocalDateTime.of(2018,9,1,8,20,30);
        System.out.println(dateTime1);  // 2018-09-01T08:20:30

        // 3.将指定时间上进行运算或判断
        LocalDateTime dateTime2 = now.plusYears(2);
        System.out.println(dateTime2);

        LocalDateTime dateTime3 = now.minusMonths(2);
        System.out.println(dateTime3);

        System.out.println(now.getYear());
        System.out.println(now.getMonthValue() + "<--->" + now.getMonth());
        System.out.println(now.getDayOfMonth());

        // 判断当前时间now 是否在 dateTime1之后
        boolean after = now.isAfter(dateTime1);
        System.out.println(after);

        // 判断当前时间now是否在 dateTime2之前 (dateTime2 = now.plusYears(2);)
        boolean before = now.isBefore(dateTime2);
        System.out.println(before);
    }

3、Duration和Period

  • Instant时间戳
    用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始,所经历的描述进行运算
  • Duration:用于计算两个“时间”间隔(具体到时分秒),即Time
  • Period:用于计算两个“日期”间隔(具体到年月日),即Date
    使用示例:
    @Test
    public void test2() throws InterruptedException {
        Instant start = Instant.now();

        Thread.sleep(1000);
        Instant end = Instant.now();

        Duration duration = Duration.between(start, end);
        System.out.println("耗费时间: " + duration);
        System.out.println("human,耗费时间: " + duration.toMillis() + "毫秒");
        // 在原本的持续时间上其他计算操作(增加10毫秒,1个小时,2天)
        System.out.println(duration.plusMillis(10));
        System.out.println(duration.plusHours(1));
        System.out.println(duration.plusDays(2));
    }
  • 运行结果:
    在这里插入图片描述
    @Test
    public void test3(){
        LocalDate start = LocalDate.of(2018,10,10);
        LocalDate now = LocalDate.now();

        Period period = Period.between(start, now);
        System.out.println("两时间相隔的年数: " +period.getYears());
        System.out.println("两时间相隔的月数: " +period.getMonths());
        System.out.println("两时间相隔的天数: " +period.getDays());

        // isNegative(): 结果为false,now在start之前,为true,now在start之后
        System.out.println(period.isNegative());
    }

4、时间校正器

  • TemporalAdjuster:时间校正器,有时我们可能需要调整某个日期的时间,例如:将日期调整到“下个周日”等操作。
  • TemporalAdjusters: 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
    例如获取下个周日:
    在这里插入图片描述
  • 使用示例:
  public static void main(String[] args) {
        LocalDateTime dateTime1 = LocalDateTime.now();
        System.out.println(dateTime1);

        // 修改dateTime1变为当前月的16号
        LocalDateTime dateTime2 = dateTime1.withDayOfMonth(16);
        System.out.println(dateTime2);

        // 获取下一个休息日(下一个星期六)
        LocalDateTime vocation = dateTime1.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
        System.out.println(vocation);

        // 自定义下一个工作日
        LocalDateTime nextWorkDay = dateTime1
               // .plusDays(2)  // 测试其他日期
                .with((dt) -> {
            LocalDateTime work = (LocalDateTime) dt;

            if (work.getDayOfWeek().equals(DayOfWeek.FRIDAY)) {
                return work.plusDays(3);  // 当前是星期五,下一个工作日是在3天后
            } else if (work.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
                return work.plusDays(2); //  当前是星期六,下一个工作日是在2天后
            } else {
                return work.plusDays(1); // 星期天 或 星期一到星期五
            }
        });
        System.out.println("下一个工作日:" +nextWorkDay);

    }

补充:
LocalDateTime中的with方法中参数类型是TemporalAdjuster类型,TemporalAdjuster是函数式接口,唯一需要实现的方法就是adjustInto,需要传入一个参数并返回结果(可以使用Lambda表达式来实现)。
在这里插入图片描述
在这里插入图片描述

5、时间格式化

  • java.time.format.DateTimeFormatter类:该类提供了两种格式化方法:
    • 预定义的标准格式,比如:ISO_LOCAL_DATE、ISO_LOCAL_DATE_TIME、DateTimeFormatter.ISO_DATE
      在这里插入图片描述
    • 自定义的格式,通过ofPattern()指定输出日期和时间的格式,比如:
  `DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")`
  • 代码示例
  // DateTimeFormatter: 格式化时间/日期
    @Test
    public void test1(){
        DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_DATE_TIME;
        LocalDateTime now = LocalDateTime.now();

        String strDate = now.format(timeFormatter);
        System.out.println(strDate);

        System.out.println("------------");

        // 指定输出时间的格式
        DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String str = ofPattern.format(now);
        System.out.println(str);
    }

6、时区处理

  • Java8 中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime
    其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式
  • 例如 :Asia/Shanghai 等
    • ZoneId:该类中包含了所有的时区信息
    • getAvailableZoneIds() : 可以获取所有时区时区信息
    • of(id) : 用指定的时区信息获取ZoneId 对象
@Test
public void test2(){
    // 获取所有可用时区
    Set<String> zoneIds = ZoneId.getAvailableZoneIds();
    zoneIds.forEach(System.out::println);

    // 输出日期和时间的基础上,还增加对应的时区信息
    System.out.println(ZonedDateTime.now());   // 2022-08-10T11:05:45.394+08:00[Asia/Shanghai]

    LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println("上海时区的当前时间: "+now);

    // 获取 Europe/Athens时区的当前时间
    LocalDateTime now2 = LocalDateTime.now(ZoneId.of("Europe/Athens"));
    System.out.println("Europe/Athens时区的当前时间: "+now2);  // 2022-08-10T06:01:22.482

    LocalDateTime now3 = LocalDateTime.now();
    System.out.println("当前所在地的时间,对应在Europe/Athens时区的时间: "+now3.atZone(ZoneId.of("Europe/Athens")));
    //  2022-08-10T11:01:22.498+03:00[Europe/Athens]   --> 与UTC时区相比相差三个小时

    System.out.println(now3.atZone(ZoneId.of("UTC")));  // 2022-08-10T11:04:23.840Z[UTC]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值