diff --git a/Makefile.am b/Makefile.am index f381ef23a..de02ebaa2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -429,6 +429,7 @@ libutil_a_SOURCES = \ src/util/FormatString.cxx src/util/FormatString.hxx \ src/util/Tokenizer.cxx src/util/Tokenizer.hxx \ src/util/TextFile.hxx \ + src/util/TimeParser.cxx src/util/TimeParser.hxx \ src/util/UriUtil.cxx src/util/UriUtil.hxx \ src/util/Manual.hxx \ src/util/RefCount.hxx \ diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx index 8919439ef..d2bbc9b32 100644 --- a/src/SongFilter.cxx +++ b/src/SongFilter.cxx @@ -25,9 +25,12 @@ #include "util/ConstBuffer.hxx" #include "util/StringAPI.hxx" #include "util/ASCII.hxx" +#include "util/TimeParser.hxx" #include "util/UriUtil.hxx" #include "lib/icu/Collate.hxx" +#include + #include #include @@ -180,24 +183,6 @@ SongFilter::~SongFilter() /* this destructor exists here just so it won't get inlined */ } -#if !defined(__GLIBC__) && !defined(WIN32) - -/** - * Determine the time zone offset in a portable way. - */ -gcc_const -static time_t -GetTimeZoneOffset() -{ - time_t t = 1234567890; - struct tm tm; - tm.tm_isdst = 0; - gmtime_r(&t, &tm); - return t - mktime(&tm); -} - -#endif - gcc_pure static time_t ParseTimeStamp(const char *s) @@ -210,26 +195,13 @@ ParseTimeStamp(const char *s) /* it's an integral UNIX time stamp */ return (time_t)value; -#ifdef WIN32 - /* TODO: emulate strptime()? */ - return 0; -#else - /* try ISO 8601 */ - - struct tm tm; - const char *end = strptime(s, "%FT%TZ", &tm); - if (end == nullptr || *end != 0) + try { + /* try ISO 8601 */ + const auto t = ParseTimePoint(s, "%FT%TZ"); + return std::chrono::system_clock::to_time_t(t); + } catch (const std::runtime_error &) { return 0; - -#ifdef __GLIBC__ - /* timegm() is a GNU extension */ - return timegm(&tm); -#else - tm.tm_isdst = 0; - return mktime(&tm) + GetTimeZoneOffset(); -#endif /* !__GLIBC__ */ - -#endif /* !WIN32 */ + } } bool diff --git a/src/util/TimeParser.cxx b/src/util/TimeParser.cxx new file mode 100644 index 000000000..b42d93380 --- /dev/null +++ b/src/util/TimeParser.cxx @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014-2017 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 "TimeParser.hxx" + +#include + +#include +#include + +#if !defined(__GLIBC__) && !defined(WIN32) + +/** + * Determine the time zone offset in a portable way. + */ +gcc_const +static time_t +GetTimeZoneOffset() +{ + 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) +{ + assert(s != nullptr); + assert(format != nullptr); + +#ifdef WIN32 + /* TODO: emulate strptime()? */ + throw std::runtime_error("Time parsing not implemented on Windows"); +#else + struct tm tm; + const char *end = strptime(s, format, &tm); + if (end == nullptr || *end != 0) + throw std::runtime_error("Failed to parse time stamp"); + +#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__ */ + + return std::chrono::system_clock::from_time_t(t); + +#endif /* !WIN32 */ +} diff --git a/src/util/TimeParser.hxx b/src/util/TimeParser.hxx new file mode 100644 index 000000000..4c7c66a77 --- /dev/null +++ b/src/util/TimeParser.hxx @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014-2017 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. + */ + +#ifndef TIME_PARSER_HXX +#define TIME_PARSER_HXX + +#include + +/** + * Parse a time stamp. + * + * Throws std::runtime_error on error. + */ +std::chrono::system_clock::time_point +ParseTimePoint(const char *s, const char *format); + +#endif