diff --git a/NEWS b/NEWS
index 846b059c1..1e87c7d14 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,7 @@ ver 0.20 (not yet released)
 * protocol
   - "commands" returns playlist commands only if playlist_directory configured
   - "search"/"find" have a "window" parameter
+  - report song duration with milliseconds precision
 * tags
   - ape: drop support for non-standard tag "album artist"
 * output
diff --git a/doc/protocol.xml b/doc/protocol.xml
index a0145053a..549b82c6c 100644
--- a/doc/protocol.xml
+++ b/doc/protocol.xml
@@ -515,6 +515,15 @@
                   </returnvalue>
                 </para>
               </listitem>
+              <listitem>
+                <para>
+                  <varname>duration</varname>:
+                  <footnote id="since_0_20"><simpara>Introduced with <application>MPD</application> 0.20</simpara></footnote>
+                  <returnvalue>
+                    Duration of the current song in seconds.
+                  </returnvalue>
+                </para>
+              </listitem>
               <listitem>
                 <para>
                   <varname>bitrate</varname>:
diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx
index 05d462b6d..07b9458ca 100644
--- a/src/SongPrint.cxx
+++ b/src/SongPrint.cxx
@@ -122,5 +122,8 @@ song_print_info(Client &client, const DetachedSong &song, bool base)
 
 	const auto duration = song.GetDuration();
 	if (!duration.IsNegative())
-		client_printf(client, "Time: %u\n", duration.RoundS());
+		client_printf(client, "Time: %i\n"
+			      "duration: %1.3f\n",
+			      duration.RoundS(),
+			      duration.ToDoubleS());
 }
diff --git a/src/TagPrint.cxx b/src/TagPrint.cxx
index 4937fa622..51ee80d83 100644
--- a/src/TagPrint.cxx
+++ b/src/TagPrint.cxx
@@ -23,8 +23,6 @@
 #include "tag/TagSettings.h"
 #include "client/Client.hxx"
 
-#define SONG_TIME "Time: "
-
 void tag_print_types(Client &client)
 {
 	int i;
@@ -53,7 +51,10 @@ tag_print_values(Client &client, const Tag &tag)
 void tag_print(Client &client, const Tag &tag)
 {
 	if (!tag.duration.IsNegative())
-		client_printf(client, SONG_TIME "%i\n", tag.duration.RoundS());
+		client_printf(client, "Time: %i\n"
+			      "duration: %1.3f\n",
+			      tag.duration.RoundS(),
+			      tag.duration.ToDoubleS());
 
 	tag_print_values(client, tag);
 }
diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx
index cd7f42289..a320fb6ba 100644
--- a/src/command/PlayerCommands.cxx
+++ b/src/command/PlayerCommands.cxx
@@ -182,6 +182,10 @@ handle_status(Client &client,
 			      player_status.elapsed_time.ToDoubleS(),
 			      player_status.bit_rate);
 
+		if (!player_status.total_time.IsNegative())
+			client_printf(client, "duration: %1.3f\n",
+				      player_status.total_time.ToDoubleS());
+
 		if (player_status.audio_format.IsDefined()) {
 			struct audio_format_string af_string;