Heim  >  Artikel  >  Java  >  Ausführliche Erklärung der Verwendung von Zeit in Java8 (mit Beispielen)

Ausführliche Erklärung der Verwendung von Zeit in Java8 (mit Beispielen)

不言
不言nach vorne
2019-02-01 10:52:132971Durchsuche

Dieser Artikel bietet Ihnen eine detaillierte Erklärung der Zeitnutzung in Java8 (mit Beispielen). Ich hoffe, dass er für Freunde in Not hilfreich ist.

Warum wird eine neue Zeit-API benötigt?

Vor der Datums-/Uhrzeit-API vor Java 8 gab es viele Probleme mit vorhandenen datums- und zeitbezogenen Klassen, die wichtigsten waren:

  1. Die Die Definition von Datums-/Uhrzeitklassen in Java ist nicht konsistent. Es gibt Datumsklassen in den Paketen java.util und java.sql. Darüber hinaus sind Klassen, die zum Formatieren und Parsen verwendet werden, im Paket java.text definiert

  2. java.util.Date enthält sowohl Datum als auch Uhrzeit, während java.sql.Date nur das Datum enthält und es nicht sinnvoll ist, es in das java.sql-Paket aufzunehmen. Außerdem haben beide Klassen den gleichen Namen, was an sich schon ein sehr schlechtes Design ist.
  3. Es gibt keine klar definierten Klassen für Zeit, Zeitstempel, Formatierung und Parsing. Für Formatierungs- und Analyseanforderungen haben wir die abstrakte Klasse java.text.DateFormat, aber normalerweise wird für solche Anforderungen die Klasse SimpleDateFormat verwendet.
  4. Alle Datumsklassen sind veränderlich und daher nicht threadsicher, was eines der größten Probleme bei Java-Datumsklassen darstellt.
  5. Die Datumsklasse bietet keine Internationalisierung und hat keine Zeitzonenunterstützung, daher hat Java die Klassen java.util.Calendar und java.util.TimeZone eingeführt, aber sie verfügen auch über alle oben genannten Probleme.
  6. Es gibt einige andere Probleme mit den in den vorhandenen Datums- und Kalenderklassen definierten Methoden, aber die oben genannten Probleme machen deutlich, dass Java eine robuste Datums-/Uhrzeitklasse benötigt. Aus diesem Grund spielt Joda Time eine wichtige Rolle als hochwertiger Ersatz für Java-Datums-/Uhrzeitanforderungen.
  7. Hinweis bei der Verwendung von Daten vor Java8
  8.    使用Calendar类实现日期和时间字段之间转换
       使用DateFormat类来格式化和分析日期字符串
       而Date只用来承载日期和时间信息
  9. Java8-Konzepte

Sofortige Zeit (Instant), Dauer (Dauer) , Datum (date), Uhrzeit (time), Zeitzone (time-zone) und Zeitraum (Period). Java 8 verwendet immer noch das ISO-Kalendersystem und im Gegensatz zu seinen Vorgängern sind die Klassen im java.time-Paket unveränderlich und Thread-sicher. Die neue Zeit- und Datums-API befindet sich im Paket java.time. Hier sind einige Schlüsselklassen darin:

    Instant – es stellt einen Zeitstempel dar (weil es einen bestimmten Zeitpunkt darstellt). ist der Offset relativ zum 1. Januar 1970; aber im Gegensatz zur Klasse java.util.Date ist er auf Nanosekundenebene genau:
  • Dauer: Dauer, Zeitunterschied
  • Lokales Datum – ein Datum, das keine bestimmte Uhrzeit enthält, z. B. 14.01.2019. Es kann zum Speichern von Geburtstagen, Jahrestagen, Eintrittsdaten usw. verwendet werden. >
  • LocalTime – stellt die Zeit ohne Datum dar
  • LocalDateTime – es enthält Datum und Uhrzeit, aber es gibt immer noch keine Offset-Informationen oder Zeitzone
  • Period: Zeitraum
  • ZoneOffset: Zeitzonenoffset, zum Beispiel: +8:00
  • ZonedDateTime – Dies ist eine vollständige Datum/Uhrzeit einschließlich Zeitzone, Offset von UTC/Greenwich Time
  • Uhr: Uhr, z. B. Abrufen der aktuellen Zeit in New York, USA
  • Java8-Zeit-API-Funktionen

Unveränderlichkeit: In der neuen Datums-/Uhrzeit-API sind alle Klassen unveränderlich, was gut für Multithreading ist
  • Trennung von Belangen: Basierend auf einigen Vorteilen der Joda-Bibliothek kombiniert die neue API menschenlesbares Datum und Uhrzeit. Maschinenzeit (Unix-Zeitstempel) ist klar getrennt, wodurch verschiedene Klassen für Datum (Datum), Uhrzeit (Uhrzeit) definiert werden. Datum, Uhrzeit (DateTime), Zeitstempel (Unix-Zeitstempel) und Zeitzone: In allen Klassen sind Methoden klar definiert, um das gleiche Verhalten zu erreichen. Um beispielsweise die aktuelle Instanz zu erhalten, können wir now() verwenden. method.format() und parse sind in allen Klassen () definiert, anstatt wie zuvor eine separate Klasse zu haben. Um das Problem besser bewältigen zu können, verwenden alle Klassen das Factory-Muster und das Strategiemuster einer der Klassen wird mit anderen Klassen zusammenarbeiten. Die Arbeit ist nicht schwierig
  • Praktische Operationen: Alle neuen Datums-/Uhrzeit-API-Klassen implementieren eine Reihe von Methoden, um allgemeine Aufgaben zu erledigen. wie zum Beispiel: Addition, Subtraktion, Formatierung, Parsen, Extrahieren einzelner Teile aus Datum/Uhrzeit usw.
  • Erweiterbarkeit: Die neue Datums-/Uhrzeit-API funktioniert auf dem ISO-8601-Kalendersystem. aber wir können auch auf Nicht-IOS-Kalender angewendet werden
  • Java8 Datum/Uhrzeit-API-Paket
  • Java.time-Paket: Das ist neu. Das Basispaket der Java-Datums-/Uhrzeit-API. Alle wichtigen Basisklassen sind Teil dieses Pakets, wie zum Beispiel: LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration usw. Alle diese Klassen sind unveränderlich und Thread-sicher, und in den meisten Fällen erfüllen diese Klassen allgemeine Anforderungen effektiv.

java.time.chrono-Paket: Dieses Paket definiert einige verallgemeinerte APIs für Nicht-ISO-Kalendersysteme. Wir können die AbstractChronology-Klasse erweitern, um unser eigenes Kalendersystem zu erstellen.
  • java.time.format包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为java.time包中相应的类已经提供了格式化和解析的方法。

  • java.time.temporal包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。

  • java.time.zone包:这个包包含支持不同时区以及相关规则的类。

java8 API介绍和使用

LocalDate

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
TemporalAdjuster

但是有些时候我们要面临更复杂的时间操作,比如将时间调到下一个工作日,或者是下个月的最后一天,这时候我们可以使用with()方法的另一个重载方法,它接收一个TemporalAdjuster参数,可以使我们更加灵活的调整日期:

LocalDate date7 = date.with(nextOrSame(DayOfWeek.SUNDAY));      // 返回下一个距离当前时间最近的星期日
LocalDate date9 = date.with(lastInMonth(DayOfWeek.SATURDAY));   // 返回本月最后一个星期六

如果本身API不满足你的需求,你还可以创建自定义的TemporalAdjuster接口的实现

LocalTime

类似于 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

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

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() + "分钟");

ZonedDateTime

无论是我们的 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 时间与老版本时间转换

因为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);

Das obige ist der detaillierte Inhalt vonAusführliche Erklärung der Verwendung von Zeit in Java8 (mit Beispielen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen