SECRET OF CSS

Date Formatting With Kotlin Multiplatform | by Gorm Sorensen | Jun, 2022


Write a simple multiplatform date formatter using 1) native libraries or 2) kotlinx.datetime

Clock and calendar
Photo by Towfiqu barbhuiya on Unsplash

Formatting dates often feels borderline tiresome to implement. Personally, every time I have to convert a date to a certain format, I think to myself — now again? Didn’t we do this before? There should be a way of writing this sort of code once and not having to bother with it again.

A simple example of a date formatting requirement could be that we must display the text 18.06.2022 if (you guessed it) the current date is June 18th, 2022. Should be easy enough to do on multiple platforms, right? Sure. But you have to write the “same” formatting logic twice if you write code for Android and iOS, for example, in two separate code bases.

Luckily for us, we can utilize Kotlin Multiplatform Mobile (KMM) which enables us to write the same code once and use it across multiple platforms.

I won’t go into detail about KMM in this post. Let’s go straight to the point and dive into the code instead.

In this post I will present two alternatives:

  1. A generic formatter written purely in Kotlin, where we will add code to commonMain , androidMain , and iosMain , thereby introducing platform-specific logic;
  2. Using a multiplatform library called kotlinx-datetime (check the repo here), where we will keep all of our code in commonMain .

For brevity, I will only focus on supporting timestamps in ISO 8601 format, as well as only supporting one date format, namely dd.MM.yyyy .

Let’s define this simple class which we put in commonMain:

(Yes, DateTime isn’t a perfect name for such a class. We could name it Iso8601TimestampFormatter or similarly, but I chose to name it differently for brevity.)

It’s quite basic. We pass in an ISO 8601 timestamp as well as a format (dd.MM.yyyy), and it should return the formatted date.

So how does it look on Android?

We will utilize the java.time APIs, although it requires our Android minimum SDK to be 26 (you can use ThreeTen Backport or desugaring if you want to use these APIs on lower SDK versions).

The following should be placed in androidMain :

We are parsing our timestamp using the ZonedDateTime API, and subsequently formatting it with our format via DateTimeFormatter .

Then how about iOS?

Instead of using Java libraries, which aren’t accessible on iOS, we will instead refer to the Foundation framework. It contains all the things we need for date formatting (and more, of course).

Let’s put the following in iosMain :

We create an instance of NSDateFormatter and set the time zone, locale, and format. Finally, we invoke stringFromDate() to get the formatted string.

You may ask — why not use the Swift APIs like DateFormatter , TimeZone , etc.? The TL;DR is that we are unable to import any pure Swift modules from Kotlin code in the current state of KMM. Importing Objective-C APIs (NS…), however, works just fine. See this link for more info.

Let’s test it! ✅

As our tests are platform-independent, it is sufficient to place them in commonTest only:

When we ask Android Studio to run the tests, we are asked to use a specific target (depending on how our project is configured):

Screenshot of Android Studio when running all tests in DateTimeTests
Screenshot of Android Studio when running all tests in DateTimeTests

Normally you’d choose between :testDebugUnitTest and iosX64 . Running either, we can see that the tests pass:

Test results (all passed)
Test results

If you don’t want to run the tests via Android Studio’s popup (or having to waste precious time selecting different options in a popup, for that matter), you can simply run the command $ ./gradlew :shared:allTests which will execute all tests in the project across all possible targets.

Alright, cool, but I don’t really want to write platform-dependent code if it can be avoided

Let’s see if we can avoid that, then! The good people over at Kotlin/JetBrains created the kotlinx-datetime library, which we’ll look at next.

I recommend reading the documentation on the GitHub page for this library. As for our work, let’s get right down to it by adding the dependency to our commonMain source set:

Now let’s use Instant and LocalDateTime to find the day, month, and year from the timestamp, and finally return the formatted date as a string:

The zeroPrefixed() extension function is one I created to circumvent the fact that there’s no non-JVM String.format() equivalent in Kotlin’s standard library at this moment, which would otherwise enable us to easily format integers as strings with prefixed zeros. You can find the code for zeroPrefixed() here.

Write the tests (why wouldn’t you?)

We looked at two approaches to writing date formatting logic that works on both Android and iOS.

Requiring platform-specific logic, the first approach enables us to use date time patterns/formats directly. However, we are forced to write code that caters to each individual platform.

Evidently, the approach needing less code is the latter. It relies on a third-party library that doesn’t at this moment support formatting various date time patterns/formats out of the box, which is why we have to write that part ourselves. On the bright side, it’s a multiplatform library, and we don’t have to worry about any platform-specific APIs.

Hope you have fun coding in Kotlin for multiple platforms!



News Credit

%d bloggers like this: