JDK8时间日期处理大全

一、前言

  • 学习概述:在Java开发中必不可少的要处理时间,那前后端如何传递时间参数?数据库的时间使用什么类型?Java中时间转换的各种方式有哪些?经过本篇文章带大家一一了解。如果有不正确的地方,欢迎大家评论讨论,谢谢。
  • 学习目标:读完这篇文章之后,希望你能学会熟练使用JDK8z中各种时间转换方法。

二、常用时间类

1.为什么JDK8新增了时间处理类

老版本的java.util.Date与java.util.Calendar类是线程不安全的,并且使用起来复杂。

线程不安全示例:

final static SimpleDateFormat YYY_MM_DD_HH_MM_SS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Date date = null;
                try {
                    date = YYY_MM_DD_HH_MM_SS.parse("2022-05-11 23:12:12");
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                System.out.println(date);
            }).start();
        }
    }

2.JDK8新增的时间类 

JDK8在java.util.time目录下增加了大量的时间处理类

类名用途
Instant用于老版本时间转换为新版本
Duration表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性。可用于秒杀
Period表示一段时间的年、月、日
LocalDate是一个不可变的日期时间对象,表示日期,通常被视为年月日
LocalTime是—个不可变的日期时间对象,代表一个时间,通常被看作是时分秒
LocalDateTime不可变的日期时间对象,代表日期时间,通常被视为年-月-日-时分秒
ZonedDateTime

有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间

3.时区ZoneId

  //打印所有时区
        ZoneId.getAvailableZoneIds().iterator().forEachRemaining(System.out::println);
        //获取默认时区
        System.out.println(ZoneId.systemDefault());

4. 修改时间

public static void dateOperator(){
        LocalDateTime now = LocalDateTime.now();
        //当前时间添加一小时
        System.out.println(now.plusHours(1));
        //当前时间减去一小时
        System.out.println(now.minusHours(2));
        //当前时间修改为4点
        System.out.println(now.withHour(4));
        //修改为10点
        System.out.println(now.with(ChronoField.HOUR_OF_DAY, 10));
        //计算1年10天5小时后的时间
        Period of = Period.of(1, 10, 5);
        System.out.println(now.plus(of));
    }

5. Date/Calendar转为LocalDate

public static void date2Local(){
        //1.java.util.Date类转为LocalDate
        Date date = new Date();
        //Date包含日期和时间,但是并不提供时区信息
        Instant instant = date.toInstant();
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
        System.out.println("java.util.Date=" + date + "; toLocalDate=" + zonedDateTime.toLocalDate());

        //2.java.sql.Date类转为LocalDate
        java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
        LocalDate r = sqlDate.toLocalDate();
        System.out.println("java.sql.Date=" + sqlDate + "; toLocalDate = " + r);

        //3.java.sql.Timestamp类转为LocalDate
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        LocalDateTime localDateTime = timestamp.toLocalDateTime();
        System.out.println("java.sql.Timestamp=" + timestamp + "; toLocalDate = " + localDateTime);

        //4.java.util.Calendar类转为LocalDate
        Calendar calendar = Calendar.getInstance();
        //4.1 先获取Calendar的时区
        TimeZone timeZone = calendar.getTimeZone();
        //4.2 TimeZone转为ZoneId
        ZoneId zoneId = timeZone.toZoneId();
        ZonedDateTime calZone = ZonedDateTime.ofInstant(calendar.toInstant(), zoneId);
        System.out.println("java.util.Calendar=" + calendar + "; toLocalDate=" + calZone);

        //方式二:Calendar封装的月份是从0开始的,所以在这里获取到的month要+1
        LocalDateTime.of(calendar.get(Calendar.YEAR),calendar.get(Calendar.MONTH) + 1,calendar.get(Calendar.DAY_OF_MONTH)
                ,calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND));

    }

6. DateTimeFormatter格式化器

public static void dateTimeFormatter(){
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now.format(DateTimeFormatter.BASIC_ISO_DATE));
        System.out.println(now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        System.out.println(now.format(DateTimeFormatter.ISO_DATE_TIME));

        /**
         * 注意此种方式在不同时区的显示方式不一样,在其他时区不会显示中文,会根据当前
         * 系统的默认时区来进行区别显示.
         */
        System.out.println(now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)));
        System.out.println(now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
        System.out.println(now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)));
        System.out.println(now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)));
    }

打印结果:

BASIC_ISO_DATE:20220512
ISO_LOCAL_DATE_TIME:2022-05-12T22:14:10.602321
ISO_DATE_TIME:2022-05-12T22:14:10.602321
FormatStyle.FULL:2022年5月12日星期四
FormatStyle.MEDIUM:2022年5月12日
FormatStyle.SHORT:2022/5/12
FormatStyle.LONG:2022年5月12日

7. TemporalAdjusters调节器

public static void temporalAdjusters(){
        LocalDateTime now = LocalDateTime.now();
        //将时间修改为当月的第一天
        System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));
        //将时间修改为下一个月的第一天
        System.out.println(now.with(TemporalAdjusters.firstDayOfNextMonth()));
        //将时间修改为当月的最后一天
        System.out.println(now.with(TemporalAdjusters.lastDayOfMonth()));
        //将时间修改为当月的最后一个星期五
        System.out.println(now.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)));
        //将时间修改为下一个星期五
        System.out.println(now.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)));
        //将时间修改为上一个星期五
        System.out.println(now.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY)));
    }

8. TemporalQuery查询器

学习的时态类对象(LocalDate,LocalTime)都有一个方法叫做query,可以针对日期进行查询。
R query(TemporalQuery query)这个方法是一个泛型方法,返回的数据就是传入的泛型类的类型,TemporalQuery是一个泛型接口,里面有一个抽象方法是R queryFrom (TemporalAccessor temporal),TemporalAccessor是Temporal的父接口,实际上也就是LocalDate,LocalDateTime相关类的顶级父接口,这个queryFrom的方法的实现逻辑就是,传入一个日期/时间对象通过自定义逻辑返回数据.

如果要计划曰期距离某一个特定天数差距多少天,可以自定义类实现TemporalQuery接口并且作为
参数传入到query方法中。

三、TemporalQuery例题

1.问题描述

  • 需求:计算当前时间距离下一个劳动节还有多少天?

2.问题解析

思路:

1. 使用TemporalQuery查询器 传入当前时间,由查询器处理完后返回

3.编码实现

static class Next51Query implements TemporalQuery<Long>{

        @Override
        public Long queryFrom(TemporalAccessor temporal) {
            LocalDate from = LocalDate.from(temporal);
            LocalDate next = LocalDate.of(from.getYear(), Month.MAY, 1);
            //当前时间已经过了五一
            if(from.isAfter(next)) {
                next = next.plusYears(1);
            }
            //通过ChronoUnit计算两个时间的间隔
            return ChronoUnit.DAYS.between(from, next);
        }
    }

public static void main(String[] args) {
        LocalDate of = LocalDate.of(2022, 5, 4);
        System.out.println(of.query(new Next51Query()));
    }

4.输出结果

362

四、TemporalAdjusters例题

1.问题描述

  • 需求:发工资是每个月15号 如果15号是周末 则调整为上一个周五

2.编码实现

public static void testPayAdjuster(){
        LocalDate of = LocalDate.of(2022, 5, 15);
        LocalDate from = LocalDate.from(new PayAdjuster().adjustInto(of));
        System.out.println("预计发薪日=" + of + ";实际发薪日=" + from);
    }
    /**
     * 获取下一个发薪日的日期
     * 发工资是每个月15号 如果15号是周末 则调整为上一个周五
     */
    static class PayAdjuster implements TemporalAdjuster{

        @Override
        public Temporal adjustInto(Temporal temporal) {
            LocalDate from = LocalDate.from(temporal);
            from = from.withDayOfMonth(15);
            if(from.getDayOfWeek().equals(DayOfWeek.SUNDAY) || from.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
                from = from.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
            }
            return from;
        }
    }

3.输出结果

预计发薪日=2022-05-15;实际发薪日=2022-05-13

五、时间在前后端中如何处理

1. 后端接口中统一使用毫秒时间戳

时间戳是从格林威治时间 1970 年 1 月 1 日至当前时间的总秒数,例如 1608523771000。注意这个基准是带时区的,格林威治时间也就是零时区,我们是东八区。

2. 前端按时区格式化时间

不同地区展示时间格式不同,具体用哪个格式,该有由前端根据页面场景来灵活使用。所以用什么形式属于展现层面的问题,展现是前端的范畴。 后端接口负责的是数据

六、时间在数据库中如何存储

1. 切记不能用字符串存储

  1. 字符串占用的空间更大! 
  2. 字符串存储的日期比较效率比较低(逐个字符进行比对),无法用日期相关的 API 进行计算和比较。

2. DateTime/TimeStamp/Long比较

DateTime 类型是没有时区信息的(时区无关) ,DateTime 类型保存的时间都是当前会话所设置的时区对应的时间。当你的时区更换之后,比如你的服务器更换地址或者更换客户端连接时区设置的话,就会导致你从数据库中读出的时间错误。不要小看这个问题,很多系统就是因为这个问题闹出了很多笑话。

Timestamp 和时区有关。Timestamp 类型字段的值会随着服务器时区的变化而变化,自动换算成相应的时间,说简单点就是在不同时区,查询到同一个条记录此字段的值会不一样。
 

日期类型存储空间日期展示格式日期范围是否存在时区问题
Datetime8字节YYYY-MM-DD HH:MM:SS1000-01-01 00:00:00 ~9999-12-31 23:59:59
Timestamp4字节YYYY-MM-DD HH:MM:SS1970-01-01 00:00:00 ~2037-12-31 23:59:59
数值型时间戳4字节(int) 8字节(bigint)全数字如16088918507121970-01-01 00:00:01 之后的时间

个人推荐使用bigint来存储毫秒时间戳,后端存储的是0时区的毫秒时间戳,返回给前端时按用户所在时区展示时间。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK 8引入了新的时间日期类,用于处理日期时间的操作。其中包括了`LocalDate`、`LocalTime`、`LocalDateTime`、`Instant`、`Duration`和`Period`等类。 `LocalDate`类表示一个日期,可以用来表示年月日信息。你可以使用`of`方法来创建一个指定日期的`LocalDate`对象,例如`LocalDate.parse("2019-03-04", fmt)`可以创建一个表示2019年3月4日的`LocalDate`对象。你也可以使用`plusDays`方法来计算指定日期之后的日期。 `LocalTime`类表示一个时间,可以用来表示时分秒信息。可以使用`of`方法来创建一个指定时间的`LocalTime`对象。 `LocalDateTime`类表示一个日期时间的组合,可以用来表示年月日时分秒信息。你可以使用`of`方法来创建一个指定日期时间的`LocalDateTime`对象。 `Instant`类表示一个时刻,可以用来表示从1970年1月1日UTC时间开始的纳秒数。可以使用`now`方法来获取当前的`Instant`对象。 `Duration`类表示一个时间段,可以用来表示以秒和纳秒为单位的时间差。你可以使用`between`方法来计算两个时间之间的差距。 `Period`类表示一个日期段,可以用来表示年、月、日之间的差距。你可以使用`between`方法来计算两个日期之间的差距。 需要注意的是,`Period`类无法直接计算隔月的差距,而是只能计算日期的差距。如果需要计算隔月的差距,可以使用`toEpochDay`方法将日期转换为自1970年1月1日以来的天数,然后进行计算。 例如,通过`LocalDate.now()`可以获取当前时间,通过`plusDays`方法可以计算当前时间之后100天的日期,而通过`Period.between`方法可以计算两个日期之间的差距并获取天数。 请注意,在上述代码中,`DateTimeFormatter`类用于指定日期的格式化和解析。 中的代码可以计算两个日期之间的差距,并将结果以年、月、日的形式输出。 中的代码展示了如何计算隔月的差距,通过`toEpochDay`方法获取两个日期之间的天数差。 总结来说,JDK 8时间日期类提供了许多强大的功能,可以方便地进行日期时间的操作和计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值