第十二章:新的日期时间API
在Java 8之前,我们常用的日期时间API是java.util.Date和java.util.Calendar这两个类。
如果我们要构建一个指定年月日的日期时间对象,比如2019-9-2,使用java.util.Date类的构造方法Date(int year, int month, int date),传入的年份year参数必须是年份2019减去1900,即传入119。每次创建对象前还需要先进行计算,很不方便。
JDK 1.1提供的java.util.Calendar类在使用时也有很多不方便的地方,比如对一个日期加两天,使用add方法,传入2;对一个日期减两天,也使用add方法,传入-2。还有一点是这个类默认每周第一天是周日。使用起来也有点不方便。
归根到底,JDK1.8之前的日期时间API都是可变的,是线程不安全的。
另外,对时间日期进行格式化的类SimpleDateFormat在java.text包下,该类的定义位置不规范。它也是线程不安全的。
而在Java 8中,时间格式转化器是java.time.format.DateTimeFormatter类,它被声明为final,是不可变的类,线程安全。
另外,Java 8中提供的新日期时间API包含两类:一个是为了便于人阅读使用,包含LocalDate、LocalTime、LocalDateTime这三个类,它们都是用final修饰的类,是不可变的对象,分别表示ISO-8601日历系统中的日期、时间、日期和时间。另外一个是便于机器处理的类,Instant:用来表示机器的日期和时间格式:时间戳。
ISO-8601日历系统:是国际标准化组织制定的现代公民的日期和时间的表示法。 时间戳:从UNIX元年:1970年1月1日 00:00:00到某个时间之间的毫秒值。
传统时间格式转换器SimpleDateFormat线程安全问题演示
当多个线程同时操作同一个SimpleDateFormat对象时,就会出现线程安全问题。
演示代码:
public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 使用线程池模拟多线程 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 定义解析日期字符串任务:使用SimpleDateFormat对象解析 Callable<Date> task = () -> sdf.parse("2019-8-29"); // 存储结果容器 List<Future<Date>> result = new ArrayList<>(); for (int i = 0; i < 10; i++) { // 执行得到解析结果 result.add(threadPool.submit(task)); } // 遍历输出 for (Future<Date> r : result) { System.out.println(r.get()); } threadPool.shutdown(); } 复制代码
异常信息如下:
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points 复制代码
解决方法: 1、将SimpleDateFormat定义为方法内的局部变量,且方法中没有多线程的操作。 2、使用ThreadLocal进行线程封闭。为每个线程保存一个SimpleDateFormat对象。
传统时间格式转换器线程安全问题解决方案:使用ThreadLocal进行线程封闭
线程封闭类代码:
package cn.org.lilu.chapter12; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * @Auther: lilu * @Date: 2019/8/29 * @Description: 传统时间格式转换器线程安全问题解决方案:使用ThreadLocal进行线程封闭 */ public class TraditionalSimpleDateFormatThreadLocal { private static final ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; public static Date convert(String source) throws Exception { return threadLocal.get().parse(source); } } 复制代码
演示代码:
public static void main(String[] args) throws Exception { // 使用线程池模拟多线程 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 定义解析日期字符串任务:每个任务都有一份SimpleDateFormat对象的副本 Callable<Date> task = () -> TraditionalSimpleDateFormatThreadLocal.convert("2019-8-29"); // 解析结果容器 List<Future<Date>> result = new ArrayList<>(); for (int i = 0; i < 10; i++) { // 执行得到解析结果 result.add(threadPool.submit(task)); } // 遍历输出 for (Future<Date> r : result) { System.out.println(r.get()); } threadPool.shutdown(); } 复制代码
运行结果正常。
Java 8新的时间格式转化器DateTimeFormatter
DateTimeFormatter类定义在java.time.format包下,且声明为final类,不可变,线程安全。
代码演示:
public static void main(String[] args) throws Exception { // 按照哪种格式进行格式转换 // DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE; DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd"); // LocalDate.parse("20190829",dtf):将第一个字符串参数按照第二个参数定义的格式器解析,返回一个LocalDate对象 Callable<LocalDate> task = () -> LocalDate.parse("20190829",dtf); ExecutorService threadPool = Executors.newFixedThreadPool(10); List<Future<LocalDate>> result = new ArrayList<>(); for (int i = 0; i < 10; i++) { result.add(threadPool.submit(task)); } // 遍历输出 for (Future<LocalDate> r : result) { System.out.println(r.get()); } threadPool.shutdown(); } 复制代码
Java 8日期时间API:LocalDate、LocalTime、LocalDateTime
这三个类的用法几乎一样,LocalDate表示日期,LocalTime表示时间,LocalDateTime包含前两者,表示日期和时间。
可由前两个类组合出第三个类,也可由第三个类提取出前两个类。
年月日对象:LocalDate
可使用静态工厂方法now获取当前日期;
可使用静态工厂方法of创建一个LocalDate日期对象,可从一个LocalDate日期对象中获取该日期的年份、月份、这个月的第几天、这周的星期几、今年的第几天、这个月的长度(有几天)和是否为闰年等信息。
代码演示:
/** * LocalDate:年月日 */ @Test public void testLocalDate() { // 静态工厂方法now获取当前日期 LocalDate now = LocalDate.now(); // 静态工厂方法of创建一个LocalDate实例 LocalDate date = LocalDate.of(2019,8,26); // 年份 int year = date.getYear(); // 月份 Month month = date.getMonth(); // 这个月第几天 int dayOfMonth = date.getDayOfMonth(); // 这周星期几 DayOfWeek dayOfWeek = date.getDayOfWeek(); // 今年第几天 int dayOfYear = date.getDayOfYear(); // 这个月的长度(有几天) int lengthOfMonth = date.lengthOfMonth(); // 是否闰年 boolean leapYear = date.isLeapYear(); System.out.println(now); // 2019-09-02 System.out.println(year); // 2019 System.out.println(month); // AUGUST System.out.println(dayOfMonth); // 26 System.out.println(dayOfWeek); // MONDAY System.out.println(dayOfYear); // 238 System.out.println(lengthOfMonth); // 31 System.out.println(leapYear); // false } 复制代码
时分秒对象:LocalTime
可使用静态工厂方法now获取当前时间的时分秒(包含纳秒)。
可使用静态工厂方法of创建一个LocalTime时间对象,可从一个LocalTime时间对象中获取该时间的时、分、秒和纳秒等信息。
of方法包含三个重载,方法签名如下:
/**
* @param hour the hour-of-day to represent, from 0 to 23
* @param minute the minute-of-hour to represent, from 0 to 59
*/
public static LocalTime of(int hour, int minute);
/**
* @param hour the hour-of-day to represent, from 0 to 23
* @param minute the minute-of-hour to represent, from 0 to 59
* @param second the second-of-minute to represent, from 0 to 59
*/
public static LocalTime of(int hour, int minute, int second);
/**
* @param hour the hour-of-day to represent, from 0 to 23
* @param minute the minute-of-hour to represent, from 0 to 59
* @param second the second-of-minute to represent, from 0 to 59
* @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
*/
public static LocalTime of(int hour, int minute, int second, int nanoOfSecond); 复制代码
使用代码示例:
/** * LocalTime:时分秒 * 一天中的时间,比如:13:45:20,可以使用LocalTime类表示 * 可以使用of重载的三个静态工厂方法创建LocalTime实例 * 第一个重载方法接收小时和分钟 * 第二个重载方法同时还接收秒 * 第三个重载方法同时还接收纳秒 */ @Test public void testLocalTime() { LocalTime now = LocalTime.now(); LocalTime localTime = LocalTime.of(13, 45, 20,1); int hour = localTime.getHour(); int minute = localTime.getMinute(); int second = localTime.getSecond(); int nano = localTime.getNano(); System.out.println(now); // 19:47:51.212 System.out.println(hour); // 13 System.out.println(minute); // 45 System.out.println(second); // 20 System.out.println(nano); // 1 } 复制代码
日期时间字符串解析
LocalDate和LocalTime都可以通过解析代表它们的字符串创建。使用静态方法parse。一旦传递的字符串参数无法被解析为合法的LocalDate或LocalTime对象,这两个parse方法都会抛出一个继承自RuntimeException的DateTimeParseException异常。
代码示例:
/** * LocalDate和LocalTime都可以通过解析代表它们的字符串创建。使用静态方法parse。 * 一旦传递的字符串参数无法被解析为合法的LocalDate或LocalTime对象, * 这两个parse方法都会抛出一个继承自RuntimeException的DateTimeParseException异常。 */ @Test public void testParse() { // 小于10的必须在前面补0,否则抛出异常 LocalDate localDate = LocalDate.parse("2019-08-26"); LocalTime localTime = LocalTime.parse("13:45:20"); System.out.println(localDate); System.out.println(localTime); } 复制代码
年月日时分秒对象:LocalDateTime
可使用静态工厂方法now获取当前时间的年月日时分秒纳秒;
可使用静态工厂方法of创建一个LocalDateTime日期时间对象;
可由LocalDate和LocalTime组合出LocalDateTime对象;
可从LocalDateTime对象中提取出LocalDate和LocalTime。
代码示例:
/** * LocalDateTime:年月日时分秒 */ @Test public void testLocalDateTime() { LocalDateTime localDateTime = LocalDateTime.now(); LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.AUGUST, 26, 10, 47, 20); LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); // 由LocalDate和LocalTime组合出LocalDateTime LocalDateTime localDateTime2 = LocalDateTime.of(localDate,localTime); LocalDateTime localDateTime3 = localDate.atTime(10,51,32); LocalDateTime localDateTime4 = localDate.atTime(localTime); LocalDateTime localDateTime5 = localTime.atDate(localDate); // 由LocalDateTime提取出LocalDate和LocalTime LocalDate localDateFromLocalDateTime = localDateTime2.toLocalDate(); LocalTime localTimeFromLocalDateTime = localDateTime2.toLocalTime(); System.out.println(localDateTime); // 2019-09-02T19:57:16.516 System.out.println(localDateTime1); // 2019-08-26T10:47:20 System.out.println(localDateTime2); // 2019-09-02T19:57:16.517 System.out.println(localDateTime3); // 2019-09-02T10:51:32 System.out.println(localDateTime4); // 2019-09-02T19:57:16.517 System.out.println(localDateTime5); // 2019-09-02T19:57:16.517 System.out.println(localDateFromLocalDateTime); // 2019-09-02 System.out.println(localTimeFromLocalDateTime); // 19:57:16.517 } 复制代码
时间戳对象:Instant
机器的日期和时间格式:从UNIX元年时间开始到现在所经过的秒数对时间进行建模。包含的是由秒及纳秒组成的数字。
使用静态工厂方法now获取当前时刻的时间戳,默认获取的是UTC时区(世界协调时间)所在的时刻,可做时区偏移运算获取带偏移量的日期时间对象OffsetDateTime。可使用toEpochMilli方法获取表示的时间戳秒数。
可使用静态工厂方法ofEpochSecond等对时间戳进行运算。
Instant的设计初衷是为了便于机器使用。它包含的是由秒及纳秒所构成的数字。所以,它无法处理那些我们非常容易理解的时间单位。比如下面这行代码:
int day = Instant.now().get(ChronoField.DAY_OF_MONTH); 复制代码
它会抛出如下异常:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth 复制代码
示例代码:
/** * Instant:从UNIX元年时间开始到现在所经过的秒数对时间进行建模。包含的是由秒及纳秒组成的数字。 * * 静态工厂方法:ofEpochSecond包含两个重载版本 * // 传入一个代表秒数的值创建一个Instant实例 * Instant ofEpochSecond(long epochSecond) * // 第一个参数:代表秒数的值,第二个参数:纳秒数,对第一个参数传入的秒数进行调整,确保保存的纳秒分片在0到999 999 999之间。 * Instant ofEpochSecond(long epochSecond, long nanoAdjustment) * * 静态工厂方法:now */ @Test public void testInstant() { Instant instant1 = Instant.ofEpochSecond(3); Instant instant2 = Instant.ofEpochSecond(3, 0); Instant instant3 = Instant.ofEpochSecond(2, 1_000_000_000); Instant instant4 = Instant.ofEpochSecond(4, -1_000_000_000); System.out.println(instant1); System.out.println(instant2); System.out.println(instant3); System.out.println(instant4); Instant now = Instant.now(); // 默认获取UTC时区的时间 System.out.println(now); // 时区偏移运算:获取偏移8小时的时区的时间。OffsetDateTime:带偏移量的日期时间对象 OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime); // 转化成时间戳 System.out.println(now.toEpochMilli()); // java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth int day = now.get(ChronoField.DAY_OF_MONTH); System.out.println(day); } 复制代码
计算两个时间之间的间隔:Duration 和计算两个日期之间的间隔:Period
可以使用Duration类的静态方法between计算两个时间点之间的间隔,between方法接收的参数是两个Temporal对象,虽然LocalDateTime和Instant都是Temporal接口的实现类,但是它们是为不同的目的而设计的,一个是为了便于人阅读使用, 另一个是为了便于机器处理,所以我们不能将它们混用,即不能计算LocalDateTime和Instant对象之间的间隔。
可以使用Period类的静态方法between计算两个日期之间的间隔。
也可使用静态工厂方法直接创建Duration和Period类的对象。
示例代码:
/** * 计算两个时间之间的间隔:Duration * 计算两个日期之间的间隔:Period */ @Test public void testDuration() throws Exception { LocalTime localTime1 = LocalTime.parse("13:45:20"); LocalTime localTime2 = LocalTime.parse("13:45:30"); LocalDateTime localDateTime1 = LocalDateTime.now(); Thread.sleep(100); LocalDateTime localDateTime2 = LocalDateTime.now(); Instant instant1 = Instant.ofEpochSecond(3); Instant instant2 = Instant.ofEpochSecond(6); System.out.println(Duration.between(localTime1,localTime2)); // PT10S System.out.println(Duration.between(localDateTime1,localDateTime2)); // PT0.1S System.out.println(Duration.between(instant1,instant2)); // PT3S // 计算两个LocalDate之间的时长 Period periodBetween = Period.between(LocalDate.of(2019, 8, 26), LocalDate.of(2019, 8, 28)); System.out.println(periodBetween); // P1D System.out.println(periodBetween.getDays()); // 2 // Duration和Period的静态工厂方法直接创建实例 Duration durationOfMinutes = Duration.ofMinutes(3); Duration durationOf = Duration.of(3, ChronoUnit.MINUTES); Period periodOfDays = Period.ofDays(10); Period periodOfWeeks = Period.ofWeeks(3); Period periodOf = Period.of(2, 6, 1); System.out.println(durationOfMinutes); // PT3M System.out.println(durationOf); // PT3M System.out.println(periodOfDays); // P10D System.out.println(periodOfWeeks); // P21D System.out.println(periodOf); // P2Y6M1D } 复制代码
操作、解析和格式化日期
LocalDate、LocalTime、LocalDateTime以及Instant这样表示时间点的日期-时间类提供了很多通用的方法用来操作日期-时间。所有的方法都返回一个修改了属性的对象。它们都不会修改原来的对象。
最直接也最简单的方法是使用"withAttribute"方法。"withAttribute"方***创建对象的一个副本,并按照需要修改它的属性。
也可使用通用的with方法,它接受的第一个参数是一个TemporalField对象,第二个参数是需要修改的值。
with方法还有一个重载的方法,它接收一个日期调整器TemporalAdjuster对象,更加灵活地处理日期。
代码示例:
/** * 操纵、解析和格式化日期 * * LocalDate、LocalTime和LocalDateTime类都为final类,不可变,每次操作后都返回一个新的对应对象 */ @Test public void testUpdateTime() { LocalDate date1 = LocalDate.of(2019, 8, 26); LocalDate date2 = date1.withYear(2020); LocalDate date3 = date2.withDayOfMonth(25); LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9); System.out.println(date1); // 2019-08-26 System.out.println(date2); // 2020-08-26 System.out.println(date3); // 2020-08-25 System.out.println(date4); // 2020-09-25 LocalDate date5 = LocalDate.of(2019,8,26); LocalDate date6 = date5.plusWeeks(1); // 加一周 LocalDate date7 = date6.minusYears(3); // 减三年 LocalDate date8 = date7.plus(6, ChronoUnit.MONTHS); // 加六月 System.out.println(date5); // 2019-08-26 System.out.println(date6); // 2019-09-02 System.out.println(date7); // 2016-09-02 System.out.println(date8); // 2017-03-02 } 复制代码
日期调整器:TemporalAdjuster
TemporalAdjuster是一个函数式接口,接口方法签名如下:
Temporal adjustInto(Temporal temporal); 复制代码
接收一个Temporal对象,返回一个Temporal对象。由于所有的日期时间API都实现了Temporal接口,故它可以用来自定义更加复杂的日期时间操作。
Java 8提供了TemporalAdjusters类,该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
同时还支持定制TemporalAdjuster,定制的方式有两种:
一:实现TemporalAdjuster接口;
二:使用Lambda表达式定制TemporalAdjuster对象,推荐使用TemporalAdjusters类的静态工厂方法ofDateAdjuster,该方法签名如下:
public static TemporalAdjuster ofDateAdjuster(UnaryOperator<LocalDate> dateBasedAdjuster) 复制代码
接收一个UnaryOperator函数式接口,返回一个TemporalAdjuster对象。
UnaryOperator函数式接口中方法签名如下,它总是返回它的输入。
static <T> UnaryOperator<T> identity() { return t -> t; } 复制代码
相关代码示例如下:
/** * 日期调整器 */ @Test public void testTemporalAdjuster() { LocalDate date1 = LocalDate.of(2019,8,26); // TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) // dayOfWeek表示星期几 // 如果ordinal为0,则表示本日期所在的月的上一个月的最后一个星期几 // 如果ordinal为正数,则以本日期所在的月从前向后数,第ordinal个星期几 // 如果ordinal为负数,则以本日期所在的月从后往前数,第-ordinal个星期几 LocalDate date2 = date1.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.FRIDAY)); System.out.println("date2=" + date2); // TemporalAdjuster firstDayOfMonth():创建一个新的日期,它的值为当月的第一天 LocalDate date3 = date1.with(TemporalAdjusters.firstDayOfMonth()); System.out.println("date3=" + date3); // TemporalAdjuster firstDayOfNextMonth():创建一个新的日期,它的值为下月的第一天 LocalDate date4 = date1.with(TemporalAdjusters.firstDayOfNextMonth()); System.out.println("date4=" + date4); // TemporalAdjuster firstDayOfNextYear():创建一个新的日期,它的值为明年的第一天 LocalDate date5 = date1.with(TemporalAdjusters.firstDayOfNextYear()); System.out.println("date5=" + date5); // TemporalAdjuster firstDayOfYear():创建一个新的日期,它的值为今年的第一天 LocalDate date6 = date1.with(TemporalAdjusters.firstDayOfYear()); System.out.println("date6=" + date6); // TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek):创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的日期(这个月的第一个星期几) LocalDate date7 = date1.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY)); System.out.println("date7=" + date7); // TemporalAdjuster lastDayOfMonth():创建一个新的日期,它的值为这个月的最后一天 LocalDate date8 = date1.with(TemporalAdjusters.lastDayOfMonth()); System.out.println("date8=" + date8); // TemporalAdjuster lastDayOfYear():创建一个新的日期,它的值为今年的最后一天 LocalDate date9 = date1.with(TemporalAdjusters.lastDayOfYear()); System.out.println("date9=" + date9); // TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek):创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的日期 LocalDate date10 = date1.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)); System.out.println("date10=" + date10); // TemporalAdjuster next(DayOfWeek dayOfWeek):创建一个新的日期,并将其值设定为指定日期之后第一个符合指定星期几的日期 LocalDate date11 = date1.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); System.out.println("date11=" + date11); // TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek): // 创建一个新的日期,并将其值设定为指定日期之后第一个符合指定星期几的日期; // 如果指定日期已符合要求,则直接返回该日期 LocalDate date12 = date1.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)); System.out.println("date12=" + date12); // TemporalAdjuster previous(DayOfWeek dayOfWeek):创建一个新的日期,并将其值设定为指定日期之前第一个符合指定星期几的日期 LocalDate date13= date1.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)); System.out.println("date13=" + date13); // TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek): // 创建一个新的日期,并将其值设定为指定日期之前第一个符合指定星期几的日期; // 如果指定日期已符合要求,则直接返回该日期 LocalDate date14 = date1.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); System.out.println("date14=" + date14); // 使用Lambda表达式定制TemporalAdjuster对象,推荐使用TemporalAdjusters类的静态工厂方法ofDateAdjuster // TemporalAdjuster ofDateAdjuster(UnaryOperator<LocalDate> dateBasedAdjuster) TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(temporal -> { DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); int dayToAdd = 1; if (dayOfWeek == DayOfWeek.FRIDAY) dayToAdd = 3; else if (dayOfWeek == DayOfWeek.SATURDAY) dayToAdd = 2; return temporal.plus(dayToAdd,ChronoUnit.DAYS); }); } /** * 定制TemporalAdjuster * * 计算下一个工作日 */ class NextWorkingDay implements TemporalAdjuster { /** * 周一到周五为工作日 * 如果是周日到周四,则返回下一天 * 如果是周五、周六、返回下周周一 * @param temporal * @return */ @Override public Temporal adjustInto(Temporal temporal) { // 得到今天星期几 DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); int dayToAdd = 1; if (dayOfWeek == DayOfWeek.FRIDAY) dayToAdd = 3; else if (dayOfWeek == DayOfWeek.SATURDAY) dayToAdd = 2; return temporal.plus(dayToAdd,ChronoUnit.DAYS); } } 复制代码
TemporalAdjusters类中包含的工厂方法列表:
方法签名 | 描述 |
---|---|
TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) | dayOfWeek表示星期几 如果ordinal为0,则表示本日期所在的月的上一个月的最后一个星期几 如果ordinal为正数,则以本日期所在的月从前向后数,第ordinal个星期几 如果ordinal为负数,则以本日期所在的月从后往前数,第-ordinal个星期几 |
TemporalAdjuster firstDayOfMonth() | 创建一个新的日期,它的值为当月的第一天 |
TemporalAdjuster firstDayOfNextMonth() | 创建一个新的日期,它的值为下月的第一天 |
TemporalAdjuster firstDayOfNextYear() | 创建一个新的日期,它的值为明年的第一天 |
TemporalAdjuster firstDayOfYear() | 创建一个新的日期,它的值为今年的第一天 |
TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek) | 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的日期(这个月的第一个星期几) |
TemporalAdjuster lastDayOfMonth() | 创建一个新的日期,它的值为这个月的最后一天 |
TemporalAdjuster lastDayOfYear() | 创建一个新的日期,它的值为今年的最后一天 |
TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) | 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的日期 |
TemporalAdjuster next(DayOfWeek dayOfWeek) | 创建一个新的日期,并将其值设定为指定日期之后第一个符合指定星期几的日期 |
TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek) | 创建一个新的日期,并将其值设定为指定日期之后第一个符合指定星期几的日期;如果指定日期已符合要求,则直接返回该日期 |
TemporalAdjuster previous(DayOfWeek dayOfWeek) | 创建一个新的日期,并将其值设定为指定日期之前第一个符合指定星期几的日期 |
TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek) | 创建一个新的日期,并将其值设定为指定日期之前第一个符合指定星期几的日期;如果指定日期已符合要求,则直接返回该日期 |
时区
时区的处理是新版日期和时间API新增加的重要功能,使用新版日期和时间API时区的处理被极大地简化了。新的java.time.ZoneId类是老版java.util.TimeZone的替代品。它的设计目标就是要让你无需为时区处理的复杂和繁琐而操心,跟其他日期和时间类一 样,ZoneId类也是被final修饰而无法修改的。
代码示例:
/** * 带时区的日期时间API */ @Test public void testZoneLocalDateTime() { // 查看所有支持的时区 Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); for (String s: availableZoneIds) { System.out.println(s); } // 通过时区构建LocalDateTime对象 LocalDateTime localDateTimeNow = LocalDateTime.now(ZoneId.of("Asia/Shanghai")); // 指定时区构建带时区的日期时间对象 ZonedDateTime zonedDateTime = localDateTimeNow.atZone(ZoneId.of("Asia/Shanghai")); System.out.println(localDateTimeNow); // 2019-09-03T10:35:25.677 // 2019-09-03T10:35:25.677+08:00[Asia/Shanghai] 与UTC时间相差8小时 System.out.println(zonedDateTime); } 复制代码
日期时间API的部分UML图
以上介绍的新的日期时间API相关类的UML图如下:
下面这张图能帮助我们更好的理解LocaleDate、 LocalTime、LocalDateTime以及ZoneId之间的差异。