Java8日期时间API 秒速五厘米 2021-09-21 13:56 867阅读 0赞 ### 文章目录 ### * * Java 8以前日期时间API存在的问题 * 关于时间和时区 * * GMT和UTC * 时区 * Unix时间戳 * * Java 中的 Unix 时间 * LocalDate、LocalTime、LocalDateTime * * 获取对象的方法 * 常用方法 * * 与获取相关的方法(get系类的方法) * 转换的方法 * 判断的方法 * 增减年月日时分秒的方法(plus/minus系列的方法) * 指定年月日时分秒的方法 * 将日期格式化为字符串的方法 * 解析字符串为日期时间的方法 * TemporalAdjuster接口 - 时间调节器 * Duration类 - 用于计算两个“时间”间隔的类 * Period类 - 用于计算两个“日期”间隔的类 * Instant 时间戳类 * Clock - 时钟系统 * ZonedDate、ZonedTime、ZonedDateTime - 带时区的日期时间 * * ZoneId - 世界时区类 * DateTimeFormatter类 - 用于解析日期字符串和格式化日期输出 * * 格式化输出 & 字符串解析 * 使用\`SimpleDateFormat\`的正确姿势 * Java8 日期时间类与Date类的相互转化 * * Date和Instant互相转换 * Date与LocalDateTime互相转换 * Date与LocalDate互相转换 * Date转换为LocalTime * GregorianCalendar与ZonedDateTime相互转换 * Java 8日期时间类图 * Java 8时间日期 API 中的设计模式 * Java 8的日期时间API总结 ## Java 8以前日期时间API存在的问题 ## 作为开发者,经常需要处理日期时间。在Java 8以前,相信你对 `java.util.Date` 、`java.util.Calendar` 、`java.util.GregoiranCalendar` 和 `java.text.SimpleDateFormat` 这四大类非常熟悉,它们分别用于处理日期、日历、公历、日期时间格式化。 这四个类有好多陷阱和坑,比如 1. **非线程安全**:这四大类都不是线程安全的。开发者在使用这些类时必须自己处理多线程并发问题。 2. **设计不佳** :一方面日期和日期格式化分布在多个包中;另一方面,`java.util.Date` 的默认日期为1970年1月1日,没有统一性。而且 `Date` 类也缺少直接操作日期的相关方法。 3. **时区处理困难**:因为设计不佳,开发人员不得不编写大量代码来处理时区问题。 4. 还有其它一些问题,如Calendar类月份从零开始计算等。 面对种种问题,Java 8 终于重新设计了所有日期时间、日历及时区相关的 API。并把它们都统一放置在 `java.time` 包和子包下。并作出了以下改进 1. 新的日期时间 API 是线程安全的。不仅没有 `setter` 方法,而且任何对实例的变更都会返回一个新的实例,保证原来的实例不变。 2. 新的日期时间 API 提供了大量的方法,用于修改日期时间的各个部分,并返回一个新的实例。 3. 借鉴了第三方日期时间库`joda`很多的优点。 4. 在时区方面,新的日期时间 API 引入了 **域** ( domain ) 这个概念。 同时 Java 8 还针对原来复杂的 API 进行重新组合和拆分,分成了好多个类。 ## 关于时间和时区 ## ### GMT和UTC ### [GMT][],即格林尼治标准时间,也就是世界时。GMT的正午是指当太阳横穿格林尼治子午线(本初子午线)时的时间。但由于地球自转不均匀不规则,导致GMT不精确,现在已经不再作为世界标准时间使用。 [UTC][],即协调世界时。是经过平均太阳时(以格林威治时间 GMT 为准)、地轴运动修正后的新时标,以「秒」为单位的国际原子时所综合精算而成的时间。为确保UTC与GMT相差不会超过0.9秒,在有需要的情况下(例如 `1998-12-31T23:59:60Z`)会在UTC内加上正或负闰秒。协调世界时区会使用 “Z” 来表示,协调世界时也会被称为 “Zulu time”。UTC现在作为世界标准时间使用。 所以,UTC与GMT基本上等同,误差不超过0.9秒。不过日常使用中,GMT 与 UTC 的功能与精确度是没有差别的。 ### 时区 ### 时区作为地理概念,表示 “遵守统一时间标准的一个地区”。 使用与 UTC 的偏移来表示时区,例如:中国所在时区为 UTC+08:00(又称为 Chinese Standard Time,即 “中国标准时间”) 地球自西向东旋转,东边比西边先看到太阳,东边的时间也比西边的早。为了统一世界的时间,1884年的国际经度会议规规定将全球划分为24个[时区][Link 1](东、西各12个时区)。规定英国(格林尼治天文台旧址)为零时区(基准 UTC),东1-12区,西1-12区,中国北京处于东8区(UTC+08:00),那么我们的时间会领先基准-也就是我们在早上 9 点时,伦敦是早上 1 点。 ### Unix时间戳 ### 计算机中的[Unix时间戳][Unix],使用自 `1970-01-01T00:00:00Z`(Z 即表示 UTC 时间)至今的毫秒差作为表示时间的数值,并且移除期间的“闰秒”(例如 `1998-12-31T23:59:60Z`),这么做当然是为了简化计算机对时间操作的复杂度。Unix 时间体系中,每天固定 86400 秒,这个时间是绝对公立的,它和时区没有任何关系。 #### Java 中的 Unix 时间 #### Java 确保:每天 24 小时、每小时 60 分、每分钟 60 秒。 Java 中获取 “当前” 时间的方法,其底层实现,全部由 `java.lang.System.currentTimeMillis()` 提供自 UTC 1970-01-01T00:00:00 的毫秒数。`java.lang.System.currentTimeMillis()` 作为 native 方法,其实现与 JVM 所在的机器相关(通常使用 [NTP 协议][NTP]保持更新)。 ## LocalDate、LocalTime、LocalDateTime ## `java.time.LocalDate` 用于表示 “本地日期”,无 “时间”。`LocalDate` 不承载时区信息。 `java.time.LocalTime` 用于表示 “本地时间”,无 “日期”。`LocalTime` 不承载时区信息。 `java.time.LocalDateTime` 用于表示 “本地日期与时间”。`LocalDateTime` 不承载时区信息。 `LocalDate` 实例与 `LocalTime` 实例能够共同构建 `LocalDateTime` 实例,由 `LocalDateTime` 实例能够获取 `LocalDate` 实例与 `LocalTime` 实例。 由于 LocalDateTime 不承载时区信息,因此,其不能与 Instant 相互转换,必须提供时区信息。 ### 获取对象的方法 ### **获取对象**的方法: 1. 通过**静态方法** :now()(获取的时间是**系统当前的时间**) 2. 通过**静态方法**:of()(方法参数可以**指定时间**) @Test public void test01() { /* 通过静态方法 now() 返回该类的实例 */ //获取当前的日期时分秒 LocalDateTime now = LocalDateTime.now(); System.out.println(now); //获取当前的日期 LocalDate now1 = LocalDate.now(); System.out.println(now1); //获取当前的时分秒 LocalTime now2 = LocalTime.now(); System.out.println(now2); System.out.println("========================================="); /* 静态方法 of() 返回该类的实例 */ //指定日期时分秒 LocalDateTime localDateTime = LocalDateTime.of(2048, 11, 25, 12, 00, 30); System.out.println(localDateTime); //指定日期 LocalDate date = LocalDate.of(2020, 12, 12); System.out.println(date); //指定时分秒 LocalTime time = LocalTime.of(14, 20, 30); System.out.println(time); } 输出结果 2020-12-12T16:02:30.502 2020-12-12 16:02:30.502 ========================================= 2048-11-25T12:00:30 2020-12-12 14:20:30 ### 常用方法 ### #### 与获取相关的方法(get系类的方法) #### * getYear():获取年 * getHour():获取**小时** * getMinute():获取**分钟** * getSecond():获取**秒值** * getDayOfMonth():获得**月份天数(1-31)** * getDayOfYear():获得**年份天数(1-366)** * getDayOfWeek():获得**星期几(返回一个 DayOfWeek枚举值)** * getMonth():获得**月份(返回一个 Month 枚举值)** * getMonthValue():获得**月份(1-12)** * getYear():获得**年份** @Test public void test02() { //获取日期时分秒 LocalDateTime now = LocalDateTime.now(); //获取年份 int year = now.getYear(); System.out.println(year); //获取月份枚举 //Month 枚举类,定义了十二个月份 Month month = now.getMonth(); System.out.println(month); //获取月份的数值 int monthValue = now.getMonthValue(); System.out.println(monthValue); //获取当天在本月的第几天 int dayOfMonth = now.getDayOfMonth(); System.out.println(dayOfMonth); //获取小时 int hour = now.getHour(); System.out.println(hour); //获取分钟 int minute = now.getMinute(); System.out.println(minute); //获取秒值 int second = now.getSecond(); System.out.println(second); } 输出结果 2020 DECEMBER 12 12 16 2 48 #### 转换的方法 #### * toLocalDate():将LocalDateTime转换为相应的LocalDate对象 * toLocalTime():将LocalDateTime转换为相应的LocalTime对象 @Test public void test04() { //获取当前年月日,时分秒 LocalDateTime now = LocalDateTime.now(); System.out.println(now); //将LocalDateTime转换为相应的LocalDate对象 LocalDate localDate = now.toLocalDate(); System.out.println(localDate); //将LocalDateTime转换为相应的LocalTime对象 LocalTime localTime = now.toLocalTime(); System.out.println(localTime); } 输出结果 2020-12-12T16:07:23.045 2020-12-12 16:07:23.045 #### 判断的方法 #### * isAfter():判断一个日期**是否在指定日期之后** * isBefore():判断一个日期**是否在指定日期之前** * isEqual():判断两个日期**是否相同** * isLeapYear():判断**是否是闰年**(注意是**LocalDate类 和 LocalDateTime类**特有的方法) @Test public void test05() { //获取当前的日期 LocalDate now = LocalDate.now(); //指定的日期 LocalDate of = LocalDate.of(2015, 12, 12); //判断一个日期是否在另一个日期之前 boolean before = of.isBefore(now); System.out.println(before); //判断一个日期是否在另一个日期之后 boolean after = of.isAfter(now); System.out.println(after); //判断这两个日期是否相等 boolean after1 = now.equals(of); System.out.println(after1); //判断闰年 boolean leapYear = of.isLeapYear(); System.out.println(leapYear); } 输出结果 true false false false #### 增减年月日时分秒的方法(plus/minus系列的方法) #### 增加相关的方法 * plusYears(int offset):增加指定年份 * plusMonths(int offset):增加指定月份 * plusWeeks(int offset):增加指定周 * plusDates(int offset):增加指定日 * plusHours(int offset):增加指定时 * plusMinuets(int offset):增加指定分 * plusSeconds(int offset):增加指定秒 * plusNanos(int offset):增加指定纳秒 减少相关的方法 * minusYears(int offset):减少指定年 * minusMonths(int offset):减少指定月 * minusWeeks(int offset):减少指定周 * minusDates(int offset):减少指定日 * minusHours(int offset):减少指定时 * minusMinuets(int offset):减少指定分 * minusSeconds(int offset):减少指定秒 * minusNanos(int offset):减少指定纳秒 @Test public void test07() { //增加时间量的方法 plusXXX系类的方法 返回的是一个新的日期对象 LocalDateTime now = LocalDateTime.now(); System.out.println(now); //可以给当前的日期增加时间量 LocalDateTime newDate = now.plusYears(1); int year = newDate.getYear(); System.out.println(year); System.out.println("================================"); //减去时间量的方法minusXXX 系列的方法 返回的是一个新的日期对象 LocalDate now1 = LocalDate.now(); System.out.println(now1); LocalDate newDate2 = now1.minusDays(10); int dayOfMonth = newDate2.getDayOfMonth(); System.out.println(dayOfMonth); } 输出结果 2020-12-12T16:12:43.228 2021 ================================ 2020-12-12 2 #### 指定年月日时分秒的方法 #### * with(TemporalAdjuster adjuster):指定特殊时间 * withYear(int year):指定年 * withDayOfYear(int dayOfYear):指定日 * withMonth(int month):指定月 * withDayOfMonth(int dayOfMonth):指定日 @Test public void test08() { //指定某个日期的方法 with()方法 LocalDate now2 = LocalDate.now(); System.out.println(now2); LocalDate localDate = now2.withYear(2014); System.out.println(localDate); // TemporalAdjusters工具类,提供了一些获取特殊日期的方法 LocalDate with = now2.with(TemporalAdjusters.firstDayOfMonth()); System.out.println(with); LocalDate with1 = now2.with(TemporalAdjusters.firstDayOfNextMonth()); System.out.println(with1); //获取这个月的第几个星期几是几号,比如 TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.FRIDAY) // 代表的意思是这个月的第二个星期五是几号 LocalDate with2 = now2.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.FRIDAY)); System.out.println(with2); } 输出结果 2020-12-12 2014-12-12 2020-12-01 2021-01-01 2020-12-11 #### 将日期格式化为字符串的方法 #### * format():**格式化字符串** @Test public void test03() { //获取当前日期时分秒 LocalDateTime now = LocalDateTime.now(); //默认格式 年-月-日T时:分:秒 System.out.println(now); //指定格式 DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒"); //传入格式 String dateStr = now.format(ofPattern); System.out.println(dateStr); } 输出结果 2020-12-12T16:06:12.705 2020年12月12日 16时06分12秒 #### 解析字符串为日期时间的方法 #### * paser(String str):将一个日期字符串解析成日期对象,注意字符串日期的写法的格式要正确,否则解析失败 * paser(String str, DateTimeFormatter formatter):将字符串按照参数传入的格式进行解析 @Test public void test06() { //给出一个符合默认格式要求的日期字符串 String dateStr = "2020-01-01"; //把日期字符串解析成日期对象 如果日期字符串时年月日 解析时用 LocalDate LocalDate parse = LocalDate.parse(dateStr); System.out.println(parse); System.out.println("==========================================="); //给出一个符合默认格式要求的 时分秒 字符串 String dateTimeStr = "14:20:30"; //把 时分秒 字符串解析成时分秒对象 LocalTime parse1 = LocalTime.parse(dateTimeStr); System.out.println(parse1); System.out.println("========================================="); //给出一个符合默认格式要求的 日期时分秒 字符串 String str = "2018-12-12T14:20:30"; //把 日期时分秒 字符串解析成时分秒对象 LocalDateTime parse2 = LocalDateTime.parse(str); System.out.println(parse2); System.out.println("========================================"); //给出一个自定义日期时分秒格式字符串 String dateStr2 = "2020年12月12日 12:13:14"; //给出一个自定义解析格式 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); //按照指定的格式去解析 LocalDateTime parse3 = LocalDateTime.parse(dateStr2, formatter); System.out.println(parse3); } 输出结果 2020-01-01 =========================================== 14:20:30 ========================================= 2018-12-12T14:20:30 ======================================== 2020-12-12T12:13:14 ## TemporalAdjuster接口 - 时间调节器 ## 前面看到的所有日期操作都是相对比较直接的。有的时候,你需要进行一些更加灵活复杂的操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,就需要时间调节器 TemporalAdjuster,可以更加灵活地处理日期。TemporalAdjusters 工具提供了一些通用的功能,并且你还可以新增你自己的功能。 @Test public void testTemporalAdjuster() { LocalDate now = LocalDate.now(); //指定日期 //对于一些特殊的日期,可以通过一个工具类TemporalAdjusters 来指定 //见名知意,本月第一天 TemporalAdjuster temporalAdjuster = TemporalAdjusters.firstDayOfMonth(); LocalDate with = now.with(temporalAdjuster); System.out.println(with); //下周周末 TemporalAdjuster next = TemporalAdjusters.next(DayOfWeek.SUNDAY); LocalDate with1 = now.with(next); System.out.println(with1); System.out.println("==================================="); LocalDate now1 = LocalDate.now(); //自定义日期 - 下一个工作日 LocalDate with2 = now1.with(new TemporalAdjuster() { @Override //参数 nowDate 当前的日期对象 public Temporal adjustInto(Temporal nowDate) { //向下转型 LocalDate date = (LocalDate) nowDate; if (date.getDayOfWeek().equals(DayOfWeek.FRIDAY)) { LocalDate localDate = date.plusDays(3); return localDate; } else if (date.getDayOfWeek().equals(DayOfWeek.SATURDAY)) { LocalDate localDate = date.plusDays(2); return localDate; } else { LocalDate localDate = date.plusDays(1); return localDate; } } }); System.out.println("下一个工作日是:" + with2); } 输出结果 2020-12-01 2020-12-13 =================================== 下一个工作日是:2020-12-14 ## Duration类 - 用于计算两个“时间”间隔的类 ## Duration 表示一个时间段,Duration 包含两部分:seconds 表示秒,nanos 表示纳秒,它们的组合表达了时间长度。 因为 Duration 表示时间段,所以 Duration 类中不包含 now() 静态方法。**注意,Duration 不包含毫秒这个属性。** `Duration`只能处理两个`LocalTime`, `LocalDateTime`, `ZonedDateTime`; 如果传入的是`LocalDate`,将会抛出异常 **常用API**: * **静态方法 between()**:计算两个**时间的间隔**,默认是**秒** * toDays():将时间转换为以天为单位的 * toHours():将时间转换为以时为单位的 * toMinutes():将时间转换为以分钟为单位的 * toMillis():将时间转换为以毫秒为单位的 * toNanos():将时间转换为以纳秒为单位的 @Test public void test10() { //计算时间的间隔 Instant start = Instant.now(); for (int i = 0; i < 100000; i++) { // System.out.println(i); } Instant end = Instant.now(); Duration duration = Duration.between(start, end); long l = duration.toNanos(); //间隔的时间 System.out.println("循环耗时:" + l + "纳秒"); } 输出结果 循环耗时:1000000纳秒 ## Period类 - 用于计算两个“日期”间隔的类 ## Period 在概念上和 Duration 类似,区别在于 Period 是以年月日来衡量一个时间段。Duration 用于计算两个时间间隔,Period 用于计算两个日期间隔,所以 between() 方法只能接收 LocalDate 类型的参数。 * **静态方法 between()**:计算两个**日期之间的间隔** * getYears():获取年份 * getMonths():获取月份 * getDays():获取天数 @Test public void test11() { //计算两个日期的间隔 LocalDate birthday = LocalDate.of(2012, 12, 12); LocalDate now = LocalDate.now(); //我从出生到现在,有多少岁,零几个月,几天 //计算两个日期的间隔 Period between = Period.between(birthday, now); int years = between.getYears(); int months = between.getMonths(); int days = between.getDays(); System.out.println("玛雅人的地球都消灭了" + years + "年" + months + "月" + days + "天了..."); } 输出结果 玛雅人的地球都消灭了8年0月0天了... ## Instant 时间戳类 ## `java.time.Instant` 时间线上的一个瞬时点,承载纳秒级精度的 Unix 时间戳,其 `String toString()` 方法基于 ISO-8601 进行格式化。`Instant` 不承载时区信息。 **获取对象的方法**:now():注意默认获取出来的是默认时区,和我们**相差八个小时**(因为我们在**东八时区**) **设置偏移量的方法**:atOffset() **获取系统默认时区时间的方法**:atZone():方法的参数是要一个**时区的编号**(可以通过时区编号类获取ZonedDateTime类的对象) **get系列的方法** * getEpochSecond():获取从**1970-01-01 00:00:00**到**当前时间**的**秒值** * toEpochMilli():获取从**1970-01-01 00:00:00**到**当前时间**的**毫秒值** * getNano():把获取到的**当前时间**的秒数 **换算成纳秒** **ofEpoch系列方法** * ofEpochSecond():给计算机元年**增加秒数** * ofEpochMilli():给计算机元年**增加毫秒数** @Test public void test09() { // Instant 时间戳类从1970 -01 - 01 00:00:00 截止到当前时间的毫秒值 Instant now = Instant.now(); System.out.println(now); //获取的是默认时区,获取的不是中国 的时区 //获取当前时区的,我们可以添加偏移量,返回偏移过后的日期 OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime); System.out.println("==========================="); //从1970 - 01 - 01 00:00:00 截止到当前时间的毫秒值 long l = System.currentTimeMillis(); System.out.println(l); long time = new Date().getTime(); System.out.println(time); //JDK1.8 Instant 时间戳类从1970 -01 - 01 00:00:00 截止到当前时间的毫秒值 Instant now1 = Instant.now(); //toEpochMilli():从1970 -01 - 01 00:00:00 截止到当前时间间隔的毫秒值 long l1 = now1.toEpochMilli(); System.out.println(l1); //获取从1970 -01 - 01 00:00:00 截止到当前时间间隔的秒值 long epochSecond = now1.getEpochSecond(); System.out.println(epochSecond); System.out.println("=========================="); //给计算机元年增加相应的时间量 Date date = new Date(1000 * 60 * 60 * 24); System.out.println(date); //现在 给计算机元年增加相应的时间量 //5. ofEpochSecond() 方法 给计算机元年增加秒数 //ofEpochMilli() 给计算机元年增加毫秒数 Instant instant = Instant.ofEpochMilli(1000 * 60 * 60 * 24); System.out.println(instant); //ofEpochSecond() 方法 给计算机元年增加秒数 Instant instant1 = Instant.ofEpochSecond(60 * 60 * 24); System.out.println(instant1); } 输出结果 2020-12-12T08:48:46.480Z 2020-12-12T16:48:46.480+08:00 =========================== 1607762926539 1607762926540 1607762926540 1607762926 ========================== Fri Jan 02 08:00:00 CST 1970 1970-01-02T00:00:00Z 1970-01-02T00:00:00Z ## Clock - 时钟系统 ## Clock 是时钟系统,用于查找当前时刻。你可以用它来获取某个时区下当前的日期或者时间。可以用 Clock 来替代旧的 System.currentTimeInMillis() 与 TimeZone.getDefault() 方法。 @Test public void testClock() { //系统默认时间 Clock clock = Clock.systemDefaultZone(); System.out.println(clock.instant().toString()); //世界协调时UTC Clock clock1 = Clock.systemUTC(); //通过Clock获取当前时刻 System.out.println("当前时刻为:" + clock1.instant()); //获取clock对应的毫秒数,与System.currentTimeMillis()输出相同 System.out.println(clock1.millis()); System.out.println(System.currentTimeMillis()); System.out.println(new Date(System.currentTimeMillis()).toString()); //在clock基础上增加6000秒,返回新的Clock Clock clock2 = Clock.offset(clock1, Duration.ofSeconds(6000)); //纽约时间 Clock clock3 = Clock.system(ZoneId.of("America/New_York")); System.out.println("Current DateTime with NewYork clock: " + LocalDateTime.now(clock3)); System.out.println(clock3.millis()); } 输出结果 2020-12-12T09:05:07.025Z 当前时刻为:2020-12-12T09:05:07.036Z 1607763907036 1607763907036 Sat Dec 12 17:05:07 CST 2020 Current DateTime with NewYork clock: 2020-12-12T04:05:07.040 1607763907041 ## ZonedDate、ZonedTime、ZonedDateTime - 带时区的日期时间 ## 这个三个类方法及用法和 LocalDate、 LocalTime、 LocalDateTime 基本一样,只不过ZonedDate、ZonedTime、ZonedDateTime 这三个**带有特定时区** ### ZoneId - 世界时区类 ### Java 使用 ZoneId 来标识不同的时区。时区从基准 UTC 开始的一个固定偏移。ZoneId 的子类 ZoneOffset,代表了这种从伦敦格林威治零度子午线开始的时间偏移,也就是时差。 **常用API**: * getAvailableZoneIds():获取世界各个地方的时区的集合 * systemDefault():获取系统默认时区的ID * of(String zoneName):根据各个地区的时区ID名创建对象 @Test public void test13() { //ZoneID 世界时区类 //获取世界各地的时区编号。 Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); for (String availableZoneId : availableZoneIds) { System.out.println(availableZoneId); } System.out.println("====================="); //获取系统的默认时区编号 ZoneId zoneId = ZoneId.systemDefault(); System.out.println(zoneId); //获取其他国家的日期 LocalDateTime now = LocalDateTime.now(); //获取指定时区的日期时间 ZoneId zoneId1 = ZoneId.of("Europe/Monaco"); ZonedDateTime zonedDateTime = now.atZone(zoneId1); //获得指定时区的当前时间 System.out.println(zonedDateTime); System.out.println("====================="); //根据时区,获取该地区的日期 LocalDateTime now1 = LocalDateTime.now(ZoneId.of("America/Phoenix")); //获得指定时区的当前时间(不带时区信息) System.out.println(now1); } 输出结果 America/Toronto Asia/Singapore Australia/Lindeman America/Los_Angeles SystemV/EST5EDT Pacific/Majuro America/Argentina/Buenos_Aires Europe/Nicosia Pacific/Guadalcanal Europe/Athens US/Pacific Europe/Monaco ===================== Asia/Shanghai 2020-12-12T20:56:27.214+01:00[Europe/Monaco] ===================== 2020-12-12T05:56:27.225 ## DateTimeFormatter类 - 用于解析日期字符串和格式化日期输出 ## DateTimeFormatter用于解析日期字符串和格式化日期输出,创建格式化器最简单的方法是通过 DateTimeFormatter 的静态工厂方法以及常量。 在java8之前,我们进行时间格式化主要是使用`SimpleDateFormat`,而在java8中,主要是使用`DateTimeFormatter`,java8中,预定义了一些标准的时间格式,我们可以直接将时间转换为标准的时间格式 **常用API** : * ofPattern(“yyyy-MM-dd”):静态方法,通过给定格式获取对象 * format():把一个日期对象的**默认格式** 格式化成**指定的格式**的**字符串** @Test public void test12() { // 之前格式化日期的类 new SimpleDateFormat() //JDK1.8 DateTimeFormatter //指定格式 静态方法 ofPattern() DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // DateTimeFormatter 自带的格式方法 LocalDateTime now = LocalDateTime.now(); //把日期对象,格式化成字符串 String format = formatter.format(now); //刚才的方式是使用的日期自带的格式化方法 String format1 = now.format(formatter); System.out.println(format); System.out.println(format1); } 输出结果 2020-12-12 17:28:50 2020-12-12 17:28:50 ### 格式化输出 & 字符串解析 ### `java.time.format.DateTimeFormatter` 能够进行 `TemporalAccessor` 类型(包括:`LocalDate`、`LocalTime`、`LocalDateTime`、`ZonedDateTime`)的格式化输出。同时,`LocalDate`、`LocalTime`、`LocalDateTime`、`ZonedDateTime` 提供了静态的 parse 方法,能够进行字符串解析。 `LocalDate`、`LocalTime`、`LocalDateTime`、`ZonedDateTime` 允许基于类型的默认格式进行格式化输出和字符串解析。 <table> <thead> <tr> <th>类型</th> <th>默认格式示例</th> </tr> </thead> <tbody> <tr> <td><code>Instant</code></td> <td>2017-11-23T10:15:30.00Z</td> </tr> <tr> <td><code>LocalDate</code></td> <td>2017-11-23</td> </tr> <tr> <td><code>LocalTime</code></td> <td>10:15:30</td> </tr> <tr> <td><code>LocalDateTime</code></td> <td>2017-11-23T10:15:30</td> </tr> <tr> <td><code>ZonedDateTime</code></td> <td>2017-11-23T10:15:30+01:00[Asia/Shanghai]</td> </tr> </tbody> </table> ## 使用`SimpleDateFormat`的正确姿势 ## 方法一:在需要执行格式化的地方都新建SimpleDateFormat实例,使用局部变量来存放SimpleDateFormat实例 public static String formatDate(Date date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } 这种方法可能会导致短期内创建大量的SimpleDateFormat实例,如解析一个excel表格里的字符串日期。 方法二:为了避免创建大量的SimpleDateFormat实例,往往会考虑把SimpleDateFormat实例设为静态成员变量,共享SimpleDateFormat对象。这种情况下就得对SimpleDateFormat添加同步。 private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date) { synchronized (sdf) { return sdf.format(date); } } 这种方法的缺点也很明显,就是在高并发的环境下会导致解析被阻塞。 方法三(**推荐**):要在高并发环境下能有比较好的体验,可以使用ThreadLocal来限制SimpleDateFormat只能在线程内共享,这样就避免了多线程导致的线程安全问题。 private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static String formatDate(Date date) { return threadLocal.get().format(date); } ## Java8 日期时间类与Date类的相互转化 ## 在转换中,我们需要注意,因为java8之前Date是包含日期和时间的,而LocalDate只包含日期,LocalTime只包含时间,所以与Date在互转中,势必会丢失日期或者时间,或者会使用起始时间。如果转LocalDateTime,那么就不存在信息误差。 ### Date和Instant互相转换 ### @Test public void test18() { //Date与Instant互相转换 Instant instant = Instant.now(); Date date = Date.from(instant); System.out.println(date); Instant instant2 = date.toInstant(); System.out.println(instant2); } 输出结果 Sat Dec 12 21:18:13 CST 2020 2020-12-12T13:18:13.129Z ### Date与LocalDateTime互相转换 ### @Test public void test19() { //Date - LocalDateTime Date date = new Date(); System.out.println("current date: " + date); LocalDateTime localDateTime1 = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); System.out.println("localDateTime1: " + localDateTime1); LocalDateTime localDateTime2 = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); System.out.println("localDateTime2: " + localDateTime2); //LocalDateTime - Date LocalDateTime localDateTime = LocalDateTime.now(); System.out.println("localDateTime: " + localDateTime); Date date1 = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); System.out.println("date1: " + date1); } 输出结果 current date: Sat Dec 12 21:27:47 CST 2020 localDateTime1: 2020-12-12T21:27:47.592 localDateTime2: 2020-12-12T21:27:47.592 localDateTime: 2020-12-12T21:27:47.652 date1: Sat Dec 12 21:27:47 CST 2020 ### Date与LocalDate互相转换 ### @Test public void test20() { Date date = new Date(); System.out.println("current date: " + date); // Date -LocalDate LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); System.out.println("localDate: " + localDate); // LocalDate -Date LocalDate localDate1 = LocalDate.now(); //因为LocalDate不包含时间,所以转Date时,会默认转为当天的起始时间,00:00:00 Instant instant = localDate1.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); Date date1 = Date.from(instant); System.out.println("date1: " + date1); } 输出结果 current date: Sat Dec 12 21:37:51 CST 2020 localDate: 2020-12-12 date1: Sat Dec 12 00:00:00 CST 2020 ### Date转换为LocalTime ### @Test public void test21() { Date date = new Date(); System.out.println("current date: " + date); // Date - LocalTime LocalTime localTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime(); System.out.println("localTime: " + localTime); } 输出结果 current date: Sat Dec 12 21:40:47 CST 2020 localTime: 21:40:47.596 ### GregorianCalendar与ZonedDateTime相互转换 ### @Test public void test22() { //GregorianCalendar与ZonedDateTime相互转换 ZonedDateTime zonedDateTime = new GregorianCalendar().toZonedDateTime(); System.out.println("zonedDateTime: " + zonedDateTime); GregorianCalendar calendar = GregorianCalendar.from(zonedDateTime); System.out.println("calendar: " + calendar.getTime()); } 输出结果 zonedDateTime: 2020-12-12T21:55:08.286+08:00[Asia/Shanghai] calendar: Sat Dec 12 21:55:08 CST 2020 ## Java 8日期时间类图 ## ![img][] 类图所示,java.time.temporal 提供的接口: 1. `TemporalField`:日期与时间 “字段”,例如:2017-11-18 中的 18 “天” 2. `TemporalUnit`:时间 “单位”,例如:1 年 13 天的 13 “天” 3. `TemporalAccessor`:“时间相关” 对象的 “只读” 接口 4. `Temporal`:“时间相关” 对象的 “读写” 接口,继承自 `TemporalAccessor` 5. `TemporalAdjuster`:`Temporal` 类型对象 “设置 & 调整” 的函数式接口 6. `TemporalAmount`:时间段 java.time 提供的类: 1. `Instant`、`LocalDate`、`LocalTime`、`LocalDateTime`、`ZonedDateTime`:实现 `Temporal` 与 `TemporalAdjuster` 接口 2. `Duration`、`Period`:实现 `TemporalAmount` 接口 ## Java 8时间日期 API 中的设计模式 ## * 工厂模式:now()、of() 等工厂方法直接生成日期或者日期时间。 * 策略模式:LocalDate/LocalTime/LocalDateTime/ZonedDateTime,针对日期、时间、日期和时间、带时区的日期时间,使用具体的时间日期类处理。策略模式在设计一整套东西时,对开发者特别友好。 前面也提到,所有新的日期时间 API 类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分。一旦你使用了其中某个类的方法,那么非常容易上手其他类的使用。 * 构建者模式:Java 8 开始在 Calendar 中加入了构建者类,可以按如下方式生成新的 Calendar 对象。 这里设计模式与标准的教科书式的设计模式可能有所区别,所以我们在使用设计模式时也应灵活处理,不是一成不变的。 ## Java 8的日期时间API总结 ## 前面详细介绍了Java 8的日期时间API,现在进行简单总结一下。 新的时间与日期 API 中很重要的一点是,它定义清楚了基本的时间与日期的概念,比方说日期、时间、瞬时时间、持续时间、时区及时间段。它们都是基于 ISO8601 日历系统,它是世界民用历法,也就是我们所说的公历。 `java.time`包下主要包含下面几个主要的类: * LocalDate:表示不带时间的日期,比如:2016-10-20 * LocalTime:表示不带日期的时间,比如:23:12:10 * LocalDateTime:日期时间,比如:2016-10-20 23:14:21 * TemporalAdjuster : 时间调节器 * TemporalAdjusters:获得指定日期时间等,如当月的第一天、今年的最后一天等 * Duration:持续时间,计算两个“时间”的间隔 * Period:日期间隔,计算两个“日期”的间隔 * Instant:Unix 时间,它代表的是时间戳,比如 `2018-01-14T02:20:13.592Z` * Clock:时钟,获取某个时区下的瞬时时间 * ZoneId:时区id,例如 `Asia/Shanghai`等 * ZonedDateTime:带时区的日期时间 * DateTimeFormatter:时间格式化 新的 API 区分各种日期时间概念并且各个概念使用相似的方法定义模式,这种相似性非常有利于 API 的学习。总结一下一般的方法规律: * of:静态工厂方法,用于创建实例 * now:静态工厂方法,用当前时间创建实例 * parse:静态工厂方法,从字符串解析得到对象实例 * get:获取对象的部分状态 * is:检查某些东西的是否是 true,例如比较时间前后 * with:返回一个部分状态改变了的时间日期对象拷贝(单独一个with方法,参数为TemporalAdjusters类型) * plus:返回一个时间增加了的时间日期对象拷贝 * minus:返回一个时间减少了的时间日期对象拷贝 * to:把当前时间日期对象转换成另外一个,可能会损失部分状态。 * at:把这个对象与另一个对象组合起来,例如:date.atTime(time) * format:将时间日期格式化为字符串 最后再次声明,Java 8 中新的时间与日期 API 中的所有类都是不可变且线程安全的,任何修改操作都会返回一个新的实例,而之前 java.util.Date、Calendar 以及 SimpleDateFormat 这些关键的类都不是线程安全的。 [GMT]: https://baike.baidu.com/item/%E4%B8%96%E7%95%8C%E6%97%B6 [UTC]: https://baike.baidu.com/item/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6 [Link 1]: https://baike.baidu.com/item/%E6%97%B6%E5%8C%BA [Unix]: https://baike.baidu.com/item/unix%E6%97%B6%E9%97%B4%E6%88%B3 [NTP]: https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E6%97%B6%E9%97%B4%E5%8D%8F%E8%AE%AE [img]: /images/20210920/5272a9bc669945cc97afe58162dfa419.png
相关 Java 8时间日期API详解 Java 8引入了新的时间日期API,使得处理时间、日期和时间间隔变得更加简单高效。以下是Java 8时间日期API的详细介绍: 1. **基本类型**: - `Loc 灰太狼/ 2024年09月10日 16:09/ 0 赞/ 25 阅读
相关 Java8-时间日期API ava 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。 **在旧版的 Java 中,日期时间 API 存在诸多问题,其中有... 旧城等待,/ 2024年04月17日 14:42/ 0 赞/ 38 阅读
相关 Java 8 新日期时间 API 一、年月日 `Java` SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd" 短命女/ 2022年07月15日 08:46/ 0 赞/ 193 阅读
相关 Java 8 新的时间日期 API 1. 概述 1.1 简介 Java 8 引入了一套全新的时间日期API,操作起来更简便。简单介绍下,`LocalDate`和`LocalTime`和`LocalDa ゞ 浴缸里的玫瑰/ 2022年04月10日 01:37/ 0 赞/ 238 阅读
相关 Java8的时间日期API [Java8的时间日期API][Java8_API] 原先的时间 api 大部分已经过时了 Date构造器 需要传入年月日 但是对时间的加减操作比较麻烦 Cale 末蓝、/ 2022年01月28日 12:43/ 0 赞/ 309 阅读
相关 Java 8 新日期时间 API ( 上 ) – 本地日期时间 Java 8 新日期时间 API ( 上 ) – 本地日期时间 引言 作为开发者,经常需要处理日期时间。如果你跟随者 `Java 5` 一路走来,那么一定会对 `j 谁借莪1个温暖的怀抱¢/ 2021年09月22日 02:56/ 0 赞/ 425 阅读
相关 JAVA8新特性--时间日期API 类库介绍 JAVA8中新增了java.time包,新的处理日期时间的类都放在此包下。 此处介绍几个比较重要的类及其API: LocalDateTime:处理日 缺乏、安全感/ 2021年06月24日 15:59/ 0 赞/ 562 阅读
相关 Java 8 日期时间 API Java 8 日期时间 API,Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。 在旧版的 Java 中,日期时间... 灰太狼/ 2020年05月23日 17:22/ 0 赞/ 826 阅读
还没有评论,来说两句吧...