It's about time

Taking a look through time, a new Kotlin multi-platform library

It's about time
People like us, who believe in physics, know that the distinction between past, present, and future is only a stubbornly persistent illusion.

-- Albert Einstein

Time representation is a necessity for nearly all applications. The ability to display, reference, track, and manipulate moments of time plays an important role in the development of applications. But when developing a Kotlin multi-platform application, this becomes a difficult task, as there is no notion of a moment of time in the standard library. Waiting for one to be added would be a waste of time, so I decided to create one. This article takes an early look at this new library that is still in development.

TL;DR

Introducing a new Kotlin multi-platform time library.

The library

Kotlin does provide a new experimental duration class which serves as a period of time, but no notion of a moment, or instant, of time. There is already a promising Kotlin multi-platform library called Klock, but this doesn't make use of the new experimental Duration class. So I created a library that expands on the Duration class by provided a Moment interface. These Moments are obtained by a TimeProvider. Getting this correct was pretty difficult and took some time, but, while the library is still in the early stages of development, I am pretty satisfied with the API and it's ease of use. Let's have a look.

TimeProvider

The TimeProvider interface is the entry point to doing anything time related, such as, obtaining a specific Moment in time.

val now = timeProvider.now()

val later = now + 20.minutes

val tomorrow = timeProvider.tomorrow()

val diffDuration = later to tomorrow

Any Moment in time can be obtained by using the TimeProvider.moment() function and providing the Duration since the epoch as a parameter. Moments in time for a specific Time Zone can be retrieved by passing in a TimeZoneRegionId. Without passing in a TimeZoneRegionId, the default is used, by accessing the TimeZoneOffsetProvider.defaultTimeZoneRegionId property that is available to the TimeProvider.

val moment = timeProvider.moment(
        durationSinceEpoch = 20_000.hours,
        regionId = TimeZoneRegionId("America/New_York"))

Each platform target is responsible for providing their own implementation of the TimeProvider interface. For instance, the jvmMain source set provides a JavaTimeProvider implementation. This implementation delegates to the java.time classes. Then each platform can provide their own platform specific implementations and the Kotlin common code can just reference the interfaces, such as, TimeProvider.

@Provides
fun provideTimeProvider(): TimeProvider = JavaTimeProvider(locale = Locale.getDefault())

Moment

The Moment interface represents an instant of time, as a duration since the epoch, with a TimeOffset from UTC for a specified TimeZoneRegionId and it's corresponding rules (daylight savings time, etc). A Moment can also represent UTC/GMT time which has an offset equal to zero, and which can be retrieved for any Moment using the Moment.toUtc() function.

Kotlin Durations can be added or subtracted from a Moment, and a Duration between two Moments can be retrieved using the to infix function. Also, CalendarDate and TimeOfDay objects can be retrieved from the Moment instance.

val yesterday = timeProvider.yesterday()

val later = timeProvider.now() + 3.hours

val duration = yesterday to later

val date = later.calendarDate
val time = later.timeOfDay

CalendarDate

The CalendarDate class represents the date of a Moment in a calendar year with no information about the time within the day.

val date = moment.calendarDate

date.year
date.month
date.dayInYear
date.dayInMonth
date.dayOfWeek

TimeOfDay

The TimeOfDay class is similar to CalendarDate but instead of providing information about the date within a calendar year, it provides information about the time within a calendar date.

val time = moment.timeOfDay

time.elapsedDurationInDay
time.minuteInHour
time.secondInMinute
time.millisecondInSecond
time.hourInDay(ClockConvention.TWENTY_FOUR_HOUR_CLOCK)

Parsing and Formatting

The library comes with two interfaces, MomentFormatter and MomentParser, which provide a way to transform a Moment into a String for a particular pattern and vice-versa. These can be obtained from the TimeProvider interface.

val formatter = timeProvider.formatter(MomentFormatPattern("MM/dd/yyyy"))

val string = formatter.format(timeProvider.now())

val parser = timeProvider.parser(MomentFormatPattern("MM/dd/yyyy"))

val moment = parser.parse("2/12/2020")

JSON

One idea of the library is to have JSON serializers, for popular JSON parsing libaries, that handle common time formats. This way it is simple to pass Moments between components. These aren't implemented yet, as of writing this article, but I imagine that they would reside in their own modules per library supported.

Why not mimic java.time?

There are some similarities between the two libraries and java.time has provided some inspiration in the development of this library. But there seems to be a common source of confusion for the components within the java.time library, such as, OffsetDateTime, ZonedDateTime, LocalDateTime, and Instant. I wanted to avoid this confusion for a casual user of the library and wanted to provide a more Kotlin-esque API. For this reason, the Moment interface was created, which acts similar to a combination of the OffsetDateTime, ZonedDateTime, and Instant classes. The CalendarDateAndTime class corresponds to Java Time's LocalDateTime class. And a new interface was created, TimeProvider, that acts as a single source for everything time related.

Recapitulate

This new library, still in the early stages of development, provides a promising vision of working with time within Kotlin multi-platform code. Still, implementations need to be finalized, tests need to be written, and other platforms need to be targeted. And since the library uses the experimental Duration class, the library is experimental as well. The implementation of time related code isn't as trivial as one might expect, but, overall, the future of this Kotlin time library looks bright, however, only time will tell.