Java時間操做類庫—Joda-Time

點贊再看,養成習慣,公衆號搜一搜【一角錢技術】關注更多原創技術文章。本文 GitHub org_hejianhui/JavaStudy 已收錄,有個人系列文章。html

前言

上一週在作一個產品的需求的時候有個動態計算時間段(如如今是13:00,則時間段爲15:10-17:十、17:10-19:十、19:10-21:10;即最先的出發時間爲當前時間+參數【2h10min】,最遲的時間段爲開始時間在20點前結束時間在20點後的時間段),期間大量使用到了日期時間類庫,本着熟悉日期時間類庫纔有了這篇文章,文章最後我會把我如何實現的這個需求的一個算法貼出來。java

1、JDK8之前版本中的時間類庫

1.1 原始時間類庫存在的缺陷與不足

咱們在使用Java8以前的類庫時,都會在處理日期-時間的時候老是不爽,這其中包括且不限於如下的槽點:git

在Java 1.0版本中,對時間、日期的操做徹底依賴於 java.util.Data 類,只能以毫秒的精度表示時間,沒法表示日期。github

  • 在易用性方面有着很大的缺陷,年份的起始時間選擇是1900年,月份是從0開始。
  • toString 方法返回值不直觀,帶有時區。

在Java1.1 版本中,廢棄了不少Date 類中的不少方法,而且新增了 java.util.Calendar。可是與Date相同,Calendar 類也有相似的問題和設計缺陷,致使在使用這些類寫出的代碼也很容易出錯。算法

  • 月份依然是從0開始計算。
  • 經常使用的日期、時間操做須要同時使用Date、Canendar、SimpleDateFormat,比較繁瑣。
  • 部分特性只存在於某一個類(解析和格式化日期貨時間的DateFormat方法只存在於Date類中)。
  • DateFormat 不是線程安全的,若是兩個線程嘗試使用同一個formatter 解析日期,可能會獲得沒法預期的結果。
  • Date 和 Canendar 都是可變的。

1.2 關於SimpleDateFormat 線程不安全的緣由

因爲 parse 方法使用的貢獻變量 calendar 不是線程安全的。在 format (subFormat) 方法中進行了 calendar 的賦值,在 parse 進行了值得處理,所以在併發的狀況下回形成 calendar 清理不及時,值被覆蓋的狀況。數據庫

/** * The {@link Calendar} instance used for calculating the date-time fields * and the instant of time. This field is used for both formatting and * parsing. * * <p>Subclasses should initialize this field to a {@link Calendar} * appropriate for the {@link Locale} associated with this * <code>DateFormat</code>. * @serial */
protected Calendar calendar;

@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos){
    pos.beginIndex = pos.endIndex = 0;
    return format(date, toAppendTo, pos.getFieldDelegate());
}

// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
    // Convert input date to time field list
    calendar.setTime(date);

	// At this point the fields of Calendar have been set. Calendar
	// will fill in default values for missing fields when the time
	// is computed.

	pos.index = start;

	Date parsedDate;
	try {
        parsedDate = calb.establish(calendar).getTime();
        // If the year value is ambiguous,
        // then the two-digit year == the default start year
        if (ambiguousYear[0]) {
            if (parsedDate.before(defaultCenturyStart)) {
                parsedDate = calb.addYear(100).establish(calendar).getTime();
            }
        }
	}
}
複製代碼

1.3 如何解決上述線程不安全問題?

  1. 使用ThreadLocal 爲每一個線程都建立一個線程獨享 SimpleDateFormat 變量;
  2. 須要的時候建立局部變量;
  3. 使用 org.apacle.commons.lang3.time.DateFormatUtils
  4. 使用Joda-Time (後面介紹)

2、Joda-Time 日期時間類庫

2.1 簡介

Joda-Time 是Joda提供的一個遵循Apache2.0 開源協議的 JDK之外的優質日期和時間開發庫。express

Joda除Joda-Time以外的項目有Joda-Money、Joda-Beans、Joda-Convert、Joda-Collect Joda官網api

2.1.1 爲何使用Joda-Time

  1. 使用方便:Calendar 訪問「正常的」日期困難,而且缺少簡單的防腐,Joda-Time 擁有簡單的字段訪問,好比得到年的 getYear() 和 得到星期 中的天  getDayOfWeek() 。
  2. 易於擴展:JDK支持經過使用子類實現多個日曆系統,可是這是很是笨重的,而且在實現中很難先出另外一個日曆系統。Joda-Time 支持基於 Chronology 類實現多個可插拔的日曆系統。
  3. 功能全面:Joda-Time 提供了全部的日期和時間計算的必須功能,它提供了既裝即用的特性。
  4. 最新的時區計算:時區的實現基於公共時區信息數據庫,每一年更新數次。新版本的Joda-Time 包括了這個數據庫的全部更改,應儘早進行必要的更新,手動更新區域數據很容易。
  5. 日曆支持:提供了8中日曆系統。
  6. 互通性:內部使用毫秒進行標識,這與JDK或者其餘公共的時間表示相一致。
  7. 性能良好:支持針對全部訪問的域進行最小的計算。
  8. 良好的測試覆蓋率:有全方位的測試人員保證庫的質量、
  9. 具備完整文檔:有一個完整的用戶指南,改指南提供了一個概述,涵蓋常見的使用場景。javadoc 很是詳細,涵蓋API的其他部分。
  10. 發展:自2002年以來積極發展,是一個成熟的可靠的代碼庫,一些相關的項目目前也是可用的。
  11. 開源:遵循Apache 2.0開源協議發佈。

2.1.2 Joda-Time 的關鍵優勢

  1. LocalDate:只包含日期
  2. LocalTime:只包含時間
  3. Instant:時間軸上的時間點
  4. DateTime:時區中完整的日期和時間
  5. DateTimeZone:更好的時區
  6. Duration和Period:持續時間
  7. Interval:兩個時間點之間的時間
  8. 全面而且靈活的時間格式化與轉換

正由於Joda-Time 與 Java8 以前的時間類庫相比,具有了如此多的優勢,因此 Joda-Time 成爲事實上的標準的Java日期和時間庫。安全

2.2 特性解讀

2.2.1 Joda-Time和JDK的互操做性

互操做性是指:Joda 類可以生成 java.util.Date 的實例(以及Calendar),這可讓咱們保留現有對JDK的依賴,又可以使用Joda處理複雜的日期/時間計算。markdown

Date To Joda-Time

Date date = new Date();
DateTime dateTime = new DateTime(date);
複製代碼

Canendar To Joda-Time

Calendar calendar = Calendar.getInstance();
DateTime dateTime = new DateTime(calendar);
複製代碼

Joda-Time To Date

Date date = new Date();  
DateTime dateTime = new DateTime(date);                       
Date date2 = dateTime.toDate();
複製代碼

Joda-Time To Calendar

Calendar calendar = Calendar.getInstance();  
dateTime = new DateTime(calendar);  
Calendar calendar2 = dateTime.toCalendar(Locale.CHINA); 
複製代碼

2.2.2 Joda的關鍵日期/時間概念理解

Joda 使用瞭如下概念,使得它們能夠應用到任何日期/時間庫:

不可變性(Immutability)

Joda-Time與java.lang.String相似,它們的實例均沒法修改(由於任意對其值改變的操做都會生成新的對象),這也表明了它們是線程安全的。

瞬時性(Instant)

如接口 org.joda.time.ReadableInstant 中所表示的那樣,Instant 表示的是一個精確的時間點,是從 epoch:1970-01-01T00:00:00Z 開始計算的毫秒數,這也的設計也使得其子類均可以與JDK Date 以及 Calendar 類兼容。

/** * Defines an instant in the datetime continuum. * This interface expresses the datetime as milliseconds from 1970-01-01T00:00:00Z. * <p> * The implementation of this interface may be mutable or immutable. * This interface only gives access to retrieve data, never to change it. * <p> * Methods in your application should be defined using <code>ReadableInstant</code> * as a parameter if the method only wants to read the instant without needing to know * the specific datetime fields. * <p> * The {@code compareTo} method is no longer defined in this class in version 2.0. * Instead, the definition is simply inherited from the {@code Comparable} interface. * This approach is necessary to preserve binary compatibility. * The definition of the comparison is ascending order by millisecond instant. * Implementors are recommended to extend {@code AbstractInstant} instead of this interface. * * @author Stephen Colebourne * @since 1.0 */
public interface ReadableInstant extends Comparable<ReadableInstant> {

    /** * Get the value as the number of milliseconds since * the epoch, 1970-01-01T00:00:00Z. * * @return the value as milliseconds */
    long getMillis();
  																					······
}
複製代碼

DateTime 類繼承圖以下: image.png

局部性(Partial)

瞬時性表達的是與epoch相對的時間上的一個精確時刻,而一個局部時間指的是一個時間的一部分片斷,其能夠經過一些方法使得時間產生變更(本質上仍是生成了新的類),這樣能夠把它當作重複週期中的一點,用到多個地方。

年表(Chronology)

Joda-Time的設計核心就是年表(org.joda.time.Chronology),從根本上將,年表是一種日曆系統,是一種計算時間的特殊方式,而且在其中執行日曆算法的框架。Joda-Time支持的8種年表以下所示:

  • ISO(默認) - org.joda.time.chrono.ISOChronology
  • GJ - org.joda.time.chrono.GJChronology
  • Gregorian - org.joda.time.chrono.GregorianChronology
  • Julian - org.joda.time.chrono.JulianChronology
  • Coptic - org.joda.time.chrono.CopticChronology
  • Buddhist - org.joda.time.chrono.BuddhistChronology
  • Ethiopic - org.joda.time.chrono.EthiopicChronology
  • Islamic - org.joda.time.chrono.IslamicChronology

以上的每一種年表均可以做爲特定日曆系統的計算引擎,是可插拔的實現。

時區(Time zone)

具體定義詳見百科解釋,在實際編碼過程當中任何嚴格的時間計算都必須涉及時區(或者相對於GMT),Joda-Time中對應的核心類爲org.joda.time.DateTimeZone,雖然平常的使用過程當中,並未涉及到對時區的操做,可是DateTimeZone如何對DateTime產生影響是比較值得注意的,此處不進行贅述。

2.3 具體使用方法

上面介紹我完了Joda-Time的一些概念,接下來具體使用咱們來進行說明:

2.3.1 建立 Joda-Time 對象

瞬時性-ReadableInstant

// 1.使用系統時間
DateTime dateTime1 = new DateTime();
// 2.使用jdk中的date
Date jdkDate1 = new Date();
DateTime dateTime2 = new DateTime(jdkDate1);
// 3.使用毫秒數指定
Date jdkDate2 = new Date();
long millis = jdkDate.getTime();
DateTime dateTime3 = new DateTime(millis);
// 4.使用Calendar
Calendar calendar = Calendar.getInstance();
DateTime dateTime4 = new DateTime(calendar);
// 5.使用多個字段指定一個瞬間時刻(局部時間片斷)
// year month day hour(midnight is zero) minute second milliseconds
DateTime dateTime5 = new DateTime(2000, 1, 1, 0, 0, 0, 0);
// 6.由一個DateTime生成另外一個DateTime
DateTime dateTime6 = new DateTime(dateTime1);
// 7.有時間字符串生成DateTime
String timeString = "2019-01-01T00:00:00-06:00";
DateTime dateTime7 = DateTime.parse(timeString);
複製代碼

局部性-ReadablePartial

當程序中處理的日期、時間並不須要是完整時刻的時候,能夠建立一個局部時間,好比只但願專一於年/月/日, 或者一天中的時間,或者是一週中的某天。Joda-Time中有表示這些時間的是org.joda.time.ReadablePartial接口,實現它的兩個類LocalDate和LocalTime是分別用來表示年/月/日和一天中的某個時間的。

// 顯示地提供所含的每一個字段
LocalDate localDate = new LocalDate(2019, 1, 1);
// 6:30:06 PM
LocalTime localTime = new LocalTime(18, 30, 6, 0);
複製代碼

LocalDate是替代了早期Joda-Time版本中使用的org.joda.time.YearMonthDay,LocalTime是替代早期版本的org.joda.time.TimeOfDay。(均已被標註爲過期狀態)。

時間跨度

Joda-Time提供了三個類用於表示時間跨度(在某些業務需求中,它們可能會很是有用)。

  • Duration

這個類表示以毫秒爲單位的絕對精度,提供標準數學轉換的方法,同時把時間跨度轉換爲標準單位。

  • Period

這個類表示以年月日單位表示。

  • Interval

這個類表示一個特定的時間跨度,使用一個明確的時刻界定這段時間跨度的範圍。Interval 爲半開 區間,因此由其封裝的時間跨度包括這段時間的起始時刻,可是不包含結束時刻。

2.3.2 使用Joda-Time的方法處理時間

DateTime today = new DateTime();
// 獲取777秒以前的時間
DateTime dateTime1 = today.minus(777 * 1000);
// 獲取明天的時間
DateTime tomorrow = today.plusDays(1);
// 獲取當月第一天的日期
DateTime dateTime2 = today.withDayOfMonth(1); 
// 獲取當前時間三個月後的月份的最後一天
DateTime dateTime3 = today.plusMonths(3).dayOfMonth().withMaximumValue();
複製代碼

下面列出部分DateTime方法列表: plus/minus開頭的方法(好比:plusDay, minusMonths):用來返回在DateTime實例上增長或減小一段時間後的實例

  1. plus(long duration) 增長指定毫秒數並返回

  2. plusYears(int years) 增長指定年份並返回

  3. plusMonths(int months) 增長指定月份並返回

  4. plusWeeks(int weeks) 增長指定星期並返回

  5. plusDays(int days) 增長指定天數並返回

  6. plusHours(int hours) 增長指定小時並返回

  7. plusMinutes(int minutes) 增長指定分鐘並返回

  8. plusSeconds(int seconds) 增長指定秒數並返回

  9. plusMillis(int millis) 增長指定毫秒並返回

與之相反的是minus前綴的 plus是增長 minus是減小

with開頭的方法:用來返回在DateTime實例更新指定日期單位後的實例

  1. withCenturyOfEra(int centuryOfEra) 更新時間世紀單位並返回
  2. withYearOfCentury(int yearOfCentury)更新世紀年並返回
  3. withYear(int year) 更新時間年並返回
  4. withWeekyear(int weekyear) 更新時間週數並返回
  5. withMonthOfYear(int monthOfYear)更新時間月份並返回
  6. withDayOfYear(int dayOfYear) 更新時間天數並返回
  7. withDayOfMonth(int dayOfMonth) 更新時間天數並返回
  8. withDayOfWeek(int dayOfWeek) 更新時間天數並返回
  9. withHourOfDay(int hour) 更新時間小時並返回
  10. withMinuteOfHour(int minute) 更新時間分鐘並返回
  11. withSecondOfMinute(int second) 更新時間秒數並返回
  12. withMillisOfSecond(int millis) 更新時間毫秒並返回
  13. withMillisOfDay(int millis) 更新時間毫秒並返回
  14. withTimeAtStartOfDay() 獲取當天最先時間

判斷DateTime對象大小狀態的一些操做方法

  1. compareTo(DateTime d) 比較兩時間大小 時間大於指定時間返回 1 時間小於指定時間返回-1 相等返回0
  2. equals(DateTime d) 比較兩時間是否相等
  3. isAfter(long instant) 判斷時間是否大於指定時間
  4. isAfterNow() 判斷時間是否大於當前時間
  5. isBefore(long instant) 判斷時間是否小於指定時間
  6. isBeforeNow() 判斷時間是否小於當前時間
  7. isEqual(long instant) 判斷時間是否等於指定時間
  8. isEqualNow() 判斷時間是否等於當前時間

2.3.3 以Joda-Time的方式格式化時間

// 傳入的格式化模板只需與JDK SimpleDateFormat兼容的格式字符串便可
public static String convert(Date date,String dateFormat){
    return new DateTime(date).toString(dateFormat);
}
// 將JDK中的Date轉化爲UTC時區的DateTime
DateTime dateTime = new DateTime(new Date(), DateTimeZone.UTC);
// 將String轉換爲DateTime
public static Date convertUTC2Date(String utcDate){
    DateTime dateTime =DateTime.parse(utcDate, DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
    return dateTime.toDate();
 }

複製代碼

更多使用方法請參考官方文檔。

3、JAVA 8中新的時間類庫

3.1 簡介

因爲JDK以前版本的類庫的缺陷和糟糕的使用體驗,再加上已經成爲事實標準Joda-Time的影響力,Oracle決定在JAVA API中提供高質量的日期和時間支持,這也就是整合了大部分Joda-Time特性的JDK 8新的時間類庫。(Joda-Time的做者實際參與開發,而且實現了JSR310的所有內容,新的API位於java.time下。經常使用的類有如下幾個:LocalDate、LocalTime、Instant、Duration和Period。)

因爲JDK 8 新的時間類庫大量借鑑了Joda-Time的設計思想乃至命名,所以若是你是Joda-Time的使用者,那你能夠無學習成本的使用新的API(固然,它們之間也存在些許差異須要注意到)。

3.2 使用方法

3.2.1 使用LocalDate 和LocalTime

首先是LocalDate,該類的實例是一個不可變對象,它只提供了簡單的日期,並不含當天的時間信息。另外,它也不附帶任何與時區相關的信息。

// 使用指定的日期建立LocalDate
LocalDate date = LocalDate.of(2019, 1, 1);
// 獲取當前日期
LocalDate today = LocalDate.now();
// 獲取今日的屬性
int year = date.getYear();
Month month = date.getMonth();
int day = date.getDayOfMonth();
DayOfWeek dow = date.getDayOfWeek();
int len = date.lengthOfMonth();
boolean leap = date.isLeapYear();
// 經過ChronoField的枚舉值獲取須要的屬性字段
int year = date.get(ChronoField.YEAR);
複製代碼

接着是LocalTime,它表示了一天內的某個時刻。

LocalTime time = LocalTime.of(18, 18, 18);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
複製代碼

LocalDate和LocalTime均可以經過使用靜態方法parse來解析字符串進行建立。

LocalDate date = LocalDate.parse("2019-01-01");

LocalTime time = LocalTime.parse("18:18:18");
複製代碼

也能夠向parse方法傳遞一個DateTimeFormatter,該類的實例定義瞭如何格式化一個日期或者時間對象。它實際上是老版java.util.DateFormat的替代品。

3.2.2 LocalDateTime

// 直接建立LocalDateTime
LocalDateTime dt1 = LocalDateTime.of(2019, Month.JANUARY, 1, 18, 18, 18);
// 合併日期和時間
LocalDate date = LocalDate.parse("2019-01-01");
LocalTime time = LocalTime.parse("18:18:18");
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(18, 18, 18);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
// 從LocalDateTime中提取LocalDate或者LocalTime
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();
複製代碼

3.3.3 Instant

Instant類是爲了方便計算機理解的而設計的,它表示一個持續時間段上某個點的單一大整型數,實際上它是以Unix元年時間(傳統的設定爲UTC時區1970年1月1日午夜時分)開始所經歷的秒數進行計算(最小計算單位爲納秒)。

// 傳遞一個秒數已建立該類的實例
Instant.ofEpochSecond(3);
// 傳遞一個秒數+納秒 2 秒以後再加上100萬納秒(1秒)
Instant.ofEpochSecond(2, 1_000_000_000);
複製代碼

3.3.4 Duration與Period

Duration是用於比較兩個LocalTime對象或者兩個Instant之間的時間差值。

Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);
複製代碼

Period是用於對年月日的方式對多個時間進行比較。

Period tenDays = Period.between(LocalDate.of(2019, 1, 1), lcalDate.of(2019, 2, 2));
複製代碼

固然,Duration和Period類都提供了不少很是方便的工廠類,直接建立對應的實例。

Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
複製代碼

3.3.5 操做、解析和格式化日期

// 直接使用withAttribute的方法修改
LocalDate date1 = LocalDate.of(2019, 1, 1);
LocalDate date2 = date1.withYear(2019);
LocalDate date3 = date2.withDayOfMonth(1);
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 1);
複製代碼

全部聲明瞭Temporal接口的類LocalDate、LocalTime、LocalDateTime以及Instant,它們都使用get和with方法,將對象值的讀取和修改區分開,若是使用了不支持的字段訪問字段,會拋出一個UnsupportedTemporalTypeException異常。相似的,plus方法和minus方法都聲明於Temporal接口。經過這些方法,對TemporalUnit對象加上或者減去一個數字,咱們能很是方便地將Temporal對象前溯或者回滾至某個時間段,經過ChronoUnit枚舉咱們能夠很是方便地實現TemporalUnit接口。

3.3.6 更多定製化的處理時間

向重載的with方法傳遞一個定製化的TemporalAdjuster對象,能夠更加靈活地處理日期。時間和日期的API已經提供了大量預約義的TemporalAdjuster,能夠經過TemporalAdjuster類的靜態工廠方法訪問它們。這些方法的名稱很是直觀,方法名就是問題描述。某些狀況下,若是你須要定義本身的TemporalAdjuster,只須要聲明TemporalAdjuster接口而且本身實現對應的方法便可。

LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.with(TemporalAdjuster.nextOrSame(DayOfWeek.SUNDAY));
LocalDate date3 = date2.with(TemporalAdjuster.lastDayOfMonth());
複製代碼

3.3.7 解析日期-時間對象

平常工做中,格式化以及解析日期-時間對象是另外一個很是重要的功能,而新的java.time.format包就是特別爲咱們達到這個目的而設計的。這其中,最重要的類是DateTimeFormatter。全部的DateTimeFormatter實例都能用於以必定的格式建立表明特定日期或時間的字符串。(與老的java.util.DateFormat相比較,全部的DateTimeFormatter實例都是線程安全的)

// 使用不一樣的格式器生成字符串
LocalDate date = LocalDate.of(2019, 1, 1);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
// 生成LocalDate對象
LocalDate date1 = LocalDate.parse("20190101", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2019-01-01", DateTimeFormatter.ISO_LOCAL_DATE);
複製代碼
// 使用特定的模式建立格式器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1 = LocalDate.of(2019, 1, 1);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);
複製代碼

3.3.8 處理不一樣的時區和日曆系統

在新的日期-時間類庫中,爲了最大程度上的減小在處理時區帶來的繁瑣和複雜而使用了新的java.time.ZoneId類(與其餘日期和時間類同樣,ZoneId類也是沒法修改的) 來替代老版的java.util.TimeZone。時區是按照必定的規則將區域劃分紅標準時間相同的區間。在ZoneRules這個類中包含了40個這樣的實例。能夠簡單地經過調用ZoneId的getRules()獲得指定時區的規則。每一個特定的ZoneId對象都由一個地區ID標識,地區ID都爲「{區域}/{城市}」的格式。好比:

ZoneId romeZone = ZoneId.of("Asia/Shanghai");
複製代碼

Java 8中在原先的TimeZone中加入了新的方法toZoneId,其做用是將一個老的時區對象轉換爲ZoneId:

ZoneId zoneId = TimeZone.getDefault().toZoneId();
複製代碼

獲得的ZoneId對象後能夠將它與LocalDate、LocalDateTime或者是Instant對象整合起來,構造爲一個ZonedDateTime實例,它表明了相對於指定時區的時間點:

LocalDate date = LocalDate.of(2019, Month.JANUARY, 1);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2019, Month.JANUARY, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);
複製代碼

經過ZoneId,還能夠將LocalDateTime轉換爲Instant:

LocalDateTime dateTime = LocalDateTime.of(2019, Month.JANUARY, 18, 13, 45);
Instant instantFromDateTime = dateTime.toInstant(romeZone);
複製代碼

一樣能夠經過反向的方式獲得LocalDateTime對象:

Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
複製代碼

與Joda-Time所不一樣的是,Java8中的日期-時間類庫提供了4種其餘的日曆系統,這些日曆系統中的每個都有一個對應的日誌類,分別是ThaiBuddhistDate、MinguoDate 、JapaneseDate 以及HijrahDate 。全部這些類以及LocalDate 都實現了ChronoLocalDate接口,可以對公曆的日期進行建模。利用LocalDate對象,你能夠建立這些類的實例。一樣的,利用它們提供的靜態工廠方法,你能夠建立任何一個Temporal對象的實例。

LocalDate date = LocalDate.of(2019, Month.JANUARY, 1);
JapaneseDate japaneseDate = JapaneseDate.from(date);
複製代碼

參考資料

Joda-Time 簡介 Joda Time項目和java8時間api

動態計算時間段

需求:如如今是13:00,則時間段爲15:10-17:十、17:10-19:十、19:10-21:10;即最先的出發時間爲當前時間+參數【2h10min】,最遲的時間段爲開始時間在20點前結束時間在20點後的時間段),求解共有多少個時間段?

分析

  1. 第一個時間段的開始時間:當前時間+參數【2h10min】,中間的時間段是2h;
  2. 經過理解這句:最遲的時間段爲開始時間在20點前結束時間在20點後的時間段,咱們能夠假設最大的時間變量爲 max
  3. 假設當前時間爲now,總共有n個時間段,能夠推導出公式:now + (2h * n) + 10min  <= max;

注意:計算過程都轉換成毫秒

public class Test {
    // 毫秒
    static final long slot = 130 * 60 * 1000;

    private static List<TimeSelectItem> buildStartEndTime(Long now, Long max) {
        // now + (2h * n) + 10min <= max;

        Long n = (max - now - 60 * 1000) / (120 * 60 * 1000);
        System.out.println("max:" + max);
        System.out.println("now:" + now);
        System.out.println(" max - now:" + (max - now));
        System.out.println("n:" + n);

        List<TimeSelectItem> timeSelectItems = new ArrayList<>();

        Long startTimestamp = now + slot;
        Long endTimestamp = startTimestamp + 120 * 60 * 1000;

        for (int i = 1; i <= n; i++) {
            // 起始時間
            // startTimestamp = startTimestamp + i * (120 * 60 * 1000);
            // 結束時間
            endTimestamp = startTimestamp + (120 * 60 * 1000);

            System.out.println(startTimestamp);
            System.out.println(endTimestamp);

            TimeSelectItem item = new TimeSelectItem();

            DateTime dt = new DateTime(startTimestamp);
            int hour = dt.hourOfDay().get();
            int millis = dt.getMinuteOfHour();
            String startTag = hour + ":" + millis;

            DateTime dt1 = new DateTime(endTimestamp);
            int hour1 = dt1.hourOfDay().get();
            long millis1 = dt1.getMinuteOfHour();
            String enTag = hour1 + ":" + millis1;

            item.setDisplayName(startTag + " - " + enTag);

            item.setStartTimestamp(startTimestamp);
            item.setEndTimestamp(endTimestamp);
            timeSelectItems.add(item);

            startTimestamp = endTimestamp;
        }
        return timeSelectItems;
    }

    public static void main(String[] args) {
        Long start = DateTime.now().getMillis();
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.set(Calendar.HOUR_OF_DAY, 20);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);


        DateTime dt = new DateTime();
        dt.withHourOfDay(20);
        Long end = c.getTimeInMillis();
       
        // List<TimeSelectItem> list = buildStartEndTime(1614747600000L, 1614772800000L);
        List<TimeSelectItem> list = buildStartEndTime(1614834000000L, end);
        for (TimeSelectItem item : list ) {
            System.out.println(item);
        }
    }
}
複製代碼

文章持續更新,能夠公衆號搜一搜「 一角錢技術 」第一時間閱讀, 本文 GitHub org_hejianhui/JavaStudy 已經收錄,歡迎 Star。

相關文章
相關標籤/搜索