ZonedDateTime to Date before Java 8 in early Android

ZonedDateTime to Date before Java 8 in early Android

我正在尝试替换 ZonedDateTime.toInstant 方法,因为它仅在 API 26 之后才适用于 Android。
但是我的应用应该支持 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库。

避免遗留日期时间 classes

CalendarDate这样的旧日期时间class是糟糕的,真的,非常糟糕class是的。它们充满了糟糕的设计决策和黑客攻击,这些都是由不了解日期时间处理的人构建的。避开他们。由于采用 JSR 310,它们完全被 java.time classes 取代——实际上,很多原因。

避免这些遗留问题 classes。仅使用 java.time classes。

ThreeTen-Backport

对于 Java 6 和 Java 7,大多数 java.time 功能在 ThreeTen-Backport 项目。

ThreeTen-ABP

该反向端口进一步适应 ThreeTen-ABP 项目中较早的 Android (<26)。

我敦促您将这个库添加到您的项目中,这样您就可以避免使用悲剧性的遗留 classes。

转化

在您需要与尚未更新到 java.time 的旧代码交互的地方,在传统代码和现代代码之间来回转换。

在 Java 8 及更高版本中,通过调用旧 classes 上的新 to…from… 方法进行转换。

在后端口中,使用 org.threeten.bp.DateTimeUtils class 上的 to… 转换方法进行转换。

经过的时间

你的问题是关于计算经过时间的。

要计算年、月和日,请使用 Period

要计算天数(与日历无关的 24 小时时间块)、小时、分钟、秒和小数秒,请使用 Duration

搜索 Stack Overflow 了解更多信息。这些 classes 已经被覆盖了很多次。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

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 小时)更粗略的单位。如果您需要 return 周、月或年,只需使用 OffsetDateTime 而不是 Instant

问题:我可以在 Android API 级别上使用 java.time 吗?

是的,java.time 在新旧 Android 设备上都能很好地工作。它只需要至少 Java 6.

  • 在 Java 8 及更高版本和更新的 Android 设备上(从 API 级别 26),现代 API 是内置的。在这种情况下,使用子包从 java.time 导入。
  • 在 Java 6 和 7 中获取现代 类 的 ThreeTen Backport(ThreeTen 用于 JSR 310;请参阅底部的链接)。
  • 在(较旧的)Android 使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。并确保使用子包从 org.threeten.bp 导入日期和时间 类。

流?

编辑: @Basil Bourque 在评论中提问:

I wonder if this could be crunched down into a stream, perhaps a one-liner?

可以,但我认为在这种情况下不会有优势:

    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);
}

您只需通过以下 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