簡體   English   中英

ZonedDateTime 到早期 Android 中 Java 8 之前的日期

[英]ZonedDateTime to Date before Java 8 in early Android

我正在嘗試替換ZonedDateTime.toInstant方法,因為它僅在適用於 Android 的 API 26 之后可用。
但我的應用程序應該支持 API 19。
我想將 ZonedDateTime 轉換為日期,以便我可以執行以下操作:

final Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
final long millis = calendar.getTimeInMillis();

我想要實現的是以下內容:
我想計算當前日期和另一個日期之間的差異,以秒、分鍾、小時為單位,......最高可能的單位獲勝,所以我會得到例如5 days ago的結果。

tl;博士

對於 26之前的 Android,使用ThreeTen-ABP庫。

避免遺留的日期時間類

舊的日期時間類,例如CalendarDate可怕的,真的,可怕的可悲的類。 它們充滿了糟糕的設計決策和黑客,由不了解日期時間處理的人構建。 避開它們。 隨着 JSR 310 的采用,它們完全被java.time類所取代——實際上,有很多原因。

避免這些遺留類。 僅使用java.time類。

ThreeTen-Backport

對於 Java 6 和 Java 7,大部分java.time功能都在ThreeTen-Backport項目中向后移植。

三十-ABP

ThreeTen-ABP項目中,該后向移植進一步適用於早期的 Android (<26)。

我敦促您將此庫添加到您的項目中,這樣您就可以避免使用悲劇性的遺留類。

轉換

如果您需要與尚未更新為java.time 的舊代碼進行交互,請在舊代碼和現代代碼之間來回轉換。

在 Java 8 及更高版本中,通過調用在舊類中找到的新的to…from…方法進行轉換。

在后向端口中,使用org.threeten.bp.DateTimeUtils類中的to…轉換方法to…轉換。

已用時間

您的問題涉及計算經過時間。

要計算年、月和日,請使用Period

要計算天數(與日歷無關的 24 小時時間塊)、小時、分鍾、秒和小數秒,請使用Duration

搜索堆棧溢出以獲取更多信息。 這些類已經介紹了很多次。


關於java.time

java.time框架內置於 Java 8 及更高版本中。 這些類取代麻煩的老傳統日期時間類,如java.util.DateCalendar ,和SimpleDateFormat

要了解更多信息,請參閱Oracle 教程 並在 Stack Overflow 上搜索許多示例和解釋。 規范是JSR 310

現在處於維護模式Joda-Time項目建議遷移到java.time類。

您可以直接與您的數據庫交換java.time對象。 使用符合JDBC 4.2或更高版本的JDBC 驅動程序 不需要字符串,不需要java.sql.*類。

從哪里獲得 java.time 類?

在此處輸入圖片說明

您可以通過以下 gradle 配置簡單地啟用“支持更新的 Java 語言 API”:

<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->

android {
  defaultConfig {
    //Only required when setting minSdkVersion to 20 or lower
    multiDexEnabled true
  }

  compileOptions {
    // Flag to enable support for the new language APIs
    coreLibraryDesugaringEnabled true
    // Sets Java compatibility to Java 8
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  // Dependency with the implementation code for the APIs
  coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.5'
}

來源: https : //medium.com/androiddevelopers/support-for-newer-java-language-apis-bca79fc8ef65

Basil Bourque 已經有一個很好的答案。 在您發表評論后,我想我可以更具體一點:

public static String diff(String thenStr) {
    Instant now = Instant.now();
    Instant then = Instant.parse(thenStr);
    ChronoUnit[] units = ChronoUnit.values();
    // try longest units first, they are in the far end of the array
    for (int i = units.length - 1; i >= 0; i--) {
        if (then.isSupported(units[i])) {
            long diffInCurrentUnit = units[i].between(then, now);
            if (diffInCurrentUnit != 0) {
                return "" + diffInCurrentUnit + ' ' + units[i].toString().toLowerCase();
            }
        }
    }
    return "0";
}

讓我們試試看:

    System.out.println(diff("2019-02-23T16:50:21Z"));
    System.out.println(diff("2019-02-23T20:15:21Z"));

剛才運行時的輸出:

 3 hours 36 seconds

我使用的進口:

import org.threeten.bp.Instant;
import org.threeten.bp.temporal.ChronoUnit;
import org.threeten.bp.temporal.UnsupportedTemporalTypeException;

Instant不支持比天(24 小時)更粗的單位。 如果您需要能夠返回數周、數月或數年,只需使用OffsetDateTime而不是Instant

問題:我可以在我的 Android API 級別上使用 java.time 嗎?

是的,java.time 在較舊和較新的 Android 設備上都能很好地工作。 它只需要至少Java 6

  • 在 Java 8 及更高版本和更新的 Android 設備(從 API 級別 26)中,現代 API 是內置的。 在這種情況下,從帶有子包的java.time導入。
  • 在 Java 6 和 7 中獲得 ThreeTen Backport,現代類的 backport(ThreeTen for JSR 310;請參閱底部的鏈接)。
  • 在(較舊的)Android 上使用 ThreeTen Backport 的 Android 版本。 它被稱為 ThreeTenABP。 並確保使用子包從org.threeten.bp導入日期和時間類。

一條流?

編輯: @Basil Bourque 在評論中問道:

我想知道這是否可以壓縮成一條小溪,也許是一條單線?

它可以,但我認為在這種情況下這不會是一個優勢:

    return IntStream.range(0, units.length)
            .map(i -> units.length - i - 1)
            .mapToObj(i -> units[i])
            .filter(then::isSupported)
            .filter(unit -> unit.between(then, now) != 0)
            .map(unit -> "" + unit.between(then, now) + ' ' + unit.toString().toLowerCase())
            .findFirst()
            .orElse("0");

我發現向后獲取數組元素的代碼.map(i -> units.length - i - 1)有點難以閱讀。 我們需要計算兩次差值,首先是過濾,然后是組合字符串結果。 但它有效,如果您願意,可以使用它。

使用內部流管道可以避免雙重計算,但同​​樣難以閱讀:

            .flatMap(unit -> LongStream.of(unit.between(then, now))
                    .filter(diff -> diff != 0)
                    .mapToObj(diff -> "" + diff + ' ' + unit.toString().toLowerCase()))

鏈接

解決方案(ThreeTen-Backport 庫):
它工作得很好,我已經在 KitKat 模擬器上試過了。

private static final ChronoUnit[] chronoUnits = {ChronoUnit.YEARS, ChronoUnit.MONTHS, ChronoUnit.DAYS, ChronoUnit.HOURS, ChronoUnit.MINUTES, ChronoUnit.SECONDS};
private static final Map<ChronoUnit, Integer> chronoUnitPluralIdMap = new HashMap<ChronoUnit, Integer>() {{
    put(ChronoUnit.YEARS, R.plurals.chrono_unit_years_ago);
    put(ChronoUnit.MONTHS, R.plurals.chrono_unit_months_ago);
    put(ChronoUnit.DAYS, R.plurals.chrono_unit_days_ago);
    put(ChronoUnit.HOURS, R.plurals.chrono_unit_hours_ago);
    put(ChronoUnit.MINUTES, R.plurals.chrono_unit_minutes_ago);
    put(ChronoUnit.SECONDS, R.plurals.chrono_unit_seconds_ago);
}};

public static String getTimeStringUntilNowFromUTC(Context context, String utcDate) {
    Instant now = Instant.now(Clock.systemUTC());
    Instant then = Instant.parse(utcDate);
    for (ChronoUnit chronoUnit : chronoUnits) {
        if (then.isSupported(chronoUnit)) {
            long units = chronoUnit.between(then, now);
            if (units > 0) {
                //noinspection ConstantConditions
                return context.getResources().getQuantityString(chronoUnitPluralIdMap.get(chronoUnit), (int)units, (int)units);
            }
        }
    }
    return "-";
}

public static String getTimeBetweenTwoDates(Context context, String date1, String date2) {
    Instant date1Instant = Instant.parse(date1);
    Instant date2Instant = Instant.parse(date2);
    final long seconds = ChronoUnit.SECONDS.between(date1Instant, date2Instant);
    return getMinutesSecondsString(context, seconds);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM