文章来源:http://www.javashuo.com/article/p-gwqadnbt-kw.html
在Java 8以前的日期/时间API以前,现有的与日期和时间相关的类存在诸多问题,其中主要有:html
使用Calendar类实现日期和时间字段之间转换 使用DateFormat类来格式化和分析日期字符串 而Date只用来承载日期和时间信息
瞬时时间(Instant),持续时间(duration),日期(date),时间(time),时区(time-zone)以及时间段(Period)。Java 8仍然延用了ISO的日历体系,而且与它的前辈们不一样,java.time包中的类是不可变且线程安全的。新的时间及日期API位于java.time包中,下面是里面的一些关键的类:java
LocalDate 依然是一个不可变类,它关注时间中年月日部分sql
初始化实例 public static LocalDate now():截断当前系统时间的年月日信息并初始化一个实例对象 public static LocalDate of(int year, int month, int dayOfMonth):显式指定年月日信息 public static LocalDate ofYearDay(int year, int dayOfYear):根据 dayOfYear 能够推出 month 和 dayOfMonth public static LocalDate ofEpochDay(long epochDay):相对于格林零时区时间的日偏移量 …………
示例api
// 取当前日期: LocalDate today = LocalDate.now(); // -> 2019-01-31 // 根据年月日取日期,12月就是12: LocalDate crischristmas = LocalDate.of(2018, 12, 25); // -> 2018-12-25 // 根据字符串取: LocalDate endOfFeb = LocalDate.parse("2018-12-25"); // 严格按照ISO yyyy-MM-dd验证,02写成2都不行,固然也有一个重载方法容许本身定义格式 // 如何获取1周后的日期 LocalDate oneToday = today.plus(1, ChronoUnit.WEEKS); // ->2019-02-07 //一年前的日期 LocalDate previousYear = today.minus(1, ChronoUnit.YEARS); // 取本月第1天: LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 2019-01-01 // 取本月第2天: LocalDate secondDayOfThisMonth = today.withDayOfMonth(2); // 2019-01-02 // 取本月最后一天,不再用计算是28,29,30仍是31: LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth()); // 2019-01-31 // 取下一天: LocalDate firstDay = lastDayOfThisMonth.plusDays(1); // 变成了2019-02-01 // 取2019年1月第一个周一 LocalDate firstMonday = LocalDate.parse("2019-01-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2019-01-07
在java8中,可使用MonthDay,该类不包含年份信息,固然还有一个类是YearMonth安全
LocalDate birthday = LocalDate.of(1990, 10, 12); MonthDay birthdayMd = MonthDay.of(birthday.getMonth(), birthday.getDayOfMonth()); MonthDay today = MonthDay.from(LocalDate.of(2019, 10, 12)); System.out.println(today.equals(birthdayMd)); //结果 true
可是有些时候咱们要面临更复杂的时间操做,好比将时间调到下一个工做日,或者是下个月的最后一天,这时候咱们可使用with()方法的另外一个重载方法,它接收一个TemporalAdjuster参数,可使咱们更加灵活的调整日期:多线程
LocalDate date7 = date.with(nextOrSame(DayOfWeek.SUNDAY)); // 返回下一个距离当前时间最近的星期日 LocalDate date9 = date.with(lastInMonth(DayOfWeek.SATURDAY)); // 返回本月最后一个星期六
若是自己API不知足你的需求,你还能够建立自定义的TemporalAdjuster接口的实现线程
相似于 LocalDate,LocalTime 专一于时间的处理,它提供小时,分钟,秒,毫微秒的各类处理设计
初始化LocalTime实例 public static LocalTime now():根据系统当前时刻获取其中的时间部份内容 public static LocalTime of(int hour, int minute):显式传入小时和分钟来构建一个实例对象 public static LocalTime of(int hour, int minute, int second):经过传入时分秒构造实例 public static LocalTime of(int hour, int minute, int second, int nanoOfSecond):传入时分秒和毫微秒构建一个实例 public static LocalTime ofSecondOfDay(long secondOfDay):传入一个长整型数值表明当前日已通过去的秒数 public static LocalTime ofNanoOfDay(long nanoOfDay):传入一个长整型表明当前日已通过去的毫微秒数
示例unix
//包含毫秒 LocalTime now = LocalTime.now(); // 11:09:09.240 //不包含毫秒 LocalTime now = LocalTime.now().withNano(0)); // 11:09:09 //构造时间 LocalTime zero = LocalTime.of(0, 0, 0); // 00:00:00 LocalTime mid = LocalTime.parse("12:00:00"); // 12:00:00 LocalTime twoHour = now.plusHours(2);
LocalDateTime类是LocalDate和LocalTime的结合体,能够经过of()方法直接建立,也能够调用LocalDate的atTime()方法或LocalTime的atDate()方法将LocalDate或LocalTime合并成一个LocalDateTimecode
LocalDateTime ldt1 = LocalDateTime.of(2017, Month.JANUARY, 4, 17, 23, 52); LocalDate localDate = LocalDate.of(2017, Month.JANUARY, 4); LocalTime localTime = LocalTime.of(17, 23, 52); LocalDateTime ldt2 = localDate.atTime(localTime);
LocalDateTime也提供用于向LocalDate和LocalTime的转化:
LocalDate date = ldt1.toLocalDate(); LocalTime time = ldt1.toLocalTime();
Instant用于表示一个时间戳,它与咱们常使用的System.currentTimeMillis()有些相似,不过Instant能够精确到纳秒(Nano-Second),System.currentTimeMillis()方法只精确到毫秒(Milli-Second)。若是查看Instant源码,发现它的内部使用了两个常量,seconds表示从1970-01-01 00:00:00开始到如今的秒数,nanos表示纳秒部分(nanos的值不会超过999,999,999)。Instant除了使用now()方法建立外,还能够经过ofEpochSecond方法建立:
Instant instant = Instant.ofEpochSecond(120, 100000);
关于时间差的计算,主要涉及到两个类,年月日的日期间差值的计算使用 Period 类足以,而时分秒毫秒的时间的差值计算则须要使用Duration类。
LocalDateTime from = LocalDateTime.of(2019, Month.JANUARY, 5, 10, 7, 0); // 2019-01-05 10:07:00 LocalDateTime to = LocalDateTime.of(2019, Month.FEBRUARY, 5, 10, 7, 0); // 2019-02-05 10:07:00 Duration duration = Duration.between(from, to); // 表示从 2019-01-05 10:07:00 到 2019-02-05 10:07:00 这段时间 long days = duration.toDays(); // 这段时间的总天数 long hours = duration.toHours(); // 这段时间的小时数 long minutes = duration.toMinutes(); // 这段时间的分钟数 long seconds = duration.getSeconds(); // 这段时间的秒数 long milliSeconds = duration.toMillis(); // 这段时间的毫秒数 long nanoSeconds = duration.toNanos(); // 这段时间的纳秒数
Duration对象还能够经过of()方法建立,该方法接受一个时间段长度,和一个时间单位做为参数:
Duration duration1 = Duration.of(5, ChronoUnit.DAYS); // 5天 Duration duration2 = Duration.of(1000, ChronoUnit.MILLIS); // 1000毫秒
Period在概念上和Duration相似,区别在于Period是以年月日来衡量一个时间段,好比2年3个月6天
Period period = Period.of(2, 3, 6);
Period对象也能够经过between()方法建立,值得注意的是,因为Period是以年月日衡量时间段,因此between()方法只能接收LocalDate类型的参数:
Period period = Period.between( LocalDate.of(2019, 1, 5), LocalDate.of(2019, 2, 5));
示例
LocalDate date = LocalDate.of(2019,01,22); LocalDate date1 = LocalDate.now(); Period period = Period.between(date,date1); System.out.println(period.getYears() + "年" + period.getMonths() + "月" + period.getDays() + "天"); LocalTime time = LocalTime.of(20,30); LocalTime time1 = LocalTime.of(23,59); Duration duration = Duration.between(time,time1); System.out.println(duration.toMinutes() + "分钟");
不管是咱们的 LocalDate,或是 LocalTime,甚至是 LocalDateTime,它们基本是时区无关的,内部并无存储时区属性,而基本用的系统默认时区。每每有些场景之下,缺少必定的灵活性。
ZonedDateTime 能够被理解为 LocalDateTime 的外层封装,它的内部存储了一个 LocalDateTime 的实例,专门用于普通的日期时间处理。此外,它还定义了 ZoneId 和 ZoneOffset 来描述时区的概念。
ZonedDateTime 和 LocalDateTime 的一个很大的不一样点在于,后者内部并无存储时区,因此对于系统的依赖性很强,每每换一个时区可能就会致使程序中的日期时间不一致。
然后者则能够经过传入时区的名称,使用 ZoneId 进行匹配存储,也能够经过传入与零时区的偏移量,使用 ZoneOffset 存储时区信息。
初始化实例 public static ZonedDateTime now():系统将以默认时区计算并存储日期时间信息 public static ZonedDateTime now(ZoneId zone):指定时区 public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone):指定日期时间和时区 public static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) public static ZonedDateTime ofInstant(Instant instant, ZoneId zone):经过时刻和时区构建实例对象 等等
示例
ZonedDateTime zonedDateTime = ZonedDateTime.now(); System.out.println(zonedDateTime); //->2019-01-31T16:27:23.179+08:00[Asia/Shanghai] LocalDateTime localDateTime = LocalDateTime.now(); ZoneId zoneId = ZoneId.of("America/Los_Angeles"); ZonedDateTime zonedDateTime1 = ZonedDateTime.of(localDateTime,zoneId); System.out.println(zonedDateTime1); // ->2019-01-31T16:27:23.179-08:00[America/Los_Angeles] Instant instant = Instant.now(); ZoneId zoneId1 = ZoneId.of("GMT"); ZonedDateTime zonedDateTime2 = ZonedDateTime.ofInstant(instant,zoneId1); System.out.println(zonedDateTime2); // ->2019-01-31T08:27:23.183Z[GMT]
第二个小例子,LocalDateTime 实例保存了时区无关的当前日期时间信息,也就是这里的年月日时分秒,接着构建一个 ZonedDateTime 实例并传入一个美国时区(西七区)。你会发现输出的日期时间为西七区的 16 点 27 分。
像这种关联了时区的日期时间就很可以解决那种,换时区致使程序中时间错乱的问题。由于我关联了时区,不管你程序换到什么地方运行了,日期+时区 本就已经惟一肯定了某个时刻,就至关于我在存储某个时刻的时候,说明了这是某某时区的某某时间,即使你换了一个地区,也不至于把这个时间按本身当前的时区进行解析并直接使用。
第三个小例子,构建 ZonedDateTime实例的时候,给定一个时刻和一个时区,而这个时刻值就是相对于给定时区的标准时间所通过的毫秒数。
有关 ZonedDateTime 的其余日期时间的处理方法和 LocalDateTime 是同样的,由于 ZonedDateTime 是直接封装了一个 LocalDateTime 实例对象,因此全部相关日期时间的操做都会间接的调用 LocalDateTime 实例的方法,咱们再也不赘述。
Java 8 的新式日期时间 API 中,DateTimeFormatter 做为格式化日期时间的主要类,它与以前的 DateFormat 类最大的不一样就在于它是线程安全的,若是须要的话,能够赋值给一个静态变量。
DateTimeFormatter类提供了许多预约义的格式器,你也能够自定义本身想要的格式。固然根据约定,它还有一个parse()方法是用于将字符串转换成日期的,若是转换期间出现任何错误,它会抛出DateTimeParseException异常。相似的,DateFormatter类也有一个用于格式化日期的format()方法,它出错的话则会抛出DateTimeException异常
再说一句,“MMM d yyyy”与“MMm dd yyyy”这两个日期格式也略有不一样,前者能识别出"Jan 2 2018"与"Jan 14 2018"这两个串,然后者若是传进来的是"Jan 2 2018"则会报错,由于它指望月份处传进来的是两个字符。为了解决这个问题,在天为个位数的状况下,你得在前面补0,好比"Jan 2 2018"应该改成"Jan 02 2018"。
public static void main(String[] a){ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(formatter.format(localDateTime)); String str = "2008年08月23日 23:59:59"; DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); LocalDateTime localDateTime2 = LocalDateTime.parse(str,formatter2); System.out.println(localDateTime2); }
由于java8以前Date是包含日期和时间的,而LocalDate只包含日期,LocalTime只包含时间,因此与Date在互转中,势必会丢失日期或者时间,或者会使用起始时间。若是转LocalDateTime,那么就不存在信息偏差。
/Date与Instant的相互转化 Instant instant = Instant.now(); Date date = Date.from(instant); Instant instant2 = date.toInstant(); //Date转为LocalDateTime Date date2 = new Date(); LocalDateTime localDateTime2 = LocalDateTime.ofInstant(date2.toInstant(), ZoneId.systemDefault()); //LocalDateTime转Date LocalDateTime localDateTime3 = LocalDateTime.now(); Instant instant3 = localDateTime3.atZone(ZoneId.systemDefault()).toInstant(); Date date3 = Date.from(instant); //LocalDate转Date //由于LocalDate不包含时间,因此转Date时,会默认转为当天的起始时间,00:00:00 LocalDate localDate4 = LocalDate.now(); Instant instant4 = localDate4.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); Date date4 = Date.from(instant); // Calendar to Instant Instant time = Calendar.getInstance().toInstant(); System.out.println(time); // TimeZone to ZoneId ZoneId defaultZone = TimeZone.getDefault().toZoneId(); System.out.println(defaultZone); // ZonedDateTime from specific Calendar ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime(); System.out.println(gregorianCalendarDateTime); GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime); System.out.println(gc);