JAVA-LocalDateTime时间格式化,转换时间戳和源码分析(万字长文详解)

本文详细介绍了Java 8中新引入的时间类型LocalDateTime的使用方法,包括时间的获取、格式化、转换等,并深入分析了时间格式化的源码实现。

JAVA-LocalDateTime时间格式化,转换时间戳和源码分析

LocalDateTime

LocalDateTime作为java8新加的时间类型,也是后面开发中常用的时间类型。

作为时间类型,最关注的点无非是这几个

  • 获取当前时间
  • 获取指定时间
  • 时间格式化
  • 时间转时间戳
  • 时间戳转时间
  • 时间比较
  • 时间加减

这些点通过LocalDateTime来操作,都会比较简单

获取当前时间

只需要now一下就可以获取到当前的时间,还是很方便的。

LocalDateTime time = LocalDateTime.now();

再看一下之前的Date

Date date = new Date();

获取指定时间

这个有比较多的方式

  • 通过原来的datedateTime类型来生成
  • 通过传年月日时分秒生成
LocalDateTime time = LocalDateTime.of(2022,11,30,6,6,6);

原来Date类的方式。比较奇怪,他的年份会+1900,所以2022年就得是122,月份也会+1,所以11月就是10.但是这个方法呢后面会被删除,已经被标记为弃用了,使用Calendar代替。

Date date = new Date(122,10,30,6,6,6);

看一下Calendar的使用。这个年份就正常了,是2022,但是月份还是会+1.

Calendar calendar = Calendar.getInstance();
calendar.set(2022,10,30,6,6,6);

时间格式化

时间格式化都是通过format函数,需要传一个DateTimeFormatter对象进去,我们可以通过DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")来生成自己想要的格式。

DateTimeFormatter类里面也有一些定义好的格式可以直接用,除了下面列出的还有一些其他的,感兴趣可以看一下,不过我觉得都没啥用。

  • ISO_DATE_TIME 2011-12-03T10:15:30
  • ISO_OFFSET_DATE_TIME 2011-12-03T10:15:30+01:00
  • ISO_LOCAL_DATE_TIME 2011-12-03T10:15:30
time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

看一下Date的格式化。这个需要借用SimpleDateFormat类来完成格式化。

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
format.format(date);

时间转时间戳

时间转时间戳分为两种,一种是当你已经有一个LocalDateTime类型的时间了,需要转换成秒或者毫秒的时间戳。

时间转换秒级时间戳

只需要直接用toEpochSecond方法就可以了。

LocalDateTime time = LocalDateTime.now();
time.toEpochSecond(ZoneOffset.ofHours(8));

Date类型没有办法直接获取秒级时间戳,只能获取毫秒级再转秒。

时间转换毫秒级时间戳

转换毫秒需要先转换成instant对象,然后才能转换成毫秒级时间戳。

LocalDateTime time = LocalDateTime.now();
time.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();

Date获取毫秒就很简单了。

Date date = new Date();
date.getTime();

字符串转换成时间戳

时间转时间戳分为两种,除了上面的,还有一种是有一个格式化好的字符串,比如2022-12-18 10:00:00这种,但是这个是字符串并不是时间类型。所以要先转换成LocalDateTime类型,然后就可以转换成时间戳了。

其实就是上面格式化的一种反向操作。使用parse方法就可以了。

LocalDateTime.parse("2022-12-18 10:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime.parse("2022-12-18", DateTimeFormatter.ofPattern("yyyy-MM-dd"));

Date类型的字符串转时间戳也是通过SimpleDateFormat类来完成。

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = format.parse("2022-12-18 10:00:00")

时间戳转时间

那如果我们现在转换成时间戳以后又想转换成时间呢?也可以通过instant对象来做到。

毫秒时间戳转时间

Instant.ofEpochMilli(1671365543834)是将一个毫秒时间戳转换成instant对象。在通过ofInstant方法就可以将instant对象转换成LocalDateTime对象了。

LocalDateTime.ofInstant(Instant.ofEpochMilli(1671365543834), ZoneOffset.ofHours(8));

Date

Date date = new Date(1669759566000L);
秒时间戳转时间

Instant.ofEpochSecond(1671365543)是将一个秒时间戳转换成一个instant对象。在通过ofInstant方法就可以将instant对象转换成LocalDateTime对象了。

LocalDateTime.ofInstant(Instant.ofEpochSecond(1671365543), ZoneOffset.ofHours(8));

Date类不支持秒,只能把秒转成毫秒在转时间戳。

时间比较

通过compareTo方法可以进行时间的一个比较大小。如果大于会返回1,小于返回-1.

LocalDateTime time = LocalDateTime.now();
time.compareTo(LocalDateTime.now());

Date也是通过compareTo方法进行比较

Date date = new Date(1669759566000L);
date.compareTo(new Date());

时间加减

如果加上几天,就是plusDays。加几个小时就是plusHours。当然也可以使用plus

LocalDateTime time = LocalDateTime.now();
time.plusDays(1);
time.plusHours(1);
time.plus(Period.ofDays(1));

如果减去几天就是minusDays.减去几个小时就是minusHours。当然也可以使用minus

LocalDateTime time = LocalDateTime.now();
time.minusDays(1);
time.minusHours(1);
time.minus(Period.ofDays(1));

Date类不支持时间加减,只能通过Calendar类实现。

Date date = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, 1);
//减去
calendar.add(Calendar.DAY_OF_MONTH, -1);

时间格式在入参出参中的使用

入参的时候需要通过JsonFormat注解来指定需要的是字符串类型和对应的时间格式。

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
private LocalDate date;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime time;

出参的时候就很简单了,因为只是返回了一个字符串。

private String time;

格式化时间源码分析

格式化的时候这两个年是不一样的,具体的可以看一下源码。我们来找一下。

首先点进去是LocalDateTime这个类里面

@Override  // override for Javadoc and performance
public String format(DateTimeFormatter formatter) {
   
   
    //判断参数是否空
    Objects.requireNonNull(formatter, "formatter");
    //真正的执行格式化
    return formatter.format(this);
}

接下来点进去,看一下怎么执行的,可以看到又调用了formatTo这个函数,说明主要的格式化代码都在这里面。

public String format(TemporalAccessor temporal) {
   
   
       //创建了一个32长度的字符串构建器
        StringBuilder buf = new StringBuilder(32);
        //格式化主要代码
        formatTo(temporal, buf);
        //转成字符串
        return buf.toString();
    }

看一下formatTo函数,可以发现主要是调用printerParser这个对象的format方法,那我们这个对象是哪来的呢,是在一开始指定格式化类型的时候来的。不同的格式化类型对应不同的解析器,也就会执行不同的format方法。

public void formatTo(TemporalAccessor temporal, Appendable appendable) {
   
   
        //判断参数,这里不用管
        Objects.requireNonNull(temporal, "temporal");
        Objects.requireNonNull(appendable, "appendable");
        try {
   
   
            //创建一个DateTimePrintContext对象
            DateTimePrintContext context = new DateTimePrintContext(temporal, this);
            //判断,显然我们之前传过来的就是一个StringBuilder
            if (appendable instanceof StringBuilder) {
   
   
                //主要看这个怎么处理  这里有个 printerParser 对象,这个对象是怎么来的呢,其实是上面DateTimeFormatter.ofPattern的时候给创建的。
                printerParser.format(context, (StringBuilder) appendable);
            } else {
   
   
                //这里其实就是如果传的不是个StringBuilder,就在创建一个然后执行
                // buffer output to avoid writing to appendable in case of error
                StringBuilder buf = new StringBuilder(32);
                printerParser.format(context, buf);
                appendable.append(buf);
            }
        } catch (IOException ex) {
   
   
            throw new DateTimeException(ex.getMessage(), ex);
        }
    }

接下来我们看一下ofPattern这个方法里面是怎样的吧。这里是创建了一个 时间格式化的建造者,然后把我们这个字符串添加进去了。

//这里的字符串就是我们传的 yyyy-MM-dd HH:mm:ss
public static DateTimeFormatter ofPattern(String pattern) {
   
   
    return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
}

看一下appendPattern里面是怎么把字符串加进去的。

public DateTimeFormatterBuilder appendPattern(String pattern) {
   
   
    //忽略
    Objects.requireNonNull(pattern, "pattern");
    //主要的解析逻辑
    parsePattern(pattern);
    return this;
}

继续追踪到parsePattern方法里面。这个方法代码比较多,这里只关注我们想知道的。其余的有兴趣的可以看一下。

private void parsePattern(String pattern) {
   
   
    //这里给字符串做循环,注意 pattern = yyyy-MM-dd HH:mm:ss 这个字符串。
    for (int pos = 0; pos < pattern.length(); pos++) {
   
   
        //取出字符 比如第一个就是 y 对应的ASCII码就是121
        char cur = pattern.charAt(pos);
        //这里就是判断是否是大小写字母了,也就是A-Z或者a-z
        if ((cur >= 'A' && cur <= 'Z'
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值