Java8之前的日期时间API

表示时刻信息的 Date

Date的设计饱受诟病,其缺陷包括但不限于:
  • 类名误导,该类实际上不仅反映日期,还反映时间
  • 方法名误导,getDate()返回日期中的天,getDay()返回的是周几
  • 年份是与1900年的差值,可读性极差
  • 月份是从0计数的,可读性极差
  • 周几是相对于周日的差值,可读性极差
  • 不提供时区设置,内部总是使用本地时区
  • 不提供历法设置,内部使用格里历或儒略历
  • 不提供格式化的转换,从字符串中解析日期时相当难用
  • 参数返回太随意,比如设置1月33日,实际是2月2日
  • 存在同名类,java.sql包下依然有一个作用相同的Date类
  • 该类允许扩展,实际上,应当把日期-时间类设计为不可变的final类
当前定位

Date 的目前定位是,唯一表示一个时刻,现在的 Date 类中接近百分之八十的方法都已废弃,被标记为 @Deprecated。还有几个为数不多没有被废弃的方法:

  • public long getTime() :返回内部存储的毫秒数
  • public void setTime(long time):重新设置内存的毫秒数
  • public boolean before(Date when):比较给定的时刻是否早于当前 Date 实例
  • public boolean after(Date when):比较给定的时刻是否晚于当前 Date 实例

描述年历的 Calendar

Calendar 用于表示年月日等日期信息,它是一个抽象类,一般通过以下四种工厂方法获取它的实例对象:

public static Calendar getInstance()
public static Calendar getInstance(TimeZone zone)
public static Calendar getInstance(Locale aLocale)
public static Calendar getInstance(TimeZone zone,Locale aLocale)
//最终调用的方法,因为不同的时区与国家语言对于时刻和年月日信息的输出是不同的,所以这也是为什么一个 Calendar 实例必须传入时区和国家信息的一个原因
private static Calendar createCalendar(TimeZone zone,Locale aLocale)

DateFormat 格式化转换

DateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");
//将一个日期对象格式化为字符串
public final String format(Date date)
//将一个格式化的字符串装换为一个日期对象
public Date parse(String source)
  • yyyy:年份用四位进行输出

  • MM:月份用两位进行输出

  • dd:两位表示日信息

  • HH:两位来表示小时数

  • mm:两位表示分钟数

  • ss:两位来表示秒数

  • E:表示周几,如果 Locale 在中国则会输出 星期x,如果在美国或英国则会输出英文的星期

  • a:表示上午或下午

Java8 的时间日期 API

Java 8的日期和时间类包括Instant、Duration、Period、LocalDate、LocalTime、LocalDateTime,这些类都包含在java.time包中。

表示时刻的 Instant

Instant是时间线上的一个点,表示一个时间戳。Instant可以精确到纳秒,这超过了long的最大表示范围,所以在Instant的实现中是分成了两部分来表示,一部分是seconds,表示从1970-01-01 00:00:00开始到现在的秒数,另一个部分是nanos,表示纳秒部分。

//根据系统当前时间创建一个 Instant 实例,表示当前时刻
Instant now = Instant.now(); 
//通过传入一个标准时间的偏移值来构建一个 Instant 实例
Instant instant = Instant.ofEpochSecond(long epochSecond, long nanoAdjustment);
//通过毫秒数值直接构建一个 Instant 实例
public static Instant ofEpochMilli(long epochMilli)

时间段Duration

Duration是两个时间戳的差值,使用java.time中的时间戳类,例如Instant、LocalDateTime等实现了Temporal类的日期时间类为参数

LocalDateTime from = LocalDateTime.of(2020,4, 22, 16, 6, 0);
LocalDateTime to = LocalDateTime.of(2020,5, 22, 16, 6, 0);
Duration duration = Duration.between(from, to);

Duration duration1 = Duration.of(5, ChronoUnit.DAYS);       // 5天
Duration duration2 = Duration.of(1000, ChronoUnit.MILLIS);  // 1000毫秒

日期段Period

Period是以年月日来衡量一个时间段,由于Period是以年月日衡量时间段,所以between()方法只能接收LocalDate类型的参数

// 2020-03-22 到 2020-04-22 这段时间
Period period = Period.between(LocalDate.of(2020, 3, 22),LocalDate.of(2020, 4, 22));

处理日期的 LocalDate

LocalDate 是一个不可变类,它关注时间中年月日部分。

//用当前系统时间的年月日信息初始化一个实例对象
public static LocalDate now();
//显式指定年月日信息
public static LocalDate of(int year, int month, int dayOfMonth);
//根据 dayOfYear 可以推出 month 和 dayOfMonth
public static LocalDate ofYearDay(int year, int dayOfYear);
//相对于格林零时区时间的日偏移量
public static LocalDate ofEpochDay(long epochDay);

LocalDate localDate = LocalDate.of(2020, 4, 22);     // 初始化一个日期:2022-04-22
int year = localDate.getYear();                     // 年份:2020
Month month = localDate.getMonth();                 // 月份:MARCH
int dayOfMonth = localDate.getDayOfMonth();         // 月份中的第几天:22
DayOfWeek dayOfWeek = localDate.getDayOfWeek();     // 一周的第几天:FRIDAY
int length = localDate.lengthOfMonth();             // 月份的天数:30
boolean leapYear = localDate.isLeapYear();          // 是否为闰年:true

处理时间的 LocalTime

类似于 LocalDate,LocalTime 专注于时间的处理

//根据系统当前时刻获取其中的时间部分内容
public static LocalTime now();
//显式传入小时和分钟来构建一个实例对象
public static LocalTime of(int hour, int minute);
//通过传入时分秒构造实例
public static LocalTime of(int hour, int minute, int second);
//传入时分秒和纳秒构建一个实例
public static LocalTime of(int hour, int minute, int second, int nanoOfSecond);
//传入一个长整型数值代表当前日已经过去的秒数
public static LocalTime ofSecondOfDay(long secondOfDay);
//传入一个长整型代表当前日已经过去的纳秒数
public static LocalTime ofNanoOfDay(long nanoOfDay);

LocalTime localTime = LocalTime.of(16, 25, 52);     // 初始化一个时间:16:25:52
int hour = localTime.getHour();                     // 时:16
int minute = localTime.getMinute();                 // 分:25
int second = localTime.getSecond();                 // 秒:52

处理日期和时间的 LocalDateTime

LocalDateTime类是LocalDate和LocalTime的结合体

//通过of()方法直接创建
LocalDateTime ldt1 = LocalDateTime.of(2020, Month.FEBRUARY, 22, 16, 23, 12);
//可以调用LocalDate的atTime()方法或LocalTime的atDate()方法将LocalDate或LocalTime合并成一个LocalDateTime
LocalDate localDate = LocalDate.of(2020, Month.FEBRUARY, 22);
LocalTime localTime = LocalTime.of(16, 23, 12);
LocalDateTime ldt2 = localDate.atTime(localTime);

//向LocalDate和LocalTime的转化
LocalDate date = ldt1.toLocalDate();
LocalTime time = ldt1.toLocalTime();

日期操作

比较简单的日期操作

//比较简单的日期操作
LocalDate date = LocalDate.of(2020, 4, 22);          // 2020-04-22
LocalDate date1 = date.withYear(2021);              // 修改为 2021-04-22
LocalDate date2 = date.withMonth(3);                // 修改为 2020-03-22
LocalDate date3 = date.withDayOfMonth(1);           // 修改为 2020-04-01
LocalDate date4 = date.plusYears(1);                // 增加一年 2021-04-22
LocalDate date5 = date.minusMonths(2);              // 减少两个月,到2020年的2月
LocalDate date6 = date.plus(5, ChronoUnit.DAYS);    // 增加5天 2020-04-27

比较复杂的日期操作,比如将时间调到下一个工作日,或者是下个月的最后一天,这时候我们可以使用with()方法的另一个重载方法,它接收一个TemporalAdjuster参数,可以使我们更加灵活的调整日期:

//返回下一个距离当前时间最近的星期日
LocalDate date7 = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
// 返回本月最后衣蛾周六
LocalDate date9 = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.SATURDAY));

下面列出时间调节器类TemporalAdjuster提供的一些方法

方法名 描述
dayOfWeekInMonth 返回同一个月中每周的第几天
firstDayOfMonth 返回当月的第一天
firstDayOfNextMonth 返回下月的第一天
firstDayOfNextYear 返回下一年的第一天
firstDayOfYear 返回本年的第一天
firstInMonth 返回同一个月中第一个星期几
lastDayOfMonth 返回当月的最后一天
lastDayOfNextMonth 返回下月的最后一天
lastDayOfNextYear 返回下一年的最后一天
lastDayOfYear 返回本年的最后一天
lastInMonth 返回同一个月中最后一个星期几
next / previous 返回后一个/前一个给定的星期几
nextOrSame / previousOrSame 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回

时区

ZoneId
//接收一个“区域/城市”的字符串作为参数
ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
//获取系统默认时区
ZoneId systemZoneId = ZoneId.systemDefault();
//获取所有合法的“区域/城市”字符串
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
ZonedDateTime

ZonedDateTime 可以被理解为 LocalDateTime 的外层封装,它的内部存储了一个 LocalDateTime 的实例,专门用于普通的日期时间处理。此外,它还定义了 ZoneId 和 ZoneOffset 来描述时区的概念。

//系统将以默认时区计算并存储日期时间信息
public static ZonedDateTime now();
//指定时区
public static ZonedDateTime now(ZoneId zone);
//指定日期时间和时区
public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)public static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone)
//通过时刻和时区构建实例对象
public static ZonedDateTime ofInstant(Instant instant, ZoneId zone)//有了ZoneId,我们就可以将一个LocalDate、LocalTime或LocalDateTime对象转化为ZonedDateTime对象:
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, shanghaiZoneId);

格式化日期时间

DateTimeFormatter 作为格式化日期时间的主要类,它与之前的 DateFormat 类最大的不同就在于它是线程安全的,其他的使用上的操作基本类似。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.now();
formatter.format(localDateTime);

String str = "2020年04月23日 23:59:59";
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
LocalDateTime localDateTime2 = LocalDateTime.parse(str,formatter2);

世界标准时间(UTC)/格林威治时间(GMT)

UTC是协调世界时(Universal Time Coordinated)英文缩写,是由国际无线电咨询委员会规定和推荐,并由国际时间局(BIH)负责保持的以秒为基础的时间标度。UTC相当于本初子午线(即经度0度)上的平均太阳时,过去曾用格林威治平均时(GMT)来表示.北京时间比UTC时间早8小时,以1999年1月1日0000UTC为例,UTC时间是零点,北京时间为1999年1月1日早上8点整。

具体参考:https://pansci.asia/archives/84978

参考

https://www.cnblogs.com/mcbye/p/java8-date-time-api.html

https://juejin.im/post/5addc7a66fb9a07aa43bd2a0#heading-5

https://www.jianshu.com/p/68d8291c3bd5