java-jdk8的时间日期API

20 篇文章 1 订阅

文章目录

一、时间处理会遇到的问题?

  1. 时间的格式化,也就是同String的转换
  2. 时间的国际化,时区的设置
  3. 时间戳的转换
  4. 时间的计算
    1. 时间和时间差
    2. 2个时间的时间差
  5. 跟DB交互,跟DB中时间类型的转换

二、1.8之前的时间API

在这里插入图片描述

1.util.Date:

represents a specific instant in time, with millisecond precision.
It allowed the interpretation of dates as year, month, day, hour, minute, and second values. It also allowed the formatting and parsing of date strings.
Unfortunately, the API for these functions was not amenable to internationalization.

核心类,计算机中一切都是数字,把时间转成从标准纪元1970.1.1开始的long类型毫秒数,早于这个时间是负数, long类型足够表达常用时间。
用毫秒值来表示时间是最通用的,计算机没有格式的概念,对计算机来说只有数字是可读的。所以所有的时间类的对象都可以用毫秒数来构建,所有的时间计算都可以转成毫秒计算再转回来。

1.1 使用:

Date时间比较:before,after

2. DateFormat:

2.1 理解:时间格式化类,将String和Date的转化逻辑封装成一个类,是时间对象跟String转化的工具类,需要在构造方法中设置格式化字符串,然后用format()和parse()按定好的格式将String和Date互转(解析字符串,格式化对象)。

2.2 使用:

{1} text包,不是util。
{2} 抽象类,jdk中就一个实现类SimpleDateFormat,但其他的第三方包有很多实现类。
{3} sql.date也能用

3. Calendar:

3.1 理解:日期计算工具类,抽象类,用于long类型毫秒数或者Date对象和格里高利历法标准下时间的转化和计算。

{1} GregorianCalendar是util.Calendar的一个子类,提供了大多数国家使用的标准日历系统和日期计算方法。能将一个日期对象的详细信息取到,比如周几,年中第几天等。

3.2 使用:

{1} util包下
{2} 一般通过Date对象来构建。通常先把String日期通过SimpleDateFormat转成Date,再把Date转成GC
[1] Date中的set年月日的方法已经不建议使用,1.8文档推荐用Calendar的set。
(1) year是从1900开始

{3} java中月是从0开始,日从1开始
[1] 提供了很多常量,不需直接用数字当参数。比如一月设置成0.
[2] 外国人没有几月和星期几的概念,都是无序的独立单词。

{4} 提供很多日期计算方法,如add(x,x)。

4.sql包下

java.sql包下有三个与数据库相关的日期时间类型,

{1} Date:表示日期,只有年月日,没有时分秒。会丢失时间;
{2} Time:表示时间,只有时分秒,没有年月日。会丢失日期;
{3} Timestamp:表示时间戳,有年月日时分秒,以及毫秒。

usage

{1} 这3个类都是util.Date的子类,可以直接用util.date 类型接收 sql.date类型,父类引用指向子类对象
{2} 当需要把java.util.Date转换成数据库的三种时间类型时,需要转成sqlDate,这需要使用数据库三种时间类型的构造器。java.sql包下的Date、Time、TimeStamp三个类的构造器都需要一个long类型的参数,表示毫秒值(时间戳)。创建这三个类型的对象,只需要有毫秒值即可。已知java.util.Date有getTime()方法可以获取毫秒值(时间戳),那么这个转换也就不是什么问题了。

java.utl.Date d = new java.util.Date();

java.sql.Date date = new java.sql.Date(d.getTime());  //会丢失时分秒

Time time = new Time(d.getTime());            //会丢失年月日

Timestamp timestamp = new Timestamp(d.getTime());  //不会丢失

[1] 想要在DB中显示完整时间,要在Entity中和DB中把时间都设置成TimeStamp
[2] java中日期是从0开始的,0代表一月,一日

三、1.8后的时间类

1.src

1.8 之前时间API的问题

  1. 使用Calendar类实现日期和时间字段之间转换,DateFormat类来格式化和分析日期字符串,Date只用来承载日期和时间信息,时区支持TimeZone。涉及到时间计算和转化时,需要复杂的转换
  2. 所有的日期类都是可变的,因此他们都不是线程安全的,这是Java日期类最大的问题之一
  3. Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义
  4. java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计

2.concept

2.1 时间的单位

123
纳秒10-9 sChronoUnit.NANOS
毫秒10-3 sChronoUnit.MILLIS

2.2 时区

  1. 因适应人的主观感受,将时间划分为不同的失去,不同时区的时间差为整数个小时。
  2. 本地时间就是当前时区的时间,UTC偏移量就是相对于标准时间(本初子午线的本地时间)的带方向的整数。比如东八区相对标准时间的偏移量为+8
  3. 计算2个时区之间的时间差时,不能直接用2个时区的本地时间,还要把UTC偏移量考虑进来。最简单的方式就是都转成UTC标准时间。

2.3 工具类

Clock 时钟,比如获取目前美国纽约的时间
ZoneOffset时区偏移量,比如:+8:00

对时间的处理可以粗略的划分为三个部分:时间点、时间段和时间的解析与格式化。

3.java.time.temporal接口:时间点

12
Instant它代表的是时间戳(因为它代表了一个时间点,即相对于1970年1月1日的偏移量;但与java.util.Date类不同的是其精确到了纳秒级别。
LocalDate不包含具体时间的日期,比如2019-01-14。它可以用来存储生日,周年纪念日,入职日期等。
LocalTime它代表的是不含日期的时间
LocalDateTime它包含了日期及时间,不过还是没有偏移信息或者说时区。
ZoneOffset时区偏移量,比如:+8:00
ZonedDateTime这是一个包含时区的完整的日期时间在这里插入图片描述
WeekFields用来进行周计算的API
须知:
为什么命名为LocalXxx,是因为这些对象创建时以计算机上的本地时区为基准,但Instant默认是以世界标准时间,所以使用LocalXxx构建Instant对象时,要设置时区
//获取毫秒数
Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();

3.2 temporal的API

now
of 表示构建
minus,plus,with 表示修改
leng* 表示获取长度
get* 表示获取在父级日期中的子级日期的序号
parse表示把字符串转成LD
format表示把this date按参数转成String
toString直接输出字符串
通过TemporalAdjuster的TemporalAdjusters进行复杂的时间对象获取

// 将日期调整到该月的最后一天
LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());

4. java.time.TemporalAmount接口 时间段

4.1 src

表示一段时间==》必然有前后2个时间点

Duration

Duration表示小时、秒级别的时间段。只能通过2个时间点来构建

Duration.between(time1, time2);

Period

表示年月日级别的时间段

Period.between(date1, date2);

5.时间转换

DateTimeFormatter

四、1.8-usage

18个实践

18个用例

1.工具类

Clock 时钟,比如获取目前美国纽约的时间
ZoneOffset时区偏移量,比如:+8:00

2.格式化

须知:

  1. HH是24小时制,把大于12的数转成hh会报错

2.1 字符串转时间

注意

LocalDate只包含年月日这种,如果是单独的年月、月日,不能用LocalDate。要用YearMonth,MonthDay这种

{1} 单独日期和时间都可以直接通过静态的parse方法转,但类似于yyyy-MM-dd HH:mm:ss这种如果要直接转成LocalDateTime,根据文档,需要在date和time之间加一个大写的T且T左右不能有空格。
@Test
public void contextLoads() {
    LocalDateTime dateTime = LocalDateTime.parse("2014-10-01T15:23:23");
    System.out.println(dateTime.getMonth());
}
{2} 或者通过DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
TemporalAccessor temporalAccessor = formatter.parse(values[0]);
LocalDateTime date = LocalDateTime.from(temporalAccessor);
{3} 将年月字符串转为时间,然后求这个月的最后一天
println(YearMonth.parse("201912",DateTimeFormatter.ofPattern("yyyyMM")).atEndOfMonth())

2.2 时间转字符串

须知:

[1] 进行String.format日期转换时,被转换的必须是日期对象
[2] 官方没有提供YYYY-mm-DD HH:MM:SS 这种最常用的格式,又没有自定义格式化字符串的方法。所以只能设置多个格式串。即使如此,这种方式也是目前发现的最优雅的

@Test
public void contextLoads() {
    LocalDateTime localDateTime = LocalDateTime.now();
    Date date = new Date();
    /*
    进行String.format日期转换时,被转换的必须是日期对象
    官方没有提供YYYY-mm-DD HH:MM:SS 这种最常用的格式,又没有自定义格式化字符串的方法
        所以只能设置多个转换符,==》就要对应设置几个格式化字符串
        即使如此,这种方式也是目前发现的最优雅的
     */
    String str = String.format("%1$tF %1$tT", localDateTime);
    System.out.println(str);
}

3.新旧接口转换

转换到旧的对象转换到新的对象
Instant - java.util.DateDate.from(instant)date.toInstant()
LocalDate - java.sql.DateDate.valueOf(localDate)date.toLocalDate()
LocalTime - java.sql.TimeTime.valueOf(localTime)date.toLocalTime()
ZoneId - java.util.TimeZoneTimeZone.getTimeZone(id)timeZone.toZoneId()

4.取毫秒数

  1. System.currentTimeMillis()和new Date().getTime()返回的是一样的,都是1970.01.01 00:00:00到现在的毫秒数
  2. LocalDate.now()
  3. LocalDateTime对象和LocalDate对象取毫秒数不一样
/*
LocalDate对象获取毫秒数时间戳,要用atStartOfDay()
 */
val dtf = DateTimeFormatter.ofPattern("yyyy-M-d")
println(LocalDate.parse("2020-4-8", dtf).atStartOfDay(ZoneOffset.of("+8")).toInstant.toEpochMilli)

5. WeekFields

WeekFields用来进行周的计算,有2个问题要明确:

{1} 不同国家地区对周的起始时间有不同的认知

周的计算需要确定一周是从周一还是周日开始,所以会有2个常用的构造器
WeekFields.ISO代表每周从周一开始算。
WeekFields.SUNDAY_START代表每周从周日开始算。

TemporalField weekFields = WeekFields.ISO.weekOfYear();
System.out.println("第几周:" + LocalDate.of(2017, 1, 1).get(weekFields));

{2} 还有个问题就是一年的第一周是如何确定的

java中一周至少4天,>=4天,在进行周计算时会被认为是一周
如果某年的前4天中有周一,那么周一的前面几天属于 第0周
如果某年的前4天中没有周一,那么第一个周一的前面几天属于 第1周,当年没有 第0周

if the 1st day of the year is a Monday, week one starts on the 1st and there is no week zero
if the 2nd day of the year is a Monday, week one starts on the 2nd and the 1st is in week zero
if the 4th day of the year is a Monday, week one starts on the 4th and the 1st to 3rd is in week zero
if the 5th day of the year is a Monday, week two starts on the 5th and the 1st to 4th is in week one

所以计算每年有多少周时,需要判断第一个周一前面是第一周还是第0周。比如2017年,就有53个周

int firstWeek = LocalDate.of(2017, 1, 1).get(weekFields);
int lastWeek = LocalDate.of(2017, 12, 31).get(weekFields);
System.out.println("2017年的周数:" + (firstWeek == 0 ? lastWeek + 1 : lastWeek));
2017年的周数:53

6. 随机获取某年、某月中的一天

年、月的长度都不是固定的,所以要先判断指定年、月的长度,然后才能设置取的范围

 @Test
 /*
 * 随机从年、月中取日
 * */
 void ranDayFromYearAndMonth(){
     /*
     构建指定年的随机日,可以自行设置随机数
     获取指定年的长度
      */
     int len = Year.of(2018).length();
     LocalDate.ofYearDay(2018, (int)(Math.random() * len + 1));
      /*
     构建指定月的随机日,可以自行设置随机数
     获取指定月的长度,因为没有年的数据,所以要判断是否是leapYear,才能获取月的长度
      */
     int lenMonth = Month.of(12).length(true);
     //如果指定年和月,用YearMonth
     YearMonth.of(2019, 12);
     MonthDay month = MonthDay.of(12,(int)(Math.random() * lenMonth + 1));
 }

7. 格式化中介DateTimeFormatter.ofPattern

顾名思义,可以把年月日和年月日时分秒、时分秒转成对应的格式化对象,跟LocalDate、LocalDateTime配合完成字符串和时间对象的互转。

1. traps

[1] 格式化字符串和要转化的字符串必须严格对应
  1. 比如用yyyy-MM-dd HH:mm:ss接2020-01-02 12:12:12.123就接不了,这种带毫秒的时间戳后面的毫秒用大写S表示,2020-01-02 12:12:12.123yyyy-MM-dd HH:mm:ss.SSS接即可。

     /*
     用大写S解析带毫秒数的时间字符串
      */
     val dtf2 = DateTimeFormatter.ofPattern("yyyy-M-d HH:mm:ss.SSS")
     println(LocalDateTime.parse("2020-4-8 12:12:12.333", dtf2).toInstant(ZoneOffset.of("+8")).toEpochMilli)
    
  2. 2020-4-7这种也不能用yyyy-MM-dd,要用yyyy-M-d来接

[2] LocalDateTime.parse()只能解析带时分秒的时间字符串
/*
 这里会报错,LocalDateTime.parse()只能解析带时分秒的时间字符串
 */
  val dtf1 = DateTimeFormatter.ofPattern("yyyy-M-d")
  println(LocalDateTime.parse("2020-4-8", dtf1).toInstant(ZoneOffset.of("+8")).toEpochMilli)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值