1. JDK8 之前的日期时间API

1.1 java.lang.System类

System类提供的public static long currentTimeMillis()用于返回当前时间与1971年1月1日0时0分0秒之间以毫秒为单位的时间差,此方法适用于计算时间差。

计算世界时间的主要标准有:

  • UTC(Coordinated Universal Time)
  • GMT(Greenwich Mean Time)
  • CST(Central Standard Time)
@Test
public void test() {
    long time = System.currentTimeMillis();
    System.out.println(time);
}

返回的是一个时间差,往往我们将其称为时间戳

1.2 java.util.Date类

表示特定的瞬间,精确到毫秒

  • 构造器:

    • Date():使用无参构造器创建的对象可以获取本地当前时间
    • Date(long date)
  • 常用方法:

    • getTime():返回自1970年1月1日00:00:00 GMT以来此Date对象表示的毫秒数
    • toString():把此Date对象转换为一下形式的Stringdow mon dd hh:mm:ss zzz yyyy其中,dow是一周中的某一天(Sun, Mon, Tue, Wed, Thu, Fri, Sat),zzz是时间标准
    • 其他方法大多数都过时了
@Test
public void test2() {
    // 两个构造器的使用
    Date date = new Date();
    System.out.println("date: " + date);
    System.out.println("date.getTime(): " + date.getTime());

    Date date1 = new Date(0);
    System.out.println("date1: " + date1);
}

运行结果:

date: Wed Mar 17 21:52:49 CST 2021
date.getTime(): 1615989169194
date1: Thu Jan 01 08:00:00 CST 1970

1.3 java.sql.Date类

java.sql.Date对应着数据库中日期类型的变量,是java.util.Date类的子类

@Test
public void test3() {
    java.sql.Date date = new java.sql.Date(3547857487584L);
    System.out.println(date);
}

运行结果:

2082-06-05

如何将java.util.Date对象转换为java.sql.Date对象:

@Test
public void test4() {
    // 情况一:
    Date date1 = new java.sql.Date(2735648723645L);
    java.sql.Date date2 = (java.sql.Date) date1;
    System.out.println("date1: " + date1);
    System.out.println("date2: " + date2);

    // 情况二:
    Date date3 = new Date();
    java.sql.Date date4 = new java.sql.Date(date3.getTime());
    System.out.println("date3: " + date3);
    System.out.println("date4: " + date4);
}

运行结果:

date1: 2056-09-08
date2: 2056-09-08
date3: Wed Mar 17 22:04:50 CST 2021
date4: 2021-03-17

由于java.sql.Datejava.util.Date的子类,所以情况一使用了面向对象的多态,但是对于情况二,只能使用构造器的方式转换

1.4 SimpleDateFormat类

  • Date类的API不易于国际化,因此大部分都被废弃了,java.text.SimpleDateFormat类是一个不与语言环境有关的方式来格式化和解析日期的具体类。
  • 它允许进行格式化:日期->文本、解析:文本->日期
  • 格式化:

    • SimpleDateFormat():默认的模式和语言环境创建对象
    • public SimpleDateFormat(String pattern):该构造方法可以用参数pattern指定的格式创建一个对象,该对象调用public String format(Date date)方法格式化时间对象date
  • 解析:

    • public Date parse(String source):从给定字符串的开始解析文本,以生成一个日期。
@Test
public void testSimpleDateFormat() throws ParseException {
    // 实例化-默认构造器
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
    // 格式化:日期 ---> 字符串
    Date date = new Date();
    System.out.println(simpleDateFormat.format(date));
    // 解析:格式化的逆过程,字符串 ---> 日期
    String str = "21-3-18 下午4:08";
    System.out.println(simpleDateFormat.parse(str));

    // 实例化-指定格式构造器
    SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    // 格式化
    System.out.println(simpleDateFormat1.format(date));
    // 解析
    String str2 = "2021-03-18 04:17:09";
    System.out.println(simpleDateFormat1.parse(str2));

}

运行结果:

21-3-18 下午4:17
Thu Mar 18 16:08:00 CST 2021
2021-03-18 04:17:17
Thu Mar 18 04:17:09 CST 2021

1.4 Calendar 日历类

  • Calendar是一个抽象基类,主要用于完成日期字段之间互相操作的功能
  • 获取Calendar实例的方法

    • 使用Calendar.getInstance()方法
    • 调用它的子类GregorianCalendar的构造器
  • 一个Calendar的实例是系统时间的抽象表示,通过get(int field)方法来取得想要的时间信息。如YEARMONTHDAT_OF_WEEKHOUR_OF_DAYMINUTESECOND

    • public void set(int field, int value)
    • public void add(int field, int amount)
    • public final Date getTime()
    • public final void setTime(Date date)
  • 注意:

    • 获取月份时:一月份是0,二月是1,······,十二月是11
    • 获取星期时:周日是1,周一是2,······,周六是7
@Test
public void test3() {
    // 实例化
    Calendar calendar = Calendar.getInstance();
    // get()
    System.out.println("calendar.get(Calendar.DAY_OF_MONTH): " + calendar.get(Calendar.DAY_OF_MONTH));
    System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));

    // set()
    calendar.set(Calendar.DAY_OF_YEAR, 20);
    System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));

    // add()
    calendar.add(Calendar.DAY_OF_YEAR, 3);
    System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));

    calendar.add(Calendar.DAY_OF_YEAR, -3);
    System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));

    // getTime()
    Date date = calendar.getTime();
    System.out.println("date: " + date);

    // setTime()
    calendar.setTime(new Date());
    System.out.println("calendar: " + calendar.get(Calendar.DAY_OF_YEAR));
}

运行结果:

calendar.get(Calendar.DAY_OF_MONTH): 18
calendar.get(Calendar.DAY_OF_YEAR): 77
calendar.get(Calendar.DAY_OF_YEAR): 20
calendar.get(Calendar.DAY_OF_YEAR): 23
calendar.get(Calendar.DAY_OF_YEAR): 20
date: Wed Jan 20 17:56:24 CST 2021
calendar: 77

2. JDK8中日期时间API介绍

2.1 出现的背景

JDK1.0中引入的java.util.Date类,在JDK1.1中引入Calendar类后大多数方法已经被弃用,而Calendar类仍然面临很多问题:

  • 可变性:像日期和时间这样的类应该是不可变的
  • 偏移性:Date中的年份从1990年开始,而月份都从0开始
  • 格式化:格式化只对Date有用,Calendar则不行
  • 此外,它们也不是线程安全的,且不能处理闰秒
日期和时间的操作一直是Java程序员最痛苦的地方之一
  • 第三次引入时间API是成功的,并且Java8中引入的java.timeAPI纠正了过去的缺陷,将来很长一段时间内我们都将使用该API
  • Java8吸收了 Joda-Time 的精华,以一个新的开始为 Java 创建优秀的 API。新的java.time API 中包含了所有关于本地日期、本地时间、本地日期时间、时区和持续时间的类。历史悠久的Date类新增了toInstant()方法,用于把Date转换成新的表示形式。这些新增的本地户时间日期API大大简化了日期时间和本地化的管理

2.2 新时间日期API

  • java.time:包含值对象的基础包
  • java.time.chrono:提供不同的日历系统的访问
  • java.time.format:格式化和解析时间和日期
  • java.time.temporal:包括底层框架和扩展特性
  • java.time.zone:包含时区支持的类
大多数开发者只会用到基础包和format包,也可能会用到temporal包。因此,尽管有68个新的公开类型,对大多数开发者而言,一般只用到其中的三分之一

2.3 LocalDate、LocalTime、LocalDateTime的使用

方法描述
now()now(Zoned zone)静态方法,根据当前时间创建对象/指定时区的对象
of()静态方法,根据指定日期时间创建对象
getDayOfMonthgetDayOfYear()获得月份天数(1~31),获得年份天数(1~366)
getDayOfWeek()获得星期几(返回一个DayOfWeek枚举值)
getMonth()获得月份,返回一个Month枚举值
getMonthValue()getDayOfYear()getYear()获得月份(1~12),获得年份
getHour()getDayOfYear()getMinute()getDayOfYear()getSecond()获得当前对象对应的小时、分钟、秒
withDayOfMonth()withDayOfYear()withMonthOfYear()withYear()将月份天数、年份天数、月份、年份修改为指定的值并返回新对象
plusDay()plusWeeks()plusMonths()plusYears()plusHours()向当前对象添加几天、几周、几个月、几年、几小时
minusMonths()minusWeeks()minusDays()minusYears()minusHours()从当前对象减去几月、几周、几天、几年、几小时
@Test
public void test() {
    LocalDate localDate = LocalDate.now();
    LocalTime localTime = LocalTime.now();
    LocalDateTime localDateTime = LocalDateTime.now();
    System.out.println("localDate: " + localDate);
    System.out.println("localTime: " + localTime);
    System.out.println("localDateTime: " + localDateTime);

    LocalDate localDate1 = LocalDate.of(2020, 2, 18);
    LocalTime localTime1 = LocalTime.of(18, 30);
    LocalDateTime localDateTime1 = LocalDateTime.of(2020, 3, 18, 16, 6, 3);
    System.out.println("localDate1: " + localDate1);
    System.out.println("localTime1: " + localTime1);
    System.out.println("localDateTime1: " + localDateTime1);
    
}

运行结果:

localDate: 2021-03-18
localTime: 18:50:05.300
localDateTime: 2021-03-18T18:50:05.300
localDate1: 2020-02-18
localTime1: 18:30
localDateTime1: 2020-03-18T16:06:03

2.4 Instant类的使用

  • Instant:时间线上的一个瞬时点。这可能被用来记录应用程序中的时间时间戳
  • 在处理时间和日期的时候,我们通常会想到年、月、日、时、分、秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。在UNIX中,这个数从1970年开始,以秒为单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。
  • java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如:时区。从概念上讲,它只是简单地表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级
  • (1ns = 10^-9s) 1秒 = 1000毫秒 = 10^6 微秒 = 10^9纳秒
方法描述
now()静态方法,返回默认UTC时区的Instant类的对象
ofEpochMilli(long epochMilli)静态方法,返回在1970-01-01 00:00:0基础上加上指定毫秒数之后的Instant类对象
atOffset(ZoneOffset offset)结合即时的偏移创建一个OffsetDateTime
toEpochMilli()返回1970-01-01 00:00:00到当前时间的毫秒数,即为时间戳
@Test
public void test1() {
    // 获取实例
    Instant instant = Instant.now(); // 默认UTC时间
    System.out.println("instant: " + instant);

    // 添加时间偏移量
    OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));// 东八区(+8)时间
    System.out.println("offsetDateTime: " + offsetDateTime);

    // 获取毫秒数
    long milli = instant.toEpochMilli();
    System.out.println("milli: " + milli);

    // 通过给定毫秒数获取一个Instant实例
    Instant instant1 = Instant.ofEpochMilli(1616067858186L);
    System.out.println("instant1: " + instant1);
}

运行结果

instant: 2021-03-18T11:46:03.575Z
offsetDateTime: 2021-03-18T19:46:03.575+08:00
milli: 1616067963575
instant1: 2021-03-18T11:44:18.186Z

2.5 DateTimeFormatter类的使用

java.time.format.DateTimeFormatter类提供了三种格式化方法:

  • 预定义的标准格式。如:ISO_LOCAL_DATE_TIMEISO_LOCAL_DATEISO_LOCAL_TIME
  • 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG)
  • 自定义格式。如:ofPattern("yyyy-MM-dd hh:mm:ss E")
方法描述
ofPattern(String pattern)静态方法,返回一个指定字符串格式的DateTimeFormatter
format(TemporalAccessor t)格式化一个日期、时间。返回字符串
parse(CharSequence text)将指定格式的字符序列解析为一个日期、时间
@Test
public void test2() {
    // 实例化方式一:使用标准格式
    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    System.out.println(formatter.format(LocalDateTime.now()));

    TemporalAccessor parse = formatter.parse("2021-03-18T20:00:04.94");
    System.out.println(parse);


    // 实例化方式二:本地化相关格式
    DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
    System.out.println(formatter1.format(LocalDateTime.now()));

    // 实例化方式三:自定义格式
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
    System.out.println(formatter2.format(LocalDateTime.now()));

    TemporalAccessor parse1 = formatter2.parse("2021-03-18 08:06:36");
    System.out.println(parse1);
}

运行结果:

2021-03-18T20:07:23.296
{},ISO resolved to 2021-03-18T20:00:04.940
21-3-18 下午8:07
2021-03-18 08:07:23
{HourOfAmPm=8, MilliOfSecond=0, MinuteOfHour=6, MicroOfSecond=0, NanoOfSecond=0, SecondOfMinute=36},ISO resolved to 2021-03-18

2.6 其它日期时间相关API

  • Zoneld:该类中包含了所有的时区信息,一个时区的ID,如Europe/Paris
  • ZonedDateTime:一个在ISO-8601日历系统时区的日期时间,如 2007-12-03T10:15:30+1:00 Europe/Paris,其中每个时区都有对应的ID,地区ID都为{Area}/{City}格式
  • Clock:使用时区提供对当前即时、日期和时间的访问的时钟
  • Duration:持续时间,用于计算两个”时间“间隔
  • Period:日期间隔,用于计算两个”日期“间隔
  • TemporalAdjuster:时间矫正器。有时我们可能需要获取例如:将日期调整到”下一个工作日“等操作
  • TemporalAdjusters:该类通过静态方法firstDayOfXxx()lastDayOfXxx()nextXxx()提供了大量常用TemporalAdjuster的实现
最后修改:2021 年 03 月 18 日
如果觉得我的文章对你有用,请随意赞赏