Date manipulation in Android without a library


IMPORTANT: The newer Java 8 Time API (that is available in all Android versions via desugaring) is a better choice than what is described in this post. More details.


I think that manipulating dates in an Android app is a quite common use case. If your app has anything to do with date/time, at some point you will need to do calculations with dates. For example, you might need to calculate the dates that define "last week" or "this week".

Now, there are some quite powerful libraries out there that can handle and manipulate dates and times quite well (e.g. Joda-Time). But what if you want to make some basic date/time manipulation without using a library?

Calendar

Android got you covered. The good news is that there's a class called Calendar, that is available since API level 1 (not every class methods is, but most of them are). No compat libraries, no if  API statements, built-in the OS since the beginning. The bad news is that the API is kind of ugly but Kotlin makes it bearable.

Initialization

You can initialize a Calendar instance at a specific point in time using 2 ways.

val calendar = Calendar.getInstance().apply { // [1]
    add(Calendar.DAY_OF_MONTH, 16)
    add(Calendar.MONTH, 6) // [2]
    add(Calendar.YEAR, 2020)
    set(Calendar.HOUR_OF_DAY, 21)
    set(Calendar.MINUTE, 48)
    set(Calendar.SECOND, 47)
    set(Calendar.MILLISECOND, 0)
}
1. Set each time filed separately
val calendar = Calendar.getInstance().apply { 
    time = 1594936127000 // [3] Thursday, July 16, 2020 9:48:47 PM
}
2. By setting the epoch milliseconds
  1. apply is a scoped function used to manipulate the Calendar instance  returned by the getInstance() method.
  2. The month is zero-index. So January = 0 .... December = 11.
  3. Just in case, the definition of "epoch":
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970.

Check epochconverter for some useful tools when working with this kind of timestamp.

Manipulating date and time

Once you get an instance of Calendar pointing to a specific point in time, it's easy to manipulate date and time. Note that if we do not explicitly set a time in the Calendar instance, like shown before, then the current time is set by default.

Start of a time period

You can selectively change the time fields you need. For instance, to get the first day of the current month:

val startOfMonth = Calendar.getInstance().apply {
    set(Calendar.DAY_OF_MONTH, 1)
    set(Calendar.HOUR_OF_DAY, 0)
    set(Calendar.MINUTE, 0)
    set(Calendar.SECOND, 0)
}

You can do the same for getting the beginning of the current week:

val startOfWeek = Calendar.getInstance().apply {
    firstDayOfWeek = Calendar.MONDAY
    set(Calendar.DAY_OF_WEEK, it.firstDayOfWeek)
    set(Calendar.HOUR_OF_DAY, 0)
    set(Calendar.MINUTE, 0)
    set(Calendar.SECOND, 0)
    set(Calendar.MILLISECOND, 0)
}

Here we are defining the first day of the week to be Monday. Then we are setting the current time of the calendar to the last Monday.

Notice that we are using the field setters to set the time to the beginning of the day. This is because the time was set to the current time when the getInstance() was called. So, except if you call this on midnight, you would want to take care of hours, minutes, etc.

Add/remove time periods

Similarly to setting specific time fields, you can add or subtract time periods after getting a Calendar instance. For instance, for getting the beginning of the previous month:

val startOfPreviousMonth = Calendar.getInstance().apply {
    time = startOfMonth.time
    add(Calendar.MONTH, -1)
}

Here we are initially setting the time at the beginning of this month (from the last section). Then we are subtracting a month. You can do this for days, months, hours, etc.

Converting to other date/time representations

At any time you can convert the Calendar instance to Date or epoch milliseconds by calling the time or timeInMillis properties respectively.

startOfPreviousMonth.time // for getting a Date()
startOfPreviousMonth.timeInMillis // for getting a Long (epoch milliseconds)

Happy date and time manipulations :)