java獲取date的年月日:java中獲取當前時間的方法

前言

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

一、JDK8以前版本中的時間類庫

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

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

在Java 1.0版本中,對時間、日期的操作完全依賴於 java.util.Data 類,只能以毫秒的精度表示時間,無法表示日期。

  • 在易用性方面有着很大的缺陷,年份的起始時間選擇是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 (後面介紹)

二、Joda-Time 日期時間類庫

2.1 簡介

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

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

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處理複雜的日期/時間計算。

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 類繼承圖如下:

Java時間操作類庫—Joda-Time

局部性(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();
 }

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

三、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:10、17:10-19:10、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);
        }
    }
}

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/218870.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-09 00:33
下一篇 2024-12-09 00:33

相關推薦

發表回復

登錄後才能評論