1

The following:

java.time.Duration.ofDays(30).toString

produces this output:

PT720H

Is there a simple way to pretty print this, for example, as "30 days".

Likewise, for something like "P2DT3H4M", I'd like to output this as "2 days, 3 hours and 4 minutes".

I'm aware of the answer here "pretty print" duration in java, but not sure if I can use/convert Java's Duration to Joda's Duration to be able to use this?

Rory
  • 798
  • 2
  • 12
  • 37

2 Answers2

1

I ended up using this (extended from this answer https://stackoverflow.com/a/49628638/5300930).

I'm sure that there should be a simpler way to do this using the Java/Scala libraries? Note, my input will always be a java.time.Duration, I can use the Joda libraries, but they must be able to start with a java.time.Duration as input.

  /** Encodes a [[java.time.Duration]] into a more readable format.
    *
    * @example
    * encodeDuration(Duration.ofDays(30).plusHours(3))  // returns "30 days, 3 hours"
    * encodeDuration(Duration.ofDays(1))                // returns "1 day"
    * encodeDuration(Duration.parse("PT720H"))          // returns "30 days"
    * encodeDuration(Duration.parse("PT12000H10S"))     // returns "1 year, 4 months, 15 days, 10 seconds"
    */
implicit val encodeDuration: Encoder[java.time.Duration] = (duration: Duration) => {
    val oneMinute = 60
    val twoMinutes = oneMinute * 2
    val oneHour = oneMinute * 60
    val twoHours = oneHour * 2
    val oneDay = oneHour * 24
    val twoDays = oneDay * 2
    val oneMonth = oneDay * 30
    val twoMonths = oneMonth * 2
    val oneYear = oneDay * 365
    val twoYears = oneYear * 2

    // scalastyle:off cyclomatic.complexity
    def encodeDuration(result: List[String], seconds: Long): List[String] = {

      seconds match {
        case seconds if seconds <= 0                                 =>
          List.empty[String]
        case seconds if seconds == 1                                 =>
          result ::: List(s"${seconds} second")
        case seconds if seconds < oneMinute                          =>
          result ::: List(s"${seconds} seconds")
        case seconds if seconds >= oneMinute && seconds < twoMinutes =>
          List(s"${seconds / oneMinute} minute") ::: encodeDuration(result, seconds % oneMinute)
        case seconds if seconds >= oneMinute && seconds < oneHour =>
          List(s"${seconds / oneMinute} minutes") ::: encodeDuration(result, seconds % oneMinute)
        case seconds if seconds >= oneHour && seconds < twoHours     =>
          List(s"${seconds / oneHour} hour") ::: encodeDuration(result, seconds % oneHour)
        case seconds if seconds >= twoHours && seconds < oneDay      =>
          List(s"${seconds / oneHour} hours") ::: encodeDuration(result, seconds % oneHour)
        case seconds if seconds >= oneDay && seconds < twoDays       =>
          List(s"${seconds / oneDay} day") ::: encodeDuration(result, seconds % oneDay)
        case seconds if seconds >= twoDays && seconds < oneMonth     =>
          List(s"${seconds / oneDay} days") ::: encodeDuration(result, seconds % oneDay)
        case seconds if seconds >= oneMonth && seconds < twoMonths   =>
          List(s"${seconds / oneMonth} month") ::: encodeDuration(result, seconds % oneMonth)
        case seconds if seconds >= twoMonths && seconds < oneYear    =>
          List(s"${seconds / oneMonth} months") ::: encodeDuration(result, seconds % oneMonth)
        case seconds if seconds >= oneYear && seconds < twoYears     =>
          List(s"${seconds / oneYear} year") ::: encodeDuration(result, seconds % oneYear)
        case seconds if seconds >= twoYears                          =>
          List(s"${seconds / oneYear} years") ::: encodeDuration(result, seconds % oneYear)
      }
    }
    // scalastyle:on cyclomatic.complexity

    Encoder.encodeString(encodeDuration(List.empty[String], duration.getSeconds).mkString(", "))
  }
Rory
  • 798
  • 2
  • 12
  • 37
0

You can do java.time.Duration.ofDays(30).toDays() to receive the amount of days in the Duration, or java.time.Duration.ofDays(30).getSeconds() for seconds.

If you'd like to format it differently, use String.format(), so something like this:

        Duration dur = Duration.parse("P2DT3H4M5.6S");
        Long s = dur.getSeconds();
        String formatted = String.format("%d days, %02d hours, %02d minutes", s / 3600, (s % 3600) / 60, (s % 60));

Here's the doc: https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html

zbys
  • 453
  • 2
  • 8
  • I added a way to do it in my answer above – zbys Aug 02 '18 at 13:47
  • Thanks, but this doesn't print years or months, also will print zeros if for example the duration has no days - e.g. 0 days (see examples in my answer for desired output) – Rory Aug 02 '18 at 14:55
  • You can just modify the format to include years and months, I just didn't put it in my example – zbys Aug 02 '18 at 14:57