背景

Java对日期,日历及时间的处理一直以来都饱受诟病,比如java.util.Datejava.util.Calendar类易用性差,不支持时区,非线程安全;还有用于格式化日期的类DateFormat也是非线程安全的等问题.

Java8引入的新的一系列API,对时间日期的处理提供了更好的支持,清楚的定义了时间日期的一些概念,比如说,瞬时时间(Instant),持续时间(duration),日期(date),时间(time),时区(time-zone)以及时间段(Period).

同时,借鉴了Joda库的一些优点,比如将人和机器对时间日期的理解区分开的.

简介

新的时间日期API核心位于java.time内,另外也在java.time.chrono,java.time.format,java.time.temporal和java.time.zone有相关的API,但使用频次较少.

Java8常用的日期和时间类包含LocalDate,LocalTime,Instant,Duration,Period,LocalDateTime以及ZonedDateTime等.

  • LocalDate: 不包含时间的日期,比如2019-10-14.可以用来存储生日,周年纪念日,入职日期等.
  • LocalTime: 与LocalDate想对照,它是不包含日期的时间.
  • LocalDateTime: 包含了日期及时间,没有偏移信息(时区).
  • ZonedDateTime: 包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的.
  • Instant: 时间戳,与System.currentTimeMillis()类似.
  • Duration: 表示一个时间段.
  • Period: 用来表示以年月日来衡量一个时间段.

另外,还有新的日期解析格式化类DateTimeFormatter.

学习最佳的途径就是去实践它,现在我们示例的形式来讲每个知识点进行讲解.

Clock

该类用于获取指定时区的当前日期,时间.该类可以取代System类的currentTimeMillis()方法,该类提供了大量的方法获取当前的日期和时间

//获取当前Clock
Clock clock = Clock.systemUTC();
//获取clock对应的毫秒数
//等于System.currentTimeMillis()
System.out.println(clock.millis());
System.out.println(System.currentTimeMillis());

LocalDate-如何获得日期

LocalDate类内只包含日期,不包含具体时间.只需要表示日期而不包含时间,就可以使用它.

// 只获取日期
LocalDate today = LocalDate.now();
System.out.println(today);int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();System.out.printf("Year : %d Month : %d day : %d \t %n", year, month, day);

同时,还可以通过LocalDate获取日期是月份的第几天,周的第几天,月份的天数,是否为闰年等.看下面的代码是不是非常方便.

LocalDate today = LocalDate.now();
// 月份中的第几天
int dayOfMonth = today.getDayOfMonth();
// 一周的第几天
DayOfWeek dayOfWeek = today.getDayOfWeek();
// 月份的天数
int length = today.lengthOfMonth();
// 是否为闰年
boolean leapYear = today.isLeapYear();

上面通过now获取LocalDate对象,也可以通过静态方法of()或parse创建任意一个日期.再也不用像之前一样年只能从1900年开始,月必须从0开始等.

LocalDate oneDay = LocalDate.of(2019,10,1);
System.out.println(oneDay);LocalDate parseDay = LocalDate.parse("2019-10-01");
System.out.println(parseDay);

打印结果:2019-10-01.

LocalDate重写了equals方法,让日期的比较也变得简单了.

// 定义任意日期
LocalDate oneDay = LocalDate.of(2019, 10, 1);
System.out.println(oneDay);// 定义任意比较
LocalDate anyDay = LocalDate.of(2019, 10, 1);
System.out.println(oneDay.equals(anyDay));

同时,针对日期还可延伸出MonthDay或YearMonth类,顾名思义,只包含月天或年月.同样适用于equals方法来比较.

另外使用before和after可以比较两个日期前后时间.

boolean notBefore = LocalDate.parse("2019-10-01").isBefore(LocalDate.parse("2019-10-02"));
boolean isAfter = LocalDate.parse("2019-10-01").isAfter(LocalDate.parse("2019-10-02"));

对日期进行前一天后一天或前一个月的加减也变得十分方便.

LocalDate tomorrowDay = LocalDate.now().plusDays(1);
LocalDate nextMonth =  LocalDate.now().plusMonths(1);

还有,我们在实战的时候往往要获取某一天的开始时间和当天所在月的第一天等.

LocalDate.now().atStartOfDay();
LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());

LocalTime-如何获取时间

LocalTime和LocalDate类似,区别在于LocalDate不包含具体时间,而LocalTime包含具体时间.同样可以使用now或of方法来获得对象.

LocalTime localTime = LocalTime.now();LocalTime oneTime = LocalTime.of(10,10,10);

LocalDate类似它也拥有parse,isBefore,获取时间单元等方法,就不再赘述.

需要注意的是,LocalTime获得的时间格式为:11:41:58.904.也就是,HH:mm:ss.nnn,这里nnn是纳秒.

还有一个在实战中查询日期区间时我们经常定义的"23:59:59.99"常量再也不用自己定义了.

// 23:59:59.999999999
LocalTime maxTime = LocalTime.MAX;
// 00:00
LocalTime minTime = LocalTime.MIN;

LocalDateTime-日期和时间的组合

LocalDateTime表示日期和时间组合.可以通过of()方法直接创建,也可以调用LocalDate的atTime()方法或LocalTime的atDate()方法将LocalDate或LocalTime合并成一个LocalDateTime.

创建时间示例:

LocalDateTime now = LocalDateTime.now();LocalDateTime oneTime = LocalDateTime.of(2019,10,14,10,12,12);// 拼接日期
LocalTime.now().atDate(LocalDate.now());

LocalDateTime与LocalDate和LocalTime之间可以相互转化.其他日期增减等操作与上面的类似.

Instant-获取时间戳

Instant用于一个时间戳,与System.currentTimeMillis()类似,但Instant可以精确到纳秒(Nano-Second).

Instant除了可以使用now()方法创建,还可以通过ofEpochSecond方法创建.

Instant now = Instant.now();Instant.ofEpochSecond(365 * 24 * 60, 100);

其中ofEpochSecond第一个参数表示秒,第二个参数表示纳秒.整体表示:从1970-01-01 00:00:00开始后的365天100纳秒的时间点.

Duration-获取时间段

Duration的内部实现与Instant类似,但Duration表示时间段,通过between方法创建.

LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration = Duration.between(from, to);// 区间统计换算
// 总天数
long days = duration.toDays();
// 小时数
long hours = duration.toHours();
// 分钟数
long minutes = duration.toMinutes();
// 秒数
long seconds = duration.getSeconds();
// 毫秒数
long milliSeconds = duration.toMillis();
// 纳秒数
long nanoSeconds = duration.toNanos();

Duration对象还可以通过of()方法创建,该方法参数为时间段长度和时间单位.

// 7天
Duration duration1 = Duration.of(7, ChronoUnit.DAYS);
// 60秒
Duration duration2 = Duration.of(60, ChronoUnit.SECONDS);

Period-获取日期段

Period与Duration类似,获取一个时间段,只不过单位为年月日,也可以通过of方法和between方法创建,between方法接收的参数为LocalDate.

Period period = Period.of(1, 10, 25);Period period1 = Period.between(LocalDate.now(), LocalDate.now().plusYears(1));

ZoneId-获取时区标识符

ZoneId类指定时区标识符, 并提供用于在Instant和LocalDateTime之间进行转换的规则.

JAVA 中常用的zoneId 有2种格式:

  1. 时区偏移量的形式:GMT+8

  2. 区域的形式:Asia/Shanghai(常用),java中使用该形式的zoneId会自动计算夏令时.

时区 国家 城市 协调世界时
Asia/Harbin 中国 哈尔滨 UTC+8
Asia/Shanghai 中国 上海 UTC+8
Asia/Chungking 中国 重庆 UTC+8
Asia/Urumqi 中国 新疆-乌鲁木齐 UTC+6
Asia/Kashgar 中国 新疆-喀什地区 UTC+6

ZonedDateTime-创建时区时间

ZonedDateTime类,用于处理带时区的日期和时间.ZoneId表示不同的时区.大约有40不同的时区.

获取所有时区集合:

Set allZoneIds = ZoneId.getAvailableZoneIds();

创建时区:

ZoneId zoneId = ZoneId.of("Asia/Shanghai");

把LocalDateTime转换成特定的时区:

ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), zoneId);

另外和时区一起使用的类是OffsetDateTime类,OffsetDateTime是不变的,表示date-time偏移,存储所有日期和时间字段,精确至纳秒,从UTC/Greenwich计算偏移.

时间日期格式化

Java8对日期的格式化操作非常简单,首先看到上面的类大多都提供了parse方法,可以直接通过解析字符串得到对应的对象.

而日期和时间的格式化可通过LocalDateTime的format方法进行格式化.

LocalDateTime dateTime = LocalDateTime.now();
String str = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(str);
str = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println(str);

可以使用DateTimeFormatter预置的格式,也可以通过DateTimeFormatter.ofPattern方法来指定格式.

关键点回顾

Java8中关于时间日期的API有以下关键点:

  • 提供了javax.time.ZoneId用来处理时区.
  • 提供了LocalDate与LocalTime类.
  • 时间与日期API中的所有类都是线程安全的.
  • 明确定义了基本的时间与日期概念.
  • 核心API:Instant,LocalDate,LocalTime,LocalDateTime,ZonedDateTime.
  • DateTimeFormatter类用于在Java中进行日期的格式化与解析.

好了,关于Java8新特性的时间日期功能就将到这里,用起来是不是简单明快多了,赶紧在项目中练练手吧.

Java中日期处理的一些坑

日期本地化

很经典的一个例子就是给一个格式化的日期串,比如 Sun Feb 13 15:00:10 +0000 2011
我们该怎么把他提取到 LocalDateTime 里.

看上去没难度,查一下 DateTimeFormatter 的文档,找到每个部分对应的 pattern 即可:

public void test(){String dateString = "Sun Feb 13 15:00:10 2011";DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy");LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);System.out.println(dateTime);
}

然而,这段代码却极大可能会报错:

Exception in thread "main" java.time.format.DateTimeParseException: Text 'Sun Feb 13 15:00:10 2011' could not be parsed at index 0at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)......

说是无法解析,研究了半天才发现 DateTimeFormatter 这个类默认进行了本地化的设置,如果默认环境是中文,那么他只能解析用中文表示的字符串,类似 星期日 二月 13 15:00:10 2011

解决的方法也很简单,强行传入一个本地化参数即可:

public void test(){String dateString = "Sun Feb 13 15:00:10 2011";DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy", Locale.US);LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);System.out.println(dateTime);
}

带时区的数据

如果我们希望打印带时区信息的格式串,一定要用 ZonedDateTime 而不能用 LocalDateTime ,比如

public void test(){DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss xx yyyy", Locale.US);String zoned = formatter.format(ZonedDateTime.now());//正常String local = formatter.format(LocalDateTime.now());//报错}

正常的会打印出 Sun Mar 11 14:22:43 +0800 2018
,而错误的就会报下面的错:

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: OffsetSecondsat java.time.LocalDate.get0(LocalDate.java:680)at java.time.LocalDate.getLong(LocalDate.java:659)at java.time.LocalDateTime.getLong(LocalDateTime.java:720)......

正确使用格式串

关于格式串的含义可以参考 java doc
,这里有很多意义相近的表示方法,用的时候要 用心
区分.

比如文档里有这两行:

Symbol  Meaning         Presentation      Examples
------  -------         ------------      -------Y     week-based-year   year           1996; 96y     year-of-era      year           2004; 04

所以我们指的年到底是哪个年呢?这个要好好区分,其实我们一般用的年是指 ‘year-of-era’ ,如果用了另外一个就会发生解析错误.

LocalDate和Date等新旧日期类的转化

// from milliseconds to LocalDateTime
val newNow = LocalDateTime.ofInstant(Instant.ofEpochSecond(milliseconds), ZoneOffset.systemDefault());// 01. java.util.Date --> java.time.LocalDateTime
public void UDateToLocalDateTime() {java.util.Date date = new java.util.Date();Instant instant = date.toInstant();ZoneId zone = ZoneId.systemDefault();LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
}// 02. java.util.Date --> java.time.LocalDate
public void UDateToLocalDate() {java.util.Date date = new java.util.Date();Instant instant = date.toInstant();ZoneId zone = ZoneId.systemDefault();LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);LocalDate localDate = localDateTime.toLocalDate();
}// 03. java.util.Date --> java.time.LocalTime
public void UDateToLocalTime() {java.util.Date date = new java.util.Date();Instant instant = date.toInstant();ZoneId zone = ZoneId.systemDefault();LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);LocalTime localTime = localDateTime.toLocalTime();
}// 04. java.time.LocalDateTime --> java.util.Date
public void LocalDateTimeToUdate() {LocalDateTime localDateTime = LocalDateTime.now();ZoneId zone = ZoneId.systemDefault();Instant instant = localDateTime.atZone(zone).toInstant();java.util.Date date = Date.from(instant);
}// 05. java.time.LocalDate --> java.util.Date
public void LocalDateToUdate() {LocalDate localDate = LocalDate.now();ZoneId zone = ZoneId.systemDefault();Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();java.util.Date date = Date.from(instant);
}// 06. java.time.LocalTime --> java.util.Date
public void LocalTimeToUdate() {LocalTime localTime = LocalTime.now();LocalDate localDate = LocalDate.now();LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);ZoneId zone = ZoneId.systemDefault();Instant instant = localDateTime.atZone(zone).toInstant();java.util.Date date = Date.from(instant);
}

java.sql.Date与java.util.Date的转化

java.sql.Date date1 = new java.sql.Date(new java.util.Date().getTime());java.util.Date date2 = new java.util.Date(new java.sql.Date().getTime());

LocalDate和java.sql.Date的相互转换

# java.time.LocalDate转换为java.sql.Date
java.sql.Date.valueOf(localDate);# java.sql.Date转换为java.time.LocalDate
sqlDate.toLocalDate();

LocalDate和java.sql.Timestamp的相互转换

# java.time.LocalDateTime转换为java.sql.Timestamp
java.sql.Date.valueOf(localDateTime);# java.sql.Timestamp转换为java.time.LocalDateTime
sqlDate.toLocalDateTime();

Java8时间日期库DateTime API及示例相关推荐

  1. Java 8中的时间日期库DateTime API及示例

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! Java8新特性的功能已经更新了不少篇幅了,今天重点讲解时间日期库中DateTime相关处理 ...

  2. sqlserver获取前一天的日期_Java8新特性时间日期库DateTime API及示例

    点击上方蓝字关注「程序新视界」 Java8新特性的功能已经更新了不少篇幅了,今天重点讲解时间日期库中DateTime相关处理.同样的,如果你现在依旧在项目中使用传统Date.Calendar和Simp ...

  3. java instant获取微秒转成日期格式_Java8新特性时间日期库DateTime API及示例

    点击上方蓝字关注「程序新视界」 Java8新特性的功能已经更新了不少篇幅了,今天重点讲解时间日期库中DateTime相关处理.同样的,如果你现在依旧在项目中使用传统Date.Calendar和Simp ...

  4. java system datetime_Java8新特性时间日期库DateTime API及示例

    Java8新特性的功能已经更新了不少篇幅了,今天重点讲解时间日期库中DateTime相关处理.同样的,如果你现在依旧在项目中使用传统Date.Calendar和SimpleDateFormat等API ...

  5. Java 8新的时间日期库的20个使用示例

    Java 8还引入了一套全新的时间日期API,在本篇教程中我们将通过几个简单的任务示例来学习如何使用Java 8的这套API.Java对日期,日历及时间的处理一直以来都饱受诟病,尤其是它决定将java ...

  6. java怎么给时间赋值_Java 8新时间日期库java.time的使用示例

    Instant--它代表的是时间戳 LocalDate--不包含具体时间的日期,比如 2020-01-14.它可以用来存储生日,周年纪念日,入职日期等. LocalTime--它代表的是不含日期的时间 ...

  7. Java 8新的时间日期库,这二十个案例看完你还学不会算我的!!!

    Java对日期,日历及时间的处理一直以来都饱受诟病,尤其是它决定将java.util.Date定义为可修改的以及将SimpleDateFormat实现成非线程安全的.看来Java已经意识到需要为时间及 ...

  8. Java 8 时间日期库的20个使用演示样例

    除了lambda表达式,stream以及几个小的改进之外,Java 8还引入了一套全新的时间日期API,在本篇教程中我们将通过几个简单的任务演示样例来学习怎样使用Java 8的这套API.Java对日 ...

  9. java8 时间加一秒_java8 时间加一秒_java8新的时间日期库及使用示例

    来自:Java译站 链接:http://it.deepinmind.com/java/2015/03/17/20-examples-of-date-and-time-api-from-Java8.ht ...

最新文章

  1. 字节跳动AI Lab社招以及实习生内推
  2. 【深度学习】Tensorflow搭建卷积神经网络实现情绪识别
  3. 服务器弱口令修改,Tomcat服务器弱口令漏洞攻击实验
  4. VTK:图像透明度用法实战
  5. Deployment descriptor
  6. centos 7 mysql 导入_linux centos7.3导出、导入、Mariadb(MySQL)数据库命令
  7. 20164317《网络对抗技术》Exp3 免杀原理与实践
  8. 学习yii2.0框架阅读代码(九)
  9. ectouch2.0 php5.5_ectouch: ECTouch是一款开源的电商系统,为中小企业提供最佳的新零售解决方案。...
  10. figma 导入导出 fig 文件
  11. 论uni-app中,文本首行缩进两个字符解决方法
  12. 时间戳——微信保存视频时发现的问题
  13. (LeetCode)数数关系——Non-decreasing Array(非递减数组)
  14. 使用Google reCAPTCHA进行人机验证
  15. 旋转卡(qia)壳 平面最远点对
  16. 微信红包又创新纪录 跨年夜发红包数达23.1亿次
  17. 第二届童装品牌团2015年春夏联展
  18. 网传学习通1.7亿密码泄露:有什么补救措施?
  19. 《全国青少年软件编程等级考试》2021年3月C语言一级真题(含答案)
  20. 电影类网站如何选择服务器

热门文章

  1. dreamweaver cc 2018 mac版怎么破解?怎么汉化dw mac版?
  2. 审美--《艺术与审美》课程学习笔记
  3. linux恢复树莓派内存卡容量,树莓派自动扩展tf卡剩余空间
  4. Linux中单引号双引号的区别与用法
  5. 基于opencv的c++图像处理(图像滤波)
  6. ROS与Arduino:用Twist消息+用键盘+用Xbox one手柄 控制小车
  7. excel - 遍历一块区域的方法 (TBD)
  8. 研报:什么是RWAs 哪些DeFi协议在引入RWAs
  9. CSS入门(css 基本教程)
  10. iOS - MKMapView 地图