Java8-日期和时间
- 使用
LocalDate
和LocalTime
LocalDate
该类的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息。另外,它也不附带任何与时区相关的信息。可以通过静态工厂方法of
创建一个LocalDate实例。
LocalDate date = LocalDate.of(2014, 3, 18);//2014-03-18
int year = date.getYear();//2014
Month month = date.getMonth();//MARCH
int day = date.getDayOfMonth();//18
DayOfWeek dow = date.getDayOfWeek();//TUESDAY
int len = date.lengthOfMonth();//31(这个月有多少天)
boolean leap = date.isLeapYear();//false(是不是闰年)
还可以使用工厂方法从系统时钟中获取当前的日期:
LocalDate today = LocalDate.now();
这些日期-时间类,都提供了这些静态工厂方法,还可以通过传递一个TemporalField
参数给get
方法拿到同样的信息。TemporalField
是一个接口,它定义了如何访问temporal
对象某个字段的值。ChronoField
枚举实现了这一接口,所以可以很方便地使用get
方法得到枚举元素的值。(这些类都实现了Temporal
接口,Temporal
接口定义了如何读取和操纵为时间建模的对象的值。)
int year = date.get(ChronoField.YEAR);
int month = date.get(ChronoField.MONTH_OF_YEAR);
int day = date.get(ChronoField.DAY_OF_MONTH);
使用静态方法parse,LocalDate
和LocalTime
都可以通过解析代表它们的字符串创建,一旦传递的字符串参数无法被解析为合法的LocalDate
或LocalTime
对象,这两个parse方法都会抛出一个继承自RuntimeException
的DateTimeParseException
异常。
LocalDate date = LocalDate.parse("2014-03-18");
LocalTime time = LocalTime.parse("13:45:20");
- 合并日期和时间
这个复合类名叫LocalDateTime
,是LocalDate
和LocalTime
的合体。它同时表示了日期和时间,但不带有时区信息,可以直接创建,也可以通过合并日期和时间对象构造
// 2014-03-18T13:45:20
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(13, 45, 20);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
通过atTime
或者atDate
方法,向LocalDate
传递一个时间对象,或者向LocalTime
传递一个日期对象的方式,可以创建一个LocalDateTime
对象。
也可以使用toLocalDate
或者toLocalTime
方法,从LocalDateTime
中提取LocalDate
或者LocalTime
组件
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();
-
机器的日期和时间格式
从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是新的java.time.Instant
类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。可以通过向静态工厂方法
ofEpochSecond
传递一个代表秒数的值创建一个该类的实例。静态工厂方法ofEpochSecond
还有一个增强的重载版本,它接收第二个以纳秒为单位的参数值,对传入作为秒数的参数进行调整。重载的版本会调整纳秒参数,确保保存的纳秒分片在0到999 999999之间。
Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3, 0);
Instant.ofEpochSecond(2, 1_000_000_000);//2秒之后加上100万纳秒(1秒)
Instant.ofEpochSecond(4, -1_000_000_000); 4秒之前100弯纳秒(1秒)
由于Instant类也支持静态工厂方法now,它能够帮你获取当前时刻的时间戳,但是,Instant
的设计初衷是为了便于机器使用。它包含的是由秒及纳秒所构成的数字。所以,它无法处理那些我们非常容易理解的时间单位。
int day = Instant.now().get(ChronoField.DAY_OF_MONTH);
会抛出如下异常:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field:
DayOfMonth
- 定义
Duration
或Period
可以创建两个LocalTimes
对象、两个LocalDateTimes
对象,或者两个Instant
对象之间的duration
注意:LocalDateTime
和Instant
不能混用,否则会触发一个DateTimeException
异常
Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);
以年、月或者日的方式对多个时间单位建模,可以使用Period
类。使用该类的工厂方法between
,可以得到两个LocalDate
之间的时长,
Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
LocalDate.of(2014, 3, 18));
image.png
image.png
以上这些日期-时间对象都是不可修改的,为了更好地支持函数式编程,确保线程安全。
- 操纵、解析和格式化日期
- 以相对直观的方式修改
localDate
属性
若需要修改个LocalDate
对象,withAttribute
方法会创建对象的一个副本,并按照需要修改它的属性。(with
方法也可以达到同样目的,它接受的第一个参数是一个TemporalField
对象)
//所有的方法都返回一个修改了属性的对象。它们都不会修改原来的对象!
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
属性
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.plusWeeks(1);
LocalDate date3 = date2.minusYears(3);
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); //2011-09-25
plus
方法也是通用方法,它和minus
方法都声明于Temporal
接口中,通过这些方法,对TemporalUnit
对象加上或者减去一个数字,能非常方便地将Temporal
对象前溯或者回滚至某个时间段,通过ChronoUnit
枚举可以非常方便地实现TemporalUnit
接口。
- 使用
TemporalAdjuster
import static java.time.temporal.TemporalAdjusters.*;
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));//2014-03-18
LocalDate date3 = date2.with(lastDayOfMonth()); //2014-03-31
image.png
也可以自定义一个TemporalAdjuster
接口
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
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);
});
date = date.with(nextWorkingDay);
ofDateAdjuster
接受一个UnaryOperator<LocalDate>
的参数。
- 解析日期-时间对象
LocalDate date = LocalDate.of(2014, 3, 18);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);//20140318
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); //2014-03-18
使用工厂方法parse达到重创该日期对象的目的:
LocalDate date1 = LocalDate.parse("20140318",
DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18",
DateTimeFormatter.ISO_LOCAL_DATE);
和老的java.util.DateFormat
相比较,所有的DateTimeFormatter
实例都是线程安全的。所以,你能够以单例模式创建格式器实例,就像DateTimeFormatter
所定义的那些常量,并能在多个线程间共享这些实例。
也可以按照某个特定的模式创建格式器:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);//按照指定模式生成字符串
LocalDate date2 = LocalDate.parse(formattedDate, formatter); //利用同样的格式器解析生成的字符串
DateTimeFormatterBuilder
类还提供了更复杂的格式器,你可以选择恰当的方法,一步一步地构造自己的格式器。同时,提供了强大的解析功能。
‘DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);
//处理不同的时区和使用不同的日历 12.3