1. LocalDate、LocalTime、Instant、Duration以及Period
1.1 使用LocalDate和LocalTime
// 指定年月日创建日期
LocalDate date1 = LocalDate.of(2014, Month.MARCH, 18);
LocalDate date2 = LocalDate.of(2014, 3, 18);
// 从系统时钟中获取当前的日期
LocalDate date3 = LocalDate.now();
// 通过解析日期字符串生成日期
LocalDate date4 = LocalDate.parse("2014-03-08");
// 获取日期相关field信息
int year = date2.getYear();
int year1 = date2.get(ChronoField.YEAR);
Month month = date2.getMonth();
int month1 = date2.get(ChronoField.MONTH_OF_YEAR);
int day = date2.getDayOfMonth();
int day1 = date2.get(ChronoField.DAY_OF_MONTH);
DayOfWeek dow = date2.getDayOfWeek();
int len = date2.lengthOfMonth();
boolean leap = date2.isLeapYear();
// 指定时、分、秒、纳秒等信息创建本地时间
LocalTime time1 = LocalTime.of(12, 30);
LocalTime time2 = LocalTime.of(12, 30, 30);
LocalTime time3 = LocalTime.of(12, 30, 30, 500);
// 通过解析时间字符串创建本地时间
LocalTime time4 = LocalTime.parse("12:30:30");
// 获取当前本地时间
LocalTime time5 = LocalTime.now();
// 获取时间字段信息
int hour = time2.getHour();
int hour1 = time2.get(ChronoField.HOUR_OF_DAY);
int minute = time2.getMinute();
int second = time2.getSecond();
1.2 合并日期和时间
// 合并本地日期和时间。LocalDateTime内部直接存储LocatDate和LocalTime对象,所以真的是简单的合并。
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
LocalTime time = LocalTime.of(12, 30, 30);
LocalDateTime localDateTime1 = LocalDateTime.of(date, time);
// 通过指定年月日时分秒创建LocalDateTime
LocalDateTime localDateTime2 = LocalDateTime.of(2014, Month.MARCH, 18, 12, 30, 30);
// 创建当前的LocalDateTime
LocalDateTime localDateTime3 = LocalDateTime.now();
// LocalDate指定时间创建LocalDateTime
LocalDateTime localDateTime4 = date.atTime(12, 30, 30);
LocalDateTime localDateTime5 = date.atTime(time);
// LocalTime指定本地日期创建LocalDateTime
LocalDateTime localDateTime6 = time.atDate(date);
// 从LocalDateTime中获取LocalDate、LocalTime对象
LocalDate localDate = localDateTime1.toLocalDate();
LocalTime localTime = localDateTime1.toLocalTime();
1.3 机器的日期和时间格式
作为人,我们习惯于以星期几、几号、几点、几分这样的方式理解日期和时间。毫无疑问,这种方式对于计算机而言并不容易理解。从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是新的java.time.Instant类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。
// 通过指定Unix元年起的秒数及纳秒数创建Instant
Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3, 0);
Instant.ofEpochSecond(2, 1_000_000_000);
Instant.ofEpochSecond(4, -1_000_000_000);
// 获取当前的Instant
Instant now = Instant.now();
1.4 定义Duration或Period
由于LocalDateTime和Instant是为不同的目的而设计的,一个是为了便于人阅读使用,另一个是为了便于机器处理,所以你不能将二者混用。如果你试图在这两类对象之间创建duration,会触发一个DateTimeException异常。此外,由于Duration类主要用于以秒和纳秒衡量时间的长短,你不能仅向between方法传递一个LocalDate对象做参数。
// 计算两个LocalTime间的Duration
LocalTime time1 = LocalTime.now();
LocalTime time2 = LocalTime.of(12, 30, 30);
Duration between = Duration.between(time2, time1);
// 计算两个LocalDateTime间的Duration
LocalDateTime datetime1 = LocalDateTime.now();
LocalDateTime datetime2 = LocalDateTime.of(2014, 3, 18, 12, 30, 30);
Duration between1 = Duration.between(datetime2, datetime1);
// 计算两个Instant间的Duration
Instant instant1 = Instant.now();
Instant instant2 = Instant.ofEpochSecond(1000, 0);
Duration between2 = Duration.between(instant2, instant1);
// 通过指定时间直接创建Duration
Duration duration = Duration.ofMinutes(3);
Duration duration2 = Duration.of(3, ChronoUnit.MINUTES);
如果你需要以年、月或者日的方式对多个时间单位建模,可以使用Period类。
// 计算两个日期之间的Period
LocalDate date1 = LocalDate.now();
LocalDate date2 = LocalDate.of(2014, 3, 18);
Period between = Period.between(date2, date1);
// 指定间隔天数
Period period = Period.ofDays(10);
// 指定间隔周数
Period period1 = Period.ofWeeks(3);
// 同时指定间隔年、月、日
Period period2 = Period.of(2, 6, 1);
2. 操纵、解析和格式化日期
// 比较直接的操作方式
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.withYear(2011);
LocalDate date3 = date2.withDayOfMonth(25);
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);
// 相对的操作方式
LocalDate date5 = LocalDate.of(2014, 3, 18);
LocalDate date6 = date5.plusWeeks(1);
LocalDate date7 = date6.minusYears(3);
LocalDate date8 = date7.plus(6, ChronoUnit.MONTHS);
2.1 使用TemporalAdjuster
// 使用TemporalAdjusters预置的方法
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
LocalDate date3 = date2.with(TemporalAdjusters.lastDayOfMonth());
// 自定义TemporalAdjuster的实现:获取下一个工作日
TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(temporal -> {
DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
date3 = date3.with(nextWorkingDay);
2.2 打印输出及解析日期-时间对象
// 使用预定义DateTimeFormatter格式化日期
LocalDate date = LocalDate.of(2014, 3, 18);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
// 使用预定义DateTimeFormatter解析日期字符串
LocalDate date1 = LocalDate.parse("20140318", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE);
// 按照某个模式创建DateTimeFormatter
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date3 = LocalDate.of(2014, 3, 18);
String formattedDate = date3.format(formatter2);
LocalDate date4 = LocalDate.parse(formattedDate, formatter2);
// 创建一个本地化的DateTimeFormatter
DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
LocalDate date5 = LocalDate.of(2014, 3, 18);
String formattedDate1 = date5.format(italianFormatter);
LocalDate date6 = LocalDate.parse(formattedDate1, italianFormatter);
// 构造一个DateTimeFormatter
DateTimeFormatter italianFormatter1 = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(".")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral("")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);
3. 处理不同的时区和历法
// 获取ZoneId
ZoneId romeZone = ZoneId.of("Europe/Rome");
// 通过TimeZone获取ZoneId
ZoneId zoneId = TimeZone.getDefault().toZoneId();
// 通过LocalDate创建ZonedDateTime
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
// 通过LocalDateTime创建ZonedDateTime
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
// 通过Instant创建ZonedDateTime
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);
// 将LocatDateTime转换为Instant
LocalDateTime dateTime1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
dateTime1.toInstant(OffsetDateTime.now().getOffset());
// 将Instant转换为LocalDateTime
Instant now = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
3.1 利用和UTC/格林尼治时间的固定偏差计算时区
// 获取NewYork的时区偏差。注意,使用这种方式定义的ZoneOffset并未考虑任何日光时的影响,所以在大多数情况下,不推荐使用。
ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");
// OffsetDateTime
LocalDateTime dateTime2 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(dateTime2, newYorkOffset);
3.2 使用别的日历系统
// 使用日本日历系统获取日本时间
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
JapaneseDate japaneseDate = JapaneseDate.from(date);
// not recommended
// 日期及时间API的设计者建议我们使用LocalDate,尽量避免使用ChronoLocalDate,原因是开发者在他们的代码中可能会做一些假设,而这些假设在不同的日历系统中,有可能不成立。
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.dateNow();
// 如何在ISO日历中计算当前伊斯兰年中斋月的起始和终止日期
HijrahDate ramadanDate = HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1)
.with(ChronoField.MONTH_OF_YEAR, 9);
System.out.println("Ramadan starts on " +
IsoChronology.INSTANCE.date(ramadanDate) +
" and ends on " +
IsoChronology.INSTANCE.date((
ramadanDate.with(TemporalAdjusters.lastDayOfMonth())
))
);
4. A test about ZonedDateTime
/**
* 结论:
* LocalDateTime默认本地时区,但对象本身并未记录时区信息。
* LocalDateTime转换为Instant时需指定时区。
* 如果通过LocalDateTime转ZonedDateTime设置了非本地时区,则转换为UTC毫秒时会跟默认时区时的转换结果不一样。
* 同一UTC毫秒值,通过Instant转成不同时区的ZonedDateTime并打印输出时,显示时间不一样。
*/
System.out.println("---------");
// 获取本地日期-时间(默认Zone)
LocalDateTime now = LocalDateTime.now();
// 打印本地日期-时间(默认Zone)
System.out.println(now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// 打印UTC毫秒值
System.out.println(now.toInstant(OffsetDateTime.now().getOffset()).getEpochSecond());
System.out.println("---------");
// 修改Zone (默认Zone -> Europe/Rome Zone)
ZonedDateTime romeNow = ZonedDateTime.of(now, ZoneId.of("Europe/Rome"));
// 打印Rome日期-时间
System.out.println(romeNow.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// 打印UTC毫秒值
System.out.println(romeNow.toInstant().getEpochSecond());
System.out.println("---------");
// 取当前UTC毫秒值
long timeInMillis = Calendar.getInstance().getTimeInMillis();
// 默认UTC时区
ZonedDateTime zonedDateTime = Instant.ofEpochMilli(timeInMillis).atOffset(ZoneOffset.ofTotalSeconds(0))
.toZonedDateTime();
// 打印UTC日期-时间
String format = zonedDateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println(format);
// 打印UTC毫秒值
System.out.println(zonedDateTime.toInstant().getEpochSecond());
System.out.println("---------");
// 取当前UTC毫秒值
long timeInMillis1 = Calendar.getInstance().getTimeInMillis();
// 设置本地Zone
ZonedDateTime zonedDateTime1 = Instant.ofEpochMilli(timeInMillis1).atOffset(OffsetDateTime.now().getOffset())
.toZonedDateTime();
// 打印本地日期-时间
String format1 = zonedDateTime1.format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println(format1);
// 打印UTC毫秒值
System.out.println(zonedDateTime1.toInstant().getEpochSecond());
System.out.println("---------");