ZonedDateTime to Date before Java 8 in early Android
You can enable "Support for newer Java language APIs" simply by this gradle configuration:
<!-- 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'
}
Source: https://medium.com/androiddevelopers/support-for-newer-java-language-apis-bca79fc8ef65
tl;dr
For Android before 26, use the ThreeTen-ABP library.
Avoid legacy date-time classes
The old date-time classes such as Calendar
and Date
are terrible, really, awful wretched classes. They are laden with bad design decisions and hacks, built by people who did not understand date-time handling. Avoid them. They were entirely supplanted by the java.time classes with the adoption of JSR 310 for a reason – actually, many reasons.
Avoid these legacy classes. Use only java.time classes.
ThreeTen-Backport library
For Java 6 and Java 7, most of the java.time functionality is back-ported in the ThreeTen-Backport project.
ThreeTen-ABP
That back-port is further adapted to earlier Android (<26) in the ThreeTen-ABP project.
I urge you to add this library to your project so you can avoid ever using the tragic legacy classes.
Conversion
Where you need to interface with old code not yet updated to java.time, convert back-and-forth between legacy and modern.
In Java 8 and later, convert by calling the new to…
and from…
methods found on the old classes.
In the back-port, convert using the to…
conversion methods found on the org.threeten.bp.DateTimeUtils
class.
Elapsed time
Your Question talks about calculating elapsed time.
To count years, months, and days, use Period
.
To count days (24-hour chunks of time unrelated to the calendar), hours, minutes, seconds, and fractional second, use Duration
.
Search Stack Overflow for more info. These classes have been covered many time already.
About java.time
The 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
.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6 and Java SE 7
- Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.time classes.
- For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
There’s already a good answer by Basil Bourque. After your comments I thought I could be just a little more specific:
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";
}
Let’s try it out:
System.out.println(diff("2019-02-23T16:50:21Z"));
System.out.println(diff("2019-02-23T20:15:21Z"));
Output when running just now:
3 hours 36 seconds
Imports I used:
import org.threeten.bp.Instant;
import org.threeten.bp.temporal.ChronoUnit;
import org.threeten.bp.temporal.UnsupportedTemporalTypeException;
Instant
doesn’t support coarser units than days (at 24 hours). If you need to be able to return weeks, months or years, just use OffsetDateTime
instead of Instant
.
Question: Can I use java.time on my Android API level?
Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.
- In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in. In this case import from
java.time
with subpackages. - In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
- On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from
org.threeten.bp
with subpackages.
A stream?
EDIT: @Basil Bourque asked in a comment:
I wonder if this could be crunched down into a stream, perhaps a one-liner?
It can, but I don’t think it will be an advantage in this case:
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");
I find the code for taking the array elements backward, .map(i -> units.length - i - 1)
, a bit hard to read. We need to calculate the difference twice, first for filtering and then for assembling the string result. But it works, and you may go with it if you like.
The double calculation can be avoided with an inner stream pipeline, again harder to read, though:
.flatMap(unit -> LongStream.of(unit.between(then, now))
.filter(diff -> diff != 0)
.mapToObj(diff -> "" + diff + ' ' + unit.toString().toLowerCase()))
Links
- Oracle tutorial: Date Time explaining how to use java.time.
- Java Specification Request (JSR) 310, where
java.time
was first described. - ThreeTen Backport project, the backport of
java.time
to Java 6 and 7 (ThreeTen for JSR-310). - ThreeTenABP, Android edition of ThreeTen Backport
- Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.