From b886dfae4d4a4d5ca49fd1323e18bae0e78dfab1 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 18 Jan 2017 13:19:13 +0100
Subject: [PATCH] DetachedSong, db/LightSong, db/simple/Song: use
 std::chrono::system_clock::time_point

---
 src/DetachedSong.hxx                       | 14 +++++++++-----
 src/SongFilter.cxx                         |  4 ++--
 src/SongPrint.cxx                          |  5 +++--
 src/SongSave.cxx                           | 11 ++++++++---
 src/SongUpdate.cxx                         |  6 +++---
 src/db/LightSong.hxx                       |  9 ++++++---
 src/db/plugins/ProxyDatabasePlugin.cxx     |  6 +++++-
 src/db/plugins/simple/Song.cxx             |  2 +-
 src/db/plugins/simple/Song.hxx             |  6 +++++-
 src/db/plugins/upnp/UpnpDatabasePlugin.cxx |  4 ++--
 src/db/update/Container.cxx                |  2 +-
 src/db/update/UpdateSong.cxx               |  6 ++----
 test/test_translate_song.cxx               |  6 ++++--
 13 files changed, 51 insertions(+), 30 deletions(-)

diff --git a/src/DetachedSong.hxx b/src/DetachedSong.hxx
index 2a08e4994..5b9e07a7b 100644
--- a/src/DetachedSong.hxx
+++ b/src/DetachedSong.hxx
@@ -25,11 +25,10 @@
 #include "Chrono.hxx"
 #include "Compiler.h"
 
+#include <chrono>
 #include <string>
 #include <utility>
 
-#include <time.h>
-
 struct LightSong;
 class Storage;
 class Path;
@@ -63,7 +62,12 @@ class DetachedSong {
 
 	Tag tag;
 
-	time_t mtime = 0;
+	/**
+	 * The time stamp of the last file modification.  A negative
+	 * value means that this is unknown/unavailable.
+	 */
+	std::chrono::system_clock::time_point mtime =
+		std::chrono::system_clock::time_point::min();
 
 	/**
 	 * Start of this sub-song within the file.
@@ -201,11 +205,11 @@ public:
 		tag.MoveItemsFrom(std::move(other.tag));
 	}
 
-	time_t GetLastModified() const {
+	std::chrono::system_clock::time_point GetLastModified() const {
 		return mtime;
 	}
 
-	void SetLastModified(time_t _value) {
+	void SetLastModified(std::chrono::system_clock::time_point _value) {
 		mtime = _value;
 	}
 
diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx
index ecda29617..a467c75a3 100644
--- a/src/SongFilter.cxx
+++ b/src/SongFilter.cxx
@@ -145,7 +145,7 @@ SongFilter::Item::Match(const DetachedSong &song) const noexcept
 		return uri_is_child_or_same(value.c_str(), song.GetURI());
 
 	if (tag == LOCATE_TAG_MODIFIED_SINCE)
-		return song.GetLastModified() >= time;
+		return song.GetLastModified() >= std::chrono::system_clock::from_time_t(time);
 
 	if (tag == LOCATE_TAG_FILE_TYPE)
 		return StringMatch(song.GetURI());
@@ -162,7 +162,7 @@ SongFilter::Item::Match(const LightSong &song) const noexcept
 	}
 
 	if (tag == LOCATE_TAG_MODIFIED_SINCE)
-		return song.mtime >= time;
+		return song.mtime >= std::chrono::system_clock::from_time_t(time);
 
 	if (tag == LOCATE_TAG_FILE_TYPE) {
 		const auto uri = song.GetURI();
diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx
index c06c2a6a8..a7e816822 100644
--- a/src/SongPrint.cxx
+++ b/src/SongPrint.cxx
@@ -28,6 +28,7 @@
 #include "TagPrint.hxx"
 #include "client/Response.hxx"
 #include "fs/Traits.hxx"
+#include "util/ChronoUtil.hxx"
 #include "util/UriUtil.hxx"
 
 #define SONG_FILE "file: "
@@ -88,7 +89,7 @@ song_print_info(Response &r, const LightSong &song, bool base)
 
 	PrintRange(r, song.start_time, song.end_time);
 
-	if (song.mtime > 0)
+	if (!IsNegative(song.mtime))
 		time_print(r, "Last-Modified", song.mtime);
 
 	tag_print(r, *song.tag);
@@ -101,7 +102,7 @@ song_print_info(Response &r, const DetachedSong &song, bool base)
 
 	PrintRange(r, song.GetStartTime(), song.GetEndTime());
 
-	if (song.GetLastModified() > 0)
+	if (!IsNegative(song.GetLastModified()))
 		time_print(r, "Last-Modified", song.GetLastModified());
 
 	tag_print_values(r, song.GetTag());
diff --git a/src/SongSave.cxx b/src/SongSave.cxx
index f551b7670..eadb0c254 100644
--- a/src/SongSave.cxx
+++ b/src/SongSave.cxx
@@ -27,6 +27,7 @@
 #include "tag/ParseName.hxx"
 #include "tag/Tag.hxx"
 #include "tag/Builder.hxx"
+#include "util/ChronoUtil.hxx"
 #include "util/StringStrip.hxx"
 #include "util/RuntimeError.hxx"
 
@@ -54,7 +55,9 @@ song_save(BufferedOutputStream &os, const Song &song)
 
 	tag_save(os, song.tag);
 
-	os.Format(SONG_MTIME ": %li\n", (long)song.mtime);
+	if (!IsNegative(song.mtime))
+		os.Format(SONG_MTIME ": %li\n",
+			  (long)std::chrono::system_clock::to_time_t(song.mtime));
 	os.Format(SONG_END "\n");
 }
 
@@ -67,7 +70,9 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
 
 	tag_save(os, song.GetTag());
 
-	os.Format(SONG_MTIME ": %li\n", (long)song.GetLastModified());
+	if (!IsNegative(song.GetLastModified()))
+		os.Format(SONG_MTIME ": %li\n",
+			  (long)std::chrono::system_clock::to_time_t(song.GetLastModified()));
 	os.Format(SONG_END "\n");
 }
 
@@ -99,7 +104,7 @@ song_load(TextFile &file, const char *uri)
 		} else if (strcmp(line, "Playlist") == 0) {
 			tag.SetHasPlaylist(strcmp(value, "yes") == 0);
 		} else if (strcmp(line, SONG_MTIME) == 0) {
-			song->SetLastModified(atoi(value));
+			song->SetLastModified(std::chrono::system_clock::from_time_t(atoi(value)));
 		} else if (strcmp(line, "Range") == 0) {
 			char *endptr;
 
diff --git a/src/SongUpdate.cxx b/src/SongUpdate.cxx
index 0c976b883..6eb76a9bd 100644
--- a/src/SongUpdate.cxx
+++ b/src/SongUpdate.cxx
@@ -88,7 +88,7 @@ Song::UpdateFile(Storage &storage)
 			return false;
 	}
 
-	mtime = std::chrono::system_clock::to_time_t(info.mtime);
+	mtime = info.mtime;
 	tag_builder.Commit(tag);
 	return true;
 }
@@ -151,7 +151,7 @@ DetachedSong::LoadFile(Path path)
 	if (!tag_file_scan(path, tag_builder))
 		return false;
 
-	mtime = std::chrono::system_clock::to_time_t(fi.GetModificationTime());
+	mtime = fi.GetModificationTime();
 	tag_builder.Commit(tag);
 	return true;
 }
@@ -171,7 +171,7 @@ DetachedSong::Update()
 		if (!tag_stream_scan(uri.c_str(), tag_builder))
 			return false;
 
-		mtime = 0;
+		mtime = std::chrono::system_clock::time_point::min();
 		tag_builder.Commit(tag);
 		return true;
 	} else
diff --git a/src/db/LightSong.hxx b/src/db/LightSong.hxx
index 599acba54..1b89fc9ad 100644
--- a/src/db/LightSong.hxx
+++ b/src/db/LightSong.hxx
@@ -24,8 +24,7 @@
 #include "Compiler.h"
 
 #include <string>
-
-#include <time.h>
+#include <chrono>
 
 struct Tag;
 
@@ -62,7 +61,11 @@ struct LightSong {
 	 */
 	const Tag *tag;
 
-	time_t mtime;
+	/**
+	 * The time stamp of the last file modification.  A negative
+	 * value means that this is unknown/unavailable.
+	 */
+	std::chrono::system_clock::time_point mtime;
 
 	/**
 	 * Start of this sub-song within the file.
diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx
index f5c81bb9c..c39a96fa2 100644
--- a/src/db/plugins/ProxyDatabasePlugin.cxx
+++ b/src/db/plugins/ProxyDatabasePlugin.cxx
@@ -197,7 +197,11 @@ ProxySong::ProxySong(const mpd_song *song)
 	uri = mpd_song_get_uri(song);
 	real_uri = nullptr;
 	tag = &tag2;
-	mtime = mpd_song_get_last_modified(song);
+
+	const auto _mtime = mpd_song_get_last_modified(song);
+	mtime = _mtime > 0
+		? std::chrono::system_clock::from_time_t(_mtime)
+		: std::chrono::system_clock::time_point::min();
 
 #if LIBMPDCLIENT_CHECK_VERSION(2,3,0)
 	start_time = SongTime::FromS(mpd_song_get_start(song));
diff --git a/src/db/plugins/simple/Song.cxx b/src/db/plugins/simple/Song.cxx
index 54a9c95ff..49ba6e2c9 100644
--- a/src/db/plugins/simple/Song.cxx
+++ b/src/db/plugins/simple/Song.cxx
@@ -29,7 +29,7 @@
 #include <string.h>
 
 inline Song::Song(const char *_uri, size_t uri_length, Directory &_parent)
-	:parent(&_parent), mtime(0),
+	:parent(&_parent), mtime(std::chrono::system_clock::time_point::min()),
 	 start_time(SongTime::zero()), end_time(SongTime::zero())
 {
 	memcpy(uri, _uri, uri_length + 1);
diff --git a/src/db/plugins/simple/Song.hxx b/src/db/plugins/simple/Song.hxx
index 2fa952a35..9831d638b 100644
--- a/src/db/plugins/simple/Song.hxx
+++ b/src/db/plugins/simple/Song.hxx
@@ -70,7 +70,11 @@ struct Song {
 	 */
 	Directory *const parent;
 
-	time_t mtime;
+	/**
+	 * The time stamp of the last file modification.  A negative
+	 * value means that this is unknown/unavailable.
+	 */
+	std::chrono::system_clock::time_point mtime;
 
 	/**
 	 * Start of this sub-song within the file.
diff --git a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx
index f40b70b87..4a72b6d30 100644
--- a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx
+++ b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx
@@ -63,7 +63,7 @@ public:
 		uri = uri2.c_str();
 		real_uri = real_uri2.c_str();
 		tag = &tag2;
-		mtime = 0;
+		mtime = std::chrono::system_clock::time_point::min();
 		start_time = end_time = SongTime::zero();
 	}
 };
@@ -314,7 +314,7 @@ visitSong(const UPnPDirObject &meta, const char *path,
 	song.uri = path;
 	song.real_uri = meta.url.c_str();
 	song.tag = &meta.tag;
-	song.mtime = 0;
+	song.mtime = std::chrono::system_clock::time_point::min();
 	song.start_time = song.end_time = SongTime::zero();
 
 	if (selection.Match(song))
diff --git a/src/db/update/Container.cxx b/src/db/update/Container.cxx
index 5d96b84d9..fc4ec416c 100644
--- a/src/db/update/Container.cxx
+++ b/src/db/update/Container.cxx
@@ -107,7 +107,7 @@ UpdateWalk::UpdateContainerFile(Directory &directory,
 						   *contdir);
 
 			// shouldn't be necessary but it's there..
-			song->mtime = std::chrono::system_clock::to_time_t(info.mtime);
+			song->mtime = info.mtime;
 
 			FormatDefault(update_domain, "added %s/%s",
 				      contdir->GetPath(), song->uri);
diff --git a/src/db/update/UpdateSong.cxx b/src/db/update/UpdateSong.cxx
index 6d3802069..599cd5cc9 100644
--- a/src/db/update/UpdateSong.cxx
+++ b/src/db/update/UpdateSong.cxx
@@ -51,9 +51,7 @@ UpdateWalk::UpdateSongFile2(Directory &directory,
 		return;
 	}
 
-	if (!(song != nullptr &&
-	      std::chrono::system_clock::to_time_t(info.mtime) == song->mtime &&
-	      !walk_discard) &&
+	if (!(song != nullptr && info.mtime == song->mtime && !walk_discard) &&
 	    UpdateContainerFile(directory, name, suffix, info)) {
 		if (song != nullptr)
 			editor.LockDeleteSong(directory, song);
@@ -80,7 +78,7 @@ UpdateWalk::UpdateSongFile2(Directory &directory,
 		modified = true;
 		FormatDefault(update_domain, "added %s/%s",
 			      directory.GetPath(), name);
-	} else if (std::chrono::system_clock::to_time_t(info.mtime) != song->mtime || walk_discard) {
+	} else if (info.mtime != song->mtime || walk_discard) {
 		FormatDefault(update_domain, "updating %s/%s",
 			      directory.GetPath(), name);
 		if (!song->UpdateFile(storage)) {
diff --git a/test/test_translate_song.cxx b/test/test_translate_song.cxx
index 3b5df1c45..a2144b50a 100644
--- a/test/test_translate_song.cxx
+++ b/test/test_translate_song.cxx
@@ -16,6 +16,7 @@
 #include "db/DatabaseSong.hxx"
 #include "storage/plugins/LocalStorage.hxx"
 #include "Mapper.hxx"
+#include "util/ChronoUtil.hxx"
 
 #include <cppunit/TestFixture.h>
 #include <cppunit/extensions/TestFactoryRegistry.h>
@@ -179,8 +180,9 @@ ToString(const DetachedSong &song)
 
 	char buffer[64];
 
-	if (song.GetLastModified() > 0) {
-		sprintf(buffer, "%lu", (unsigned long)song.GetLastModified());
+	if (!IsNegative(song.GetLastModified())) {
+		sprintf(buffer, "%lu",
+			(unsigned long)std::chrono::system_clock::to_time_t(song.GetLastModified()));
 		result.append(buffer);
 	}