zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Java8 日期时间的操作技巧

日期 操作 技巧 时间 java8
2023-09-11 14:14:53 时间

在 Java 8 中 推出了LocalDate、LocalTime、LocalDateTime这个三个时间处理类,以此来弥补之前的日期时间类的不足,简化日期时间的操作。

Java8
日期和时间类包含LocalDate、LocalTime、Instant、Duration以及Period,这些类都包含在java.time包中


在Java8之前,处理日期时间的类是Date、Calendar 。

  • java.util.Date和java.util.Calendar类易用性差,不支持时区,而且他们都不是线程安全的;
  • 用于格式化日期的类DateFormat被放在java.text包中,它是一个抽象类,所以我们需要实例化一个SimpleDateFormat对象来处理日期格式化,并且DateFormat也是非线程安全,这意味着如果你在多线程程序中调用同一个DateFormat对象,会得到意想不到的结果。
  • 对日期的计算方式繁琐,而且容易出错,因为月份是从0开始的,从Calendar中获取的月份需要加一才能表示当前月份。

目前在西瓜视频上免费刊登 Flutter 系列教程,每日更新,欢迎关注接收提醒

【x1】点击查看提示

【x2】各种系列的教程


1 Java8 获取当前的时间数据

LocalDate、LocalDateTime 的now()方法使用的是系统默认时区.

@SpringBootTest
class DemoDateTests {

  //日志
  private static final Logger LOG = LoggerFactory.getLogger(DemoDateTests.class);

  @Test
  void test() {
    //只获取当前时区的日期
    LocalDate localDate = LocalDate.now();
    LOG.info("localDate: " + localDate);
    //localDate: 2020-09-23
    
    //只获取当前时间
    LocalTime localTime = LocalTime.now();
    LOG.info("localTime: " + localTime);
    //localTime: 23:41:43.991

    //获取不前的日期与时间
    LocalDateTime localDateTime2 = LocalDateTime.now();
    LOG.info("localDateTime2: " + localDateTime2);
    //localDateTime2: 2020-09-23T23:41:43.991
  }
}

在这里获取到的时间 2020-09-23T23:41:43.991 中间携带一个T指的是UTC时间。

UTC时间,是指零时区的时间,它的全称是Coordinated Universal Time ,即世界协调时间。

另一个常见的缩写是GMT,即格林威治标准时间,格林威治位于 零时区,因此,我们平时说的UTC时间和GMT时间在数值上面都是一样的。

1884 年, 国际经度会议将地球表面按经线等分为24 区,称为时区。即以本初子午线为基准, 东西经度各7.5 度的范围作为零时区,
然后每隔15度为一时区,每个时区相差一小时,北京时区为东八区。
比如:北京时间 2020-09-23 12:00:00 那么 utc 时间就是 2020-09-23 04:00:00;

当然获取到的时间格式可能不是我们想要的,可以结合 DateTimeFormatter来对其进行格式化操作如下:

 @Test
 void test1() {
   
   //使用静态方法生成此对象
   LocalDateTime localDateTime = LocalDateTime.now();
   LOG.info("localDateTime: " + localDateTime);
   ///localDateTime: 2020-09-23T23:47:41.752
   

   //格式化时间
   DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
   //格式化后的时间
   String formatDate = localDateTime.format(formatter);

   LOG.info("格式化之后的时间: " + formatDate);
   //格式化之后的时间: 2020-09-23 23:47:41
 }
1.1 LocalDate

LocalDate类表示一个具体的日期,但不包含具体时间,也不包含时区信息。可以通过LocalDate的静态方法of()创建一个实例,LocalDate也包含一些方法用来获取年份,月份,天,星期几等。

@Test
 void test3() {
   //当然你也可以使用当前的时间
   //LocalDate localDate = LocalDate.now();
   // 初始化一个日期:2020-09-24
   LocalDate localDate = LocalDate.of(2020, 9, 24);    
    
   int year = localDate.getYear();                     // 年份:2020
   Month month = localDate.getMonth();                 // 月份:SEPTEMBER
   int dayOfMonth = localDate.getDayOfMonth();         // 月份中的第几天:24
   DayOfWeek dayOfWeek = localDate.getDayOfWeek();     // 一周的第几天:THURSDAY
   int length = localDate.lengthOfMonth();             // 月份的天数:30
   boolean leapYear = localDate.isLeapYear();          // 是否为闰年:true
 }

断点单元测试运行如下
在这里插入图片描述

1.2 LocalTime

LocalTime 与 LocalDate 类似,区别在于 LocalDate 不包含具体时间,而 LocalTime 包含具体时间

@Test
void test4() {
  //当前的时间
  LocalTime localTime = LocalTime.now();
  
  int hour = localTime.getHour();       // 时:07
  int minute = localTime.getMinute();   // 分:22
  int second = localTime.getSecond();   // 秒:20
}

断点单元测试运行如下
在这里插入图片描述

1.3 LocalDateTime

LocalDateTime 类是 LocalDate 和 LocalTime 的结合体,可以通过of()方法直接创建,也可以调用 LocalDate 的 atTime() 方法或 LocalTime 的 atDate() 方法将 LocalDate 或 LocalTime 合并成一个LocalDateTime.

@Test
void test5() {
  
  ///通过 of 方法创建  LocalDateTime
  LocalDateTime localDateTime = LocalDateTime.of(
      2020,//年
      Month.JANUARY,//月 
      4, //日
      //时 分 秒
      17, 23, 52);

}

可以这样理解 LocalDate 与 LocalTime 向 LocalDateTime 转化,就是将两者合并的过程,如下所示:

  @Test
  void test6() {


    //通过of方法来创建 LocalDate
    //参数为年 月 日
    LocalDate localDate = LocalDate.of(2020, Month.JANUARY, 4);

    //通过of方法来创建 LocalTime
    //参数为 时分秒
    LocalTime localTime = LocalTime.of(07, 23, 52);

    // LocalDate 转 LocalDateTime
    LocalDateTime localDateTime = localDate.atTime(localTime);

    // LocalTime  转 LocalDateTime
    LocalDateTime localDateTime1 = localTime.atDate(localDate);

  }

断点单元测试运行如下在这里插入图片描述

当然也可以反过来 LocalDateTime 转为 LocalDate 或者 LocalTime,如下所示:


LocalDate date = localDateTime.toLocalDate();

LocalTime time = localDateTime.toLocalTime();

2 LocalDateTime 与毫秒之间的妙操作

时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数

ZoneId 指地区
ZoneOffset指偏移数据

 @Test
 void test2() {

   //使用静态方法生成此对象
   LocalDateTime localDateTime = LocalDateTime.now();
   LOG.info("localDateTime: " + localDateTime);
   ///localDateTime: 2020-09-23T23:49:11.832

   //转化为时间戳(秒)
   long epochSecond = localDateTime.toEpochSecond(ZoneOffset.of("+8"));
   //转化为毫秒
   long epochMilli = localDateTime.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();

   LOG.info("时间戳为:(秒) "   + epochSecond + "; (毫秒): " + epochMilli);
   ///时间戳为:(秒) 1600876151; (毫秒): 1600876151832



   //时间戳(毫秒)转化成LocalDateTime
   Instant instant = Instant.ofEpochMilli(epochMilli);
   LocalDateTime localDateTime3 = LocalDateTime.ofInstant(instant, ZoneOffset.systemDefault());
  
   //时间戳(秒)转化成LocalDateTime
   Instant instant2 = Instant.ofEpochSecond(epochSecond);
   LocalDateTime localDateTime4 = LocalDateTime.ofInstant(instant2, ZoneOffset.systemDefault());
  

 }
3 LocalDateTime 与 Date 的妙操作

Instant 类代表的是某个时间,是精确到纳秒的,与我们常使用的System.currentTimeMillis()有些类似,不过Instant可以精确到纳秒(Nano-Second),System.currentTimeMillis()方法只精确到毫秒(Milli-Second)。

如果使用纳秒去表示一个时间则原来使用一位Long类型是不够的,需要占用更多一点的存储空间,实际上其内部是由两个Long字段组成。

需要注意的是 但Instant 代表的是一个时间,并不包括时区的概念。

  // Date 转化成 LocalDateTime
  public static LocalDateTime dateToLocalDate(Date date) {
    //Date转纳秒
    Instant instant = date.toInstant();
    //获取系统默认的时区
    ZoneId zoneId = ZoneId.systemDefault();
    //转化
    return instant.atZone(zoneId).toLocalDateTime();
  }


  // LocalDateTime 转化成 Date
  public static Date localDateTimeToDate(LocalDateTime localDateTime) {
    ZoneId zoneId = ZoneId.systemDefault();
    ZonedDateTime zdt = localDateTime.atZone(zoneId);
    return Date.from(zdt.toInstant());
  }
 // Date 毫秒数据 转化成 LocalDateTime
 public static LocalDateTime dateToLocalDateMil(Long datemilli) {
   //将毫秒数据转化为纳秒
   Instant instant = Instant.ofEpochMilli(datemilli);
   ZoneId zoneId = ZoneId.systemDefault();
   return instant.atZone(zoneId).toLocalDateTime();
 }

当然 Instant 也有很多妙操作

Instant 它的内部使用了两个常量,seconds表示从 1970-01-01 00:00:00 开始到现在的秒数,nanos 表示纳秒部分(nanos的值不会超过999,999,999)
在这里插入图片描述

Instant除了使用now()方法创建外,还可以通过ofEpochSecond方法创建:

@Test
void test7() {

  //参数一为秒,参数二为纳秒,
  //这里的代码表示从1970-01-01 00:00:00开始后三分钟的10万纳秒的时刻
  Instant instant = Instant.ofEpochSecond(180, 100000);

  LOG.info("时间为:"   + instant );

}

断点单元测试运行如下
在这里插入图片描述

4 Duration 类

在上述的操作中没有提到这个类,Duration的内部实现与Instant类似,也是包含两部分:seconds表示秒,nanos表示纳秒。两者的区别是Instant用于表示一个时间戳(或者说是一个时间点),而Duration表示一个时间段,所以Duration类中不包含now()静态方法。可以通过Duration.between()方法创建Duration对象。

@Test
 void test8() {

   ///开始时间
   LocalDateTime from = LocalDateTime.of(2020, Month.JANUARY, 9, 10, 7, 0);    // 2017-01-05 10:07:00
   //结束时间
   LocalDateTime to = LocalDateTime.of(2020, Month.FEBRUARY, 10, 10, 7, 0);

   // 表示从 2020-09-05 10:07:00 到 2020-09-05 10:07:00 这段时间
   Duration duration = Duration.between(from, to);

   long days = duration.toDays();              // 这段时间的总天数
   long hours = duration.toHours();            // 这段时间的小时数
   long minutes = duration.toMinutes();        // 这段时间的分钟数
   long seconds = duration.getSeconds();       // 这段时间的秒数
   long milliSeconds = duration.toMillis();    // 这段时间的毫秒数
   long nanoSeconds = duration.toNanos();      // 这段时间的纳秒数


   LOG.info("时间为:"   + duration );

 }

断点单元测试运行如下
在这里插入图片描述

5 Period 类

Period在概念上与Duration类似,区别在于Period是以年月日来衡量一个时间段,比如1年3个月6天(代码清单 5-1)。

Period对象可以通过构造方法与between()方法创建,值得注意的是,由于Period是以年月日衡量时间段,所以between()方法只能接收LocalDate类型的参数。

//代码清单 5-1
 @Test
 void test9() {

   //1年3个月6天
   Period period = Period.of(1, 3, 6);

   // 2017-01-05 到 2017-02-05 这段时间
   Period period2 = Period.between(
       LocalDate.of(2020, 1, 5),
       LocalDate.of(2020, 2, 5));

   LOG.info("时间为:"   + period );
 }

断点单元测试运行如下在这里插入图片描述

6 指定日期的前后时间妙操作

//代码清单 6-1
@Test
void test10() {
   //使用当前的时间
  LocalDate localDate = LocalDate.now();

  LocalDate date4 = localDate.plusYears(1);                // 增加一年 2021-09-24
  LocalDate date5 = localDate.minusMonths(2);              // 减少两个月 2021-07-24
  LocalDate date6 = localDate.plus(5, ChronoUnit.DAYS);    // 增加5天 2021-09-29

  LOG.info("时间为:"   + date6 );
}

断点单元测试运行如下
在这里插入图片描述

7 格式化日期

新的日期API中提供了一个 DateTimeFormatter 类(在上述也有使用到)用于处理日期格式化操作,它被包含在java.time.format包中,Java 8的日期类有一个format()方法用于将日期格式化为字符串,该方法接收处理一个DateTimeFormatter类型参数

//代码清单 7-1
@Test
void test11() {
  //使用当前的时间
  LocalDateTime dateTime = LocalDateTime.now();


  String strDate1 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE);    // 20200924
  String strDate2 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE);    // 2020-09-24
  String strDate3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME);    // 08:23:31.463
  String strDate4 = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));   // 2020-09-24

  LOG.info("时间为:"   + dateTime );
}

断点单元测试运行如下在这里插入图片描述
将现有的日期字符串解析为 LocalDateTime 、LocalDate、LocalTime 如下代码清单7-2所示。

解析的核心思路为 先定义 格式映射 DateTimeFormatter,然后再调用对应的parse方法

//代码清单 7-2
@Test
void test12() {

  String strDate = "2020-09-24";
  String strDate2 = "2020-09-24 08:23:31";

  LocalDate date = LocalDate.parse(strDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
  
  LocalTime time = LocalTime.parse(strDate, DateTimeFormatter.ofPattern(" HH:mm:ss"));

  //定义 formatter
  DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  //解析操作
  LocalDateTime dateTime = LocalDateTime.parse(strDate2, dateTimeFormatter);

  LOG.info("时间为:"   + dateTime );

}

断点单元测试运行如下
在这里插入图片描述


当然也有公众号了,感兴的伙伴可以关注关注

在这里插入图片描述