From 13ce07d181b1ffa8f73ba9734cbc7d72baaaec91 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 6 Nov 2019 16:05:22 +0100 Subject: [PATCH 01/34] output/shout: declare metadata as UTF-8 Apparently, Icecast defaults to ISO-8859-1 for MP3: http://icecast.org/docs/icecast-2.4.0/config-file.html#mountsettings This change forces Icecast to UTF-8 without having to configure it in Icecast's configuration file. --- NEWS | 1 + src/output/plugins/ShoutOutputPlugin.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 6bb77ecf4..5f4b2337f 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ ver 0.21.17 (not yet released) * outputs - jack: mark ports as terminal + - shout: declare metadata as UTF-8 * fix build failure with -Ddatabase=false ver 0.21.16 (2019/10/16) diff --git a/src/output/plugins/ShoutOutputPlugin.cxx b/src/output/plugins/ShoutOutputPlugin.cxx index eec956d2a..659de603f 100644 --- a/src/output/plugins/ShoutOutputPlugin.cxx +++ b/src/output/plugins/ShoutOutputPlugin.cxx @@ -383,6 +383,7 @@ ShoutOutput::SendTag(const Tag &tag) shout_tag_to_metadata(tag, song, sizeof(song)); shout_metadata_add(meta, "song", song); + shout_metadata_add(meta, "charset", "UTF-8"); if (SHOUTERR_SUCCESS != shout_set_metadata(shout_conn, meta)) { LogWarning(shout_output_domain, "error setting shout metadata"); From 75a592f6292114d4d46380d426b53c7b639f24c6 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 9 Jul 2019 18:17:41 +0200 Subject: [PATCH 02/34] system/Error: move code to IsErrno() --- src/system/Error.hxx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/system/Error.hxx b/src/system/Error.hxx index a94678872..92c638f4f 100644 --- a/src/system/Error.hxx +++ b/src/system/Error.hxx @@ -147,6 +147,14 @@ FormatErrno(const char *fmt, Args&&... args) noexcept return FormatErrno(errno, fmt, std::forward(args)...); } +gcc_pure +inline bool +IsErrno(const std::system_error &e, int code) noexcept +{ + return e.code().category() == ErrnoCategory() && + e.code().value() == code; +} + gcc_pure static inline bool IsFileNotFound(const std::system_error &e) noexcept @@ -155,8 +163,7 @@ IsFileNotFound(const std::system_error &e) noexcept return e.code().category() == std::system_category() && e.code().value() == ERROR_FILE_NOT_FOUND; #else - return e.code().category() == ErrnoCategory() && - e.code().value() == ENOENT; + return IsErrno(e, ENOENT); #endif } @@ -168,8 +175,7 @@ IsPathNotFound(const std::system_error &e) noexcept return e.code().category() == std::system_category() && e.code().value() == ERROR_PATH_NOT_FOUND; #else - return e.code().category() == ErrnoCategory() && - e.code().value() == ENOTDIR; + return IsErrno(e, ENOTDIR); #endif } @@ -181,8 +187,7 @@ IsAccessDenied(const std::system_error &e) noexcept return e.code().category() == std::system_category() && e.code().value() == ERROR_ACCESS_DENIED; #else - return e.code().category() == ErrnoCategory() && - e.code().value() == EACCES; + return IsErrno(e, EACCES); #endif } From 2c276770f0687a916719f68bde81bb134547488f Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 18:53:08 +0200 Subject: [PATCH 03/34] util/PrintException, ...: update copyright --- src/lib/curl/Global.cxx | 2 +- src/lib/curl/Global.hxx | 2 +- src/lib/curl/Handler.hxx | 2 +- src/lib/curl/Init.cxx | 2 +- src/lib/curl/Multi.hxx | 2 +- src/lib/curl/Request.cxx | 2 +- src/lib/curl/Request.hxx | 2 +- src/lib/curl/Slist.hxx | 2 +- src/lib/curl/Version.cxx | 2 +- src/lib/curl/Version.hxx | 2 +- src/net/StaticSocketAddress.hxx | 1 + src/system/EpollFD.hxx | 2 +- src/system/Error.hxx | 2 +- src/util/PrintException.cxx | 2 +- src/util/PrintException.hxx | 2 +- 15 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/lib/curl/Global.cxx b/src/lib/curl/Global.cxx index 175cd8d98..6f213c20d 100644 --- a/src/lib/curl/Global.cxx +++ b/src/lib/curl/Global.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2016 Max Kellermann + * Copyright 2008-2016 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Global.hxx b/src/lib/curl/Global.hxx index 5a3a87209..d2866c3e9 100644 --- a/src/lib/curl/Global.hxx +++ b/src/lib/curl/Global.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2016 Max Kellermann + * Copyright 2008-2016 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Handler.hxx b/src/lib/curl/Handler.hxx index 22c1a0db2..153b50603 100644 --- a/src/lib/curl/Handler.hxx +++ b/src/lib/curl/Handler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Max Kellermann + * Copyright 2008-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Init.cxx b/src/lib/curl/Init.cxx index 31b9b6744..7b1c59a84 100644 --- a/src/lib/curl/Init.cxx +++ b/src/lib/curl/Init.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Max Kellermann + * Copyright 2008-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Multi.hxx b/src/lib/curl/Multi.hxx index 88d83fb5a..ecbd0c92a 100644 --- a/src/lib/curl/Multi.hxx +++ b/src/lib/curl/Multi.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Max Kellermann + * Copyright 2016-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Request.cxx b/src/lib/curl/Request.cxx index a9a61de98..7714712fd 100644 --- a/src/lib/curl/Request.cxx +++ b/src/lib/curl/Request.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Max Kellermann + * Copyright 2008-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Request.hxx b/src/lib/curl/Request.hxx index 40abfbf6b..59af67b7c 100644 --- a/src/lib/curl/Request.hxx +++ b/src/lib/curl/Request.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Max Kellermann + * Copyright 2008-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Slist.hxx b/src/lib/curl/Slist.hxx index fd45f7d0b..e0d73b51f 100644 --- a/src/lib/curl/Slist.hxx +++ b/src/lib/curl/Slist.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Max Kellermann + * Copyright 2008-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Version.cxx b/src/lib/curl/Version.cxx index 2a00d669b..b67a4706d 100644 --- a/src/lib/curl/Version.cxx +++ b/src/lib/curl/Version.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Max Kellermann + * Copyright 2017-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/lib/curl/Version.hxx b/src/lib/curl/Version.hxx index 9e9a319d3..a79eeec95 100644 --- a/src/lib/curl/Version.hxx +++ b/src/lib/curl/Version.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Max Kellermann + * Copyright 2017-2018 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/net/StaticSocketAddress.hxx b/src/net/StaticSocketAddress.hxx index 52191ffb2..7a5d0a218 100644 --- a/src/net/StaticSocketAddress.hxx +++ b/src/net/StaticSocketAddress.hxx @@ -31,6 +31,7 @@ #define STATIC_SOCKET_ADDRESS_HXX #include "SocketAddress.hxx" +#include "Features.hxx" #include "util/Compiler.h" #include diff --git a/src/system/EpollFD.hxx b/src/system/EpollFD.hxx index ff07c038c..bf76d76a4 100644 --- a/src/system/EpollFD.hxx +++ b/src/system/EpollFD.hxx @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 Max Kellermann + * Copyright 2013-2019 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/system/Error.hxx b/src/system/Error.hxx index 92c638f4f..a22c3ba29 100644 --- a/src/system/Error.hxx +++ b/src/system/Error.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 Max Kellermann + * Copyright 2013-2015 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/util/PrintException.cxx b/src/util/PrintException.cxx index 0e1971077..b55fed920 100644 --- a/src/util/PrintException.cxx +++ b/src/util/PrintException.cxx @@ -1,5 +1,5 @@ /* - * Copyright 2007-2017 Content Management AG + * Copyright 2007-2019 Content Management AG * All rights reserved. * * author: Max Kellermann diff --git a/src/util/PrintException.hxx b/src/util/PrintException.hxx index 3abd2e538..36ee36d19 100644 --- a/src/util/PrintException.hxx +++ b/src/util/PrintException.hxx @@ -1,5 +1,5 @@ /* - * Copyright 2007-2017 Content Management AG + * Copyright 2007-2019 Content Management AG * All rights reserved. * * author: Max Kellermann From ce093be12c4f2c6aab20774b1b4a10cf206b8e35 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 4 Dec 2019 12:31:38 +0100 Subject: [PATCH 04/34] system/Error: add FormatFileNotFound() --- src/system/Error.hxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/system/Error.hxx b/src/system/Error.hxx index a22c3ba29..5027e6864 100644 --- a/src/system/Error.hxx +++ b/src/system/Error.hxx @@ -147,6 +147,18 @@ FormatErrno(const char *fmt, Args&&... args) noexcept return FormatErrno(errno, fmt, std::forward(args)...); } +template +static inline std::system_error +FormatFileNotFound(const char *fmt, Args&&... args) noexcept +{ +#ifdef _WIN32 + return FormatLastError(ERROR_FILE_NOT_FOUND, fmt, + std::forward(args)...); +#else + return FormatErrno(ENOENT, fmt, std::forward(args)...); +#endif +} + gcc_pure inline bool IsErrno(const std::system_error &e, int code) noexcept From 32799ff68289c8e3ad253a883ffcb2b1e711efa5 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 4 Dec 2019 12:29:06 +0100 Subject: [PATCH 05/34] archive/zzip: improve error reporting Most importantly, this commit translates ZZIP_ENOENT to std::system_error(ENOENT) so IsFileNotFound() returns true and find_stream_art() can suppress the log line. --- NEWS | 2 ++ src/archive/plugins/ZzipArchivePlugin.cxx | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 5f4b2337f..67f5807b4 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ ver 0.21.17 (not yet released) +* archive + - zzip: improve error reporting * outputs - jack: mark ports as terminal - shout: declare metadata as UTF-8 diff --git a/src/archive/plugins/ZzipArchivePlugin.cxx b/src/archive/plugins/ZzipArchivePlugin.cxx index e238dd4ba..31698b978 100644 --- a/src/archive/plugins/ZzipArchivePlugin.cxx +++ b/src/archive/plugins/ZzipArchivePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright 2003-2018 The Music Player Daemon Project + * Copyright 2003-2019 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include "../ArchiveVisitor.hxx" #include "input/InputStream.hxx" #include "fs/Path.hxx" +#include "system/Error.hxx" #include "util/RuntimeError.hxx" #include @@ -120,9 +121,19 @@ ZzipArchiveFile::OpenStream(const char *pathname, Mutex &mutex) { ZZIP_FILE *_file = zzip_file_open(dir->dir, pathname, 0); - if (_file == nullptr) - throw FormatRuntimeError("not found in the ZIP file: %s", - pathname); + if (_file == nullptr) { + const auto error = (zzip_error_t)zzip_error(dir->dir); + switch (error) { + case ZZIP_ENOENT: + throw FormatFileNotFound("Failed to open '%s' in ZIP file", + pathname); + + default: + throw FormatRuntimeError("Failed to open '%s' in ZIP file: %s", + pathname, + zzip_strerror(error)); + } + } return std::make_unique(dir, pathname, mutex, From 9766ac6db3294075c19c7b7efcc277440b50b3d1 Mon Sep 17 00:00:00 2001 From: Naglis Jonaitis Date: Sat, 30 Nov 2019 20:16:13 +0200 Subject: [PATCH 06/34] Fix typo in documentation --- doc/plugins.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/plugins.rst b/doc/plugins.rst index 6feb00438..7268663a4 100644 --- a/doc/plugins.rst +++ b/doc/plugins.rst @@ -42,7 +42,7 @@ Provides access to the database of another :program:`MPD` instance using libmpdc * - **password** - The password used to log in to the "master" :program:`MPD` instance. * - **keepalive yes|no** - - Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expensive of a very small amount of additional network traffic. Disabled by default. + - Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expense of a very small amount of additional network traffic. Disabled by default. upnp ---- @@ -1069,7 +1069,7 @@ Filter plugins normalize --------- -Normalize the volume during playback (at the expensve of quality). +Normalize the volume during playback (at the expense of quality). null From ef0765ca10acfb6480c46cf6a2af326fc1e777e0 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 9 Dec 2019 23:49:55 +0100 Subject: [PATCH 07/34] input: add missing boost meson dependency --- src/input/meson.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/input/meson.build b/src/input/meson.build index 2ce6f8832..4e1f7d6d9 100644 --- a/src/input/meson.build +++ b/src/input/meson.build @@ -35,6 +35,9 @@ input_glue = static_library( 'BufferedInputStream.cxx', 'MaybeBufferedInputStream.cxx', include_directories: inc, + dependencies: [ + boost_dep, + ], ) input_glue_dep = declare_dependency( From 2b301ffd2cc3a94a5f343c8b75b793ad6a8fcfeb Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 9 Dec 2019 23:50:30 +0100 Subject: [PATCH 08/34] lib/xiph: add missing meson dependency --- src/lib/xiph/meson.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/xiph/meson.build b/src/lib/xiph/meson.build index 4f3f46e64..c8f7c6fac 100644 --- a/src/lib/xiph/meson.build +++ b/src/lib/xiph/meson.build @@ -49,6 +49,9 @@ xiph = static_library( 'VorbisComments.cxx', 'XiphTags.cxx', include_directories: inc, + dependencies: [ + libvorbis_dep, + ], ) xiph_dep = declare_dependency( From 2e8f42c6ad9f460b666464ee93680b7a1cbf8911 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 16 Dec 2019 22:51:23 +0100 Subject: [PATCH 09/34] util/StringBuffer: use std::size_t instead of size_t --- src/util/StringBuffer.hxx | 6 +++--- src/util/StringFormat.hxx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/util/StringBuffer.hxx b/src/util/StringBuffer.hxx index a4dc4189a..07ce6dd31 100644 --- a/src/util/StringBuffer.hxx +++ b/src/util/StringBuffer.hxx @@ -35,14 +35,14 @@ /** * A statically allocated string buffer. */ -template +template class BasicStringBuffer { public: typedef T value_type; typedef T &reference; typedef T *pointer; typedef const T *const_pointer; - typedef size_t size_type; + typedef std::size_t size_type; static constexpr value_type SENTINEL = '\0'; @@ -104,7 +104,7 @@ public: } }; -template +template class StringBuffer : public BasicStringBuffer {}; #endif diff --git a/src/util/StringFormat.hxx b/src/util/StringFormat.hxx index 4fee2fc37..723591c69 100644 --- a/src/util/StringFormat.hxx +++ b/src/util/StringFormat.hxx @@ -36,13 +36,13 @@ template static inline void -StringFormat(char *buffer, size_t size, +StringFormat(char *buffer, std::size_t size, const char *fmt, Args&&... args) noexcept { snprintf(buffer, size, fmt, args...); } -template +template static inline void StringFormat(StringBuffer &buffer, const char *fmt, Args&&... args) noexcept @@ -50,7 +50,7 @@ StringFormat(StringBuffer &buffer, StringFormat(buffer.data(), buffer.capacity(), fmt, args...); } -template +template static inline StringBuffer StringFormat(const char *fmt, Args&&... args) noexcept { From 1c46bb1ba634961c4e395f6211e39c99dc090dc3 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 16 Dec 2019 22:52:22 +0100 Subject: [PATCH 10/34] lib/gcrypt/MD5: add missing include for uint8_t --- src/lib/gcrypt/MD5.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/gcrypt/MD5.hxx b/src/lib/gcrypt/MD5.hxx index 03bfb0347..8e52fa239 100644 --- a/src/lib/gcrypt/MD5.hxx +++ b/src/lib/gcrypt/MD5.hxx @@ -35,6 +35,8 @@ #include +#include + template struct ConstBuffer; gcc_pure From dc3c0c88663daca8cd2302df74826681850e8445 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 16 Dec 2019 22:52:50 +0100 Subject: [PATCH 11/34] pcm/Convert, ...: add missing include for std::runtime_error --- src/LocateUri.cxx | 2 ++ src/lib/curl/Slist.hxx | 1 + src/lib/nfs/FileReader.cxx | 1 + src/pcm/PcmConvert.cxx | 2 ++ 4 files changed, 6 insertions(+) diff --git a/src/LocateUri.cxx b/src/LocateUri.cxx index 7d82f5436..ff9273c20 100644 --- a/src/LocateUri.cxx +++ b/src/LocateUri.cxx @@ -29,6 +29,8 @@ #include "storage/StorageInterface.hxx" #endif +#include + static LocatedUri LocateFileUri(const char *uri, const Client *client #ifdef ENABLE_DATABASE diff --git a/src/lib/curl/Slist.hxx b/src/lib/curl/Slist.hxx index e0d73b51f..82c466b34 100644 --- a/src/lib/curl/Slist.hxx +++ b/src/lib/curl/Slist.hxx @@ -33,6 +33,7 @@ #include #include +#include /** * OO wrapper for "struct curl_slist *". diff --git a/src/lib/nfs/FileReader.cxx b/src/lib/nfs/FileReader.cxx index b0f290a0a..6fe4faaab 100644 --- a/src/lib/nfs/FileReader.cxx +++ b/src/lib/nfs/FileReader.cxx @@ -25,6 +25,7 @@ #include "util/ASCII.hxx" #include +#include #include #include diff --git a/src/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx index dfe7cd204..c62799367 100644 --- a/src/pcm/PcmConvert.cxx +++ b/src/pcm/PcmConvert.cxx @@ -21,6 +21,8 @@ #include "ConfiguredResampler.hxx" #include "util/ConstBuffer.hxx" +#include + #include void From 0cf90ee8b668ddadb7206de397070904c9437b8a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 16 Dec 2019 23:01:31 +0100 Subject: [PATCH 12/34] decoder/mad: work around bogus -Wuninitialized in GCC 10 --- src/decoder/plugins/MadDecoderPlugin.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/decoder/plugins/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx index 66cf8fb96..649cca78c 100644 --- a/src/decoder/plugins/MadDecoderPlugin.cxx +++ b/src/decoder/plugins/MadDecoderPlugin.cxx @@ -719,6 +719,11 @@ MadDecoder::DecodeFirstFrame(Tag *tag) noexcept { struct xing xing; +#if GCC_CHECK_VERSION(10,0) + /* work around bogus -Wuninitialized in GCC 10 */ + xing.frames = 0; +#endif + while (true) { MadDecoderAction ret; do { From e9af692973a5db95ac1b07f430fd801c0c07dac9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 May 2019 15:47:58 +0200 Subject: [PATCH 13/34] util/Time*: move to time/ --- meson.build | 1 + src/PlaylistDatabase.cxx | 2 +- src/SongPrint.cxx | 2 +- src/SongSave.cxx | 2 +- src/Stats.cxx | 2 +- src/TimePrint.cxx | 2 +- src/command/OtherCommands.cxx | 2 +- src/command/PlaylistCommands.cxx | 2 +- src/command/StorageCommands.cxx | 2 +- src/db/DatabasePrint.cxx | 2 +- src/db/plugins/simple/DirectorySave.cxx | 2 +- src/song/Filter.cxx | 4 ++-- src/song/ModifiedSinceSongFilter.cxx | 2 +- src/song/meson.build | 1 + src/storage/plugins/CurlStorage.cxx | 5 +++-- src/storage/plugins/meson.build | 1 + src/tag/Format.cxx | 2 +- src/tag/meson.build | 1 + src/{util => time}/ChronoUtil.hxx | 0 src/{util/TimeConvert.cxx => time/Convert.cxx} | 2 +- src/{util/TimeConvert.hxx => time/Convert.hxx} | 0 src/{util/TimeISO8601.cxx => time/ISO8601.cxx} | 6 +++--- src/{util/TimeISO8601.hxx => time/ISO8601.hxx} | 4 ++-- src/{util/TimeParser.cxx => time/Parser.cxx} | 4 ++-- src/{util/TimeParser.hxx => time/Parser.hxx} | 0 src/time/meson.build | 11 +++++++++++ src/util/meson.build | 3 --- test/run_storage.cxx | 2 +- test/test_translate_song.cxx | 2 +- 29 files changed, 42 insertions(+), 29 deletions(-) rename src/{util => time}/ChronoUtil.hxx (100%) rename src/{util/TimeConvert.cxx => time/Convert.cxx} (98%) rename src/{util/TimeConvert.hxx => time/Convert.hxx} (100%) rename src/{util/TimeISO8601.cxx => time/ISO8601.cxx} (95%) rename src/{util/TimeISO8601.hxx => time/ISO8601.hxx} (96%) rename src/{util/TimeParser.cxx => time/Parser.cxx} (98%) rename src/{util/TimeParser.hxx => time/Parser.hxx} (100%) create mode 100644 src/time/meson.build diff --git a/meson.build b/meson.build index e18e7d6bf..93952164d 100644 --- a/meson.build +++ b/meson.build @@ -304,6 +304,7 @@ if enable_database endif subdir('src/util') +subdir('src/time') subdir('src/system') subdir('src/thread') subdir('src/event') diff --git a/src/PlaylistDatabase.cxx b/src/PlaylistDatabase.cxx index 0adc58949..c29bc1c1b 100644 --- a/src/PlaylistDatabase.cxx +++ b/src/PlaylistDatabase.cxx @@ -21,8 +21,8 @@ #include "db/PlaylistVector.hxx" #include "fs/io/TextFile.hxx" #include "fs/io/BufferedOutputStream.hxx" +#include "time/ChronoUtil.hxx" #include "util/StringStrip.hxx" -#include "util/ChronoUtil.hxx" #include "util/RuntimeError.hxx" #include diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index e2e7680f4..6299cc556 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -27,7 +27,7 @@ #include "TagPrint.hxx" #include "client/Response.hxx" #include "fs/Traits.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include "util/UriUtil.hxx" #define SONG_FILE "file: " diff --git a/src/SongSave.cxx b/src/SongSave.cxx index 954203f0c..45a2c3723 100644 --- a/src/SongSave.cxx +++ b/src/SongSave.cxx @@ -27,7 +27,7 @@ #include "tag/ParseName.hxx" #include "tag/Tag.hxx" #include "tag/Builder.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include "util/StringAPI.hxx" #include "util/StringBuffer.hxx" #include "util/StringStrip.hxx" diff --git a/src/Stats.cxx b/src/Stats.cxx index b8773d6f0..2bf4458fc 100644 --- a/src/Stats.cxx +++ b/src/Stats.cxx @@ -28,7 +28,7 @@ #include "db/Stats.hxx" #include "system/Clock.hxx" #include "Log.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include #include diff --git a/src/TimePrint.cxx b/src/TimePrint.cxx index eaac793d9..8ccf8b236 100644 --- a/src/TimePrint.cxx +++ b/src/TimePrint.cxx @@ -19,7 +19,7 @@ #include "TimePrint.hxx" #include "client/Response.hxx" -#include "util/TimeISO8601.hxx" +#include "time/ISO8601.hxx" void time_print(Response &r, const char *name, diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index f441c73e0..fe9978b1e 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -35,7 +35,7 @@ #include "decoder/DecoderPrint.hxx" #include "ls.hxx" #include "mixer/Volume.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include "util/UriUtil.hxx" #include "util/StringAPI.hxx" #include "fs/AllocatedPath.hxx" diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index 2914ea82b..b5265e6f0 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -37,9 +37,9 @@ #include "client/Response.hxx" #include "Mapper.hxx" #include "fs/AllocatedPath.hxx" +#include "time/ChronoUtil.hxx" #include "util/UriUtil.hxx" #include "util/ConstBuffer.hxx" -#include "util/ChronoUtil.hxx" #include "LocateUri.hxx" bool diff --git a/src/command/StorageCommands.cxx b/src/command/StorageCommands.cxx index 2333eca4a..7926b66c3 100644 --- a/src/command/StorageCommands.cxx +++ b/src/command/StorageCommands.cxx @@ -23,8 +23,8 @@ #include "StorageCommands.hxx" #include "Request.hxx" #include "CommandError.hxx" +#include "time/ChronoUtil.hxx" #include "util/UriUtil.hxx" -#include "util/ChronoUtil.hxx" #include "util/ConstBuffer.hxx" #include "fs/Traits.hxx" #include "client/Client.hxx" diff --git a/src/db/DatabasePrint.cxx b/src/db/DatabasePrint.cxx index deb430b1a..1a8567b88 100644 --- a/src/db/DatabasePrint.cxx +++ b/src/db/DatabasePrint.cxx @@ -34,7 +34,7 @@ #include "PlaylistInfo.hxx" #include "Interface.hxx" #include "fs/Traits.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include "util/RecursiveMap.hxx" #include diff --git a/src/db/plugins/simple/DirectorySave.cxx b/src/db/plugins/simple/DirectorySave.cxx index 085139fe3..40b71f9eb 100644 --- a/src/db/plugins/simple/DirectorySave.cxx +++ b/src/db/plugins/simple/DirectorySave.cxx @@ -25,7 +25,7 @@ #include "PlaylistDatabase.hxx" #include "fs/io/TextFile.hxx" #include "fs/io/BufferedOutputStream.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include "util/StringCompare.hxx" #include "util/NumberParser.hxx" #include "util/RuntimeError.hxx" diff --git a/src/song/Filter.cxx b/src/song/Filter.cxx index 0725083a6..9e1c4590a 100644 --- a/src/song/Filter.cxx +++ b/src/song/Filter.cxx @@ -29,8 +29,9 @@ #include "AudioParser.hxx" #include "tag/ParseName.hxx" #include "tag/Tag.hxx" +#include "time/ChronoUtil.hxx" +#include "time/ISO8601.hxx" #include "util/CharUtil.hxx" -#include "util/ChronoUtil.hxx" #include "util/ConstBuffer.hxx" #include "util/RuntimeError.hxx" #include "util/StringAPI.hxx" @@ -38,7 +39,6 @@ #include "util/StringStrip.hxx" #include "util/StringView.hxx" #include "util/ASCII.hxx" -#include "util/TimeISO8601.hxx" #include "util/UriUtil.hxx" #include "lib/icu/CaseFold.hxx" diff --git a/src/song/ModifiedSinceSongFilter.cxx b/src/song/ModifiedSinceSongFilter.cxx index ed14a31ad..e490bce8a 100644 --- a/src/song/ModifiedSinceSongFilter.cxx +++ b/src/song/ModifiedSinceSongFilter.cxx @@ -19,7 +19,7 @@ #include "ModifiedSinceSongFilter.hxx" #include "LightSong.hxx" -#include "util/TimeISO8601.hxx" +#include "time/ISO8601.hxx" std::string ModifiedSinceSongFilter::ToExpression() const noexcept diff --git a/src/song/meson.build b/src/song/meson.build index 36abdc073..d8b443656 100644 --- a/src/song/meson.build +++ b/src/song/meson.build @@ -24,6 +24,7 @@ song_dep = declare_dependency( icu_dep, pcre_dep, tag_dep, + time_dep, util_dep, ], ) diff --git a/src/storage/plugins/CurlStorage.cxx b/src/storage/plugins/CurlStorage.cxx index ee1c00845..b4e73ba0a 100644 --- a/src/storage/plugins/CurlStorage.cxx +++ b/src/storage/plugins/CurlStorage.cxx @@ -35,12 +35,13 @@ #include "event/DeferEvent.hxx" #include "thread/Mutex.hxx" #include "thread/Cond.hxx" +#include "time/ChronoUtil.hxx" +#include "time/Parser.hxx" #include "util/ASCII.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include "util/RuntimeError.hxx" #include "util/StringCompare.hxx" #include "util/StringFormat.hxx" -#include "util/TimeParser.hxx" #include "util/UriUtil.hxx" #include diff --git a/src/storage/plugins/meson.build b/src/storage/plugins/meson.build index e13a2f316..0d2705fcb 100644 --- a/src/storage/plugins/meson.build +++ b/src/storage/plugins/meson.build @@ -57,5 +57,6 @@ storage_plugins_dep = declare_dependency( dependencies: [ storage_api_dep, fs_dep, + time_dep, ], ) diff --git a/src/tag/Format.cxx b/src/tag/Format.cxx index a86e4643e..01fdba995 100644 --- a/src/tag/Format.cxx +++ b/src/tag/Format.cxx @@ -20,9 +20,9 @@ #include "Format.hxx" #include "Tag.hxx" #include "ParseName.hxx" +#include "time/Convert.hxx" #include "util/format.h" #include "util/TruncateString.hxx" -#include "util/TimeConvert.hxx" #include diff --git a/src/tag/meson.build b/src/tag/meson.build index 08b9e5e92..13ca102d5 100644 --- a/src/tag/meson.build +++ b/src/tag/meson.build @@ -48,6 +48,7 @@ tag = static_library( tag_dep = declare_dependency( link_with: tag, dependencies: [ + time_dep, util_dep, ], ) diff --git a/src/util/ChronoUtil.hxx b/src/time/ChronoUtil.hxx similarity index 100% rename from src/util/ChronoUtil.hxx rename to src/time/ChronoUtil.hxx diff --git a/src/util/TimeConvert.cxx b/src/time/Convert.cxx similarity index 98% rename from src/util/TimeConvert.cxx rename to src/time/Convert.cxx index e0d506018..5f2b8bdb5 100644 --- a/src/util/TimeConvert.cxx +++ b/src/time/Convert.cxx @@ -30,7 +30,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TimeConvert.hxx" +#include "Convert.hxx" #include diff --git a/src/util/TimeConvert.hxx b/src/time/Convert.hxx similarity index 100% rename from src/util/TimeConvert.hxx rename to src/time/Convert.hxx diff --git a/src/util/TimeISO8601.cxx b/src/time/ISO8601.cxx similarity index 95% rename from src/util/TimeISO8601.cxx rename to src/time/ISO8601.cxx index 0a9480c85..b9ce08361 100644 --- a/src/util/TimeISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -30,9 +30,9 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TimeISO8601.hxx" -#include "TimeConvert.hxx" -#include "TimeParser.hxx" +#include "ISO8601.hxx" +#include "Convert.hxx" +#include "Parser.hxx" StringBuffer<64> FormatISO8601(const struct tm &tm) noexcept diff --git a/src/util/TimeISO8601.hxx b/src/time/ISO8601.hxx similarity index 96% rename from src/util/TimeISO8601.hxx rename to src/time/ISO8601.hxx index 81e5593d3..10a171d26 100644 --- a/src/util/TimeISO8601.hxx +++ b/src/time/ISO8601.hxx @@ -33,8 +33,8 @@ #ifndef TIME_ISO8601_HXX #define TIME_ISO8601_HXX -#include "StringBuffer.hxx" -#include "Compiler.h" +#include "util/StringBuffer.hxx" +#include "util/Compiler.h" #include #include diff --git a/src/util/TimeParser.cxx b/src/time/Parser.cxx similarity index 98% rename from src/util/TimeParser.cxx rename to src/time/Parser.cxx index 5576b5a7d..b09bba14f 100644 --- a/src/util/TimeParser.cxx +++ b/src/time/Parser.cxx @@ -27,8 +27,8 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TimeParser.hxx" -#include "Compiler.h" +#include "Parser.hxx" +#include "util/Compiler.h" #include diff --git a/src/util/TimeParser.hxx b/src/time/Parser.hxx similarity index 100% rename from src/util/TimeParser.hxx rename to src/time/Parser.hxx diff --git a/src/time/meson.build b/src/time/meson.build new file mode 100644 index 000000000..2c53f7848 --- /dev/null +++ b/src/time/meson.build @@ -0,0 +1,11 @@ +time = static_library( + 'time', + 'Parser.cxx', + 'Convert.cxx', + 'ISO8601.cxx', + include_directories: inc, +) + +time_dep = declare_dependency( + link_with: time, +) diff --git a/src/util/meson.build b/src/util/meson.build index 2384b96a3..98c28eb36 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -16,9 +16,6 @@ util = static_library( 'SplitString.cxx', 'FormatString.cxx', 'Tokenizer.cxx', - 'TimeParser.cxx', - 'TimeConvert.cxx', - 'TimeISO8601.cxx', 'UriUtil.cxx', 'LazyRandomEngine.cxx', 'HugeAllocator.cxx', diff --git a/test/run_storage.cxx b/test/run_storage.cxx index 562b6d1e1..9db2215f4 100644 --- a/test/run_storage.cxx +++ b/test/run_storage.cxx @@ -22,7 +22,7 @@ #include "storage/StorageInterface.hxx" #include "storage/FileInfo.hxx" #include "net/Init.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include "util/PrintException.hxx" #include diff --git a/test/test_translate_song.cxx b/test/test_translate_song.cxx index cac712785..8c2b41ebb 100644 --- a/test/test_translate_song.cxx +++ b/test/test_translate_song.cxx @@ -17,7 +17,7 @@ #include "storage/StorageInterface.hxx" #include "storage/plugins/LocalStorage.hxx" #include "Mapper.hxx" -#include "util/ChronoUtil.hxx" +#include "time/ChronoUtil.hxx" #include From e7411c0c4b325a6b1316d60a16184b70263c61bd Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 May 2019 16:08:54 +0200 Subject: [PATCH 14/34] time/Convert: add `pure` attributes --- src/time/Convert.hxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/time/Convert.hxx b/src/time/Convert.hxx index 5f75cd40f..bbe893bee 100644 --- a/src/time/Convert.hxx +++ b/src/time/Convert.hxx @@ -33,6 +33,8 @@ #ifndef TIME_CONVERT_HXX #define TIME_CONVERT_HXX +#include "util/Compiler.h" + #include /** @@ -52,6 +54,7 @@ LocalTime(std::chrono::system_clock::time_point tp); /** * Convert a UTC-based "struct tm" to a UTC-based time point. */ +gcc_pure std::chrono::system_clock::time_point TimeGm(struct tm &tm); @@ -60,6 +63,7 @@ TimeGm(struct tm &tm); /** * Convert a local "struct tm" to a UTC-based time point. */ +gcc_pure std::chrono::system_clock::time_point MakeTime(struct tm &tm); From 80e55f6bfc8be136af0f603c6c6b9c3b3b435a7b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 May 2019 16:09:27 +0200 Subject: [PATCH 15/34] time/Convert: add `noexcept` --- src/time/Convert.cxx | 4 ++-- src/time/Convert.hxx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/time/Convert.cxx b/src/time/Convert.cxx index 5f2b8bdb5..fcb19dbe5 100644 --- a/src/time/Convert.cxx +++ b/src/time/Convert.cxx @@ -69,7 +69,7 @@ LocalTime(std::chrono::system_clock::time_point tp) #ifdef __GLIBC__ std::chrono::system_clock::time_point -TimeGm(struct tm &tm) +TimeGm(struct tm &tm) noexcept { return std::chrono::system_clock::from_time_t(timegm(&tm)); } @@ -77,7 +77,7 @@ TimeGm(struct tm &tm) #endif std::chrono::system_clock::time_point -MakeTime(struct tm &tm) +MakeTime(struct tm &tm) noexcept { return std::chrono::system_clock::from_time_t(mktime(&tm)); } diff --git a/src/time/Convert.hxx b/src/time/Convert.hxx index bbe893bee..a84ecdf02 100644 --- a/src/time/Convert.hxx +++ b/src/time/Convert.hxx @@ -56,7 +56,7 @@ LocalTime(std::chrono::system_clock::time_point tp); */ gcc_pure std::chrono::system_clock::time_point -TimeGm(struct tm &tm); +TimeGm(struct tm &tm) noexcept; #endif @@ -65,6 +65,6 @@ TimeGm(struct tm &tm); */ gcc_pure std::chrono::system_clock::time_point -MakeTime(struct tm &tm); +MakeTime(struct tm &tm) noexcept; #endif From ae35df1126198d5d20bbd3c3a37c731bf5d3b388 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 May 2019 16:11:14 +0200 Subject: [PATCH 16/34] zeroconf/AvahiPoll: move TimevalToChrono() to time/Convert.cxx --- src/time/Convert.cxx | 7 +++++++ src/time/Convert.hxx | 4 ++++ src/zeroconf/AvahiPoll.cxx | 11 +++-------- src/zeroconf/meson.build | 1 + 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/time/Convert.cxx b/src/time/Convert.cxx index fcb19dbe5..044cfd303 100644 --- a/src/time/Convert.cxx +++ b/src/time/Convert.cxx @@ -81,3 +81,10 @@ MakeTime(struct tm &tm) noexcept { return std::chrono::system_clock::from_time_t(mktime(&tm)); } + +std::chrono::steady_clock::duration +ToSteadyClockDuration(const struct timeval &tv) noexcept +{ + return std::chrono::steady_clock::duration(std::chrono::seconds(tv.tv_sec)) + + std::chrono::steady_clock::duration(std::chrono::microseconds(tv.tv_usec)); +} diff --git a/src/time/Convert.hxx b/src/time/Convert.hxx index a84ecdf02..558bd2e37 100644 --- a/src/time/Convert.hxx +++ b/src/time/Convert.hxx @@ -67,4 +67,8 @@ gcc_pure std::chrono::system_clock::time_point MakeTime(struct tm &tm) noexcept; +gcc_pure +std::chrono::steady_clock::duration +ToSteadyClockDuration(const struct timeval &tv) noexcept; + #endif diff --git a/src/zeroconf/AvahiPoll.cxx b/src/zeroconf/AvahiPoll.cxx index eeb669b23..cc62e67e4 100644 --- a/src/zeroconf/AvahiPoll.cxx +++ b/src/zeroconf/AvahiPoll.cxx @@ -20,6 +20,7 @@ #include "AvahiPoll.hxx" #include "event/SocketMonitor.hxx" #include "event/TimerEvent.hxx" +#include "time/Convert.hxx" static unsigned FromAvahiWatchEvent(AvahiWatchEvent e) @@ -78,12 +79,6 @@ private: } }; -static constexpr std::chrono::steady_clock::duration -TimevalToChrono(const timeval &tv) -{ - return std::chrono::seconds(tv.tv_sec) + std::chrono::microseconds(tv.tv_usec); -} - struct AvahiTimeout final { TimerEvent timer; @@ -97,12 +92,12 @@ public: :timer(_loop, BIND_THIS_METHOD(OnTimeout)), callback(_callback), userdata(_userdata) { if (tv != nullptr) - timer.Schedule(TimevalToChrono(*tv)); + timer.Schedule(ToSteadyClockDuration(*tv)); } static void TimeoutUpdate(AvahiTimeout *t, const struct timeval *tv) { if (tv != nullptr) - t->timer.Schedule(TimevalToChrono(*tv)); + t->timer.Schedule(ToSteadyClockDuration(*tv)); else t->timer.Cancel(); } diff --git a/src/zeroconf/meson.build b/src/zeroconf/meson.build index 2858079cb..c2853b344 100644 --- a/src/zeroconf/meson.build +++ b/src/zeroconf/meson.build @@ -67,6 +67,7 @@ else dependencies: [ libavahi_client_dep, dbus_dep, + time_dep, ], ) From b826fd71f05bd56796c83cb19ac351e462e77251 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 May 2019 16:14:12 +0200 Subject: [PATCH 17/34] time/Convert: mention exceptions --- src/time/Convert.hxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/time/Convert.hxx b/src/time/Convert.hxx index 558bd2e37..835d804a1 100644 --- a/src/time/Convert.hxx +++ b/src/time/Convert.hxx @@ -39,12 +39,16 @@ /** * Convert a UTC-based time point to a UTC-based "struct tm". + * + * Throws on error. */ struct tm GmTime(std::chrono::system_clock::time_point tp); /** * Convert a UTC-based time point to a local "struct tm". + * + * Throws on error. */ struct tm LocalTime(std::chrono::system_clock::time_point tp); From f885807ecc1d81606dc8fae16092ae6dccc7402e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 May 2019 16:23:58 +0200 Subject: [PATCH 18/34] time/Convert: update copyright --- src/time/Convert.cxx | 2 +- src/time/Convert.hxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/time/Convert.cxx b/src/time/Convert.cxx index 044cfd303..518e7a325 100644 --- a/src/time/Convert.cxx +++ b/src/time/Convert.cxx @@ -1,5 +1,5 @@ /* - * Copyright 2007-2017 Content Management AG + * Copyright 2007-2019 Content Management AG * All rights reserved. * * author: Max Kellermann diff --git a/src/time/Convert.hxx b/src/time/Convert.hxx index 835d804a1..7e1330fcf 100644 --- a/src/time/Convert.hxx +++ b/src/time/Convert.hxx @@ -1,5 +1,5 @@ /* - * Copyright 2007-2017 Content Management AG + * Copyright 2007-2019 Content Management AG * All rights reserved. * * author: Max Kellermann From aeb89aa9d6f1c74fd87c6de17f9f5f4bfc41d612 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 8 May 2019 16:24:31 +0200 Subject: [PATCH 19/34] time/ISO8601: forward-declare StringBuffer --- src/song/ModifiedSinceSongFilter.cxx | 1 + src/time/ISO8601.cxx | 3 ++- src/time/ISO8601.hxx | 7 ++++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/song/ModifiedSinceSongFilter.cxx b/src/song/ModifiedSinceSongFilter.cxx index e490bce8a..b26ebbf28 100644 --- a/src/song/ModifiedSinceSongFilter.cxx +++ b/src/song/ModifiedSinceSongFilter.cxx @@ -20,6 +20,7 @@ #include "ModifiedSinceSongFilter.hxx" #include "LightSong.hxx" #include "time/ISO8601.hxx" +#include "util/StringBuffer.hxx" std::string ModifiedSinceSongFilter::ToExpression() const noexcept diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index b9ce08361..fb6d32899 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -1,5 +1,5 @@ /* - * Copyright 2007-2017 Content Management AG + * Copyright 2007-2019 Content Management AG * All rights reserved. * * author: Max Kellermann @@ -33,6 +33,7 @@ #include "ISO8601.hxx" #include "Convert.hxx" #include "Parser.hxx" +#include "util/StringBuffer.hxx" StringBuffer<64> FormatISO8601(const struct tm &tm) noexcept diff --git a/src/time/ISO8601.hxx b/src/time/ISO8601.hxx index 10a171d26..b6788a458 100644 --- a/src/time/ISO8601.hxx +++ b/src/time/ISO8601.hxx @@ -1,5 +1,5 @@ /* - * Copyright 2007-2017 Content Management AG + * Copyright 2007-2019 Content Management AG * All rights reserved. * * author: Max Kellermann @@ -33,13 +33,14 @@ #ifndef TIME_ISO8601_HXX #define TIME_ISO8601_HXX -#include "util/StringBuffer.hxx" #include "util/Compiler.h" -#include #include +#include + struct tm; +template class StringBuffer; gcc_pure StringBuffer<64> From da882a6eb62b4070ce163f805c0cd61b0a2f1561 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 21 May 2019 10:13:16 +0200 Subject: [PATCH 20/34] time/Convert: include sys/time.h for struct timeval Closes https://github.com/MusicPlayerDaemon/MPD/issues/562 --- src/time/Convert.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/time/Convert.cxx b/src/time/Convert.cxx index 518e7a325..aff35620b 100644 --- a/src/time/Convert.cxx +++ b/src/time/Convert.cxx @@ -35,6 +35,7 @@ #include #include +#include /* for struct timeval */ struct tm GmTime(std::chrono::system_clock::time_point tp) From b02890eb8a1b014e575efe4130793d31a5ac0bb1 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:09:38 +0200 Subject: [PATCH 21/34] time/Parser: explicitly initialize struct tm before strptime() This is recommended by the strptime() manpage, because strptime() does not initialize/set attributes which were not specified in the format string. --- src/time/Parser.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time/Parser.cxx b/src/time/Parser.cxx index b09bba14f..369408efb 100644 --- a/src/time/Parser.cxx +++ b/src/time/Parser.cxx @@ -65,7 +65,7 @@ ParseTimePoint(const char *s, const char *format) (void)format; throw std::runtime_error("Time parsing not implemented on Windows"); #else - struct tm tm; + struct tm tm{}; const char *end = strptime(s, format, &tm); if (end == nullptr || *end != 0) throw std::runtime_error("Failed to parse time stamp"); From fed9b6fd7444d9947adccb40a93d349e362bb84c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:21:22 +0200 Subject: [PATCH 22/34] time/Parser: use TimeGm() --- src/time/Parser.cxx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/time/Parser.cxx b/src/time/Parser.cxx index 369408efb..c4d7288b6 100644 --- a/src/time/Parser.cxx +++ b/src/time/Parser.cxx @@ -28,6 +28,7 @@ */ #include "Parser.hxx" +#include "Convert.hxx" #include "util/Compiler.h" #include @@ -72,13 +73,11 @@ ParseTimePoint(const char *s, const char *format) #ifdef __GLIBC__ /* timegm() is a GNU extension */ - const auto t = timegm(&tm); + return TimeGm(tm); #else tm.tm_isdst = 0; const auto t = mktime(&tm) + GetTimeZoneOffset(); -#endif /* !__GLIBC__ */ - return std::chrono::system_clock::from_time_t(t); - +#endif /* !__GLIBC__ */ #endif /* !_WIN32 */ } From 2a8830db70e2c5513803b52ad4a95092f8ec17f7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:15:56 +0200 Subject: [PATCH 23/34] time/Convert: fallback TimeGm() implementation Move code from Parser.cxx. --- src/time/Convert.cxx | 35 +++++++++++++++++++++++++++++++---- src/time/Convert.hxx | 4 ---- src/time/Parser.cxx | 28 +--------------------------- src/time/Parser.hxx | 2 +- 4 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/time/Convert.cxx b/src/time/Convert.cxx index aff35620b..2351f4b52 100644 --- a/src/time/Convert.cxx +++ b/src/time/Convert.cxx @@ -67,15 +67,42 @@ LocalTime(std::chrono::system_clock::time_point tp) return *tm; } -#ifdef __GLIBC__ +#ifndef __GLIBC__ + +/** + * Determine the time zone offset in a portable way. + */ +gcc_const +static time_t +GetTimeZoneOffset() noexcept +{ + time_t t = 1234567890; + struct tm tm; + tm.tm_isdst = 0; +#ifdef _WIN32 + struct tm *p = gmtime(&t); +#else + struct tm *p = &tm; + gmtime_r(&t, p); +#endif + return t - mktime(&tm); +} + +#endif /* !__GLIBC__ */ std::chrono::system_clock::time_point TimeGm(struct tm &tm) noexcept { - return std::chrono::system_clock::from_time_t(timegm(&tm)); -} +#ifdef __GLIBC__ + /* timegm() is a GNU extension */ + const auto t = timegm(&tm); +#else + tm.tm_isdst = 0; + const auto t = mktime(&tm) + GetTimeZoneOffset(); +#endif /* !__GLIBC__ */ -#endif + return std::chrono::system_clock::from_time_t(t); +} std::chrono::system_clock::time_point MakeTime(struct tm &tm) noexcept diff --git a/src/time/Convert.hxx b/src/time/Convert.hxx index 7e1330fcf..2f6f3367a 100644 --- a/src/time/Convert.hxx +++ b/src/time/Convert.hxx @@ -53,8 +53,6 @@ GmTime(std::chrono::system_clock::time_point tp); struct tm LocalTime(std::chrono::system_clock::time_point tp); -#ifdef __GLIBC__ - /** * Convert a UTC-based "struct tm" to a UTC-based time point. */ @@ -62,8 +60,6 @@ gcc_pure std::chrono::system_clock::time_point TimeGm(struct tm &tm) noexcept; -#endif - /** * Convert a local "struct tm" to a UTC-based time point. */ diff --git a/src/time/Parser.cxx b/src/time/Parser.cxx index c4d7288b6..016a0e3d5 100644 --- a/src/time/Parser.cxx +++ b/src/time/Parser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 Max Kellermann + * Copyright 2014-2019 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,31 +29,12 @@ #include "Parser.hxx" #include "Convert.hxx" -#include "util/Compiler.h" #include #include #include -#if !defined(__GLIBC__) && !defined(_WIN32) - -/** - * Determine the time zone offset in a portable way. - */ -gcc_const -static time_t -GetTimeZoneOffset() noexcept -{ - time_t t = 1234567890; - struct tm tm; - tm.tm_isdst = 0; - gmtime_r(&t, &tm); - return t - mktime(&tm); -} - -#endif - std::chrono::system_clock::time_point ParseTimePoint(const char *s, const char *format) { @@ -71,13 +52,6 @@ ParseTimePoint(const char *s, const char *format) if (end == nullptr || *end != 0) throw std::runtime_error("Failed to parse time stamp"); -#ifdef __GLIBC__ - /* timegm() is a GNU extension */ return TimeGm(tm); -#else - tm.tm_isdst = 0; - const auto t = mktime(&tm) + GetTimeZoneOffset(); - return std::chrono::system_clock::from_time_t(t); -#endif /* !__GLIBC__ */ #endif /* !_WIN32 */ } diff --git a/src/time/Parser.hxx b/src/time/Parser.hxx index 97aa0c016..bf92afa72 100644 --- a/src/time/Parser.hxx +++ b/src/time/Parser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 Max Kellermann + * Copyright 2014-2019 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions From 4859ea468f37dcfe6cac2a70b6867146911b673e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:26:43 +0200 Subject: [PATCH 24/34] time/ISO8601: implement with strptime(), without ParseTimePoint() Prepare for adding more flexible parsing. --- src/time/ISO8601.cxx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index fb6d32899..3aaa40eab 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -32,9 +32,12 @@ #include "ISO8601.hxx" #include "Convert.hxx" -#include "Parser.hxx" #include "util/StringBuffer.hxx" +#include + +#include + StringBuffer<64> FormatISO8601(const struct tm &tm) noexcept { @@ -58,5 +61,18 @@ FormatISO8601(std::chrono::system_clock::time_point tp) std::chrono::system_clock::time_point ParseISO8601(const char *s) { - return ParseTimePoint(s, "%FT%TZ"); + assert(s != nullptr); + +#ifdef _WIN32 + /* TODO: emulate strptime()? */ + (void)s; + throw std::runtime_error("Time parsing not implemented on Windows"); +#else + struct tm tm{}; + const char *end = strptime(s, "%FT%TZ", &tm); + if (end == nullptr || *end != 0) + throw std::runtime_error("Failed to parse time stamp"); + + return TimeGm(tm); +#endif /* !_WIN32 */ } From 5df2707d980c371695d0c4d558d3ca3c25936123 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:28:14 +0200 Subject: [PATCH 25/34] time/ISO8601: ParseISO8601() returns precision --- src/song/Filter.cxx | 2 +- src/time/ISO8601.cxx | 9 +++++++-- src/time/ISO8601.hxx | 12 +++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/song/Filter.cxx b/src/song/Filter.cxx index 9e1c4590a..9c0583845 100644 --- a/src/song/Filter.cxx +++ b/src/song/Filter.cxx @@ -120,7 +120,7 @@ ParseTimeStamp(const char *s) return std::chrono::system_clock::from_time_t((time_t)value); /* try ISO 8601 */ - return ParseISO8601(s); + return ParseISO8601(s).first; } static constexpr bool diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index 3aaa40eab..a7435630a 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -58,7 +58,8 @@ FormatISO8601(std::chrono::system_clock::time_point tp) return FormatISO8601(GmTime(tp)); } -std::chrono::system_clock::time_point +std::pair ParseISO8601(const char *s) { assert(s != nullptr); @@ -73,6 +74,10 @@ ParseISO8601(const char *s) if (end == nullptr || *end != 0) throw std::runtime_error("Failed to parse time stamp"); - return TimeGm(tm); + std::chrono::system_clock::duration precision = std::chrono::seconds(1); + + auto tp = TimeGm(tm); + + return std::make_pair(tp, precision); #endif /* !_WIN32 */ } diff --git a/src/time/ISO8601.hxx b/src/time/ISO8601.hxx index b6788a458..061e8f529 100644 --- a/src/time/ISO8601.hxx +++ b/src/time/ISO8601.hxx @@ -36,6 +36,7 @@ #include "util/Compiler.h" #include +#include #include @@ -50,7 +51,16 @@ gcc_pure StringBuffer<64> FormatISO8601(std::chrono::system_clock::time_point tp); -std::chrono::system_clock::time_point +/** + * Parse a time stamp in ISO8601 format. + * + * Throws on error. + * + * @return a pair consisting of the time point and the specified + * precision; e.g. for a date, the second value is "one day" + */ +std::pair ParseISO8601(const char *s); #endif From bbe403f141f8d37cc3776d72891ffe4523e59b26 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:33:21 +0200 Subject: [PATCH 26/34] test/TestISO8601: unit test for time/ISO8601 --- test/TestISO8601.cxx | 57 ++++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 13 ++++++++++ 2 files changed, 70 insertions(+) create mode 100644 test/TestISO8601.cxx diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx new file mode 100644 index 000000000..59849a0b8 --- /dev/null +++ b/test/TestISO8601.cxx @@ -0,0 +1,57 @@ +/* + * Copyright 2019 Content Management AG + * All rights reserved. + * + * author: Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "time/ISO8601.hxx" + +#include + +static constexpr struct { + const char *s; + time_t t; + std::chrono::system_clock::duration d; +} parse_tests[] = { + /* full ISO8601 */ + { "1970-01-01T00:00:00Z", 0, std::chrono::seconds(1) }, + { "1970-01-01T00:00:01Z", 1, std::chrono::seconds(1) }, + { "2019-02-04T16:46:41Z", 1549298801, std::chrono::seconds(1) }, + { "2018-12-31T23:59:59Z", 1546300799, std::chrono::seconds(1) }, + { "2019-01-01T00:00:00Z", 1546300800, std::chrono::seconds(1) }, +}; + +TEST(ISO8601, Parse) +{ + for (const auto &i : parse_tests) { + const auto result = ParseISO8601(i.s); + EXPECT_EQ(std::chrono::system_clock::to_time_t(result.first), i.t); + EXPECT_EQ(result.second, i.d); + } +} diff --git a/test/meson.build b/test/meson.build index 7345283d9..dab10ecb2 100644 --- a/test/meson.build +++ b/test/meson.build @@ -46,6 +46,19 @@ test('TestUtil', executable( ], )) +test( + 'TestTime', + executable( + 'TestTime', + 'TestISO8601.cxx', + include_directories: inc, + dependencies: [ + time_dep, + gtest_dep, + ], + ), +) + test('TestRewindInputStream', executable( 'TestRewindInputStream', 'TestRewindInputStream.cxx', From ba4cd47fd8cb853f1b4e04c7dcb5221bb0c05150 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:30:31 +0200 Subject: [PATCH 27/34] time/ISO8601: allow omitting the time of day --- NEWS | 2 ++ src/time/ISO8601.cxx | 26 ++++++++++++++++++++++---- test/TestISO8601.cxx | 6 ++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 67f5807b4..8a8c7f905 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ ver 0.21.17 (not yet released) +* protocol + - relax the ISO 8601 parser: allow omitting the time of day * archive - zzip: improve error reporting * outputs diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index a7435630a..4811813fb 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -70,14 +70,32 @@ ParseISO8601(const char *s) throw std::runtime_error("Time parsing not implemented on Windows"); #else struct tm tm{}; - const char *end = strptime(s, "%FT%TZ", &tm); - if (end == nullptr || *end != 0) - throw std::runtime_error("Failed to parse time stamp"); - std::chrono::system_clock::duration precision = std::chrono::seconds(1); + /* parse the date */ + const char *end = strptime(s, "%F", &tm); + if (end == nullptr) + throw std::runtime_error("Failed to parse date"); + + s = end; + + std::chrono::system_clock::duration precision = std::chrono::hours(24); + + /* parse the time of day */ + if (*s == 'T') { + ++s; + end = strptime(s, "%TZ", &tm); + if (end == nullptr) + throw std::runtime_error("Failed to parse time of day"); + + s = end; + precision = std::chrono::seconds(1); + } auto tp = TimeGm(tm); + if (*s != 0) + throw std::runtime_error("Garbage at end of time stamp"); + return std::make_pair(tp, precision); #endif /* !_WIN32 */ } diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx index 59849a0b8..d5e8dd2dc 100644 --- a/test/TestISO8601.cxx +++ b/test/TestISO8601.cxx @@ -45,6 +45,12 @@ static constexpr struct { { "2019-02-04T16:46:41Z", 1549298801, std::chrono::seconds(1) }, { "2018-12-31T23:59:59Z", 1546300799, std::chrono::seconds(1) }, { "2019-01-01T00:00:00Z", 1546300800, std::chrono::seconds(1) }, + + /* only date */ + { "1970-01-01", 0, std::chrono::hours(24) }, + { "2019-02-04", 1549238400, std::chrono::hours(24) }, + { "2018-12-31", 1546214400, std::chrono::hours(24) }, + { "2019-01-01", 1546300800, std::chrono::hours(24) }, }; TEST(ISO8601, Parse) From b06825829bbc71876dc8ba2c3d7acdc96e9f208e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:35:47 +0200 Subject: [PATCH 28/34] time/ISO8601: allow omitting the "Z" suffix And allow "Z" suffix after date. --- NEWS | 3 ++- src/time/ISO8601.cxx | 5 ++++- test/TestISO8601.cxx | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 8a8c7f905..986f22c87 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ ver 0.21.17 (not yet released) * protocol - - relax the ISO 8601 parser: allow omitting the time of day + - relax the ISO 8601 parser: allow omitting the time of day and the "Z" + suffix * archive - zzip: improve error reporting * outputs diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index 4811813fb..f08938e4e 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -83,7 +83,7 @@ ParseISO8601(const char *s) /* parse the time of day */ if (*s == 'T') { ++s; - end = strptime(s, "%TZ", &tm); + end = strptime(s, "%T", &tm); if (end == nullptr) throw std::runtime_error("Failed to parse time of day"); @@ -93,6 +93,9 @@ ParseISO8601(const char *s) auto tp = TimeGm(tm); + if (*s == 'Z') + ++s; + if (*s != 0) throw std::runtime_error("Garbage at end of time stamp"); diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx index d5e8dd2dc..6fdc11dc8 100644 --- a/test/TestISO8601.cxx +++ b/test/TestISO8601.cxx @@ -51,6 +51,12 @@ static constexpr struct { { "2019-02-04", 1549238400, std::chrono::hours(24) }, { "2018-12-31", 1546214400, std::chrono::hours(24) }, { "2019-01-01", 1546300800, std::chrono::hours(24) }, + + /* date with time zone */ + { "2019-02-04Z", 1549238400, std::chrono::hours(24) }, + + /* without time zone */ + { "2019-02-04T16:46:41", 1549298801, std::chrono::seconds(1) }, }; TEST(ISO8601, Parse) From 7d8b1860c315ca403560385d6b426877f2e8e46b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:38:06 +0200 Subject: [PATCH 29/34] time/ISO8601: support time zone offset --- src/time/ISO8601.cxx | 53 ++++++++++++++++++++++++++++++++++++++++++++ test/TestISO8601.cxx | 6 +++++ 2 files changed, 59 insertions(+) diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index f08938e4e..6671a8ca4 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -58,6 +58,56 @@ FormatISO8601(std::chrono::system_clock::time_point tp) return FormatISO8601(GmTime(tp)); } +static std::pair +ParseTimeZoneOffsetRaw(const char *&s) +{ + char *endptr; + unsigned long value = strtoul(s, &endptr, 10); + if (endptr == s + 4) { + s = endptr; + return std::make_pair(value / 100, value % 100); + } else if (endptr == s + 2) { + s = endptr; + + unsigned hours = value, minutes = 0; + if (*s == ':') { + ++s; + minutes = strtoul(s, &endptr, 10); + if (endptr != s + 2) + throw std::runtime_error("Failed to parse time zone offset"); + + s = endptr; + } + + return std::make_pair(hours, minutes); + } else + throw std::runtime_error("Failed to parse time zone offset"); +} + +static std::chrono::system_clock::duration +ParseTimeZoneOffset(const char *&s) +{ + assert(*s == '+' || *s == '-'); + + bool negative = *s == '-'; + ++s; + + auto raw = ParseTimeZoneOffsetRaw(s); + if (raw.first > 13) + throw std::runtime_error("Time offset hours out of range"); + + if (raw.second >= 60) + throw std::runtime_error("Time offset minutes out of range"); + + std::chrono::system_clock::duration d = std::chrono::hours(raw.first); + d += std::chrono::minutes(raw.second); + + if (negative) + d = -d; + + return d; +} + std::pair ParseISO8601(const char *s) @@ -93,8 +143,11 @@ ParseISO8601(const char *s) auto tp = TimeGm(tm); + /* time zone */ if (*s == 'Z') ++s; + else if (*s == '+' || *s == '-') + tp -= ParseTimeZoneOffset(s); if (*s != 0) throw std::runtime_error("Garbage at end of time stamp"); diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx index 6fdc11dc8..e97798831 100644 --- a/test/TestISO8601.cxx +++ b/test/TestISO8601.cxx @@ -57,6 +57,12 @@ static constexpr struct { /* without time zone */ { "2019-02-04T16:46:41", 1549298801, std::chrono::seconds(1) }, + + /* with time zone */ + { "2019-02-04T16:46:41+02", 1549291601, std::chrono::seconds(1) }, + { "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) }, + { "2019-02-04T16:46:41+02:00", 1549291601, std::chrono::seconds(1) }, + { "2019-02-04T16:46:41-0200", 1549306001, std::chrono::seconds(1) }, }; TEST(ISO8601, Parse) From d09bd9178f30e9e8bb1034efec7d713f53e924df Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 19 Aug 2019 22:37:16 +0200 Subject: [PATCH 30/34] time/ISO8601: support omitting seconds --- src/time/ISO8601.cxx | 9 ++++++--- test/TestISO8601.cxx | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index 6671a8ca4..f4d0d5d38 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -133,12 +133,15 @@ ParseISO8601(const char *s) /* parse the time of day */ if (*s == 'T') { ++s; - end = strptime(s, "%T", &tm); - if (end == nullptr) + + if ((end = strptime(s, "%T", &tm)) != nullptr) + precision = std::chrono::seconds(1); + else if ((end = strptime(s, "%H:%M", &tm)) != nullptr) + precision = std::chrono::minutes(1); + else throw std::runtime_error("Failed to parse time of day"); s = end; - precision = std::chrono::seconds(1); } auto tp = TimeGm(tm); diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx index e97798831..6b2506cce 100644 --- a/test/TestISO8601.cxx +++ b/test/TestISO8601.cxx @@ -58,6 +58,10 @@ static constexpr struct { /* without time zone */ { "2019-02-04T16:46:41", 1549298801, std::chrono::seconds(1) }, + /* without seconds */ + { "2019-02-04T16:46", 1549298760, std::chrono::minutes(1) }, + { "2019-02-04T16:46Z", 1549298760, std::chrono::minutes(1) }, + /* with time zone */ { "2019-02-04T16:46:41+02", 1549291601, std::chrono::seconds(1) }, { "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) }, From 63c5d6601626285e32456eebb26ee898dd968f0c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 2 Sep 2019 17:13:54 +0200 Subject: [PATCH 31/34] time/ISO8601: support omitting minutes --- src/time/ISO8601.cxx | 2 ++ test/TestISO8601.cxx | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index f4d0d5d38..b92a0e743 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -138,6 +138,8 @@ ParseISO8601(const char *s) precision = std::chrono::seconds(1); else if ((end = strptime(s, "%H:%M", &tm)) != nullptr) precision = std::chrono::minutes(1); + else if ((end = strptime(s, "%H", &tm)) != nullptr) + precision = std::chrono::hours(1); else throw std::runtime_error("Failed to parse time of day"); diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx index 6b2506cce..3305b47de 100644 --- a/test/TestISO8601.cxx +++ b/test/TestISO8601.cxx @@ -62,6 +62,10 @@ static constexpr struct { { "2019-02-04T16:46", 1549298760, std::chrono::minutes(1) }, { "2019-02-04T16:46Z", 1549298760, std::chrono::minutes(1) }, + /* without minutes */ + { "2019-02-04T16", 1549296000, std::chrono::hours(1) }, + { "2019-02-04T16Z", 1549296000, std::chrono::hours(1) }, + /* with time zone */ { "2019-02-04T16:46:41+02", 1549291601, std::chrono::seconds(1) }, { "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) }, From b7744be20806a9cc3697f0806eb452012e7180be Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 16 Dec 2019 23:28:47 +0100 Subject: [PATCH 32/34] song/Filter: try ParseISO8601() first Prepare for allowing ISO8601 time stamps without delimiters, such as 20191216, and prevent them from being interpreted as numeric UNIX time stamps. --- src/song/Filter.cxx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/song/Filter.cxx b/src/song/Filter.cxx index 9c0583845..e369c8c93 100644 --- a/src/song/Filter.cxx +++ b/src/song/Filter.cxx @@ -113,14 +113,19 @@ ParseTimeStamp(const char *s) { assert(s != nullptr); - char *endptr; - unsigned long long value = strtoull(s, &endptr, 10); - if (*endptr == 0 && endptr > s) - /* it's an integral UNIX time stamp */ - return std::chrono::system_clock::from_time_t((time_t)value); + try { + /* try ISO 8601 */ + return ParseISO8601(s).first; + } catch (...) { + char *endptr; + unsigned long long value = strtoull(s, &endptr, 10); + if (*endptr == 0 && endptr > s) + /* it's an integral UNIX time stamp */ + return std::chrono::system_clock::from_time_t((time_t)value); - /* try ISO 8601 */ - return ParseISO8601(s).first; + /* rethrow the ParseISO8601() error */ + throw; + } } static constexpr bool From 15ce8eb48793b3d74fea041e8fe6b8fdbb328a04 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 16 Dec 2019 23:15:48 +0100 Subject: [PATCH 33/34] time/ISO8601: support omitting field separators Closes https://github.com/MusicPlayerDaemon/MPD/issues/685 --- NEWS | 4 ++-- src/time/ISO8601.cxx | 14 ++++++++++++-- test/TestISO8601.cxx | 9 +++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 986f22c87..1467ddb97 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,7 @@ ver 0.21.17 (not yet released) * protocol - - relax the ISO 8601 parser: allow omitting the time of day and the "Z" - suffix + - relax the ISO 8601 parser: allow omitting field separators, the + time of day and the "Z" suffix * archive - zzip: improve error reporting * outputs diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx index b92a0e743..5cb4c486c 100644 --- a/src/time/ISO8601.cxx +++ b/src/time/ISO8601.cxx @@ -123,8 +123,12 @@ ParseISO8601(const char *s) /* parse the date */ const char *end = strptime(s, "%F", &tm); - if (end == nullptr) - throw std::runtime_error("Failed to parse date"); + if (end == nullptr) { + /* try without field separators */ + end = strptime(s, "%Y%m%d", &tm); + if (end == nullptr) + throw std::runtime_error("Failed to parse date"); + } s = end; @@ -136,6 +140,12 @@ ParseISO8601(const char *s) if ((end = strptime(s, "%T", &tm)) != nullptr) precision = std::chrono::seconds(1); + else if ((end = strptime(s, "%H%M%S", &tm)) != nullptr) + /* no field separators */ + precision = std::chrono::seconds(1); + else if ((end = strptime(s, "%H%M", &tm)) != nullptr) + /* no field separators */ + precision = std::chrono::minutes(1); else if ((end = strptime(s, "%H:%M", &tm)) != nullptr) precision = std::chrono::minutes(1); else if ((end = strptime(s, "%H", &tm)) != nullptr) diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx index 3305b47de..cd0897c1a 100644 --- a/test/TestISO8601.cxx +++ b/test/TestISO8601.cxx @@ -71,6 +71,15 @@ static constexpr struct { { "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) }, { "2019-02-04T16:46:41+02:00", 1549291601, std::chrono::seconds(1) }, { "2019-02-04T16:46:41-0200", 1549306001, std::chrono::seconds(1) }, + + /* without field separators */ + { "19700101T000000Z", 0, std::chrono::seconds(1) }, + { "19700101T000001Z", 1, std::chrono::seconds(1) }, + { "20190204T164641Z", 1549298801, std::chrono::seconds(1) }, + { "19700101", 0, std::chrono::hours(24) }, + { "20190204", 1549238400, std::chrono::hours(24) }, + { "20190204T1646", 1549298760, std::chrono::minutes(1) }, + { "20190204T16", 1549296000, std::chrono::hours(1) }, }; TEST(ISO8601, Parse) From 5680a3a4b71661179b81e6434928d919d186c785 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 16 Dec 2019 23:32:44 +0100 Subject: [PATCH 34/34] release v0.21.17 --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 1467ddb97..a597a4b82 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -ver 0.21.17 (not yet released) +ver 0.21.17 (2019/12/16) * protocol - relax the ISO 8601 parser: allow omitting field separators, the time of day and the "Z" suffix