Skip to content

本文主要讲解 Java8 新增的时间日期 API

// todo 计算机中的时间

Instant

表示某一时刻,Instant 类的对象是线程安全的、不可变的

java
// 获取当前时刻
Instant now = Instant.now();

Instant 对象包含秒和纳秒用来表示时间,以1970年1月1日0点(格林威治时间)左为原点(0秒0纳秒)。

java
long getEpochSecond();
int getNano();

Instant 运算

java
Instant now = Instant.now(); 
// 3 秒后
Instant later = now.plusSeconds(3); 
 // 3 秒前
Instant earlier = now.minusSeconds(3);
// 时刻对比
boolean isBefore = earlier.isBefore(now);

因为 Instant 是不可变的,所以上面的计算方法,是返回一个代表计算结果的新的 Instant 对象。

Duration

表示时间间隔

java.time.Duration 表示两个 Instant 之间的一段时间,Duration 实例同样是不可变的。但可以基于一个 Duration 对象创建新的 Duration 对象。

可以使用 Duration 类的工厂方法创建 Duration 对象,提供了 between()/ofDays()/ofSeconds()/from() 等方法,但其底层都是调用了同一个构造方法:

java
 private Duration(long seconds, int nanos) {
        super();
        this.seconds = seconds;
        this.nanos = nanos;
    }

下面是一个使用 between() 方法创建的示例:

java
Instant first = Instant.now();
// 其他耗时操作
Instant second = Instant.now();
Duration duration = Duration.between(first, second);

访问 Duration 对象的时间信息

从上述构造器源码可知,Duration 在内部维护两个值:

final int nanos;

final long seconds;

请注意没有单独的毫秒部分,只有纳秒和秒。但可以可以将整个时间间隔 Duration 转换为其他时间单位,如纳秒、分钟、小时或天:

long toNanos();

long toMillis();

long toMinutes();

long toHours();

long toDays();

toNanos() 与 getNano() 的不同之处在于 getNano() 仅返回持续时间小于一秒的部分(即整个时间段中不到 1 秒的那部分)。 toNanos() 方法返回的是转换为纳秒的整个时间段(即秒部分转成纳秒+纳秒部分)。

没有 toSeconds() 方法,因为 getSeconds() 方法已经可以获取 Duration 的秒部分。

Duration 的计算

Duration 类包含一组可用于基于 Duration 对象执行计算的方法。其中一些方法是:

Duration plus(Duration duration);

Duration plusNanos(long);

Duration plusMillis(long);

Duration plusSeconds(long);

Duration plusMinutes(long);

Duration plusHours(long);

Duration plusDays(long);

Duration minusXxx(long);

这些方法的使用大同小异,以下下是一个例子:

java
Duration start = ... 
// 加 3 天
Duration added = start.plusDays(3); 
// 减 3 天
Duration subtracted = start.minusDays(3);

同样,为了使Duration对象保持不可变,所有计算方法都返回表示计算结果的新的 Duration 对象。

LocalDate

表示本地日期

java.time.LocalDate 表示本地日期,没有时区信息。当地的日期可以是生日或法定假日等,与一年中的某一天有关,和一天中的某一时间无关。这个类对象也是不可变的,计算操作会返回一个新的 LocalDate 对象。 下面是一个创建 LocalDate 对象的例子:

java
LocalDate localDate1 = LocalDate.now();
LocalDate localDate2 = LocalDate.of(2018, 11, 11);

访问 LocalDate 中的日期信息

LocalDate 中一共有 3 个日期信息字段,分别是:

final int year;

final short month;

final short day;

对应一些获取信息的方法:

int getYear();

Month getMonth();

int getDayOfMonth();

int getDayOfYear();

DayOfWeek getDayOfWeek();

LocalDate 计算

LocalDate plusYears(long yearsToAdd);

LocalDate plusMonths(long monthsToAdd);

LocalDate plusWeeks(long weeksToAdd);

LocalDate plusDays(long daysToAdd);

LocalDate minusXxx(long xxxToSubtract); 对应上面 plus 方法的 minus 版本

下面是一个例子:

java
LocalDate localDate = LocalDate.of(2018, 12, 12);

LocalDate localDate1 = localDate.plusYears(3); // 加 3 年
LocalDate localDate2 = localDate.minusYears(3);

LocalTime

表示本地时间

java.time.LocalTime 表示没有任何时区信息的特定时间,例如,上午 10 点。同样,这是一个不可变类。 下面是一个创建 LocalTime 对象的例子:

java
LocalTime localTime1 = LocalTime.now();
LocalTime localTime2 = LocalTime.of(21, 30, 59, 11001);

LocalTime 内部维护了 4 个变量维护时间信息:

final byte hour;

final byte minute;

final byte second;

final int nano;

也包含了必要的计算时间的方法,例如 LocalTime plusHours(long hoursToAdd); 其他的和 LocalDate 大同小异,就不展开讲了。

LocalDateTime

表示本地日期和时间

java.time.LocalDateTime 类表示没有任何时区信息的本地日期和时间,同样是不可变类。

查看其源码发现其内部就是维护了一个 LocalDate 对象和一个 LocalTime 对象来表示日期时间信息。

java
final LocalDate date;
final LocalTime time;

所以完全可以把它看成是 LocalDate 和 LocalTime 的结合。

下面是一个创建 LocalDateTime 对象的例子:

java
LocalDateTime localDateTime1 = LocalDateTime.now();
LocalDateTime localDateTime2 =LocalDateTime.of(2018, 11, 11, 10, 55, 36, 123);

上面第二行代码使用 of() 工厂方法创建对象,其参数分别对应年月日时分秒纳秒。

ZonedDateTime

表示带有时区信息的日期和时间

java.time.ZonedDateTime 可以用来代表世界上某个特定事件的开始,比如会议、火箭发射等等。 它同样是不可变类,下面是一个创建此类对象的例子:

ZonedDateTime zonedDateTime = ZonedDateTime.now();
ZoneId zoneId = ZoneId.of("UTC+1");
ZonedDateTime zonedDateTime2 = ZonedDateTime.of(2015, 11, 30, 23, 45, 59, 1234, zoneId);

时区

时区由 ZoneId 类表示,如前面的示例所示。可以使用 ZoneId.now() 方法创建 ZoneId 对象。也可以使用 of() 方法指定时区信息,下面是一个例子:

java
ZoneId zoneId1 = ZoneId.of("UTC+1");
ZoneId zoneId2 = ZoneId.of("Europe/Paris");

传递给 of() 方法的参数是要为其创建 ZoneId 的时区的ID。在上面的例子中,ID 是“UTC+1”,它是 UTC (格林威治)时间的偏移量。另外也可以直接指定具体的时区 ID 字符串,这在本文开头有介绍。

ZonedDateTime 相比 LocalDateTime 只是多了地区信息,其内部维护了下面这 3 个变量来表示日期信息和地区:

final LocalDateTime dateTime; final ZoneOffset offset; final ZoneId zone;

DateTimeFormatter

java.time.DateTimeFormatter 类用于解析和格式化用 Java 8 日期时间 API 中的类表示的日期。

内置 DateTimeFormatter 对象

DateTimeFormatter 类包含一组预定义的(常量)实例,这些实例可以解析和格式化来自标准日期格式的日期。这省去了为 DateTimeFormatter 定义日期格式的麻烦。包含的部分预定义实例如下:

java
BASIC_ISO_DATE

ISO_LOCAL_DATE
ISO_LOCAL_TIME
ISO_LOCAL_DATE_TIME

ISO_OFFSET_DATE

ISO_ZONED_DATE_TIME

这些内置的 DateTimeFormatter 实例中的每一个都预先配置为格式化和解析不同格式的日期

格式化 Date 的例子

java
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;

String formattedDate = formatter.format(LocalDate.now());
System.out.println(formattedDate); // 20181204

String formattedZonedDate = formatter.format(ZonedDateTime.now());
System.out.println("formattedZonedDate = " + formattedZonedDate);// 20181204+0800

最后一行输出 20181204+0800 代表 UTC+8 时区的 2019 年、第 12 个月(12 月)和第 4 天(第 4 天)。

Timestamp 、Date 与 LocalDateTime 的互相转换

Date 转 LocalDateTime

java
LocalDateTime now = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// 或者
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

LocalDateTime 转 Date

java
Date date = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());

LocalDateTime 转时间戳

java
long timestamp = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

时间戳转 LocalDateTime

java
long timestamp = System.currentTimeMillis();
LocalDateTime localDateTime = Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDateTime();

参考

一篇文章概括 Java Date Time 的使用

你确信你了解时间吗

闰秒