Dealing with Date objects in Android — Part 2 — History of Date in Java
Working with dates in software development is a notoriously complex task. Different programming languages have attempted to solve this issue in their own ways, and Java is no exception. Over the years, Java has gone through multiple iterations of date and time APIs, each with its own set of strengths and weaknesses.
In this second part, we will explore the evolution of date and time APIs in Java and Kotlin and how it has been used in the Android World.
java.util
📜 History
Java developers first got their hands on java.util.Date
in JDK 1.0, which was released way back in 1996. At the time, the API was a welcome addition to the Java ecosystem, as it allowed developers to work with dates and times in a platform-independent way.
However, it soon became apparent that the design of java.util.Date
was deeply flawed.
For example, it did not account for time zones. But one of the biggest issues with java.util.Date
was its mutability. The class had a set of setters that allowed users to modify its internal state, making it difficult to reason about the value of a Date object at any given time. Additionally, the API was not thread-safe, which made it challenging to use in concurrent environments.
import java.util.Date
fun main() {
val date = Date().apply {
year = 2023 // Mutable
month = 4 // Mutable
day = 27 // Actually this one is not mutable, so error here 🤷
}
print(date)
}
// Tue May 29 16:56:04 UTC 3923 😄 Yeah, it is 1900 + incr (2023) so 3923
To address these issues, Java 1.1 introduced the java.util.Calendar
class, which provided more advanced functionality for working with dates and times. However, the Calendar class was also complex and challenging to use.
🌍 In the Android World
For years, it was not possible to use Java 1.8 so Android had to stick to java.util.Date
and java.util.Calendar
. They have been around since Android API Level 1.
Example of the Date picker component in Android using Calendar.
val calendar = Calendar.getInstance()
val currentYear = calendar.get(Calendar.YEAR)
val currentMonth = calendar.get(Calendar.MONTH)
val currentDay = calendar.get(Calendar.DAY_OF_MONTH)
val datePickerDialog = DatePickerDialog(this, { _, year, month, day ->
val selectedDate = Calendar.getInstance()
selectedDate.set(year, month, day)
val selectedDateObj = selectedDate.time // Convert the selected date to a Date object
handleSelectedDate(selectedDateObj)
},
currentYear,
currentMonth,
currentDay
)
datePickerDialog.show()
Joda-Time
📜 History
In 2002, Joda-Time was released as an open-source alternative to java.util.Date
.
Joda-Time was designed with domain-driven development principles in mind, and it aimed to provide a more intuitive and developer-friendly API for working with dates and times.
One of the most significant advantages of Joda-Time was its immutability. Once a date or time object was created, it could not be modified. This made it much easier to reason about the value of a Joda-Time object at any given time.
Additionally, Joda-Time provided a wide range of helper methods for common date and time operations, such as parsing and formatting dates, calculating time intervals, and adjusting time zones.
Joda-Time introduced a new immutable DateTime class, which provided a rich set of features for working with dates and times, including support for time zones.
🌍 In the Android World
How to not mention this article from Dan Lew to better understand the path taken in Android development regarding dealing with Date objects.
I’ve recently gotten fed up with how nuts the built-in calendar library is for Java/Android. It’s incredibly easy to make mistakes and it’s unintuitive at best, so I’ve finally decided to take the plunge and switch to Joda time. Dan Lew in https://blog.danlew.net/2013/08/20/joda_time_s_memory_issue_in_android/
The rest of the article explains the main issue we have when updating the timezone databases within the .jar file.
Basically, developers need to figure out the timezone rules to apply so they chose to use a third-party library like Joda and make sure the TZ.data within it is always up-to-date. A good solution to ensure your app perfectly behaves is if your users keep the app to your latest release.
ThreeTen
📜 History
In 2007, JSR-310 was introduced as a proposed standard for a new date and time API in Java. JSR-310 was heavily influenced by Joda-Time and aimed to address the shortcomings of java.util.Date
.
However, due to delays in the release of Java 8, the new date and time API was not included until Java 8 was released in 2014. To bridge the gap between Java 7 and Java 8, the Threeten project was created. ThreetenBP is a backport of the Java 8 date and time API that can be used in Java 6 and 7.
🌍 In the Android World
I see Threeten ABP has an upgrade of Joda-Android. We benefit from the JSR-310 plus our timezones rules are up-to-date. If you have read the article from Dan Lew above, let’s have a look at how TZ data is getting updated in ThreetenABP.
It’s within update.sh script 🤩
- Download the latest release jar from ThreeTen
- Update the TZ data from the last release
# Download the latest release jar
wget --trust-server-names "https://search.maven.org/remote_content?g=org.threeten&a=threetenbp&v=LATEST"
…
# Replace embedded TZDB with new one.
rm ../threetenabp/src/main/assets/org/threeten/bp/TZDB.dat
mv org/threeten/bp/TZDB.dat ../threetenabp/src/main/assets/org/threeten/bp/
Similarly to Joda, in AndroidThreeTen.java, we update the timezone rules:
public static void init(Context context, String assetPath) {
if (!initialized.getAndSet(true)) {
ZoneRulesInitializer.setInitializer(new AssetsZoneRulesInitializer(context, assetPath));
}
}
private AndroidThreeTen() {
throw new AssertionError();
}
java.time
📜 History
java.time
is the latest date-handling library in Java. It was introduced in Java 8 and is based on the lessons learned from the previous libraries. java.time
offers an intuitive API, immutability, and better timezone handling. It also includes support for the ISO calendar, which is widely used in business applications.
🌍 In the Android World
If you’re building your app using the Android Gradle plugin 4.0.0 or higher, the plugin extends support for using a number of Java 8 language APIs without requiring a minimum API level for your app. A subset of java.time
is supported.
android {
defaultConfig {
// Required when setting minSdkVersion to 20 or lower
multiDexEnabled = true
}
compileOptions {
// Flag to enable support for the new language APIs
// For AGP 4.1+
isCoreLibraryDesugaringEnabled = true
// For AGP 4.0
// 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.2.2")
}
⚠️ To better understand how to use Java 8 language features and APIs, refer to the official documentation.
You may have one question here: how about timezone rules? This is a great question and this topic will be covered in the last article of the series 😉
kotlinx-datetime
📜 History
kotlinx-datetime
is the Kotlin-specific date-handling library. It provides a Multiplatform API that can be used on JVM, Android, and native platforms.
On the Kotlin/JVM-side, kotlinx-datetime
is based on the java.time
library and offers a similar API with additional features specific to Kotlin.
🌍 In the Android World
- It’s in the alpha stage, and the development continues.
- It is ready and fully supports Kotlin Multiplatform
- @Serializable
- Simpler API powered by Kotlin extension functions.
// Support ISO-8601 format with extension
"2010-06-01T22:19:44.475Z".toInstant()
"2010-06-01T22:19:44".toLocalDateTime()
"2010-06-01".toLocalDate()
"12:01:03".toLocalTime()
"12:0:03.999".toLocalTime()