Java之时间处理

一、前言

java1.8引入的新特性中,其中就含有对日期时间的处理,下面我们一起学习和了解。

二、Date、SimpleDateFormat

这是java1.8以前的日期时间处理相关类,其存在以下缺点:

1.设计不合理,在java.utl和java.sql的包中都有日期类,java.util.Date同时包含日期和时间的,而java.sqlL.Date仅仅包含日期,此外用于格式化和解析的类在java.text包下。
2.非线程安全,java.util.Date是非线程安全的,所有的日期类都是可变的,这是java日期类最大的问题之一。
3.时区处理麻烦,日期类并不提供国际化,没有时区支持。

下面我们先复习以下它们的基本使用:

public class DateTest {

    public static void main(String[] args) throws Exception {
        new DateTest().test01();
    }

    public void test01() throws Exception{
        //Date获取当前时间
        //Date创建指定时间
        Date date = new Date();
        Date date2 = new Date(2023,01,01);
        System.out.println("Date获取当前时间:"+date);
        System.out.println("Date创建指定时间:"+date2);

        //SimpleDateFormat.format将日期java对象转换为指定格式日期字符串并输出
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println("java对象转字符串"+sdf.format(date));
        System.out.println("java对象转字符串"+sdf.format(date2));

        //SimpleDateFormat.parse将符合指定格式的日期字符串转换为日期java对象
        Date parseDate = sdf.parse("2023-01-01");
        System.out.println("日期字符串转java对象"+parseDate);
    }
}

执行结果:

Date获取当前时间:Sat Feb 18 16:48:12 CST 2023
Date创建指定时间:Thu Feb 01 00:00:00 CST 3923
java对象转字符串2023-02-18
java对象转字符串3923-02-01
日期字符串转java对象Sun Jan 01 00:00:00 CST 2023

可以看到当使用Date创建指定日期时间时会出现问题。

再看下面一个例子:

public class DateTest {

    public static void main(String[] args) throws Exception {
        new DateTest().test01();
    }

    public void test01() throws Exception{
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date parseDate = sdf.parse("2023-01-01");
        System.out.println("日期字符串转java对象"+parseDate);

        for (int i = 0;i<30;i++){
            new Thread(()->{
                try {
                    System.out.println(sdf.parse("2023-01-01"));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

执行结果:

Sun Jan 01 00:00:00 CST 2023
Sun Jan 01 00:00:00 CST 2023
Sun Jan 01 00:00:00 CST 2023
Exception in thread "Thread-5" Exception in thread "Thread-7" Exception in thread "Thread-16" java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Long.parseLong(Long.java:601)
	at java.lang.Long.parseLong(Long.java:631)
	at java.text.DigitList.getLong(DigitList.java:195)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at com.junjie.test3.DateTest.lambda$test01$0(DateTest.java:33)
	at java.lang.Thread.run(Thread.java:748)
java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Long.parseLong(Long.java:601)
	at java.lang.Long.parseLong(Long.java:631)
	at java.text.DigitList.getLong(DigitList.java:195)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at com.junjie.test3.DateTest.lambda$test01$0(DateTest.java:33)
	at java.lang.Thread.run(Thread.java:748)

可以看到SimpleDateFormat是线程不安全的。

三、java1.8新特性之日期时间处理

由上面的案例,我们可以看到旧版的Date、SimpleDateFormat存在这个一些问题,而且使用起来也及其不方便,所以下面我们学习新的日期时间处理类。

3.1 LocalDate、LocalTime、LocalDateTime

LocalDate:

    public void test01(){
        //1.创建指定的日期
        LocalDate date1 = LocalDate.of(2021,05,06);
        System.out .println( "date1 = "+date1);

        //2.得到当前的日期
        LocalDate now = LocalDate .now();
        System.out.println("now = "+now);

        // 3.根据LocalDate对象获取对应的日期信息
        System.out.println("年:" + now.getYear());
        System.out.println("月:" + now.getMonth().getValue());
        System.out.println("日:" + now.getDayOfMonth());
        System.out.println("星期:" + now.getDayOfWeek().getValue());
    }

执行结果:

date1 = 2021-05-06
now = 2023-02-18
年:2023
月:2
日:18
星期:6

LocalTime:

public void test02(){
        //1、得到指定时间
        LocalTime time = LocalTime.of(5, 26, 33, 2323);
        System.out.println("指定时间:"+time);

        //2、获取当前的时间
        LocalTime now = LocalTime.now();
        System.out.println("当前时间:"+now);

        //3、获取时间信息
        System.out.println("时"+now.getHour());
        System.out.println("分"+now.getMinute());
        System.out.println("秒"+now.getSecond());
        System.out.println("纳秒"+now.getNano());
    }

执行结果:

指定时间:05:26:33.000002323
当前时间:17:12:48.909
时17
分12
秒48
纳秒909000000

LocalDateTime:

public void test03(){
        //获取指定日期时间
        LocalDateTime dateTime = LocalDateTime.of(2023, 01, 01, 12, 12, 33, 1213);
        System.out.println("指定日期时间:"+dateTime);

        //获取当前的日期时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println("当前日期时间:"+now);

        //获取日期时间信息
        System.out.println("年:" + now.getYear());
        System.out.println("月:" + now.getMonth().getValue());
        System.out.println("日:" + now.getDayOfMonth());
        System.out.println("星期:" + now.getDayOfWeek().getValue());
        System.out.println("时"+now.getHour());
        System.out.println("分"+now.getMinute());
        System.out.println("秒"+now.getSecond());
        System.out.println("纳秒"+now.getNano());
    }

执行结果:

指定日期时间:2023-01-01T12:12:33.000001213
当前日期时间:2023-02-18T17:14:30.547
年:2023
月:2
日:18
星期:6
时17
分14
秒30
纳秒547000000

3.2 日期时间的修改、加减、比较

修改:

    public void test01(){
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = "+now);

        //修改日期时间,每次修改都会重新创建一个新的对象
        LocalDateTime localDateTime = now.withYear(1998);
        System.out.println("当前时间:"+now);
        System.out.println("修改年:"+localDateTime);
        System.out.println("修改月:"+now.withMonth(8));
        System.out.println("修改天:"+now.withDayOfMonth(3));
        System.out.println("修改时:"+now.withHour(8));
        System.out.println("修改分:"+now.withMinute(15));
     }

执行结果:

now = 2023-02-18T17:20:03.888
当前时间:2023-02-18T17:20:03.888
修改年:1998-02-18T17:20:03.888
修改月:2023-08-18T17:20:03.888
修改天:2023-02-03T17:20:03.888
修改时:2023-02-18T08:20:03.888
修改分:2023-02-18T17:15:03.888

加减:

 public void test01(){
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now = "+now);
        
        //加减法
        System.out.println("两天后:"+now.plusDays(2));
        System.out.println("两年后:"+now.plusYears(2));
        System.out.println("六月后:"+now.plusMonths(6));
        System.out.println("三小时后:"+now.plusHours(3));
        System.out.println("十年前:"+now.minusYears(10));
        System.out.println("半年前:"+now.minusMonths(6));
        System.out.println("一周前:"+now.minusDays(7));
    }

执行结果:

两天后:2023-02-20T17:20:03.888
两年后:2025-02-18T17:20:03.888
六月后:2023-08-18T17:20:03.888
三小时后:2023-02-18T20:20:03.888
十年前:2013-02-18T17:20:03.888
半年前:2022-08-18T17:20:03.888
一周前:2023-02-11T17:20:03.888

比较:

    public void test02(){
        LocalDate date = LocalDate.of(2022, 1, 3);
        LocalDate now = LocalDate.now();
        System.out.println(now.isAfter(date));
        System.out.println(now.isBefore(date));
        System.out.println(now.isEqual(date));
    }


true
false
false

使用TemporalAdjuster进行修改日期时间:

public void test03(){
        LocalDateTime now = LocalDateTime.now();
        //将当前日期调整到下个月的一号(手动实现)
        TemporalAdjuster adjuster = (temporal)->{
          LocalDateTime dateTime = (LocalDateTime) temporal;
            LocalDateTime nextMonthDay = dateTime.plusMonths(1).withDayOfMonth(1);
            return nextMonthDay;
        };
        LocalDateTime time = now.with(adjuster);
        System.out.println(time);

        //将当前日期调整到下个月的一号(使用内部封装的)
        LocalDateTime time1 = now.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println(time1);
        LocalDateTime time2 = now.with(TemporalAdjusters.firstDayOfYear());
        System.out.println(time2);
    }

执行结果:

2023-03-01T17:51:33.759
2023-03-01T17:51:33.759
2023-01-01T17:51:33.759

注意:在进行日期时间修改和加减的时候,原来的L.ocalDate对象是不会被修改,每次操作都是返回了一个新的LocalDate对象,所以在多线程场景下是数据安全的。

3.3 DateTimeFormatter日期时间格式化

代码:

 public void test01(){
        LocalDateTime now = LocalDateTime.now();
        //DateTimeFormatter.format将日期类型的对象转换为日期字符串(使用系统默认格式)
        DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        String format = now.format(isoLocalDateTime);
        System.out.println(format);
        //DateTimeFormatter.format将日期类型的对象转换为日期字符串(使用指定格式)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String format1 = now.format(formatter);
        System.out.println(format1);

        //DateTimeFormatter.parse将日期字符串转换为日期类型java对象(使用默认格式)
        LocalDateTime parse2 = LocalDateTime.parse("2023-02-18T17:25:48.267", DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        System.out.println(parse2);
        //DateTimeFormatter.parse将日期字符串转换为日期类型java对象(使用指定格式)
        LocalDateTime parse = LocalDateTime.parse("1995-04-05 22:33:22", formatter);
        System.out.println(parse);
    }

执行结果:

2023-02-18T17:30:03.752
2023-02-18 17:30:03
2023-02-18T17:25:48.267
1995-04-05T22:33:22

3.4 计算时间差

方式一:

    public void test01() throws Exception {
        Instant now = Instant.now();
        System.out.println("now = "+now);
        System.out.println("1970年1月1日00:00:00到现在所耗的纳秒:"+now.getNano());

        Thread.sleep(1);
        Instant now1 = Instant.now();
        System.out.println("沉睡一毫秒后的时间差(单位:纳秒):"+(now1.getNano()-now.getNano()));
    }

执行结果:

now = 2023-02-18T09:35:54.939Z
19701100:00:00到现在所耗的纳秒:939000000
沉睡一毫秒后的时间差(单位:纳秒):59000000

方式二:使用Duration计算时间的差值

    public void test02(){
        //计算时间差
        LocalTime now = LocalTime.now();
        LocalTime time = LocalTime.of(22, 48, 59);

        //通过Duration来计算时间差
        Duration duration = Duration.between(now, time);
        System.out.println(now+"和"+time+"相差了(单位:天)"+duration.toDays());
        System.out.println(now+"和"+time+"相差了(单位:时)"+duration.toHours());//取整时,舍去分之后的
        System.out.println(now+"和"+time+"相差了(单位:分)"+duration.toMinutes());//取整分,舍去秒之后的
        System.out.println(now+"和"+time+"相差了(单位:毫秒)"+duration.toMillis());//同理
     }

执行结果:

17:41:12.951和22:48:59相差了(单位:天)0
17:41:12.951和22:48:59相差了(单位:时)5
17:41:12.951和22:48:59相差了(单位:分)307
17:41:12.951和22:48:59相差了(单位:毫秒)18466049

方式三:使用Period计算日期的差值

public void test02(){
        LocalDate nowDate = LocalDate.now();
        LocalDate date = LocalDate.of(1997, 12, 5);
        Period period = Period.between(date, nowDate);
        System.out.println(date+"和"+nowDate+"相差了(单位:年):"+period.getYears());//只算了年份的加减,向下取整
        System.out.println(date+"和"+nowDate+"相差了(单位:月):"+period.getMonths());//只算了月份的加减
        System.out.println(date+"和"+nowDate+"相差了(单位:日):"+period.getDays());//只算了天份的加减
        System.out.println(date+"和"+nowDate+"相差了:"+period.getYears()+"年"+period.getMonths()+"个月"+period.getDays()+"天");
    }
1997-12-05和2023-02-18相差了(单位:年):25
1997-12-05和2023-02-18相差了(单位:月):2
1997-12-05和2023-02-18相差了(单位:日):13
1997-12-05和2023-02-18相差了:25年2个月13天

3.5 时区处理

这是java1.8的新特性,java1.8之前是没有对时区处理的。

LocalDate、LocalTime、LocalDateTime是不带时区的。
带时区的日期时间类分别为: ZonedDate、ZonedTime、zonedDateTime。其中每个时区都对应着ID,ID的格式为“区域/城市”。例如︰ Asia/Shanghai等。

获取所有时区:

  public void test04(){
        //获取所有的时区
        ZoneId.getAvailableZoneIds().forEach(System.out::println);
   }
、、、
、、、
Asia/Anadyr
Australia/Melbourne
Asia/Irkutsk
America/Shiprock
America/Winnipeg
Europe/Vatican
Asia/Amman
Etc/UTC
SystemV/AST4ADT
Asia/Tokyo
America/Toronto
Asia/Singapore
、、、
、、、

时区使用:

   public void test04(){
        //获取所有的时区
//        ZoneId.getAvailableZoneIds().forEach(System.out::println);

        //获取当前时间  中国使用的是东八区时区,比标准时间早8个小时
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        //获取标准时间
        ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC());
        System.out.println(now1);
        //不指定时区时,默认使用计算机系统的时区
        ZonedDateTime now2 = ZonedDateTime.now();
        System.out.println(now2);
        //使用指定时区创建日期时间
        ZonedDateTime now3 = ZonedDateTime.now(ZoneId.of("America/Marigot"));
        System.out.println(now3);
    }

执行结果:

2023-02-18T17:58:34.833
2023-02-18T09:58:34.834Z
2023-02-18T17:58:34.834+08:00[Asia/Shanghai]
2023-02-18T05:58:34.835-04:00[America/Marigot]

标准时间转换为中国时间(东八区):

public class TimeFormatTest {
   public static void main(String args[]) throws ParseException {
      ZonedDateTime zdt = ZonedDateTime.parse("2019-07-31T16:00:00.000Z");
      LocalDateTime localDateTime = zdt.toLocalDateTime();
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
      String cst = formatter.format(localDateTime.plusHours(8));
      System.out.println("北京时间:" + cst);
   }
}

你可能感兴趣的:(java)