110

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
  • 7,089
  • 15
  • 49
  • 110
  • 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

17 Answers17

233

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
  • 93,875
  • 20
  • 236
  • 194
  • 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
  • 4
    If you want to make it shorter: `String twoDigits(int n) => n >= 10 ? "$n" : "0$n";` – Michel Feinstein Mar 17 '20 at 02:52
  • 8
    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
62

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

String sDuration = "${duration.inHours}:${duration.inMinutes.remainder(60)}:${(duration.inSeconds.remainder(60))}"; 
CoderUni
  • 5,474
  • 7
  • 26
  • 58
AlexPad
  • 10,364
  • 3
  • 38
  • 48
  • 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
  • 18
    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
44

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
  • 3,386
  • 1
  • 16
  • 16
  • will this handle correctly daylight-saving switching? there are two such days every year – ccpizza Apr 18 '23 at 17:17
  • @ccpizza Not sure, if you're getting something wrong here. This is a duration, a duration does not care about daylight-saving time. If you start doing something at 1 am and at 2 am the clock jumps from 2 to 3 am, you still had one-hour duration of work. – Manuel Rauber May 25 '23 at 08:03
33

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,190
  • 2
  • 20
  • 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
19

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,553
  • 2
  • 33
  • 58
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
  • 4,062
  • 4
  • 39
  • 52
  • 1
    Applying the remainder(60) to hours is problematic, since the Duration can be more than 24 hours. So after 60 hours, your display wraps back to 0 hours. – Ber Nov 09 '22 at 08:10
  • @ber Arguably at that point you might be wanting some other form of display, since we don't typically display times like 291:30:15. But it is quirky behavior. – csga5000 Jun 03 '23 at 03:40
8

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
  • 700
  • 6
  • 14
8

Define this:

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

Usage:

String time = Duration(seconds: 3661).format(); // 01:01:01
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
3

In my opinion the easiest way

String get refactoredDuration{
    return Duration(seconds: duration).toString().split('.')[0];
  }
F-Y
  • 399
  • 5
  • 14
2

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,436
  • 2
  • 19
  • 33
Kijju
  • 96
  • 1
  • 4
1

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
  • 6,405
  • 6
  • 28
  • 69
Zombo
  • 1
  • 62
  • 391
  • 407
1
    extension 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() {
        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(':');
      }
    }



//use 
String result = Duration(seconds:102549).dayHourMinuteSecondFormatted();

//output: 
01:12:06:59----date:hour:minit:second
0

You can use this:

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

Modified the first so when hours are in 00 it will not show.

   extension VideoTimer on Duration {
  String format() {
    String twoDigits(int n) => n.toString().padLeft(2, '0');
    final String twoDigitMinutes = twoDigits(inMinutes.remainder(60));
    final String twoDigitSeconds = twoDigits(inSeconds.remainder(60));
    final hour = twoDigits(inHours);
    return "${hour == '00' ? '' : hour + ':'}$twoDigitMinutes:$twoDigitSeconds";
  }
}
0
String myDuration(Duration duration) {
  var date = duration.toString().split(":");
  var hrs = date[0];
  var mns = date[1];
  var sds = date[2].split(".")[0];
  return "$hrs:$mns:$sds";
}
0
final minuts = 83;
final show = DateFormat('H:mm').format(DateTime.fromMillisecondsSinceEpoch(minuts * 1000 * 60));
print(show);

output: 1:23

  • There are **16 existing answers** to this question, including a top-voted, accepted answer with over **two hundred votes**. Are you _certain_ your solution hasn't already been given? If not, why do you believe your approach improves upon the existing proposals, which have been validated by the community? Offering an explanation is _always_ useful on Stack Overflow, but it's _especially_ important where the question has been resolved to the satisfaction of both the OP and the community. Help readers out by explaining what your answer does different and when it might be preferred. – Jeremy Caney Jul 24 '23 at 04:11
  • This option supports localization. This is a different way of looking at the solution of the issue. He's a little prettier in my opinion. Please correct the formatting. – Andrey Scherbanev Jul 25 '23 at 06:46
  • Can you [edit] your answer to help differentiate it from the existing answers? That comment is a good start. – Jeremy Caney Jul 25 '23 at 07:13
-3
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 "$twoDigitMinutes:$twoDigitSeconds";

}

          Container( //duration of video
            child: Text("Total Duration: " + _printDuration(_controller.value.duration).toString()+" Position: " + _printDuration(_controller.value.position).toString()),
          ),
  • Code only answers are **not considered good answers**, and are likely to be downvoted and/or deleted because they are **less useful** to a community of learners. It's only obvious to you. Explain what it does, and how it's different / **better** than the existing answers. Here are some guidelines for [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer) – lepsch Sep 13 '22 at 14:26