Skip to content
Advertisement

How to get days remaining of a date and print hours minutes and seconds Java8

I’m getting from server an UTC date for instance

"endValidityDate": "2021-11-18T22:59:59Z"

I’d like to know what is the optimal way to calculate remaining days from now.

Here’s what I got now :

I’m creating a date for 2 days from now as :

DateTime.now().plusSeconds(172800)

I’m parsing it to a DateTime joda I can use other if you say so.

When doing the diff of days I’m doing this way

val diff = endValidityDate.toDate().time - Date().time
val daysRemaining = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS)
return if (daysRemaining > 1) "$daysRemaining days}"
       else TimeUnit.DAYS.convert(diff, TimeUnit.SECONDS).toString()

The scenario that I’m trying to achieve is :

If the days remaining is more than one (24h) then print “2 days remaining” for instance, and instead of showing “1 day remaining” then just add a timer as :

“0h 43m 3s”.

To do the timer I’m just subtracting the time remaining with now

val expireDate = LocalDateTime.now()
                   .plusSeconds(uiState.endValidityDate.timeLeft.toLong())
                   .toEpochSecond(ZoneOffset.UTC)
val currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)

And then on every second it happen I print it like this :

val duration = Duration.ofSeconds(it)
binding.myTextView.text = String.format(
    "%02dh: %02dm: %02ds",
    duration.seconds / 3600,
    (duration.seconds % 3600) / 60,
    duration.seconds % 60,
)

But I’m not getting the 2 days, I’m just getting as an output :

00h: 33m: 50s

So, I’m having some problems here for instance :

Is this an optimal solution? If not could you describe a better one where I can achieve my goal? Why my timer is showing with 00h: 13m: 813s? Am I doing the regex incorrectly or it’s because of epochSeconds?

To achieve

Given an UTC date from server when trying to print it to the device then it should follow this rules.

1.- If days remaining is > than 1 day then print “N days remaining”

2.- If days remaining is <= 1 then print a timer (it’s already done, the thing is how to print it correctly).

  • Minimum 1 digit (0h 2m 1s)
  • Maximum 2 digits (1h 23m 3s)

Note :

I’m using Java 8 I can change also the way I’m doing the countdown to use millis instead of epochSeconds if that’s the problem.

Advertisement

Answer

You could do that using a ZonedDateTime for now and the future datetime, and then calculate a Duration.between instead of calculating remaining seconds first and then use a Duration.ofSeconds().

Here`s a Kotlin example:

fun main() {
    val utc = ZoneId.of("UTC")
    val now = ZonedDateTime.now(utc)
    val twoDaysFromNow = now.plusDays(2)
    
    val remaining = Duration.between(now, twoDaysFromNow)
    
    println(
        String.format("%02dh: %02dm: %02ds",
                        remaining.seconds / 3600,
                        (remaining.seconds % 3600) / 60,
                        remaining.seconds % 60
                     )
    )
}

Output: 48h: 00m: 00s


If you are interested in remaining full days only, then consider using ChronoUnit.DAYS.between, maybe like this:

fun main() {
    val utc = ZoneId.of("UTC")
    val now = ZonedDateTime.now(utc)
    val twoDaysFromNow = now.plusDays(2)
    
    val remainingDays = ChronoUnit.DAYS.between(now, twoDaysFromNow)
    
    println(
        String.format("%d days", remainingDays)
    )
}

Output: 2 days


Additional:

Since it is unclear to me what data type you are trying to use for the calculation of the time left until end of validity, you will have to choose between providing more detailed information in your question or using one of the following funs:

Pass a ZonedDateTime

private fun getRemainingTime(endValidityDate: ZonedDateTime): String {
    // get the current moment in time as a ZonedDateTime in UTC
    val now = ZonedDateTime.now(ZoneId.of("UTC"))
    // calculate the difference directly
    val timeLeft = Duration.between(now, endValidityDate)
    // return the messages depending on hours left
    return if (timeLeft.toHours() >= 24) "${timeLeft.toDays()} days"
    else String.format("%02dh: %02dm: %02ds",
                        timeLeft.toHours(),
                        timeLeft.toMinutes() % 60,
                        timeLeft.toSeconds() % 60)
}

Pass an Instant

private fun getRemainingTime(endValidityDate: Instant): String {
    // get the current moment in time, this time as an Instant directly
    val now = Instant.now()
    // calculate the difference
    val timeLeft = Duration.between(now, endValidityDate)
    // return the messages depending on hours left
    return if (timeLeft.toHours() >= 24) "${timeLeft.toDays()} days"
    else String.format("%02dh: %02dm: %02ds",
                        timeLeft.toHours(),
                        timeLeft.toMinutes() % 60,
                        timeLeft.toSeconds() % 60)
}

Pass the String directly

private fun getRemainingTime(endValidityDate: String): String {
    // get the current moment in time as a ZonedDateTime in UTC
    val now = ZonedDateTime.now(ZoneId.of("UTC"))
    // parse the endValidtyDate String
    val then = ZonedDateTime.parse(endValidityDate)
    // calculate the difference
    val timeLeft = Duration.between(now, then)
    // return the messages depending on hours left
    return if (timeLeft.toHours() >= 24) "${timeLeft.toDays()} days"
    else String.format("%02dh: %02dm: %02ds",
                        timeLeft.toHours(),
                        timeLeft.toMinutes() % 60,
                        timeLeft.toSeconds() % 60)
}
4 People found this is helpful
Advertisement