82

Is there a good way to format a Duration in something like hh:mm:ss, without having to deal with time zones?

I tried this:

DateTime durationDate = DateTime.fromMillisecondsSinceEpoch(0);
String duration = DateFormat('hh:mm:ss').format(durationDate);

But I always get 1 hour to much, in this case it would say 01:00:00

And When I do this:

Duration(milliseconds: 0).toString();

I get this: 0:00:00.000000

Jonas
  • 5,514
  • 6
  • 38
  • 85
  • 1
    FYI, your approach didn't work because despite your variable names, you didn't compute *durations*. `durationDate` is a `DateTime`; it's a point in time and therefore is subject to an adjustment from UTC into your local time zone. – jamesdlin May 27 '20 at 21:06

13 Answers13

175

You can use Duration and implement this method:

String _printDuration(Duration duration) {
  String twoDigits(int n) => n.toString().padLeft(2, "0");
  String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
  String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
  return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}

Usage:

final now = Duration(seconds: 30);
print("${_printDuration(now)}");
diegoveloper
  • 79,114
  • 19
  • 208
  • 182
  • Thanks thats some clean dart. Do you know what the problem with my 1st approach was? – Jonas Feb 19 '19 at 21:59
  • Just check the doc and you will understand : https://api.dartlang.org/stable/2.1.0/dart-core/DateTime/millisecondsSinceEpoch.html – diegoveloper Feb 19 '19 at 22:01
  • 3
    If you want to make it shorter: `String twoDigits(int n) => n >= 10 ? "$n" : "0$n";` – Michel Feinstein Mar 17 '20 at 02:52
  • 7
    Or just `n.toString().padLeft(2, "0")`. – Toni Cárdenas May 10 '20 at 17:00
  • @diegoveloper Thanks dear, I have improved it by adding a condition on hour, minute and second so that if file does not have hours then show only minutes and seconds part eg. 10:20 (minutes:seconds) instead of 00:10:20 (hour:minutes:seconds). Thanks again. – Kamlesh Mar 07 '21 at 10:15
  • 1
    getAudioDuration(Duration duration) { String twoDigits(int n) => n.toString().padLeft(2, "0"); String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); List output = []; if(duration.inHours > 0){ output.add(twoDigits(duration.inHours).toString()); } if(int.parse(twoDigitSeconds) > 0){ output.add(twoDigitMinutes); output.add(twoDigitSeconds); } return output.join(':'); } – Kamlesh Mar 07 '21 at 10:16
  • `_printDuration` might not be the best name since you are just returning a string rather than printing it. Perhaps _durationToString would be better. You could also add it as an extension by the way. – Gerard Mar 18 '21 at 16:03
48

You can start creating a format yourself, come on this one:

String sDuration = "${duration.inHours}:${duration.inMinutes.remainder(60)}:${(duration.inSeconds.remainder(60))}"; 
Uni
  • 4,441
  • 5
  • 22
  • 53
AlexPad
  • 9,284
  • 3
  • 31
  • 46
  • 2
    duration.inMinutes is the total number of minutes, for example if the duration is 2 hours the minutes will be 120 – user425678 Jul 27 '19 at 19:36
  • Hey, thanks. I didn't know it was over 60 minutes. I trust your answer. For this I found the remainder method inside the library and introduced it to limit it up to 60. – AlexPad Jul 29 '19 at 08:05
  • 14
    Works great but you lose the leading zeros, if you want to keep them just add `.toString().padLeft(2, '0')` for each concerned data (hours, minutes, seconds) – Yann39 Apr 19 '20 at 19:37
32

The shortest, most elegant and reliable way to get HH:mm:ss from a Duration is doing:

format(Duration d) => d.toString().split('.').first.padLeft(8, "0");

Example usage:

main() {
  final d1 = Duration(hours: 17, minutes: 3);
  final d2 = Duration(hours: 9, minutes: 2, seconds: 26);
  final d3 = Duration(milliseconds: 0);
  print(format(d1)); // 17:03:00
  print(format(d2)); // 09:02:26
  print(format(d3)); // 00:00:00
}
Frank Treacy
  • 2,876
  • 1
  • 14
  • 15
25

Just a quick implementation.

This will display the Duration in [DD]d:[HH]h:[mm]m:[ss]s format, and will ignore the leading element if it was 0. But seconds will always present.

For example:

1d:2h:3m:4s

2h:3m:4s

3m:4s

4s

0s

  /// Returns a formatted string for the given Duration [d] to be DD:HH:mm:ss
  /// and ignore if 0.
  static String formatDuration(Duration d) {
    var seconds = d.inSeconds;
    final days = seconds~/Duration.secondsPerDay;
    seconds -= days*Duration.secondsPerDay;
    final hours = seconds~/Duration.secondsPerHour;
    seconds -= hours*Duration.secondsPerHour;
    final minutes = seconds~/Duration.secondsPerMinute;
    seconds -= minutes*Duration.secondsPerMinute;

    final List<String> tokens = [];
    if (days != 0) {
      tokens.add('${days}d');
    }
    if (tokens.isNotEmpty || hours != 0){
      tokens.add('${hours}h');
    }
    if (tokens.isNotEmpty || minutes != 0) {
      tokens.add('${minutes}m');
    }
    tokens.add('${seconds}s');

    return tokens.join(':');
  }
Evan Chu
  • 2,110
  • 2
  • 19
  • 23
  • I really like this implementation. I modified it a little bit to display `[[[DD:]hh:]mm]:ss` with zeros, such as "1:02:03" by replacing the add as `tokens.add(addZeroIfFirst(hours, tokens.isEmpty))` and `String addZeroIfFirst(int value, bool isFirst) {return sprintf((isFirst) ? "%d": "%02d", [value]);}`. This uses `sprintf` from https://pub.dev/packages/sprintf. – rlat Apr 12 '20 at 13:07
14

Here's another version. It's all preference at this point, but I liked that it was dry and didn't need a function declaration (the wrapping function is obviously optional) though it is definately a bit function chaining heavy.

Compact

String formatTime(double time) {
    Duration duration = Duration(milliseconds: time.round());
    return [duration.inHours, duration.inMinutes, duration.inSeconds].map((seg) => seg.remainder(60).toString().padLeft(2, '0')).join(':');
}

Formatted version

String timeFormatter (double time) {
    Duration duration = Duration(milliseconds: time.round());

    return [duration.inHours, duration.inMinutes, duration.inSeconds]
      .map((seg) => seg.remainder(60).toString().padLeft(2, '0'))
      .join(':');
}
csga5000
  • 3,836
  • 4
  • 38
  • 52
12

Based on @diegoveloper's answer, I made it an extension which is also extendible

extension DurationExtensions on Duration {
  /// Converts the duration into a readable string
  /// 05:15
  String toHoursMinutes() {
    String twoDigitMinutes = _toTwoDigits(this.inMinutes.remainder(60));
    return "${_toTwoDigits(this.inHours)}:$twoDigitMinutes";
  }

  /// Converts the duration into a readable string
  /// 05:15:35
  String toHoursMinutesSeconds() {
    String twoDigitMinutes = _toTwoDigits(this.inMinutes.remainder(60));
    String twoDigitSeconds = _toTwoDigits(this.inSeconds.remainder(60));
    return "${_toTwoDigits(this.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
  }

  String _toTwoDigits(int n) {
    if (n >= 10) return "$n";
    return "0$n";
  }
}

DirtyNative
  • 2,261
  • 2
  • 23
  • 48
7

Elaborating on other answers, here is an implementation that also formats days:

extension DurationFormatter on Duration {
  /// Returns a day, hour, minute, second string representation of this `Duration`.
  ///
  ///
  /// Returns a string with days, hours, minutes, and seconds in the
  /// following format: `dd:HH:MM:SS`. For example,
  ///
  ///   var d = new Duration(days:19, hours:22, minutes:33);
  ///    d.dayHourMinuteSecondFormatted();  // "19:22:33:00"
  String dayHourMinuteSecondFormatted() {
    this.toString();
    return [
      this.inDays,
      this.inHours.remainder(24),
      this.inMinutes.remainder(60),
      this.inSeconds.remainder(60)
    ].map((seg) {
      return seg.toString().padLeft(2, '0');
    }).join(':');
  }
}

Unfortunately the intl package DateFormat class does not help: it marks the format of a Duration as not implemented:

formatDuration(DateTime reference) → String
NOT YET IMPLEMENTED. [...]
Michele Volpato
  • 565
  • 3
  • 13
7

Define this:

extension on Duration {
  String format() => '$this'.split('.')[0].padLeft(8, '0');
}

Usage:

String time = Duration(seconds: 3661).format(); // 01:01:01
CopsOnRoad
  • 175,842
  • 51
  • 533
  • 380
3
format(Duration d) => d.toString().split('.').first.padLeft(8, "0");

main() {
  final d1 = Duration(hours: 17, minutes: 3);
  final d2 = Duration(hours: 9, minutes: 2, seconds: 26);
  final d3 = Duration(milliseconds: 0);
  print(format(d1)); // 17:03:00
  print(format(d2)); // 09:02:26
  print(format(d3)); // 00:00:00
}
aboger
  • 1,976
  • 6
  • 33
  • 42
gowthaman C
  • 440
  • 5
  • 14
3

In my opinion the easiest way

String get refactoredDuration{
    return Duration(seconds: duration).toString().split('.')[0];
  }
F-Y
  • 300
  • 4
  • 12
1

You can use this:

print('${duration.inHours.toString().padLeft(2, '0')}:
${duration.inMinutes.remainder(60).toString().padLeft(2, '0')}:
${duration.inSeconds.remainder(60).toString().padLeft(2, '0')}');
Joey
  • 1,377
  • 2
  • 18
  • 30
Kijju
  • 76
  • 1
  • 4
0

I prefer thinking of Millisecond as its own unit, rather than as a subunit of something else. In that sense, it will have values of 0-999, so you're going to want to Pad three instead of two like I have seen with other answers. Here is an implementation:

String format(Duration o) {
   var mil_s = (o.inMilliseconds % 1000).toString().padLeft(3, '0');
   var sec_s = (o.inSeconds % 60).toString().padLeft(2, '0');
   return o.inMinutes.toString() + ' m ' + sec_s + ' s ' + mil_s + ' ms';
}

https://api.dart.dev/dart-core/Duration-class.html

Nimantha
  • 5,793
  • 5
  • 23
  • 56
Zombo
  • 1
  • 55
  • 342
  • 375
0

You can use this:

 Text(RegExp(r'((^0*[1-9]\d*:)?\d{2}:\d{2})\.\d+$')
      .firstMatch("$duration") ?.group(1) ?? '$duration'),
Isa Sabbagh
  • 23
  • 1
  • 5