From 9d071d2a2cec9a7662a02669488569a017f0ea35 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Mon, 13 Feb 2017 21:25:04 -0700 Subject: 8.9 --- zen/time.h | 725 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 368 insertions(+), 357 deletions(-) mode change 100644 => 100755 zen/time.h (limited to 'zen/time.h') diff --git a/zen/time.h b/zen/time.h old mode 100644 new mode 100755 index a4613408..1b6a3c92 --- a/zen/time.h +++ b/zen/time.h @@ -1,357 +1,368 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#ifndef TIME_H_8457092814324342453627 -#define TIME_H_8457092814324342453627 - -#include -#include "string_tools.h" - - -namespace zen -{ -struct TimeComp //replaces std::tm and SYSTEMTIME -{ - int year = 0; // - - int month = 0; //1-12 - int day = 0; //1-31 - int hour = 0; //0-23 - int minute = 0; //0-59 - int second = 0; //0-60 (including leap second) -}; - -TimeComp localTime (time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components -time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error - -//---------------------------------------------------------------------------------------------------------------------------------- - -/* -format (current) date and time; example: - formatTime(L"%Y|%m|%d"); -> "2011|10|29" - formatTime(FORMAT_DATE); -> "2011-10-29" - formatTime(FORMAT_TIME); -> "17:55:34" -*/ -template -String formatTime(const String2& format, const TimeComp& comp = localTime()); //format as specified by "std::strftime", returns empty string on failure - -//the "format" parameter of formatTime() is partially specialized with the following type tags: -const struct FormatDateTag {} FORMAT_DATE = {}; //%x - locale dependent date representation: e.g. 08/23/01 -const struct FormatTimeTag {} FORMAT_TIME = {}; //%X - locale dependent time representation: e.g. 14:55:02 -const struct FormatDateTimeTag {} FORMAT_DATE_TIME = {}; //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001 - -const struct FormatIsoDateTag {} FORMAT_ISO_DATE = {}; //%Y-%m-%d - e.g. 2001-08-23 -const struct FormatIsoTimeTag {} FORMAT_ISO_TIME = {}; //%H:%M:%S - e.g. 14:55:02 -const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02 - -//---------------------------------------------------------------------------------------------------------------------------------- - -template -bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success - -//---------------------------------------------------------------------------------------------------------------------------------- - - - - - - - - - - - - - - - - -//############################ implementation ############################## -namespace implementation -{ -inline -std::tm toClibTimeComponents(const TimeComp& comp) -{ - assert(1 <= comp.month && comp.month <= 12 && - 1 <= comp.day && comp.day <= 31 && - 0 <= comp.hour && comp.hour <= 23 && - 0 <= comp.minute && comp.minute <= 59 && - 0 <= comp.second && comp.second <= 61); - - std::tm ctc = {}; - ctc.tm_year = comp.year - 1900; //years since 1900 - ctc.tm_mon = comp.month - 1; //0-11 - ctc.tm_mday = comp.day; //1-31 - ctc.tm_hour = comp.hour; //0-23 - ctc.tm_min = comp.minute; //0-59 - ctc.tm_sec = comp.second; //0-60 (including leap second) - ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available - //ctc.tm_wday - //ctc.tm_yday - return ctc; -} - -inline -TimeComp toZenTimeComponents(const std::tm& ctc) -{ - TimeComp comp; - comp.year = ctc.tm_year + 1900; - comp.month = ctc.tm_mon + 1; - comp.day = ctc.tm_mday; - comp.hour = ctc.tm_hour; - comp.minute = ctc.tm_min; - comp.second = ctc.tm_sec; - return comp; -} - - -template -struct GetFormat; //get default time formats as char* or wchar_t* - -template <> -struct GetFormat //%x - locale dependent date representation: e.g. 08/23/01 -{ - const char* format(char) const { return "%x"; } - const wchar_t* format(wchar_t) const { return L"%x"; } -}; - -template <> -struct GetFormat //%X - locale dependent time representation: e.g. 14:55:02 -{ - const char* format(char) const { return "%X"; } - const wchar_t* format(wchar_t) const { return L"%X"; } -}; - -template <> -struct GetFormat //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001 -{ - const char* format(char) const { return "%c"; } - const wchar_t* format(wchar_t) const { return L"%c"; } -}; - -template <> -struct GetFormat //%Y-%m-%d - e.g. 2001-08-23 -{ - const char* format(char) const { return "%Y-%m-%d"; } - const wchar_t* format(wchar_t) const { return L"%Y-%m-%d"; } -}; - -template <> -struct GetFormat //%H:%M:%S - e.g. 14:55:02 -{ - const char* format(char) const { return "%H:%M:%S"; } - const wchar_t* format(wchar_t) const { return L"%H:%M:%S"; } -}; - -template <> -struct GetFormat //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02 -{ - const char* format(char) const { return "%Y-%m-%d %H:%M:%S"; } - const wchar_t* format(wchar_t) const { return L"%Y-%m-%d %H:%M:%S"; } -}; - - -//strftime() craziness on invalid input: -// VS 2010: CRASH unless "_invalid_parameter_handler" is set: https://msdn.microsoft.com/en-us/library/ksazx244.aspx -// GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst! -inline -size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const std::tm* timeptr) -{ - return std::strftime(buffer, bufferSize, format, timeptr); -} - - -inline -size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const std::tm* timeptr) -{ - return std::wcsftime(buffer, bufferSize, format, timeptr); -} - -/* -inline -bool isValid(const std::tm& t) -{ - -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions: - _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE) - _VALIDATE_RETURN(( timeptr->tm_year >= -1900 ) && ( timeptr->tm_year <= 8099 ), EINVAL, FALSE) - -> also std::mktime does *not* help here at all! - - auto inRange = [](int value, int minVal, int maxVal) { return minVal <= value && value <= maxVal; }; - - //http://www.cplusplus.com/reference/clibrary/ctime/tm/ - return inRange(t.tm_sec , 0, 61) && - inRange(t.tm_min , 0, 59) && - inRange(t.tm_hour, 0, 23) && - inRange(t.tm_mday, 1, 31) && - inRange(t.tm_mon , 0, 11) && - //tm_year - inRange(t.tm_wday, 0, 6) && - inRange(t.tm_yday, 0, 365); - //tm_isdst -}; -*/ - -template inline -size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const std::tm* timeptr) -{ -#if defined _MSC_VER && !defined NDEBUG - //there's no way around: application init must register an invalid parameter handler that does nothing !!! - //=> strftime will abort with 0 and set errno to EINVAL instead of CRASHING THE APPLICATION! - _invalid_parameter_handler oldHandler = _set_invalid_parameter_handler(nullptr); - assert(oldHandler); - _set_invalid_parameter_handler(oldHandler); -#endif - - return strftimeWrap_impl(buffer, bufferSize, format, timeptr); -} - - -struct UserDefinedFormatTag {}; -struct PredefinedFormatTag {}; - -template inline -String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure -{ - using CharType = typename GetCharType::Type; - std::tm ctc = toClibTimeComponents(comp); - std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday - //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent - - CharType buffer[256] = {}; - const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc); - return String(buffer, charsWritten); -} - -template inline -String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) -{ - using CharType = typename GetCharType::Type; - return formatTime(GetFormat().format(CharType()), comp, UserDefinedFormatTag()); -} -} - - -inline -TimeComp localTime(time_t utc) -{ - std::tm lt = {}; - -#ifdef ZEN_WIN //use thread-safe variants of std::localtime()! - if (::localtime_s(<, &utc) != 0) -#else - if (::localtime_r(&utc, <) == nullptr) -#endif - return TimeComp(); - - return implementation::toZenTimeComponents(lt); -} - - -inline -time_t localToTimeT(const TimeComp& comp) //returns -1 on error -{ - std::tm ctc = implementation::toClibTimeComponents(comp); - return std::mktime(&ctc); -} - - -template inline -String formatTime(const String2& format, const TimeComp& comp) -{ - using FormatTag = typename SelectIf< - IsSameType::value || - IsSameType::value || - IsSameType::value || - IsSameType::value || - IsSameType::value || - IsSameType::value, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Type; - - return implementation::formatTime(format, comp, FormatTag()); -} - - -template -bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success -{ - using CharType = typename GetCharType::Type; - static_assert(IsSameType::Type>::value, ""); - - const CharType* itFmt = strBegin(format); - const CharType* const fmtLast = itFmt + strLength(format); - - const CharType* itStr = strBegin(str); - const CharType* const strLast = itStr + strLength(str); - - auto extractNumber = [&](int& result, size_t digitCount) -> bool - { - if (strLast - itStr < makeSigned(digitCount)) - return false; - - if (std::any_of(itStr, itStr + digitCount, [](CharType c) { return !isDigit(c); })) - return false; - - result = zen::stringTo(StringRef(itStr, itStr + digitCount)); - itStr += digitCount; - return true; - }; - - for (; itFmt != fmtLast; ++itFmt) - { - const CharType fmt = *itFmt; - - if (fmt == '%') - { - ++itFmt; - if (itFmt == fmtLast) - return false; - - switch (*itFmt) - { - case 'Y': - if (!extractNumber(comp.year, 4)) - return false; - break; - case 'm': - if (!extractNumber(comp.month, 2)) - return false; - break; - case 'd': - if (!extractNumber(comp.day, 2)) - return false; - break; - case 'H': - if (!extractNumber(comp.hour, 2)) - return false; - break; - case 'M': - if (!extractNumber(comp.minute, 2)) - return false; - break; - case 'S': - if (!extractNumber(comp.second, 2)) - return false; - break; - default: - return false; - } - } - else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars - { - while (itStr != strLast && isWhiteSpace(*itStr)) - ++itStr; - } - else - { - if (itStr == strLast || *itStr != fmt) - return false; - ++itStr; - } - } - - return itStr == strLast; -} -} - -#endif //TIME_H_8457092814324342453627 +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef TIME_H_8457092814324342453627 +#define TIME_H_8457092814324342453627 + +#include +#include "string_tools.h" + + +namespace zen +{ +struct TimeComp //replaces std::tm and SYSTEMTIME +{ + int year = 0; // - + int month = 0; //1-12 + int day = 0; //1-31 + int hour = 0; //0-23 + int minute = 0; //0-59 + int second = 0; //0-60 (including leap second) +}; + +TimeComp getLocalTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components +time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error + +TimeComp getUtcTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to UTC time components +time_t utcToTimeT(const TimeComp& comp); //convert UTC time components to time_t (UTC), returns -1 on error + +//---------------------------------------------------------------------------------------------------------------------------------- + +/* +format (current) date and time; example: + formatTime(L"%Y|%m|%d"); -> "2011|10|29" + formatTime(FORMAT_DATE); -> "2011-10-29" + formatTime(FORMAT_TIME); -> "17:55:34" +*/ +template +String formatTime(const String2& format, const TimeComp& comp = getLocalTime()); //format as specified by "std::strftime", returns empty string on failure + +//the "format" parameter of formatTime() is partially specialized with the following type tags: +const struct FormatDateTag {} FORMAT_DATE = {}; //%x - locale dependent date representation: e.g. 08/23/01 +const struct FormatTimeTag {} FORMAT_TIME = {}; //%X - locale dependent time representation: e.g. 14:55:02 +const struct FormatDateTimeTag {} FORMAT_DATE_TIME = {}; //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001 + +const struct FormatIsoDateTag {} FORMAT_ISO_DATE = {}; //%Y-%m-%d - e.g. 2001-08-23 +const struct FormatIsoTimeTag {} FORMAT_ISO_TIME = {}; //%H:%M:%S - e.g. 14:55:02 +const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02 + +//---------------------------------------------------------------------------------------------------------------------------------- + +template +bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success + +//---------------------------------------------------------------------------------------------------------------------------------- + + + + + + + + + + + + + + + + +//############################ implementation ############################## +namespace implementation +{ +inline +std::tm toClibTimeComponents(const TimeComp& comp) +{ + assert(1 <= comp.month && comp.month <= 12 && + 1 <= comp.day && comp.day <= 31 && + 0 <= comp.hour && comp.hour <= 23 && + 0 <= comp.minute && comp.minute <= 59 && + 0 <= comp.second && comp.second <= 61); + + std::tm ctc = {}; + ctc.tm_year = comp.year - 1900; //years since 1900 + ctc.tm_mon = comp.month - 1; //0-11 + ctc.tm_mday = comp.day; //1-31 + ctc.tm_hour = comp.hour; //0-23 + ctc.tm_min = comp.minute; //0-59 + ctc.tm_sec = comp.second; //0-60 (including leap second) + ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available + //ctc.tm_wday + //ctc.tm_yday + return ctc; +} + +inline +TimeComp toZenTimeComponents(const std::tm& ctc) +{ + TimeComp comp; + comp.year = ctc.tm_year + 1900; + comp.month = ctc.tm_mon + 1; + comp.day = ctc.tm_mday; + comp.hour = ctc.tm_hour; + comp.minute = ctc.tm_min; + comp.second = ctc.tm_sec; + return comp; +} + + +template +struct GetFormat; //get default time formats as char* or wchar_t* + +template <> +struct GetFormat //%x - locale dependent date representation: e.g. 08/23/01 +{ + const char* format(char) const { return "%x"; } + const wchar_t* format(wchar_t) const { return L"%x"; } +}; + +template <> +struct GetFormat //%X - locale dependent time representation: e.g. 14:55:02 +{ + const char* format(char) const { return "%X"; } + const wchar_t* format(wchar_t) const { return L"%X"; } +}; + +template <> +struct GetFormat //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001 +{ + const char* format(char) const { return "%c"; } + const wchar_t* format(wchar_t) const { return L"%c"; } +}; + +template <> +struct GetFormat //%Y-%m-%d - e.g. 2001-08-23 +{ + const char* format(char) const { return "%Y-%m-%d"; } + const wchar_t* format(wchar_t) const { return L"%Y-%m-%d"; } +}; + +template <> +struct GetFormat //%H:%M:%S - e.g. 14:55:02 +{ + const char* format(char) const { return "%H:%M:%S"; } + const wchar_t* format(wchar_t) const { return L"%H:%M:%S"; } +}; + +template <> +struct GetFormat //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02 +{ + const char* format(char) const { return "%Y-%m-%d %H:%M:%S"; } + const wchar_t* format(wchar_t) const { return L"%Y-%m-%d %H:%M:%S"; } +}; + + +//strftime() craziness on invalid input: +// VS 2010: CRASH unless "_invalid_parameter_handler" is set: https://msdn.microsoft.com/en-us/library/ksazx244.aspx +// GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst! +inline +size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const std::tm* timeptr) +{ + return std::strftime(buffer, bufferSize, format, timeptr); +} + + +inline +size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const std::tm* timeptr) +{ + return std::wcsftime(buffer, bufferSize, format, timeptr); +} + +/* +inline +bool isValid(const std::tm& t) +{ + -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions: + _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE) + _VALIDATE_RETURN(( timeptr->tm_year >= -1900 ) && ( timeptr->tm_year <= 8099 ), EINVAL, FALSE) + -> also std::mktime does *not* help here at all! + + auto inRange = [](int value, int minVal, int maxVal) { return minVal <= value && value <= maxVal; }; + + //http://www.cplusplus.com/reference/clibrary/ctime/tm/ + return inRange(t.tm_sec , 0, 61) && + inRange(t.tm_min , 0, 59) && + inRange(t.tm_hour, 0, 23) && + inRange(t.tm_mday, 1, 31) && + inRange(t.tm_mon , 0, 11) && + //tm_year + inRange(t.tm_wday, 0, 6) && + inRange(t.tm_yday, 0, 365); + //tm_isdst +}; +*/ + +template inline +size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const std::tm* timeptr) +{ + + return strftimeWrap_impl(buffer, bufferSize, format, timeptr); +} + + +struct UserDefinedFormatTag {}; +struct PredefinedFormatTag {}; + +template inline +String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure +{ + using CharType = typename GetCharType::Type; + std::tm ctc = toClibTimeComponents(comp); + std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday + //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent + + CharType buffer[256] = {}; + const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc); + return String(buffer, charsWritten); +} + +template inline +String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) +{ + using CharType = typename GetCharType::Type; + return formatTime(GetFormat().format(CharType()), comp, UserDefinedFormatTag()); +} +} + + +inline +TimeComp getLocalTime(time_t utc) +{ + std::tm comp = {}; + if (::localtime_r(&utc, &comp) == nullptr) + return TimeComp(); + + return implementation::toZenTimeComponents(comp); +} + + +inline +TimeComp getUtcTime(time_t utc) +{ + std::tm comp = {}; + if (::gmtime_r(&utc, &comp) == nullptr) + return TimeComp(); + + return implementation::toZenTimeComponents(comp); +} + + +inline +time_t localToTimeT(const TimeComp& comp) //returns -1 on error +{ + std::tm ctc = implementation::toClibTimeComponents(comp); + return std::mktime(&ctc); +} + + +inline +time_t utcToTimeT(const TimeComp& comp) //returns -1 on error +{ + std::tm ctc = implementation::toClibTimeComponents(comp); + ctc.tm_isdst = 0; //"Zero (0) to indicate that standard time is in effect" => unused by _mkgmtime, but take no chances + return ::timegm(&ctc); +} + + +template inline +String formatTime(const String2& format, const TimeComp& comp) +{ + using FormatTag = typename SelectIf< + IsSameType::value || + IsSameType::value || + IsSameType::value || + IsSameType::value || + IsSameType::value || + IsSameType::value, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Type; + + return implementation::formatTime(format, comp, FormatTag()); +} + + +template +bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success +{ + using CharType = typename GetCharType::Type; + static_assert(IsSameType::Type>::value, ""); + + const CharType* itFmt = strBegin(format); + const CharType* const fmtLast = itFmt + strLength(format); + + const CharType* itStr = strBegin(str); + const CharType* const strLast = itStr + strLength(str); + + auto extractNumber = [&](int& result, size_t digitCount) -> bool + { + if (strLast - itStr < makeSigned(digitCount)) + return false; + + if (std::any_of(itStr, itStr + digitCount, [](CharType c) { return !isDigit(c); })) + return false; + + result = zen::stringTo(StringRef(itStr, itStr + digitCount)); + itStr += digitCount; + return true; + }; + + for (; itFmt != fmtLast; ++itFmt) + { + const CharType fmt = *itFmt; + + if (fmt == '%') + { + ++itFmt; + if (itFmt == fmtLast) + return false; + + switch (*itFmt) + { + case 'Y': + if (!extractNumber(comp.year, 4)) + return false; + break; + case 'm': + if (!extractNumber(comp.month, 2)) + return false; + break; + case 'd': + if (!extractNumber(comp.day, 2)) + return false; + break; + case 'H': + if (!extractNumber(comp.hour, 2)) + return false; + break; + case 'M': + if (!extractNumber(comp.minute, 2)) + return false; + break; + case 'S': + if (!extractNumber(comp.second, 2)) + return false; + break; + default: + return false; + } + } + else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars + { + while (itStr != strLast && isWhiteSpace(*itStr)) + ++itStr; + } + else + { + if (itStr == strLast || *itStr != fmt) + return false; + ++itStr; + } + } + + return itStr == strLast; +} +} + +#endif //TIME_H_8457092814324342453627 -- cgit