JDK8时间日期处理大全

JDK8时间日期处理大全,第1张

一、前言

  • 学习概述:在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{

        @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时区的毫秒时间戳,返回给前端时按用户所在时区展示时间。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/917801.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)