Java 8 时间日期 API
Java 8 中新的时间与日期 API 中的所有类都是不可变且线程安全的,任何修改操作都会返回一个新的实例,而之前 java.util.Date、Calendar 以及 SimpleDateFormat 这些关键的类都不是线程安全的。
java 8 对 时间 api 重新进行了设计,常用的有以下几种类。
/***
* LocalDate:表示不带时间的日期
* LocalTime:表示不带日期的时间
* LocalDateTime:日期和时间类
* ZoneId:时区
* ZonedDateTime:一个带时区的完整时间
* Instant:Unix 时间,它代表的是时间戳,比如 2018-01-14T02:20:13.592Z
* Clock:获取某个时区下当前的瞬时时间,日期或者时间
* Duration:表示一个绝对的精确跨度,使用毫秒为单位
* Period:这个类表示与 Duration 相同的概念,但是以人们比较熟悉的单位表示,比如年、月、周
* DateTimeFormatter:格式化输出
* TemporalAdjusters:获得指定日期时间等,如当月的第一天、今年的最后一天等
*
*/
方法
/**
* of:静态工厂方法,用于创建实例
* now:静态工厂方法,用当前时间创建实例
* parse:静态工厂方法,从字符串解析得到对象实例
* get:获取时间日期对象的部分状态。* is:检查某些东西的是否是 true,例如比较时间前后
* with:返回一个部分状态改变了的时间日期对象拷贝
* plus:返回一个时间增加了的、时间日期对象拷贝
* minus:返回一个时间减少了的、时间日期对象拷贝
* to:转换到另一个类型
* at:把这个对象与另一个对象组合起来,例如 date.atTime(time)
* format:提供格式化时间日期对象的能力
*
*/
LocalDate
/**
*LocalDate
*/
//LocalDate 是用来表示无时间的日期的,也不附带任何与时区相关的信息
LocalDate today = LocalDate.now();
System.out.println("今日日期:"+ today);
// 它提供 plus()/minus() 方法可以用来增加减少日、星期或者月,ChronoUnit 则用来表示这个时间单位。// 这些方法返回的是一个新的 LocalDate 实例的引用,因为 LocalTime 是不可变的,// 任何修改操作都会返回一个新的实例。LocalDate localDate = today.plusDays(1);
// 等价于 today.plus(1, ChronoUnit.DAYS);
System.out.println("明日日期:"+ localDate);
// 日期减法
LocalDate today1 = localDate.minus(1, ChronoUnit.DAYS);
System.out.println("今日日期:" + today1);
LocalTime、LocalDateTime
LocalTime time = LocalTime.now();
System.out.println("现在的时间:" + time);
// 小时
int hour = time.getHour();
System.out.println(hour);
OffsetTime offsetTime = time.atOffset(ZoneOffset.UTC);
System.out.println("UTC 时间:"+offsetTime);
/**
*
*/
// 系统默认时间
LocalTime time2 = LocalTime.now(Clock.systemDefaultZone());
System.out.println("系统默认时间 :"+time2);
LocalDate date =LocalDate.now();
//LocalDateTime 将 LocalDate 和 LocalTime 结合起来
LocalDateTime localDateTime = LocalDateTime.of(today,time2);
System.out.println("LocalDateTime 将 LocalDate 和 LocalTime 结合起来 :" + localDateTime);
//
LocalDate localDate1 = LocalDate.of(2018, 11, 11);
System.out.println("localDate1 自定义日期:" + localDate1);
LocalTime localTime2 = LocalTime.of(12, 11, 11);
LocalTime localTime3 = LocalTime.of(12, 12, 12, 12);
System.out.println("LocalTime 自定义时间:" + localTime2);
System.out.println("LocalTime 自定义时间带纳秒:" + localTime3);
DateTimeFormatter
格式化与解析时间对象 DateTimeFormatter
格式器用于解析日期字符串和格式化日期输出,创建格式器最简单的方法是通过 DateTimeFormatter 的静态工厂方法以及常量。
//1 自定义字母模式解析
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String format = localDate1.format(dateTimeFormatter);
System.out.println(format);
//2 常用 ISO 格式常量,如 ISO_LOCAL_DATE
dateTimeFormatter = DateTimeFormatter.ISO_DATE;
format = localDate1.format(dateTimeFormatter);
System.out.println(format);
//3 本地化样式,如 ofLocalizedDate(FormatStyle.MEDIUM)
dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
format = localDate1.format(dateTimeFormatter);
System.out.println("FormatStyle.FULL ="+ format);//2018 年 11 月 11 日 星期日
dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
format = localDate1.format(dateTimeFormatter);
System.out.println("FormatStyle.MEDIUM =" + format);//2018-11-11
dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);
format = localDate1.format(dateTimeFormatter);
System.out.println("FormatStyle.LONG ="+ format);//2018-11-11
// 将日期格式化指定格式
String format1 = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy,MM,dd"));
System.out.println(format1);//2019,08,25
字符串格式化成日期
LocalDate parse = LocalDate.parse("2019-10-10");
System.out.println("默认:" + parse);
//parse = LocalDate.parse("2019/8/10");// 异常
/** 默认的解析格式是 -
* public static final DateTimeFormatter ISO_LOCAL_DATE;
* static {* ISO_LOCAL_DATE = new DateTimeFormatterBuilder()
* .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
* .appendLiteral('-')
* .appendValue(MONTH_OF_YEAR, 2)
* .appendLiteral('-')
* .appendValue(DAY_OF_MONTH, 2)
* .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
* }
*/
//System.out.println("默认:" + parse);
// 所以对于 "2019/8/10" 格式 我们需要自定义 解析格式
// 注意 2019/08/10 不能写成 2019/8/10,不然会抛出异常
parse = LocalDate.parse("2019/08/10",DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(parse); //2019-08-10
// 自定义解析格式
dates = LocalDate.parse("20190825",
DateTimeFormatter.ofPattern("yyyyMMdd"));
System.out.println(dates);//2019-08-25
dates = LocalDate.parse("2019/08/25",
DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(dates);//2019-08-25
// 25/08/2019 - > 2019/08/25
LocalDate parse2 = LocalDate.parse("25/08/2019",DateTimeFormatter.ofPattern("dd/MM/yyyy"));
String format2 = parse2.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(format2);//2019/08/25
LocalDate parse2 = LocalDate.parse("25/08/2019",DateTimeFormatter.ofPattern("dd/MM/yyyy"));
System.out.println(parse2);//2019-08-25
Duration 与 Period
Duration 表示一个时间段,Duration 包含两部分:seconds 表示秒,nanos 表示纳秒,它们的组合表达了时间长度。因为 Duration 表示时间段,所以 Duration 类中不包含 now() 静态方法。注意,Duration 不包含毫秒这个属性。
LocalDateTime from =LocalDateTime.of(2019,8,11,11,11,11,11);
LocalDateTime to =LocalDateTime.of(2019,8,10,10,10,10,10);
Duration between = Duration.between(from, to);
long l = between.toDays();
System.out.println("相差天数 =" + l); // -1
long l1 = between.toHours();
System.out.println(l);// -1
long l2 = between.toMinutes();
System.out.println(l);// -1
long l3 = between.toMillis();
System.out.println(l);// -1
long l4 = between.toNanos();
System.out.println(l);// -1
Period 在概念上和 Duration 类似,区别在于 Period 是以年月日来衡量一个时间段。
Duration 用于计算两个时间间隔,Period 用于计算两个日期间隔,所以 between() 方法只能接收 LocalDate 类型的参数。
Period of1 = Period.of(2019, 10, 10);
Period of2 = Period.of(2018, 9, 9);
System.out.println(of1);//P2019Y10M10D
LocalDate from1 =LocalDate.of(2019,8,11);
LocalDate to2 =LocalDate.of(2018,8,10);
Period between1 = Period.between(from1, to2);
//Period 得到的是差值的绝对值
System.out.println("相差年:" + between1.getYears());//-1
System.out.println("相差月:" + between1.getMonths());//0
System.out.println("相差日:" + between1.getDays());//-1
// 计算两个时间的区间距离呢
long year = from1.until(to2, ChronoUnit.YEARS);//-1
long month = from1.until(to2, ChronoUnit.MONTHS);//-12
long month1 = to2.until(from1, ChronoUnit.MONTHS);//12
System.out.println(year);
System.out.println(month);
System.out.println("正值" + month1);
Instant 与 Clock
// 加 1s
Instant instant1 = Instant.ofEpochSecond(1);
System.out.println(instant1);//1970-01-01T00:00:01Z
// 加 10 毫秒
Instant instant2 = Instant.ofEpochMilli(10);
System.out.println(instant2);//1970-01-01T00:00:00.010Z
// 当前时间 从 1970-1- 1 号到现在的时间 相当于 new Date()
Instant instant = Instant.now();
// 秒 = 2019-08-24T14:49:36.344Z
System.out.println("秒 =" + instant);
//new Date() 转为 Instant
Instant instant3 = new Date().toInstant();
System.out.println("与 new Date() 转换" + instant3);//2019-08-24T14:57:07.464Z
// 解析时间
Instant parse1 = Instant.parse("2019-08-24T14:59:16.708Z");
System.out.println(parse1);
// 加 10 天
Instant plus = parse1.plus(10, ChronoUnit.DAYS);
//2019-09-03T14:59:16.708Z
System.out.println(plus);
Clock 是时钟系统,用于查找当前时刻。你可以用它来获取某个时区下当前的日期或者时间。可以用 Clock 来替代旧的 System.currentTimeInMillis() 与 TimeZone.getDefault() 方法。
// 世界协调时 UTC
Clock clock = Clock.systemUTC();
System.out.println(clock.getZone());// time-zone Z
// 获取 clock 对应的毫秒数,与 System.currentTimeMillis() 输出相同
System.out.println(clock.millis());//
// 通过 Clock 获取当前时刻
System.out.println("通过 Clock 获取当前时刻" + clock.instant());//
// 默认时区时间
Clock clock1 = Clock.systemDefaultZone();
System.out.println(clock1.instant());
// 自定义时区
Clock clock2 = clock1.withZone(ZoneId.of("Asia/Shanghai"));
Instant instant4 = clock2.instant();
System.out.println(instant4);
// 纽约时间
Clock clock3 = Clock.system(ZoneId.of("America/New_York"));
System.out.println("纽约时间:" + LocalDateTime.now(clock3));
// 不能用 .instant() 输出 此输出是系统默认时间
//System.out.println(clock3.instant());
// 当前时区加上 100s
Clock offset = Clock.offset(clock, Duration.ofSeconds(100));
System.out.println(offset.instant());
ZoneId 和 ZonedDateTime
// 默认时区时间
ZonedDateTime now = ZonedDateTime.now();
LocalDate localDate2 = now.toLocalDate();
System.out.println(localDate2);//2019-08-24
// 特定时区下的日期和时间
ZonedDateTime Shanghai = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("Asia/Shanghai"));
System.out.println(Shanghai);//2019-08-24T23:22:31.149+08:00[Asia/Shanghai]
ZonedDateTime Tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(Tokyo);//2019-08-25T00:26:51.690+09:00[Asia/Tokyo]
ZoneId america = ZoneId.of("America/New_York");
System.out.println(ZonedDateTime.now(america));//2019-08-24T11:28:48.430-04:00[America/New_York]
使用 TemporalAdjuster 类灵活操纵日期
有的时候,你需要进行一些更加复杂的日期操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,你可以使用重载版本的 withXXX 方法,向其传递一个提供了更多定制化选择的 TemporalAdjuster 对象,更加灵活地处理日期。对于最常见的用例,日期和时间 API 已经提供了大量预定义的 TemporalAdjuster。你可以通过 TemporalAdjuster 类的静态工厂方法访问它们。
TemporalAdjuster 类中的常用工厂方法
- dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天
- firstDayOfMonth 创建一个新的日期,它的值为当月的第一天
- firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天
- firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天
- firstDayOfYear 创建一个新的日期,它的值为当年的第一天
- firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值 -
- lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天
- lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天
- lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天
- lastDayOfYear 创建一个新的日期,它的值为今年的最后一天
- lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值
- next/previous 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期
- nextOrSame/previousOrSame 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期,如果该日期已经符合要求,直接返回该对象
// 当月中 第二周 的周一
TemporalAdjuster temporalAdjuster1 = TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.MONDAY);
LocalDate with = LocalDate.now().with((temporal) -> temporal.with(temporalAdjuster1));
System.out.println(with);//2019-08-12
//firstDayOfMonth 当月的第一天
TemporalAdjuster temporalAdjuster2 = TemporalAdjusters.firstDayOfMonth();
with = LocalDate.now().with((temporal) -> temporal.with(temporalAdjuster2));
System.out.println(with);//2019-08-1
//firstDayOfNextYear 明年的第一天
System.out.println(LocalDate.now().with((temporal -> temporal.with(TemporalAdjusters.firstDayOfNextYear()))));//2020-01-01
/**
* nextOrSame/previousOrSame 创建一个新的日期,* 并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期,如果该日期已经符合要求,直接返回该对象
*/
// 今天是 2019-08-25 周日 下一个周一 返回 2019-08-26
System.out.println(LocalDate.now().with((temporal -> temporal.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)))));//2020-01-01
// 返回下一个第一个满足的指定日期 也就是 周四 第一个是 2019-08-29 第二个是 2019-09-05 但是返回一个满足的。System.out.println(LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.THURSDAY)));//2019-08-29
java.util.Date 与 LocalDate、LocalTime、LocalDateTime 转换
Date date1 = new Date();
System.out.println("current date:" + date1);
ZonedDateTime zonedDateTime = date1.toInstant().atZone(ZoneId.systemDefault());
//Date -> LocalDate
LocalDate localDate31 = zonedDateTime.toLocalDate();
System.out.println(localDate31);//2019-08-24
//Date -> LocalDate
ZonedDateTime zonedDateTime3 = date1.toInstant().atZone(ZoneId.of("Asia/Shanghai"));
LocalDate localDate32 = zonedDateTime3.toLocalDate();
System.out.println(localDate32);//2019-08-24
//Date -> LocalDateTime
LocalDateTime localDateTime1 = zonedDateTime.toLocalDateTime();
//Date -> LocalDateTime 另一种方式
LocalDateTime localDateTime2 = LocalDateTime.ofInstant(date1.toInstant(), ZoneId.systemDefault());
//Date -> LocalTime
LocalTime time1 = zonedDateTime.toLocalTime();
//Calendar --> Instant
Calendar.getInstance().toInstant();
jdk 8 中 Date 类中引入了 2 个方法,from 和 toInstant
//LocalDateTime-> Date 方法
LocalDateTime localDateTime4 = LocalDateTime.now();
System.out.println("localDateTime:" + localDateTime4);//2019-08-24T23:44:23.274
//LocalDateTime-> Date 方法
Date from2 = Date.from(localDateTime4.toInstant(ZoneOffset.UTC));
Instant instant5 = from2.toInstant();
System.out.println(from2);//Sun Aug 25 07:44:23 CST 2019
System.out.println(instant5);//2019-08-24T23:44:23.274Z
//// LocalDate -> Date
LocalDate localDate3 = LocalDate.now();
// 先把 LocalDate -> LocalDateTime
LocalDateTime localDateTime3 = localDate3.atStartOfDay();
// 再把 LocalDateTime -> Date
Date from3 = Date.from(localDateTime3.toInstant(ZoneOffset.UTC));
System.out.println(from3);