This article brings you a detailed explanation of the use of time in Java8 (with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
Why is a new time API needed?
Before the date/time API before Java 8, there were many problems with existing date and time-related classes, the main ones being:
The definition of Java's date/time classes is not consistent. There are date classes in the java.util and java.sql packages. In addition, classes used for formatting and parsing are defined in the java.text package
java.util.Date contains both date and time, while java.sql.Date only contains date, and it is not reasonable to include it in the java.sql package. Plus both classes have the same name, which in itself is a very bad design.
There are no clearly defined classes for time, timestamps, formatting and parsing. For formatting and parsing needs we have java.text.DateFormat abstract class but usually SimpleDateFormat class is used for such needs.
All date classes are mutable, therefore they are not thread-safe, which is one of the biggest problems with Java date classes.
The date class does not provide internationalization and has no time zone support, so Java introduced the java.util.Calendar and java.util.TimeZone classes, but they also have all the above problems.
There are some other problems with the methods defined in the existing date and calendar classes, but the above issues make it clear that Java needs a robust date/time class. This is why Joda Time plays an important role as a high-quality replacement for Java date/time needs.
Note when using dates before java8
使用Calendar类实现日期和时间字段之间转换 使用DateFormat类来格式化和分析日期字符串 而Date只用来承载日期和时间信息
Instant time (Instant), duration (duration) , date (date), time (time), time zone (time-zone) and time period (Period). Java 8 still uses the ISO calendar system, and unlike its predecessors, the classes in the java.time package are immutable and thread-safe. The new time and date API is located in the java.time package. Here are some key classes in it:
Instant - it represents a timestamp (because it represents a certain time point, that is, the offset relative to January 1, 1970; but unlike the java.util.Date class, it is accurate to the nanosecond level.
Duration: Duration, time difference
LocalDate - a date that does not contain a specific time, such as 2019-01-14. It can be used to store birthdays, anniversaries, entry dates, etc.
LocalTime - It represents time without date
LocalDateTime - It contains date and time, but there is still no offset information Or time zone.
Period: time period
ZoneOffset: time zone offset, for example: 8:00
ZonedDateTime - This is a complete datetime including time zone, offset from UTC/Greenwich Mean Time.
Clock: Clock , such as getting the current time in New York, USA
Invariance: In the new date/time API, all classes They are all immutable, which is good for multi-threading.
Separation of concerns: Drawing on some advantages of the Joda library, the new API combines human-readable date and time with machine Time (unix timestamp) is clearly separated, which defines different classes for date (Date), time (Time), date time (DateTime), timestamp (unix timestamp) and time zone.
Clarity: In all classes, methods are clearly defined to accomplish the same behavior. For example, to get the current instance, we can use the now() method. format() and parse() are defined in all classes ) method, instead of having an independent class as before. In order to better handle the problem, all classes use the factory pattern and the strategy pattern. Once you use the method of one of the classes, it will work together with other classes. Not difficult.
Practical operations: All new date/time API classes implement a series of methods to complete common tasks, such as: addition, subtraction, formatting, parsing , extract individual parts from a date/time, etc.
Extensibility: The new date/time API is designed to work on the ISO-8601 calendar system, but we can also Apply it to non-IOS calendars.
java.time Package: This is the new Java The basic package of the date/time API. All major basic classes are part of this package, such as: LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration, etc. All of these classes are immutable and thread-safe, and in most cases, these classes handle common requirements effectively.
java.time.chrono package: This package defines some generalized APIs for non-ISO calendar systems. We can extend the AbstractChronology class to create our own calendar system.
java.time.format包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为java.time包中相应的类已经提供了格式化和解析的方法。
java.time.temporal包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。
java.time.zone包:这个包包含支持不同时区以及相关规则的类。
LocalDate 依然是一个不可变类,它关注时间中年月日部分
初始化实例 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):相对于格林零时区时间的日偏移量 …………
示例
// 取当前日期: 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):传入一个长整型代表当前日已经过去的毫微秒数
示例
//包含毫秒 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合并成一个LocalDateTime
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类。
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毫秒
Duration的内部实现与Instant类似,也是包含两部分:seconds表示秒,nanos表示纳秒。两者的区别是Instant用于表示一个时间戳(或者说是一个时间点),而Duration表示一个时间段,所以Duration类中不包含now()静态方法。可以通过Duration.between()方法创建
Period:处理两个日期之间的差值
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);
The above is the detailed content of Detailed explanation of the use of time in java8 (with examples). For more information, please follow other related articles on the PHP Chinese website!