Java日期时间API

目录

前言

一、Java日期时间API能帮我们解决什么问题?

二、Java8之前的日期时间API

1.Date类

2.Calendar类

3.SimpleDateFormat类

三、Java8的日期时间API

1、Java8为什么更新了新的日期时间API?

2、LocalDate、LocalTime、LocalDateTime类

3、ZonedDateTime 类

4、Instant 类

5、DateTimeFormatter 类

6、其它API

7、参考:与传统日期处理的转换

总结​​​​​​​


前言

我们知道日期时间是我们在程序中经常需要处理的数据,尤其是涉及到数据库的查询时,我们对日期时间的操作会更多,那么如何用好Java日期时间API就很关键。


一、Java日期时间API能帮我们解决什么问题?

获取当前时间、获取指定的日期、获取指定日期的前一天,以及将时间或者日期格式化成字符串,甚至,我们可以指定日期时间的时区;或者指定一个特定的国家/区域、语言环境使Java程序国际化。

二、Java8之前的日期时间API

1.Date类

代码如下(示例):

public class Main {
    public static void main(String[] args) {
        long time = System.currentTimeMillis();//返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。称为时间戳
        System.out.println(time);
        Date date1 = new Date();//构造器一:等于:Date(System.currentTimeMillis()):创建一个对应当前时间的Date对象
        System.out.println(date1.toString());//Thu Apr 21 22:03:26 CST 2022

        System.out.println(date1.getTime());//1650549806094

        Date date2 = new Date(1650549806094L);//构造器二:创建指定毫秒数的Date对象
        System.out.println(date2.toString());

        java.sql.Date date3 = new java.sql.Date(35235325345L);//创建java.sql.Date对象
        System.out.println(date3);//1971-02-13

        //如何将java.util.Date对象转换为java.sql.Date对象
        Date date6 = new Date();
        java.sql.Date date7 = new java.sql.Date(date6.getTime());
    }
}

  1. Date是java.util包下的,而java.sql.Date是它的子类,用于与数据库的日期类型映射。

  2. CST:China Standard Time;在这里是中国标准时间的缩写,等同于GMT+08:00。


不过这个 CST 缩写比较纠结的是它可以同时代表四个不同的时间:

  • 中部标准时间(北美洲):Central Standard Time (USA) UT-6:00

  • 澳州中部时间:Central Standard Time (Australia) UT+9:30

  • 中国时间:China Standard Time UT+8:00

  • 古巴标准时间:Cuba Standard Time UT-4:00

2.Calendar类

public class Main {
    public static void main(String[] args) {
        //1.实例化
        //方式一:创建其子类(GregorianCalendar)的对象
        //方式二:调用其静态方法getInstance()
        Calendar calendar = Calendar.getInstance();

        //2.常用方法  get()
        int days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);
        System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
        
        //        set()  calendar可变性
        calendar.set(Calendar.DAY_OF_MONTH, 22);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);

        //        add()
        calendar.add(Calendar.DAY_OF_MONTH, -3);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);

        //getTime():日历类---> Date
        calendar.set(2022, 0, 28);
        Date date = calendar.getTime();
        System.out.println(date);//Fri Jan 28 22:29:43 CST 2022

        //setTime():Date ---> 日历类
        Date date1 = new Date();
        calendar.setTime(date1);
        days = calendar.get(Calendar.DAY_OF_MONTH);
        System.out.println(days);
    }
}

需要注意的是设置月份的时候,实际月份-1,才是要设置的值。例如:calendar.set(2022, 0, 28);设置的是2022年1月28号。

3.SimpleDateFormat类

public class Main {
    public static void main(String[] args) throws ParseException {
        Date date = new Date();
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

        String format1 = sdf1.format(date);//格式化
        System.out.println(format1);//2022-04-21 10:54:44

        //解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现),否则,抛异常
        Date date2 = sdf1.parse("2022-01-11 11:11:11");
        System.out.println(date2);//Tue Jan 11 11:11:11 CST 2022
        
        SimpleDateFormat sdf2 = new SimpleDateFormat("y-M-d h:m:s a E");
        //设置国家、和时区
        SimpleDateFormat sdf3 = new SimpleDateFormat("y-M-d h:m:s a E", Locale.UK);
        sdf3.setTimeZone(TimeZone.getTimeZone("UTC"));
        System.out.println(sdf2.format(date));//2022-4-21 10:54:44 下午 周四
        System.out.println(sdf3.format(date));//2022-4-21 2:54:44 pm Thu
    }
}

国家:Date的默认国家是:Locale.US;SimpleDateFormat的默认国家跟随系统或容器。

时区:Date和SimpleDateFormat的默认时区都是操作系统、或者程序所在容器的时区。

当使用docker等容器运行Java程序时,要注意有时docker的默认时区是UTC,即零时区。在操作系统上(一般默认是Asia/Shanghai,GMT+8,Etc/GMT-8)是东八区。如果程序中不强制指定时区,同一个时间戳这两个环境下格式化为字符串就会出现相差8个小时的情况。

三、Java8的日期时间API

1、Java8为什么更新了新的日期时间API?

JDK 1.0中包含了 一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用 了。而Calendar并不比Date好多少。

它们面临的问题是:

  1. 可变性:像日期和时间这样的类应该是不可变的。
  2. 偏移性:Date中的年份是从1900开始的,而月份都从0开始。
  3. 格式化:格式化只对Date有用,Calendar则不行。
  4. 此外,它们也不是线程安全的;不能处理闰秒等。

新时间日期API:

  • java.time – 包含值对象的基础包
  • java.time.chrono – 提供对不同的日历系统的访问
  • java.time.format – 格式化和解析时间和日期
  • java.time.temporal – 包括底层框架和扩展特性
  • java.time.zone – 包含时区支持的类

说明:大多数开发者只会用到基础包和format包,也可能会用到temporal包。因此,尽管有68个新的公开类型,大多数开发者,大概将只会用到其中的三分之一。

2、LocalDate、LocalTime、LocalDateTime类

LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。类似与Calendar

代码如下:

public class Main {
    public static void main(String[] args) throws ParseException {
        //now():获取当前的日期、时间、日期+时间
        LocalDateTime localDateTime = LocalDateTime.now();
        LocalDate localDate = localDateTime.toLocalDate();
        LocalTime localTime = localDateTime.toLocalTime();


        System.out.println(localDate);//2022-04-22
        System.out.println(localTime);//21:42:05.552107800
        System.out.println(localDateTime);//2022-04-22T21:42:05.552107800

        //of():设置指定的年、月、日、时、分、秒。没有偏移量
        LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);
        System.out.println(localDateTime1);//2020-10-06T13:23:43

        //getXxx():获取相关的属性
        System.out.println(localDateTime.getDayOfMonth());//22
        System.out.println(localDateTime.getDayOfWeek());//FRIDAY
        System.out.println(localDateTime.getMonth());//APRIL
        System.out.println(localDateTime.getMonthValue());//4
        System.out.println(localDateTime.getMinute());//42

        //体现不可变性,只能返回一个新的日期对象,原日期对象不变
        //withXxx():设置相关的属性
        LocalDate localDate1 = localDate.withDayOfMonth(22).withYear(2012);
        System.out.println(localDate);//2022-04-22
        System.out.println(localDate1);//2012-04-22

        LocalDateTime localDateTime2 = localDateTime.withHour(4);
        System.out.println(localDateTime);//2022-04-22T21:42:05.552107800
        System.out.println(localDateTime2);//2022-04-22T04:42:05.552107800

        //不可变性
        LocalDateTime localDateTime3 = localDateTime.plusMonths(3);
        System.out.println(localDateTime);//2022-04-22T21:42:05.552107800
        System.out.println(localDateTime3);//2022-07-22T21:42:05.552107800

        LocalDateTime localDateTime4 = localDateTime.minusDays(6);
        System.out.println(localDateTime);//2022-04-22T21:42:05.552107800
        System.out.println(localDateTime4);//2022-04-16T21:42:05.552107800
    }
}

3、ZonedDateTime 类

如果我们需要考虑到时区,就可以使用时区的日期时间API:

public class Main {
    public static void main(String[] args) throws ParseException {
        // 获取当前时间日期
        ZonedDateTime zonedDateTime = ZonedDateTime.parse("2022-12-03T10:15:30+05:30[Asia/Shanghai]");
        System.out.println("zonedDateTime: " + zonedDateTime);//zonedDateTime: 2022-12-03T12:45:30+08:00[Asia/Shanghai]

        ZoneId id = ZoneId.of("Europe/Paris");
        System.out.println("ZoneId: " + id);

        ZoneId currentZone = ZoneId.systemDefault();
        System.out.println("当期时区: " + currentZone);

        LocalDateTime localDateTime = LocalDateTime.now();
        ZonedDateTime zonedDateTime2 = ZonedDateTime.of(localDateTime, id);
        ZonedDateTime zonedDateTime4 = ZonedDateTime.of(localDateTime, currentZone);
        System.out.println(zonedDateTime2);//2022-04-22T21:55:52.306003400+02:00[Europe/Paris]
        System.out.println(zonedDateTime4);//2022-04-22T21:55:52.306003400+08:00[Asia/Shanghai]
    }
}

由于有夏令时、与系统或容器的默认时区不可控性,请务必遵从以下两条最佳实践

  1. 永远显式地指定你需要的时区,即使你要获取的是默认时区
  2. 使用JVM的默认时区需当心,建议时区和当前会话保持绑定

4、Instant 类

Instant的使用类似于java.util.Date类。
public class Main {
    public static void main(String[] args) throws ParseException {
        //now():获取本初子午线对应的标准时间
        Instant instant = Instant.now();
        System.out.println(instant);//2022-04-22T14:04:45.752928200Z

        //添加时间的偏移量
        OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
        System.out.println(offsetDateTime);//2022-04-22T22:04:45.752928200+08:00

        //toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数  ---> Date类的getTime()
        long milli = instant.toEpochMilli();
        System.out.println(milli);//1650636285752

        //ofEpochMilli():通过给定的毫秒数,获取Instant实例  -->Date(long millis)
        Instant instant1 = Instant.ofEpochMilli(1550475314878L);
        System.out.println(instant1);//2019-02-18T07:35:14.878Z
    }
}

5、DateTimeFormatter 类

格式化或解析日期、时间类似于SimpleDateFormat
public class Main {
    public static void main(String[] args) throws ParseException {
        //方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        //格式化:日期-->字符串
        LocalDateTime localDateTime = LocalDateTime.now();
        String str1 = formatter.format(localDateTime);
        System.out.println(localDateTime);//2022-04-22T22:18:11.785743800
        System.out.println(str1);//2022-04-22T22:18:11.7857438

        //解析:字符串 -->日期
        TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");
        System.out.println(parse);//{},ISO resolved to 2019-02-18T15:42:18.797

        //方式二:
        //本地化相关的格式。如:ofLocalizedDateTime()
        //FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTime
        DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
        //格式化
        String str2 = formatter1.format(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
        System.out.println(str2);//2022年4月22日 CST 下午10:18:11

        //本地化相关的格式。如:ofLocalizedDate()
        //FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDate
        DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
        //格式化
        String str3 = formatter2.format(LocalDate.now());
        System.out.println(str3);//2022年4月22日

        //重点: 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)
        DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
        //格式化
        String str4 = formatter3.format(LocalDateTime.now());
        System.out.println(str4);//2022-04-22 10:18:11

        //解析
        TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
        System.out.println(accessor);//{MilliOfSecond=0, MinuteOfHour=52, NanoOfSecond=0, MicroOfSecond=0, HourOfAmPm=3, SecondOfMinute=9},ISO resolved to 2019-02-18
    }
}

6、其它API

  1. ZoneId:该类中包含了所有的时区信息,一个时区的ID,如 Europe/Paris 
  2. Clock:使用时区提供对当前即时、日期和时间的访问的时钟。 
  3. 持续时间:Duration,用于计算两个“时间”间隔 
  4. 日期间隔:Period,用于计算两个“日期”间隔 
  5. TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整 到“下一个工作日”等操作。 
  6. TemporalAdjusters : 该类通过静态方法 (firstDayOfXxx()/lastDayOfXxx()/nextXxx())提供了大量的常用 TemporalAdjuster 的实现。

7、参考:与传统日期处理的转换

To 遗留类 From 遗留类
java.time.Instant与java.util.Date Date.from(instant) date.toInstant()
java.time.Instant与java.sql.Timestamp Timestamp.from(instant) timestamp.toInstant()
java.time.ZonedDateTime与 java.util.GregorianCalendar GregorianCalendar.from(zonedDateTime) cal.toZonedDateTime()
java.time.LocalDate与java.sql.Time Date.valueOf(localDate) date.toLocalDate()
java.time.LocalTime与java.sql.Time Date.valueOf(localDate) date.toLocalTime()
java.time.LocalDateTime与 java.sql.Timestamp Timestamp.valueOf(localDateTime) timestamp.toLocalDateTime()
java.time.ZoneId与java.util.TimeZone Timezone.getTimeZone(id) timeZone.toZoneId()
java.time.format.DateTimeFormatter与 java.text.DateFormat formatter.toFormat()

总结

新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。而且,新的日期时间API是线程安全的。大家可以多尝试使用Java8的新API.

你可能感兴趣的:(Java基础,java)