How to Use Threetenabp in Android Project

How to use ThreeTenABP in Android Project

Attention: This answer, while technically correct, is now out of date

Java 8+ API desugaring support now available via Android Gradle Plugin 4.0.0+

(Also see Basil Bourque's answer below)

Development on the ThreeTenABP Library is winding down. Please consider switching to Android Gradle plugin 4.0, java.time.*, and its core library desugaring feature in the coming months.

To enable support for these language APIs on any version of the Android platform, update the Android plugin to 4.0.0 (or higher) and include the following in your module’s build.gradle file:

android {
defaultConfig {
// 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 {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.5'
}


Original Answer

First Discovery: Why You Have To Use ThreeTenABP Instead of java.time, ThreeTen-Backport, or even Joda-Time

This is a really short version of the VERY LONG PROCESS of defining a new standard. All of these packages are pretty much the same thing: libraries that provide good, modern time handling functionality for Java. The differences are subtle but important.

The most obvious solution would be to use the built-in java.time package, since this is the new standard way to deal with time and dates in Java. It is an implementation of JSR 310, which was a new standard proposal for time handling based on the Joda-Time library.

However, java.time was introduced in Java 8. Android up to Marshmallow runs on Java 7 ("Android N" is the first version to introduce Java 8 language features). Thus, unless you're only targeting Android N Nougat and above, you can't rely on Java 8 language features (I'm not actually sure this is 100% true, but this is how I understand it). So java.time is out.

The next option might be Joda-Time, since JSR 310 was based on Joda-Time. However, as the ThreeTenABP readme indicates, for a number of reasons, Joda-Time is not the best option.

Next is ThreeTen-Backport, which back-ports much (but not all) of the Java 8 java.time functionality to Java 7. This is fine for most use cases, but, as indicated in the ThreeTenABP readme, it has performance issues with Android.

So the last and seemingly correct option is ThreeTenABP.

Second Discovery: Build Tools and Dependency Management

Since compiling a program -- especially one using a bunch of external libraries -- is complex, Java almost invariably uses a "build tool" to manage the process. Make, Apache Ant, Apache Maven, and Gradle are all build tools that are used with Java programs (see this post for comparisons). As noted further down, Gradle is the chosen build tool for Android projects.

These build tools include dependency management. Apache Maven appears to be the first to include a centralized package repository. Maven introduced the Maven Central Repository, which allows functionality equivalent to php's composer with Packagist and Ruby's gem with rubygems.org. In other words, the Maven Central Repository is to Maven (and Gradle) what Packagist is to composer -- a definitive and secure source for versioned packages.

Third Discovery: Gradle Handles Dependencies in Android Projects

High on my to-do list is to read the Gradle docs here, including their free eBooks. Had I read these weeks ago when I started learning Android, I would surely have known that Gradle can use the Maven Central Repository to manage dependencies in Android Projects. Furthermore, as detailed in this StackOverflow answer, as of Android Studio 0.8.9, Gradle uses Maven Central Repository implicitly through Bintray's JCenter, which means you don't have to do any extra config to set up the repo -- you just list the dependencies.

Fourth Discovery: Project Dependencies Are Listed in [project dir]/app/build.gradle

Again, obvious to those who have any experience using Gradle in Java, but it took me a while to figure this out. If you see people saying "Oh, just add compile 'this-or-that.jar'" or something similar, know that compile is a directive in that build.gradle file that indicates compile-time dependencies. Here's the official Gradle page on dependency management.

Fifth Discovery: ThreeTenABP Is Managed by Jake Wharton, not by ThreeTen

Yet another issue I spent too much time figuring out. If you look for ThreeTen in Maven Central, you'll only see packages for threetenbp, not threetenabp. If you go to the github repo for ThreeTenABP, you'll see that infamous compile 'this-or-that' line under the Download section of the Readme.

When I first hit this github repo, I didn't know what that compile line meant, and I tried to run it in my terminal (with an obvious and predictable failure). Frustrated, I didn't return to it until long after I figured the rest out, and finally realized that it's a Maven Repo line pointing to the com.jakewharton.threetenabp repo, as opposed to the org.threeten repo. That's why I thought the ThreeTenABP package wasn't in the Maven repo.

Summary: Making it work

Now it all seems pretty easy. You can get modern time handling functions in an Android project by making sure your [project folder]/app/build.gradle file has the implementation 'com.jakewharton.threetenabp:threetenabp:1.2.1' line in its dependencies section:

apply plugin: 'com.android.application'

android {
compileSdkVersion 23
buildToolsVersion "23.0.3"

defaultConfig {
applicationId "me.ahuman.myapp"
minSdkVersion 11
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}


dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'
implementation 'com.android.support:appcompat-v7:23.4.0'
implementation 'com.android.support:design:23.4.0'
implementation 'com.jakewharton.threetenabp:threetenabp:1.2.1'
}

Also add this to Application class:

public class App extends Application {    
@Override
public void onCreate() {
super.onCreate();
AndroidThreeTen.init(this);
//...
}
}

How to setup and use ThreeTenABP library

Okay sorry guys for waisting your time. I was adding the dependency in the wrong app-module. Now everything is working.

Android ThreeTenAbp get all weeks of the year to array

ThreeTenABP is just an Android-specific adaptation of the ThreeTen-Backport Java project.

ThreeTen-Backport is a back-port of the Java SE 8 date-time classes (java.time) to Java SE 6 and 7. You can find its documentation here.

Code from this answer:

private static long getNumberOfWeeksInYear(LocalDate date) {
LocalDate middleOfYear = date.withDayOfMonth(1).withMonth(6);
return middleOfYear.range(WeekFields.ISO.weekOfWeekBasedYear()).getMaximum();
}

And you can call it passing the following parameter:

LocalDate.of(year, 1, 1) // replace year by the the year you want, e.g. 2009

How to properly use ThreeTenABP to get the time in milliseconds between two dates based on UTC

There are several options depending on what you require from the difference you obtain.

It’s easiest to find the difference measured in some time unit. Use ChronoUnit.between. For example:

    ZonedDateTime zdt1 = getDate("2011-12-03T10:15:30+01:00[Europe/Paris]");
ZonedDateTime zdt2 = getDate("2017-11-23T23:43:45-05:00[America/New_York]");

long diffYears = ChronoUnit.YEARS.between(zdt1, zdt2);
System.out.println("Difference is " + diffYears + " years");

long diffMilliseconds = ChronoUnit.MILLIS.between(zdt1, zdt2);
System.out.println("Difference is " + diffMilliseconds + " ms");

This prints:

Difference is 5 years
Difference is 188594895000 ms

I am using your getDate method, so the format required is that of ZonedDateTime (modified from ISO 8601), for example 2011-12-03T10:15:30+01:00[Europe/Paris]. Seconds and fraction of second are optional, as is time zone ID in square brackets.

BTW you don’t need to convert to UTC before finding the difference. You will get the same result even if you leave out that conversion.

You may also get the difference in years, months and days. The Period class can give you this, but it cannot handle time of day, so convert to LocalDate first:

    Period diff = Period.between(zdt1.toLocalDate(), zdt2.toLocalDate());
System.out.println("Difference is " + diff);

Difference is P5Y11M21D

The output means a period of 5 years 11 months 21 days. The syntax may feel a little strange at first, but is straightforward. It is defined by the ISO 8601 standard. In this case the time zone matters since it is never the same date in all time zones.

To get the difference in hours, minutes and seconds use the Duration class (I am introducing a new time since using Duration for nearly 6 years would be too atypical (though possible)).

    ZonedDateTime zdt3 = getDate("2017-11-24T18:45:00+01:00[Europe/Copenhagen]");
Duration diff = Duration.between(zdt2, zdt3);
System.out.println("Difference is " + diff);

Difference is PT13H1M15S

A period of 13 hours 1 minute 15 seconds. The T that you already know from 2011-12-03T10:15:30+01:00[Europe/Paris] here too separates the date part from the time part so you know that in this case 1M means 1 minute, not 1 month.

Unable to compile ThreeTenABP

This is a problem with the build process, not being able to download the dependency.

This is most likely caused, because you haven't add the snapshots repositories to your project as the Readme file in the repository says.

You should be able to fix this issue by adding the following to your top level build.gradle file

buildscript {
repositories {
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
}
dependencies {
}
}

Edit:

The comment was right, and it wasn't a problem with the repository itself.
The problem is that org.threeten:threetenbp:1.3-SNAPSHOT doesn't exist in either repository. ( bintray or snapshots )

Edit #2:

Please take a look at this issue on the project

JakeWharton commented 11 hours ago You need the 1.3-SNAPSHOT of the
notzdb branch of the ThreeTenBP project.

Edit #3:

Actually, I just saw you are the one that created the issue :)
You will need to build it yourself as is not hosted on any repository:

$ git clone https://github.com/ThreeTen/threetenbp
$ cd ThreeTen/
$ git checkout no-tzdb
$ mvn clean install

Also, is worth mentioning that there are two separated projects for ThreeTen, being the last one the active where the branch is

https://github.com/ThreeTen/threeten

https://github.com/ThreeTen/threetenbp

Android ThreeTen ABP library not working in unit tests (ZoneRulesException)

This exception is caused because AndroidThreeTen.init has not been called with a context.
Local unit tests do not have an Application or a Context which are required for the Android Threeten ABP to load timezone information.

For unit tests, you can use the regular ThreeTen BP which is not designed for Android. It can load the timezone info without an Android context. None of your imports will need to change.

Add the additional threeten library to your gradle file, using the testImplementation command.

implementation "com.jakewharton.threetenabp:threetenabp:1.2.1"
testImplementation "org.threeten:threetenbp:1.4.0"

Note that the two version numbers do not coincide.

DateTimeParseException at index 0 with ThreeTenABP

This is a little bit tricky alright.

    DateTimeFormatter instantFormatter = DateTimeFormatter.ofPattern("uuuuMMdd'T'HHmmss.SSSX");
String s = "20200117T172638.000Z";
Instant instant = instantFormatter.parse(s, Instant.FROM);
System.out.println(instant);

Output from this snippet is

2020-01-17T17:26:38Z

Getting the seconds since the epoch out if the Instant works as in your question, so I am not repeating it.

Use a formatter that describes the precise format of the input string. Since there is no Instant.parse method that accepts a DateTimeFormatter as second argument, we need to do the parsing the other way around, using the (generic) DateTimeFormatter.parse(CharSequence, TemporalQuery<T>) method. And we need to pass the query Instant.FROM to that method because the backport is developed for Java versions that haven’t got method references. (Using the native java.time from Java 8 and later we would instead use the method reference Instant::from).

I know that your string is in ISO 8601 format. And I know that it is said that the java.time classes parse ISO 8601 format without any explicit formatter (I have myself written that many times on Stack Overflow already). It is not quite true: the classes of java.time parse the most common variants of ISO 8601 format without any explicit formatter. ISO 8601 comes with quite many freedoms of syntax, some that are always allowed and some that can optionally be agreed between parties exchanging ISO 8601 format. You have run into a variant that Instant.parse() doesn’t deal with, sorry.



Related Topics



Leave a reply



Submit