How to format a duration in java? (e.g format H:MM:SS)

前端 未结 19 1485
情话喂你
情话喂你 2020-11-22 03:32

I\'d like to format a duration in seconds using a pattern like H:MM:SS. The current utilities in java are designed to format a time but not a duration.

相关标签:
19条回答
  • 2020-11-22 04:09

    There's a fairly simple and (IMO) elegant approach, at least for durations of less than 24 hours:

    DateTimeFormatter.ISO_LOCAL_TIME.format(value.addTo(LocalTime.of(0, 0)))

    Formatters need a temporal object to format, so you can create one by adding the duration to a LocalTime of 00:00 (i.e. midnight). This will give you a LocalTime representing the duration from midnight to that time, which is then easy to format in standard HH:mm:ss notation. This has the advantage of not needing an external library, and uses the java.time library to do the calculation, rather than manually calculating the hours, minutes and seconds.

    0 讨论(0)
  • 2020-11-22 04:10

    This might be kind of hacky, but it is a good solution if one is bent on accomplishing this using Java 8's java.time:

    import java.time.Duration;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeFormatterBuilder;
    import java.time.temporal.ChronoField;
    import java.time.temporal.Temporal;
    import java.time.temporal.TemporalAccessor;
    import java.time.temporal.TemporalField;
    import java.time.temporal.UnsupportedTemporalTypeException;
    
    public class TemporalDuration implements TemporalAccessor {
        private static final Temporal BASE_TEMPORAL = LocalDateTime.of(0, 1, 1, 0, 0);
    
        private final Duration duration;
        private final Temporal temporal;
    
        public TemporalDuration(Duration duration) {
            this.duration = duration;
            this.temporal = duration.addTo(BASE_TEMPORAL);
        }
    
        @Override
        public boolean isSupported(TemporalField field) {
            if(!temporal.isSupported(field)) return false;
            long value = temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
            return value!=0L;
        }
    
        @Override
        public long getLong(TemporalField field) {
            if(!isSupported(field)) throw new UnsupportedTemporalTypeException(new StringBuilder().append(field.toString()).toString());
            return temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
        }
    
        public Duration getDuration() {
            return duration;
        }
    
        @Override
        public String toString() {
            return dtf.format(this);
        }
    
        private static final DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                .optionalStart()//second
                .optionalStart()//minute
                .optionalStart()//hour
                .optionalStart()//day
                .optionalStart()//month
                .optionalStart()//year
                .appendValue(ChronoField.YEAR).appendLiteral(" Years ").optionalEnd()
                .appendValue(ChronoField.MONTH_OF_YEAR).appendLiteral(" Months ").optionalEnd()
                .appendValue(ChronoField.DAY_OF_MONTH).appendLiteral(" Days ").optionalEnd()
                .appendValue(ChronoField.HOUR_OF_DAY).appendLiteral(" Hours ").optionalEnd()
                .appendValue(ChronoField.MINUTE_OF_HOUR).appendLiteral(" Minutes ").optionalEnd()
                .appendValue(ChronoField.SECOND_OF_MINUTE).appendLiteral(" Seconds").optionalEnd()
                .toFormatter();
    
    }
    
    0 讨论(0)
  • 2020-11-22 04:11

    How about the following function, which returns either +H:MM:SS or +H:MM:SS.sss

    public static String formatInterval(final long interval, boolean millisecs )
    {
        final long hr = TimeUnit.MILLISECONDS.toHours(interval);
        final long min = TimeUnit.MILLISECONDS.toMinutes(interval) %60;
        final long sec = TimeUnit.MILLISECONDS.toSeconds(interval) %60;
        final long ms = TimeUnit.MILLISECONDS.toMillis(interval) %1000;
        if( millisecs ) {
            return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
        } else {
            return String.format("%02d:%02d:%02d", hr, min, sec );
        }
    }
    
    0 讨论(0)
  • 2020-11-22 04:11
    String duration(Temporal from, Temporal to) {
        final StringBuilder builder = new StringBuilder();
        for (ChronoUnit unit : new ChronoUnit[]{YEARS, MONTHS, WEEKS, DAYS, HOURS, MINUTES, SECONDS}) {
            long amount = unit.between(from, to);
            if (amount == 0) {
                continue;
            }
            builder.append(' ')
                    .append(amount)
                    .append(' ')
                    .append(unit.name().toLowerCase());
            from = from.plus(amount, unit);
        }
        return builder.toString().trim();
    }
    
    0 讨论(0)
  • 2020-11-22 04:13

    in scala, no library needed:

    def prettyDuration(str:List[String],seconds:Long):List[String]={
      seconds match {
        case t if t < 60 => str:::List(s"${t} seconds")
        case t if (t >= 60 && t< 3600 ) => List(s"${t / 60} minutes"):::prettyDuration(str, t%60)
        case t if (t >= 3600 && t< 3600*24 ) => List(s"${t / 3600} hours"):::prettyDuration(str, t%3600)
        case t if (t>= 3600*24 ) => List(s"${t / (3600*24)} days"):::prettyDuration(str, t%(3600*24))
      }
    }
    val dur = prettyDuration(List.empty[String], 12345).mkString("")
    
    0 讨论(0)
  • 2020-11-22 04:15

    In scala (I saw some other attempts, and wasn't impressed):

    def formatDuration(duration: Duration): String = {
      import duration._ // get access to all the members ;)
      f"$toDaysPart $toHoursPart%02d:$toMinutesPart%02d:$toSecondsPart%02d:$toMillisPart%03d"
    }
    

    Looks horrible yes? Well that's why we use IDEs to write this stuff so that the method calls ($toHoursPart etc) are a different color.

    The f"..." is a printf/String.format style string interpolator (which is what allows the $ code injection to work) Given an output of 1 14:06:32.583, the f interpolated string would be equivalent to String.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)

    0 讨论(0)
提交回复
热议问题