mirror of
https://github.com/HowardHinnant/date.git
synced 2025-06-30 19:41:03 +02:00
Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
6e921e1b1d | |||
39b4edf279 | |||
2ef74cb41a | |||
ac6ca2a095 | |||
ae017078c9 | |||
156e0a786e | |||
9a9a42db74 | |||
b5b765f928 | |||
77bd6b92a4 | |||
654b97091f | |||
b899774303 | |||
0e08b942c8 | |||
811be52e1c | |||
1c285d6545 | |||
bf79dd5a81 | |||
26fc2bd372 | |||
97246a638a | |||
432bab81f9 | |||
215cacff56 | |||
3cbfa4318f | |||
115dd428cf | |||
7848566815 | |||
393b52f21b | |||
0b72599bd4 | |||
ba99134b8a | |||
5e18488899 | |||
313189b0a8 | |||
057b441ceb | |||
d7a0bf1fa7 | |||
8140d979cd | |||
7990eae740 | |||
1ec2ea0295 | |||
658a3b9495 | |||
e7969c32e8 | |||
569b2d6785 | |||
abe3ada04f | |||
9537addfc4 | |||
6952fb50a6 | |||
fe2f9c7eac | |||
a6243ce56f | |||
a55f1a103b | |||
d544e5af25 | |||
cac99da8dc | |||
a088baf9a5 | |||
e6adff6754 | |||
d784766640 | |||
9343e31599 | |||
f43c39fcf1 | |||
e12095f26f | |||
7d811743e0 | |||
a2fdba1adc |
@ -7,7 +7,7 @@
|
||||
include( FetchContent )
|
||||
FetchContent_Declare( date_src
|
||||
GIT_REPOSITORY https://github.com/HowardHinnant/date.git
|
||||
GIT_TAG 2.4.2 # adjust tag/branch/commit as needed
|
||||
GIT_TAG v3.0.0 # adjust tag/branch/commit as needed
|
||||
)
|
||||
FetchContent_MakeAvailable(date_src)
|
||||
...
|
||||
@ -17,7 +17,8 @@
|
||||
|
||||
cmake_minimum_required( VERSION 3.7 )
|
||||
|
||||
project( date VERSION 2.4.1 )
|
||||
project( date VERSION 3.0.0 )
|
||||
set(ABI_VERSION 3) # used as SOVERSION, increment when ABI changes
|
||||
|
||||
include( GNUInstallDirs )
|
||||
|
||||
@ -76,56 +77,84 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
|
||||
# public headers will get installed:
|
||||
set_target_properties( date PROPERTIES PUBLIC_HEADER include/date/date.h )
|
||||
endif ()
|
||||
target_compile_definitions( date INTERFACE
|
||||
#To workaround libstdc++ issue https://github.com/HowardHinnant/date/issues/388
|
||||
ONLY_C_LOCALE=$<IF:$<BOOL:${COMPILE_WITH_C_LOCALE}>,1,0>
|
||||
$<$<BOOL:${DISABLE_STRING_VIEW}>:HAS_STRING_VIEW=0> )
|
||||
|
||||
# These used to be set with generator expressions,
|
||||
#
|
||||
# ONLY_C_LOCALE=$<IF:$<BOOL:${COMPILE_WITH_C_LOCALE}>,1,0>
|
||||
#
|
||||
# which expand in the output target file to, e.g.
|
||||
#
|
||||
# ONLY_C_LOCALE=$<IF:$<BOOL:FALSE>,1,0>
|
||||
#
|
||||
# This string is then (somtimes?) not correctly interpreted.
|
||||
if ( COMPILE_WITH_C_LOCALE )
|
||||
# To workaround libstdc++ issue https://github.com/HowardHinnant/date/issues/388
|
||||
target_compile_definitions( date INTERFACE ONLY_C_LOCALE=1 )
|
||||
else()
|
||||
target_compile_definitions( date INTERFACE ONLY_C_LOCALE=0 )
|
||||
endif()
|
||||
if ( DISABLE_STRING_VIEW )
|
||||
target_compile_definitions( date INTERFACE HAS_STRING_VIEW=0 -DHAS_DEDUCTION_GUIDES=0 )
|
||||
endif()
|
||||
|
||||
#[===================================================================[
|
||||
tz (compiled) library
|
||||
#]===================================================================]
|
||||
if( BUILD_TZ_LIB )
|
||||
add_library( tz )
|
||||
target_sources( tz
|
||||
add_library( date-tz )
|
||||
target_sources( date-tz
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:include>/date/tz.h
|
||||
$<$<BOOL:${IOS}>:$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:include>/date/ios.h>
|
||||
PRIVATE
|
||||
include/date/tz_private.h
|
||||
$<$<BOOL:${IOS}>:src/ios.mm>
|
||||
src/tz.cpp )
|
||||
add_library( date::tz ALIAS tz )
|
||||
target_link_libraries( tz PUBLIC date )
|
||||
target_include_directories( tz PUBLIC
|
||||
if ( IOS )
|
||||
target_sources( date-tz
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:include>/date/ios.h
|
||||
PRIVATE
|
||||
src/ios.mm )
|
||||
endif()
|
||||
add_library( date::date-tz ALIAS date-tz )
|
||||
target_link_libraries( date-tz PUBLIC date )
|
||||
target_include_directories( date-tz PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include> )
|
||||
target_compile_definitions( tz
|
||||
PRIVATE
|
||||
AUTO_DOWNLOAD=$<IF:$<OR:$<BOOL:${USE_SYSTEM_TZ_DB}>,$<BOOL:${MANUAL_TZ_DB}>>,0,1>
|
||||
HAS_REMOTE_API=$<IF:$<OR:$<BOOL:${USE_SYSTEM_TZ_DB}>,$<BOOL:${MANUAL_TZ_DB}>>,0,1>
|
||||
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${BUILD_SHARED_LIBS}>>:DATE_BUILD_DLL=1>
|
||||
$<$<BOOL:${USE_TZ_DB_IN_DOT}>:INSTALL=.>
|
||||
PUBLIC
|
||||
USE_OS_TZDB=$<IF:$<AND:$<BOOL:${USE_SYSTEM_TZ_DB}>,$<NOT:$<BOOL:${WIN32}>>,$<NOT:$<BOOL:${MANUAL_TZ_DB}>>>,1,0>
|
||||
INTERFACE
|
||||
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${BUILD_SHARED_LIBS}>>:DATE_USE_DLL=1> )
|
||||
|
||||
if ( USE_SYSTEM_TZ_DB OR MANUAL_TZ_DB )
|
||||
target_compile_definitions( date-tz PRIVATE AUTO_DOWNLOAD=0 HAS_REMOTE_API=0 )
|
||||
else()
|
||||
target_compile_definitions( date-tz PRIVATE AUTO_DOWNLOAD=1 HAS_REMOTE_API=1 )
|
||||
endif()
|
||||
|
||||
if ( USE_SYSTEM_TZ_DB AND NOT WIN32 AND NOT MANUAL_TZ_DB )
|
||||
target_compile_definitions( date-tz PRIVATE INSTALL=. PUBLIC USE_OS_TZDB=1 )
|
||||
else()
|
||||
target_compile_definitions( date-tz PUBLIC USE_OS_TZDB=0 )
|
||||
endif()
|
||||
|
||||
if ( WIN32 AND BUILD_SHARED_LIBS )
|
||||
target_compile_definitions( date-tz PUBLIC DATE_BUILD_DLL=1 )
|
||||
endif()
|
||||
|
||||
set(TZ_HEADERS include/date/tz.h)
|
||||
|
||||
if( IOS )
|
||||
list(APPEND TZ_HEADERS include/date/ios.h)
|
||||
endif( )
|
||||
set_target_properties( tz PROPERTIES
|
||||
set_target_properties( date-tz PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
PUBLIC_HEADER "${TZ_HEADERS}"
|
||||
VERSION "${PROJECT_VERSION}"
|
||||
SOVERSION "${PROJECT_VERSION}" )
|
||||
SOVERSION "${ABI_VERSION}" )
|
||||
if( NOT MSVC )
|
||||
find_package( Threads )
|
||||
target_link_libraries( tz PUBLIC Threads::Threads )
|
||||
target_link_libraries( date-tz PUBLIC Threads::Threads )
|
||||
endif( )
|
||||
if( NOT USE_SYSTEM_TZ_DB AND NOT MANUAL_TZ_DB )
|
||||
find_package( CURL REQUIRED )
|
||||
target_include_directories( tz SYSTEM PRIVATE ${CURL_INCLUDE_DIRS} )
|
||||
target_link_libraries( tz PRIVATE ${CURL_LIBRARIES} )
|
||||
target_include_directories( date-tz SYSTEM PRIVATE ${CURL_INCLUDE_DIRS} )
|
||||
target_link_libraries( date-tz PRIVATE ${CURL_LIBRARIES} )
|
||||
endif( )
|
||||
endif( )
|
||||
|
||||
@ -150,13 +179,13 @@ if (CMAKE_VERSION VERSION_LESS 3.15)
|
||||
endif ()
|
||||
|
||||
if( BUILD_TZ_LIB )
|
||||
install( TARGETS tz
|
||||
install( TARGETS date-tz
|
||||
EXPORT dateConfig
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # This is for Windows
|
||||
export( TARGETS tz NAMESPACE date:: APPEND FILE dateTargets.cmake )
|
||||
export( TARGETS date-tz NAMESPACE date:: APPEND FILE dateTargets.cmake )
|
||||
endif( )
|
||||
|
||||
if( WIN32 AND NOT CYGWIN)
|
||||
@ -179,7 +208,7 @@ if( ENABLE_DATE_TESTING )
|
||||
enable_testing( )
|
||||
|
||||
add_custom_target( testit COMMAND ${CMAKE_CTEST_COMMAND} )
|
||||
add_dependencies( testit tz )
|
||||
add_dependencies( testit date-tz )
|
||||
|
||||
function( add_pass_tests TEST_GLOB TEST_PREFIX )
|
||||
file( GLOB_RECURSE FILENAMES ${TEST_GLOB} )
|
||||
@ -193,7 +222,7 @@ if( ENABLE_DATE_TESTING )
|
||||
set( TST_NAME ${PREFIX}_test )
|
||||
add_executable( ${BIN_NAME} EXCLUDE_FROM_ALL ${TEST_FILE} )
|
||||
add_test( ${TST_NAME} ${BIN_NAME} )
|
||||
target_link_libraries( ${BIN_NAME} tz )
|
||||
target_link_libraries( ${BIN_NAME} date-tz )
|
||||
# HACK: because the test files don't use FQ includes:
|
||||
target_include_directories( ${BIN_NAME} PRIVATE include/date )
|
||||
add_dependencies( testit ${BIN_NAME} )
|
||||
@ -222,7 +251,7 @@ if( ENABLE_DATE_TESTING )
|
||||
${CMAKE_CXX_COMPILER}
|
||||
-std=c++14
|
||||
-L${CMAKE_BINARY_DIR}/
|
||||
-ltz
|
||||
-ldate-tz
|
||||
-I${PROJECT_SOURCE_DIR}/include
|
||||
-I${PROJECT_SOURCE_DIR}/include/date
|
||||
-o ${BIN_NAME}
|
||||
|
16
README.md
16
README.md
@ -5,7 +5,7 @@
|
||||
|
||||
---
|
||||
|
||||
**[Try it out on wandbox!](https://wandbox.org/permlink/L8MwjzSSC3fXXrMd)**
|
||||
**[Try it out on wandbox!](https://wandbox.org/permlink/oyXjibyF680HHoyS)**
|
||||
|
||||
## Summary
|
||||
|
||||
@ -43,6 +43,14 @@ Slightly modified versions of `"date.h"` and `"tz.h"` were voted into the C++20
|
||||
|
||||
## Build & Test
|
||||
|
||||
The recommended way to use any of these libraries besides `"tz.h"` is to just include it. These are header-only libraries (except `"tz.h"`).
|
||||
|
||||
To use `"tz.h"`, there is a single source file (`src/tz.cpp`) that needs to be compiled. Here are the recommended directions: https://howardhinnant.github.io/date/tz.html#Installation.
|
||||
|
||||
One can run tests by cd'ing into the `test` subdirectory and running `testit`. There are known failures on all platforms except for macOS. And even on macOS if C++11 is used. If any of these failures present problems for you, there exist workarounds.
|
||||
|
||||
Additionally there is _unsupported_ support for [vcpkg](https://github.com/Microsoft/vcpkg) and [CMake](https://cmake.org/). I don't personally use or maintain these systems as for me they cause more problems than they solve (for this small project). If you would like to contribute to these build systems please feel free to file a PR.
|
||||
|
||||
You can download and install Date using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
@ -58,7 +66,7 @@ You can optionally build using [CMake](https://cmake.org/). Here is a guide of h
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../
|
||||
cmake -DENABLE_DATE_TESTING=ON -DBUILD_TZ_LIB=ON ../
|
||||
cmake --build . --target testit # Consider '-- -j4' for multithreading
|
||||
```
|
||||
## Projects using this library
|
||||
@ -68,5 +76,9 @@ cmake --build . --target testit # Consider '-- -j4' for multithreading
|
||||
* https://github.com/ViewTouch/viewtouch
|
||||
* https://routinghub.com
|
||||
* https://github.com/valhalla
|
||||
* https://github.com/siodb/siodb
|
||||
* https://github.com/KomodoPlatform/atomicDEX-Pro
|
||||
* https://github.com/Kotlin/kotlinx-datetime
|
||||
* https://github.com/royalbee/jewish_date
|
||||
|
||||
If you would like your project (or product) on this list, just let me know.
|
||||
|
@ -1,8 +1,8 @@
|
||||
include( CMakeFindDependencyMacro )
|
||||
include( "${CMAKE_CURRENT_LIST_DIR}/dateTargets.cmake" )
|
||||
if( NOT MSVC AND TARGET date::tz )
|
||||
if( NOT MSVC AND TARGET date::date-tz )
|
||||
find_dependency( Threads REQUIRED)
|
||||
get_target_property( _tzill date::tz INTERFACE_LINK_LIBRARIES )
|
||||
get_target_property( _tzill date::date-tz INTERFACE_LINK_LIBRARIES )
|
||||
if( _tzill AND "${_tzill}" MATCHES "libcurl" )
|
||||
find_dependency( CURL )
|
||||
endif( )
|
||||
|
@ -45,9 +45,7 @@
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#if !(__cplusplus >= 201402)
|
||||
# include <cmath>
|
||||
#endif
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
@ -71,7 +69,7 @@
|
||||
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic push
|
||||
# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR > 7)
|
||||
# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7)
|
||||
# pragma GCC diagnostic ignored "-Wpedantic"
|
||||
# endif
|
||||
# if __GNUC__ < 5
|
||||
@ -138,7 +136,7 @@ namespace date
|
||||
#endif
|
||||
|
||||
#ifndef HAS_UNCAUGHT_EXCEPTIONS
|
||||
# if __cplusplus > 201703 || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
|
||||
# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
# define HAS_UNCAUGHT_EXCEPTIONS 1
|
||||
# else
|
||||
# define HAS_UNCAUGHT_EXCEPTIONS 0
|
||||
@ -1006,6 +1004,8 @@ struct is_clock<T, std::void_t<decltype(T::now()), typename T::rep, typename T::
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<class T> inline constexpr bool is_clock_v = is_clock<T>::value;
|
||||
|
||||
#endif // HAS_VOID_T
|
||||
|
||||
//----------------+
|
||||
@ -1022,6 +1022,7 @@ protected:
|
||||
std::basic_ios<CharT, Traits>& is_;
|
||||
CharT fill_;
|
||||
std::ios::fmtflags flags_;
|
||||
std::streamsize precision_;
|
||||
std::streamsize width_;
|
||||
std::basic_ostream<CharT, Traits>* tie_;
|
||||
std::locale loc_;
|
||||
@ -1031,6 +1032,7 @@ public:
|
||||
{
|
||||
is_.fill(fill_);
|
||||
is_.flags(flags_);
|
||||
is_.precision(precision_);
|
||||
is_.width(width_);
|
||||
is_.imbue(loc_);
|
||||
is_.tie(tie_);
|
||||
@ -1043,6 +1045,7 @@ public:
|
||||
: is_(is)
|
||||
, fill_(is.fill())
|
||||
, flags_(is.flags())
|
||||
, precision_(is.precision())
|
||||
, width_(is.width(0))
|
||||
, tie_(is.tie(nullptr))
|
||||
, loc_(is.getloc())
|
||||
@ -1166,7 +1169,11 @@ private:
|
||||
static const std::intmax_t d1 = R1::den / gcd_d1_d2;
|
||||
static const std::intmax_t n2 = R2::num / gcd_n1_n2;
|
||||
static const std::intmax_t d2 = R2::den / gcd_d1_d2;
|
||||
#ifdef __cpp_constexpr
|
||||
static const std::intmax_t max = std::numeric_limits<std::intmax_t>::max();
|
||||
#else
|
||||
static const std::intmax_t max = LLONG_MAX;
|
||||
#endif
|
||||
|
||||
template <std::intmax_t Xp, std::intmax_t Yp, bool overflow>
|
||||
struct mul // overflow == false
|
||||
@ -1354,6 +1361,47 @@ using std::chrono::abs;
|
||||
|
||||
#endif // HAS_CHRONO_ROUNDING
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class To, class Rep, class Period>
|
||||
CONSTCD14
|
||||
inline
|
||||
typename std::enable_if
|
||||
<
|
||||
!std::chrono::treat_as_floating_point<typename To::rep>::value,
|
||||
To
|
||||
>::type
|
||||
round_i(const std::chrono::duration<Rep, Period>& d)
|
||||
{
|
||||
return round<To>(d);
|
||||
}
|
||||
|
||||
template <class To, class Rep, class Period>
|
||||
CONSTCD14
|
||||
inline
|
||||
typename std::enable_if
|
||||
<
|
||||
std::chrono::treat_as_floating_point<typename To::rep>::value,
|
||||
To
|
||||
>::type
|
||||
round_i(const std::chrono::duration<Rep, Period>& d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
template <class To, class Clock, class FromDuration>
|
||||
CONSTCD11
|
||||
inline
|
||||
std::chrono::time_point<Clock, To>
|
||||
round_i(const std::chrono::time_point<Clock, FromDuration>& tp)
|
||||
{
|
||||
using std::chrono::time_point;
|
||||
return time_point<Clock, To>{round_i<To>(tp.time_since_epoch())};
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
// trunc towards zero
|
||||
template <class To, class Clock, class FromDuration>
|
||||
CONSTCD11
|
||||
@ -1458,16 +1506,29 @@ operator-(const day& x, const days& y) NOEXCEPT
|
||||
return x + -y;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const day& d)
|
||||
{
|
||||
detail::save_ostream<CharT, Traits> _(os);
|
||||
os.fill('0');
|
||||
os.flags(std::ios::dec | std::ios::right);
|
||||
os.width(2);
|
||||
os << static_cast<unsigned>(d);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
|
||||
{
|
||||
detail::low_level_fmt(os, d);
|
||||
if (!d.ok())
|
||||
os << " is not a valid day";
|
||||
return os;
|
||||
@ -1585,10 +1646,12 @@ operator-(const month& x, const months& y) NOEXCEPT
|
||||
return x + -y;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month& m)
|
||||
{
|
||||
if (m.ok())
|
||||
{
|
||||
@ -1596,7 +1659,20 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
|
||||
os << format(os.getloc(), fmt, m);
|
||||
}
|
||||
else
|
||||
os << static_cast<unsigned>(m) << " is not a valid month";
|
||||
os << static_cast<unsigned>(m);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
|
||||
{
|
||||
detail::low_level_fmt(os, m);
|
||||
if (!m.ok())
|
||||
os << " is not a valid month";
|
||||
return os;
|
||||
}
|
||||
|
||||
@ -1710,10 +1786,12 @@ operator-(const year& x, const years& y) NOEXCEPT
|
||||
return year{static_cast<int>(x) - y.count()};
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year& y)
|
||||
{
|
||||
detail::save_ostream<CharT, Traits> _(os);
|
||||
os.fill('0');
|
||||
@ -1721,6 +1799,17 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
|
||||
os.width(4 + (y < year{0}));
|
||||
os.imbue(std::locale::classic());
|
||||
os << static_cast<int>(y);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
|
||||
{
|
||||
detail::low_level_fmt(os, y);
|
||||
if (!y.ok())
|
||||
os << " is not a valid year";
|
||||
return os;
|
||||
@ -1846,10 +1935,12 @@ operator-(const weekday& x, const days& y) NOEXCEPT
|
||||
return x + -y;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
|
||||
{
|
||||
if (wd.ok())
|
||||
{
|
||||
@ -1857,7 +1948,20 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
|
||||
os << format(fmt, wd);
|
||||
}
|
||||
else
|
||||
os << static_cast<unsigned>(wd.wd_) << " is not a valid weekday";
|
||||
os << wd.c_encoding();
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
|
||||
{
|
||||
detail::low_level_fmt(os, wd);
|
||||
if (!wd.ok())
|
||||
os << " is not a valid weekday";
|
||||
return os;
|
||||
}
|
||||
|
||||
@ -1966,15 +2070,26 @@ weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCE
|
||||
# pragma GCC diagnostic pop
|
||||
#endif // __GNUC__
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
|
||||
{
|
||||
return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']';
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
|
||||
{
|
||||
os << wdi.weekday() << '[' << wdi.index();
|
||||
if (!(1 <= wdi.index() && wdi.index() <= 5))
|
||||
os << " is not a valid index";
|
||||
os << ']';
|
||||
detail::low_level_fmt(os, wdi);
|
||||
if (!wdi.ok())
|
||||
os << " is not a valid weekday_indexed";
|
||||
return os;
|
||||
}
|
||||
|
||||
@ -2024,12 +2139,27 @@ operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
|
||||
{
|
||||
return low_level_fmt(os, wdl.weekday()) << "[last]";
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
|
||||
{
|
||||
return os << wdl.weekday() << "[last]";
|
||||
detail::low_level_fmt(os, wdl);
|
||||
if (!wdl.ok())
|
||||
os << " is not a valid weekday_last";
|
||||
return os;
|
||||
}
|
||||
|
||||
CONSTCD11
|
||||
@ -2204,12 +2334,28 @@ operator-(const year_month& ym, const years& dy) NOEXCEPT
|
||||
return ym + -dy;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
|
||||
{
|
||||
low_level_fmt(os, ym.year()) << '/';
|
||||
return low_level_fmt(os, ym.month());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
|
||||
{
|
||||
return os << ym.year() << '/' << ym.month();
|
||||
detail::low_level_fmt(os, ym);
|
||||
if (!ym.ok())
|
||||
os << " is not a valid year_month";
|
||||
return os;
|
||||
}
|
||||
|
||||
// month_day
|
||||
@ -2289,12 +2435,28 @@ operator>=(const month_day& x, const month_day& y) NOEXCEPT
|
||||
return !(x < y);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day& md)
|
||||
{
|
||||
low_level_fmt(os, md.month()) << '/';
|
||||
return low_level_fmt(os, md.day());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md)
|
||||
{
|
||||
return os << md.month() << '/' << md.day();
|
||||
detail::low_level_fmt(os, md);
|
||||
if (!md.ok())
|
||||
os << " is not a valid month_day";
|
||||
return os;
|
||||
}
|
||||
|
||||
// month_day_last
|
||||
@ -2351,12 +2513,27 @@ operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT
|
||||
return !(x < y);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
|
||||
{
|
||||
return low_level_fmt(os, mdl.month()) << "/last";
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
|
||||
{
|
||||
return os << mdl.month() << "/last";
|
||||
detail::low_level_fmt(os, mdl);
|
||||
if (!mdl.ok())
|
||||
os << " is not a valid month_day_last";
|
||||
return os;
|
||||
}
|
||||
|
||||
// month_weekday
|
||||
@ -2403,12 +2580,28 @@ operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
|
||||
{
|
||||
low_level_fmt(os, mwd.month()) << '/';
|
||||
return low_level_fmt(os, mwd.weekday_indexed());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
|
||||
{
|
||||
return os << mwd.month() << '/' << mwd.weekday_indexed();
|
||||
detail::low_level_fmt(os, mwd);
|
||||
if (!mwd.ok())
|
||||
os << " is not a valid month_weekday";
|
||||
return os;
|
||||
}
|
||||
|
||||
// month_weekday_last
|
||||
@ -2455,12 +2648,28 @@ operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
|
||||
{
|
||||
low_level_fmt(os, mwdl.month()) << '/';
|
||||
return low_level_fmt(os, mwdl.weekday_last());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
|
||||
{
|
||||
return os << mwdl.month() << '/' << mwdl.weekday_last();
|
||||
detail::low_level_fmt(os, mwdl);
|
||||
if (!mwdl.ok())
|
||||
os << " is not a valid month_weekday_last";
|
||||
return os;
|
||||
}
|
||||
|
||||
// year_month_day_last
|
||||
@ -2610,12 +2819,28 @@ operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
|
||||
return !(x < y);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class CharT, class Traits>
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
|
||||
{
|
||||
low_level_fmt(os, ymdl.year()) << '/';
|
||||
return low_level_fmt(os, ymdl.month_day_last());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
|
||||
{
|
||||
return os << ymdl.year() << '/' << ymdl.month_day_last();
|
||||
detail::low_level_fmt(os, ymdl);
|
||||
if (!ymdl.ok())
|
||||
os << " is not a valid year_month_day_last";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<class>
|
||||
@ -2846,12 +3071,13 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd)
|
||||
os.fill('0');
|
||||
os.flags(std::ios::dec | std::ios::right);
|
||||
os.imbue(std::locale::classic());
|
||||
os << ymd.year() << '-';
|
||||
os << static_cast<int>(ymd.year()) << '-';
|
||||
os.width(2);
|
||||
os << static_cast<unsigned>(ymd.month()) << '-';
|
||||
os << ymd.day();
|
||||
os.width(2);
|
||||
os << static_cast<unsigned>(ymd.day());
|
||||
if (!ymd.ok())
|
||||
os << " is not a valid date";
|
||||
os << " is not a valid year_month_day";
|
||||
return os;
|
||||
}
|
||||
|
||||
@ -3087,8 +3313,12 @@ inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi)
|
||||
{
|
||||
return os << ymwdi.year() << '/' << ymwdi.month()
|
||||
<< '/' << ymwdi.weekday_indexed();
|
||||
detail::low_level_fmt(os, ymwdi.year()) << '/';
|
||||
detail::low_level_fmt(os, ymwdi.month()) << '/';
|
||||
detail::low_level_fmt(os, ymwdi.weekday_indexed());
|
||||
if (!ymwdi.ok())
|
||||
os << " is not a valid year_month_weekday";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<class>
|
||||
@ -3264,7 +3494,12 @@ inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl)
|
||||
{
|
||||
return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last();
|
||||
detail::low_level_fmt(os, ymwdl.year()) << '/';
|
||||
detail::low_level_fmt(os, ymwdl.month()) << '/';
|
||||
detail::low_level_fmt(os, ymwdl.weekday_last());
|
||||
if (!ymwdl.ok())
|
||||
os << " is not a valid year_month_weekday_last";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<class>
|
||||
@ -3681,11 +3916,12 @@ struct undocumented {explicit undocumented() = default;};
|
||||
// Example: width<4>::value == 2
|
||||
// Example: width<10>::value == 1
|
||||
// Example: width<1000>::value == 3
|
||||
template <std::uint64_t n, std::uint64_t d = 10, unsigned w = 0,
|
||||
bool should_continue = !(n < 2) && d != 0 && (w < 19)>
|
||||
template <std::uint64_t n, std::uint64_t d, unsigned w = 0,
|
||||
bool should_continue = n%d != 0 && (w < 19)>
|
||||
struct width
|
||||
{
|
||||
static CONSTDATA unsigned value = 1 + width<n, d%n*10, w+1>::value;
|
||||
static_assert(d > 0, "width called with zero denominator");
|
||||
static CONSTDATA unsigned value = 1 + width<n%d*10, d, w+1>::value;
|
||||
};
|
||||
|
||||
template <std::uint64_t n, std::uint64_t d, unsigned w>
|
||||
@ -3714,9 +3950,10 @@ class decimal_format_seconds
|
||||
{
|
||||
using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
|
||||
using rep = typename CT::rep;
|
||||
static unsigned CONSTDATA trial_width =
|
||||
detail::width<CT::period::num, CT::period::den>::value;
|
||||
public:
|
||||
static unsigned constexpr width = detail::width<CT::period::den>::value < 19 ?
|
||||
detail::width<CT::period::den>::value : 6u;
|
||||
static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u;
|
||||
using precision = std::chrono::duration<rep,
|
||||
std::ratio<1, static_pow10<width>::value>>;
|
||||
|
||||
@ -3732,8 +3969,7 @@ public:
|
||||
|
||||
CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT
|
||||
: s_(std::chrono::duration_cast<std::chrono::seconds>(d))
|
||||
, sub_s_(std::chrono::treat_as_floating_point<rep>::value ? d - s_ :
|
||||
std::chrono::duration_cast<precision>(d - s_))
|
||||
, sub_s_(std::chrono::duration_cast<precision>(d - s_))
|
||||
{}
|
||||
|
||||
CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;}
|
||||
@ -3766,6 +4002,7 @@ public:
|
||||
std::chrono::duration<rep> d = s_ + sub_s_;
|
||||
if (d < std::chrono::seconds{10})
|
||||
os << '0';
|
||||
os.precision(width+6);
|
||||
os << std::fixed << d.count();
|
||||
return os;
|
||||
}
|
||||
@ -3956,9 +4193,7 @@ make24(std::chrono::hours h, bool is_pm) NOEXCEPT
|
||||
template <class Duration>
|
||||
using time_of_day = hh_mm_ss<Duration>;
|
||||
|
||||
template <class Rep, class Period,
|
||||
class = typename std::enable_if
|
||||
<!std::chrono::treat_as_floating_point<Rep>::value>::type>
|
||||
template <class Rep, class Period>
|
||||
CONSTCD11
|
||||
inline
|
||||
hh_mm_ss<std::chrono::duration<Rep, Period>>
|
||||
@ -3971,8 +4206,7 @@ template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
typename std::enable_if
|
||||
<
|
||||
!std::chrono::treat_as_floating_point<typename Duration::rep>::value &&
|
||||
std::ratio_less<typename Duration::period, days::period>::value
|
||||
std::ratio_less<typename Duration::period, days::period>::value
|
||||
, std::basic_ostream<CharT, Traits>&
|
||||
>::type
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const sys_time<Duration>& tp)
|
||||
@ -4149,10 +4383,10 @@ operator+(std::basic_string<CharT, Traits, Alloc> x, const string_literal<CharT,
|
||||
&& (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150)
|
||||
|
||||
template <class CharT,
|
||||
class = std::enable_if_t<std::is_same<CharT, char>{} ||
|
||||
std::is_same<CharT, wchar_t>{} ||
|
||||
std::is_same<CharT, char16_t>{} ||
|
||||
std::is_same<CharT, char32_t>{}>>
|
||||
class = std::enable_if_t<std::is_same<CharT, char>::value ||
|
||||
std::is_same<CharT, wchar_t>::value ||
|
||||
std::is_same<CharT, char16_t>::value ||
|
||||
std::is_same<CharT, char32_t>::value>>
|
||||
CONSTCD14
|
||||
inline
|
||||
string_literal<CharT, 2>
|
||||
@ -4735,7 +4969,7 @@ scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
|
||||
is.setstate(std::ios::eofbit);
|
||||
break;
|
||||
}
|
||||
auto c = static_cast<char>(toupper(ic));
|
||||
auto c = static_cast<char>(toupper(static_cast<unsigned char>(ic)));
|
||||
bool consume = false;
|
||||
// For each keyword which might match, see if the indx character is c
|
||||
// If a match if found, consume c
|
||||
@ -4748,7 +4982,7 @@ scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
|
||||
{
|
||||
if (*st == might_match)
|
||||
{
|
||||
if (c == static_cast<char>(toupper((*ky)[indx])))
|
||||
if (c == static_cast<char>(toupper(static_cast<unsigned char>((*ky)[indx]))))
|
||||
{
|
||||
consume = true;
|
||||
if (ky->size() == indx+1)
|
||||
@ -6123,9 +6357,16 @@ long double
|
||||
read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10)
|
||||
{
|
||||
unsigned count = 0;
|
||||
unsigned fcount = 0;
|
||||
unsigned long long i = 0;
|
||||
unsigned long long f = 0;
|
||||
bool parsing_fraction = false;
|
||||
#if ONLY_C_LOCALE
|
||||
typename Traits::int_type decimal_point = '.';
|
||||
#else
|
||||
auto decimal_point = Traits::to_int_type(
|
||||
std::use_facet<std::numpunct<CharT>>(is.getloc()).decimal_point());
|
||||
std::string buf;
|
||||
#endif
|
||||
while (true)
|
||||
{
|
||||
auto ic = is.peek();
|
||||
@ -6133,18 +6374,25 @@ read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned
|
||||
break;
|
||||
if (Traits::eq_int_type(ic, decimal_point))
|
||||
{
|
||||
buf += '.';
|
||||
decimal_point = Traits::eof();
|
||||
is.get();
|
||||
parsing_fraction = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto c = static_cast<char>(Traits::to_char_type(ic));
|
||||
if (!('0' <= c && c <= '9'))
|
||||
break;
|
||||
buf += c;
|
||||
(void)is.get();
|
||||
if (!parsing_fraction)
|
||||
{
|
||||
i = 10*i + static_cast<unsigned>(c - '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
f = 10*f + static_cast<unsigned>(c - '0');
|
||||
++fcount;
|
||||
}
|
||||
}
|
||||
(void)is.get();
|
||||
if (++count == M)
|
||||
break;
|
||||
}
|
||||
@ -6153,7 +6401,7 @@ read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned
|
||||
is.setstate(std::ios::failbit);
|
||||
return 0;
|
||||
}
|
||||
return std::stold(buf);
|
||||
return i + f/std::pow(10.L, fcount);
|
||||
}
|
||||
|
||||
struct rs
|
||||
@ -6310,6 +6558,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
using std::chrono::seconds;
|
||||
using std::chrono::minutes;
|
||||
using std::chrono::hours;
|
||||
using detail::round_i;
|
||||
typename std::basic_istream<CharT, Traits>::sentry ok{is, true};
|
||||
if (ok)
|
||||
{
|
||||
@ -6325,7 +6574,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
auto modified = CharT{};
|
||||
auto width = -1;
|
||||
|
||||
CONSTDATA int not_a_year = numeric_limits<int>::min();
|
||||
CONSTDATA int not_a_year = numeric_limits<short>::min();
|
||||
CONSTDATA int not_a_2digit_year = 100;
|
||||
CONSTDATA int not_a_century = not_a_year / 100;
|
||||
CONSTDATA int not_a_month = 0;
|
||||
@ -6523,7 +6772,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
CharT{':'}, rld{S, 1, w});
|
||||
checked_set(H, tH, not_a_hour, is);
|
||||
checked_set(M, tM, not_a_minute, is);
|
||||
checked_set(s, round<Duration>(duration<long double>{S}),
|
||||
checked_set(s, round_i<Duration>(duration<long double>{S}),
|
||||
not_a_second, is);
|
||||
ws(is);
|
||||
int tY = not_a_year;
|
||||
@ -6603,7 +6852,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
CharT{':'}, rld{S, 1, w});
|
||||
checked_set(H, tH, not_a_hour, is);
|
||||
checked_set(M, tM, not_a_minute, is);
|
||||
checked_set(s, round<Duration>(duration<long double>{S}),
|
||||
checked_set(s, round_i<Duration>(duration<long double>{S}),
|
||||
not_a_second, is);
|
||||
#endif
|
||||
}
|
||||
@ -6919,7 +7168,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
#else
|
||||
auto nm = detail::ampm_names();
|
||||
auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
|
||||
tp = i;
|
||||
tp = static_cast<decltype(tp)>(i);
|
||||
#endif
|
||||
checked_set(p, tp, not_a_ampm, is);
|
||||
}
|
||||
@ -6960,7 +7209,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
CharT{':'}, rld{S, 1, w});
|
||||
checked_set(I, tI, not_a_hour_12_value, is);
|
||||
checked_set(M, tM, not_a_minute, is);
|
||||
checked_set(s, round<Duration>(duration<long double>{S}),
|
||||
checked_set(s, round_i<Duration>(duration<long double>{S}),
|
||||
not_a_second, is);
|
||||
ws(is);
|
||||
auto nm = detail::ampm_names();
|
||||
@ -7011,7 +7260,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
|
||||
long double S;
|
||||
read(is, rld{S, 1, width == -1 ? w : static_cast<unsigned>(width)});
|
||||
checked_set(s, round<Duration>(duration<long double>{S}),
|
||||
checked_set(s, round_i<Duration>(duration<long double>{S}),
|
||||
not_a_second, is);
|
||||
}
|
||||
#if !ONLY_C_LOCALE
|
||||
@ -7048,7 +7297,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
CharT{':'}, rld{S, 1, w});
|
||||
checked_set(H, tH, not_a_hour, is);
|
||||
checked_set(M, tM, not_a_minute, is);
|
||||
checked_set(s, round<Duration>(duration<long double>{S}),
|
||||
checked_set(s, round_i<Duration>(duration<long double>{S}),
|
||||
not_a_second, is);
|
||||
}
|
||||
else
|
||||
@ -7596,6 +7845,8 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
goto broken;
|
||||
}
|
||||
}
|
||||
else // I is ambiguous, AM or PM?
|
||||
goto broken;
|
||||
}
|
||||
}
|
||||
if (H != not_a_hour)
|
||||
@ -7751,6 +8002,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
std::chrono::minutes* offset = nullptr)
|
||||
{
|
||||
using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
|
||||
using detail::round_i;
|
||||
std::chrono::minutes offset_local{};
|
||||
auto offptr = offset ? offset : &offset_local;
|
||||
fields<CT> fds{};
|
||||
@ -7759,7 +8011,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
|
||||
is.setstate(std::ios::failbit);
|
||||
if (!is.fail())
|
||||
tp = round<Duration>(sys_days(fds.ymd) - *offptr + fds.tod.to_duration());
|
||||
tp = round_i<Duration>(sys_days(fds.ymd) - *offptr + fds.tod.to_duration());
|
||||
return is;
|
||||
}
|
||||
|
||||
@ -7770,13 +8022,14 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
|
||||
std::chrono::minutes* offset = nullptr)
|
||||
{
|
||||
using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
|
||||
using detail::round_i;
|
||||
fields<CT> fds{};
|
||||
fds.has_tod = true;
|
||||
from_stream(is, fmt, fds, abbrev, offset);
|
||||
if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
|
||||
is.setstate(std::ios::failbit);
|
||||
if (!is.fail())
|
||||
tp = round<Duration>(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration());
|
||||
tp = round_i<Duration>(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration());
|
||||
return is;
|
||||
}
|
||||
|
||||
|
@ -1655,9 +1655,12 @@ inline
|
||||
bool
|
||||
month_day::ok() const NOEXCEPT
|
||||
{
|
||||
CONSTDATA julian::day d[] =
|
||||
{31_d, 29_d, 31_d, 30_d, 31_d, 30_d, 31_d, 31_d, 30_d, 31_d, 30_d, 31_d};
|
||||
return m_.ok() && 1_d <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
|
||||
CONSTDATA julian::day d[] = {
|
||||
julian::day(31), julian::day(29), julian::day(31), julian::day(30),
|
||||
julian::day(31), julian::day(30), julian::day(31), julian::day(31),
|
||||
julian::day(30), julian::day(31), julian::day(30), julian::day(31)
|
||||
};
|
||||
return m_.ok() && julian::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
|
||||
}
|
||||
|
||||
CONSTCD11
|
||||
@ -1946,9 +1949,12 @@ inline
|
||||
day
|
||||
year_month_day_last::day() const NOEXCEPT
|
||||
{
|
||||
CONSTDATA julian::day d[] =
|
||||
{31_d, 28_d, 31_d, 30_d, 31_d, 30_d, 31_d, 31_d, 30_d, 31_d, 30_d, 31_d};
|
||||
return month() != feb || !y_.is_leap() ? d[static_cast<unsigned>(month())-1] : 29_d;
|
||||
CONSTDATA julian::day d[] = {
|
||||
julian::day(31), julian::day(28), julian::day(31), julian::day(30),
|
||||
julian::day(31), julian::day(30), julian::day(31), julian::day(31),
|
||||
julian::day(30), julian::day(31), julian::day(30), julian::day(31)
|
||||
};
|
||||
return month() != feb || !y_.is_leap() ? d[static_cast<unsigned>(month())-1] : julian::day(29);
|
||||
}
|
||||
|
||||
CONSTCD14
|
||||
@ -2190,7 +2196,7 @@ year_month_day::ok() const NOEXCEPT
|
||||
{
|
||||
if (!(y_.ok() && m_.ok()))
|
||||
return false;
|
||||
return 1_d <= d_ && d_ <= (y_/m_/last).day();
|
||||
return julian::day(1) <= d_ && d_ <= (y_/m_/last).day();
|
||||
}
|
||||
|
||||
CONSTCD11
|
||||
|
@ -36,6 +36,9 @@
|
||||
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
|
||||
// zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()};
|
||||
//
|
||||
// If the rule set is missing (everything starting with ','), then the rule is that the
|
||||
// alternate offset is never enabled.
|
||||
//
|
||||
// Note, Posix-style time zones are not recommended for all of the reasons described here:
|
||||
// https://stackoverflow.com/tags/timezone/info
|
||||
//
|
||||
@ -70,7 +73,8 @@ unsigned read_date(const string_t& s, unsigned i, rule& r);
|
||||
unsigned read_name(const string_t& s, unsigned i, std::string& name);
|
||||
unsigned read_signed_time(const string_t& s, unsigned i, std::chrono::seconds& t);
|
||||
unsigned read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t);
|
||||
unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u);
|
||||
unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
|
||||
const string_t& message = string_t{});
|
||||
|
||||
class rule
|
||||
{
|
||||
@ -87,11 +91,38 @@ public:
|
||||
|
||||
bool ok() const {return mode_ != off;}
|
||||
date::local_seconds operator()(date::year y) const;
|
||||
std::string to_string() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const rule& r);
|
||||
friend unsigned read_date(const string_t& s, unsigned i, rule& r);
|
||||
friend bool operator==(const rule& x, const rule& y);
|
||||
};
|
||||
|
||||
inline
|
||||
bool
|
||||
operator==(const rule& x, const rule& y)
|
||||
{
|
||||
if (x.mode_ != y.mode_)
|
||||
return false;
|
||||
switch (x.mode_)
|
||||
{
|
||||
case rule::J:
|
||||
case rule::N:
|
||||
return x.n_ == y.n_;
|
||||
case rule::M:
|
||||
return x.m_ == y.m_ && x.n_ == y.n_ && x.wd_ == y.wd_;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
operator!=(const rule& x, const rule& y)
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
inline
|
||||
date::local_seconds
|
||||
rule::operator()(date::year y) const
|
||||
@ -119,6 +150,62 @@ rule::operator()(date::year y) const
|
||||
return t;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
rule::to_string() const
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto print_offset = [](seconds off)
|
||||
{
|
||||
std::string nm;
|
||||
if (off != hours{2})
|
||||
{
|
||||
date::hh_mm_ss<seconds> offset{off};
|
||||
nm = '/';
|
||||
nm += std::to_string(offset.hours().count());
|
||||
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
|
||||
{
|
||||
nm += ':';
|
||||
if (offset.minutes() < minutes{10})
|
||||
nm += '0';
|
||||
nm += std::to_string(offset.minutes().count());
|
||||
if (offset.seconds() != seconds{0})
|
||||
{
|
||||
nm += ':';
|
||||
if (offset.seconds() < seconds{10})
|
||||
nm += '0';
|
||||
nm += std::to_string(offset.seconds().count());
|
||||
}
|
||||
}
|
||||
}
|
||||
return nm;
|
||||
};
|
||||
|
||||
std::string nm;
|
||||
switch (mode_)
|
||||
{
|
||||
case rule::J:
|
||||
nm = 'J';
|
||||
nm += std::to_string(n_);
|
||||
break;
|
||||
case rule::M:
|
||||
nm = 'M';
|
||||
nm += std::to_string(static_cast<unsigned>(m_));
|
||||
nm += '.';
|
||||
nm += std::to_string(n_);
|
||||
nm += '.';
|
||||
nm += std::to_string(wd_.c_encoding());
|
||||
break;
|
||||
case rule::N:
|
||||
nm = std::to_string(n_);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
nm += print_offset(time_);
|
||||
return nm;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const rule& r)
|
||||
@ -178,8 +265,80 @@ public:
|
||||
friend std::ostream& operator<<(std::ostream& os, const time_zone& z);
|
||||
|
||||
const time_zone* operator->() const {return this;}
|
||||
|
||||
std::string name() const;
|
||||
|
||||
friend bool operator==(const time_zone& x, const time_zone& y);
|
||||
|
||||
private:
|
||||
date::sys_seconds get_start(date::year y) const;
|
||||
date::sys_seconds get_prev_start(date::year y) const;
|
||||
date::sys_seconds get_next_start(date::year y) const;
|
||||
date::sys_seconds get_end(date::year y) const;
|
||||
date::sys_seconds get_prev_end(date::year y) const;
|
||||
date::sys_seconds get_next_end(date::year y) const;
|
||||
date::sys_info contant_offset() const;
|
||||
};
|
||||
|
||||
inline
|
||||
date::sys_seconds
|
||||
time_zone::get_start(date::year y) const
|
||||
{
|
||||
return date::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
|
||||
}
|
||||
|
||||
inline
|
||||
date::sys_seconds
|
||||
time_zone::get_prev_start(date::year y) const
|
||||
{
|
||||
return date::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()};
|
||||
}
|
||||
|
||||
inline
|
||||
date::sys_seconds
|
||||
time_zone::get_next_start(date::year y) const
|
||||
{
|
||||
return date::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()};
|
||||
}
|
||||
|
||||
inline
|
||||
date::sys_seconds
|
||||
time_zone::get_end(date::year y) const
|
||||
{
|
||||
return date::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
|
||||
}
|
||||
|
||||
inline
|
||||
date::sys_seconds
|
||||
time_zone::get_prev_end(date::year y) const
|
||||
{
|
||||
return date::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()};
|
||||
}
|
||||
|
||||
inline
|
||||
date::sys_seconds
|
||||
time_zone::get_next_end(date::year y) const
|
||||
{
|
||||
return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()};
|
||||
}
|
||||
|
||||
date::sys_info
|
||||
time_zone::contant_offset() const
|
||||
{
|
||||
using date::year;
|
||||
using date::sys_info;
|
||||
using date::sys_days;
|
||||
using date::January;
|
||||
using date::December;
|
||||
using date::last;
|
||||
sys_info r;
|
||||
r.begin = sys_days{year::min()/January/1};
|
||||
r.end = sys_days{year::max()/December/last};
|
||||
r.abbrev = std_abbrev_;
|
||||
r.offset = offset_;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
time_zone::time_zone(const detail::string_t& s)
|
||||
{
|
||||
@ -195,7 +354,10 @@ time_zone::time_zone(const detail::string_t& s)
|
||||
if (i != s.size())
|
||||
{
|
||||
if (s[i] != ',')
|
||||
{
|
||||
i = read_signed_time(s, i, save_);
|
||||
save_ = -save_ - offset_;
|
||||
}
|
||||
if (i != s.size())
|
||||
{
|
||||
if (s[i] != ',')
|
||||
@ -219,12 +381,10 @@ time_zone::get_info(date::sys_time<Duration> st) const
|
||||
{
|
||||
using date::sys_info;
|
||||
using date::year_month_day;
|
||||
using date::sys_seconds;
|
||||
using date::sys_days;
|
||||
using date::floor;
|
||||
using date::ceil;
|
||||
using date::days;
|
||||
using date::years;
|
||||
using date::year;
|
||||
using date::January;
|
||||
using date::December;
|
||||
@ -235,36 +395,59 @@ time_zone::get_info(date::sys_time<Duration> st) const
|
||||
if (start_rule_.ok())
|
||||
{
|
||||
auto y = year_month_day{floor<days>(st)}.year();
|
||||
auto start = sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
|
||||
auto end = sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
|
||||
if (start <= st && st < end)
|
||||
auto start = get_start(y);
|
||||
auto end = get_end(y);
|
||||
if (start <= end) // (northern hemisphere)
|
||||
{
|
||||
r.begin = start;
|
||||
r.end = end;
|
||||
r.offset += save_;
|
||||
r.save = ceil<minutes>(save_);
|
||||
r.abbrev = dst_abbrev_;
|
||||
if (start <= st && st < end)
|
||||
{
|
||||
r.begin = start;
|
||||
r.end = end;
|
||||
r.offset += save_;
|
||||
r.save = ceil<minutes>(save_);
|
||||
r.abbrev = dst_abbrev_;
|
||||
}
|
||||
else if (st < start)
|
||||
{
|
||||
r.begin = get_prev_end(y);
|
||||
r.end = start;
|
||||
r.abbrev = std_abbrev_;
|
||||
}
|
||||
else // st >= end
|
||||
{
|
||||
r.begin = end;
|
||||
r.end = get_next_start(y);
|
||||
r.abbrev = std_abbrev_;
|
||||
}
|
||||
}
|
||||
else if (st < start)
|
||||
else // end < start (southern hemisphere)
|
||||
{
|
||||
r.begin = sys_seconds{(end_rule_(y-years{1}) -
|
||||
(offset_ + save_)).time_since_epoch()};
|
||||
r.end = start;
|
||||
r.abbrev = std_abbrev_;
|
||||
}
|
||||
else // st >= end
|
||||
{
|
||||
r.begin = end;
|
||||
r.end = sys_seconds{(start_rule_(y+years{1}) - offset_).time_since_epoch()};
|
||||
r.abbrev = std_abbrev_;
|
||||
if (end <= st && st < start)
|
||||
{
|
||||
r.begin = end;
|
||||
r.end = start;
|
||||
r.abbrev = std_abbrev_;
|
||||
}
|
||||
else if (st < end)
|
||||
{
|
||||
r.begin = get_prev_start(y);
|
||||
r.end = end;
|
||||
r.offset += save_;
|
||||
r.save = ceil<minutes>(save_);
|
||||
r.abbrev = dst_abbrev_;
|
||||
}
|
||||
else // st >= start
|
||||
{
|
||||
r.begin = start;
|
||||
r.end = get_next_end(y);
|
||||
r.offset += save_;
|
||||
r.save = ceil<minutes>(save_);
|
||||
r.abbrev = dst_abbrev_;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // constant offset
|
||||
{
|
||||
r.begin = sys_days{year::min()/January/1};
|
||||
r.end = sys_days{year::max()/December/last};
|
||||
r.abbrev = std_abbrev_;
|
||||
}
|
||||
else
|
||||
r = contant_offset();
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -277,7 +460,6 @@ time_zone::get_info(date::local_time<Duration> tp) const
|
||||
using date::days;
|
||||
using date::sys_days;
|
||||
using date::sys_seconds;
|
||||
using date::years;
|
||||
using date::year;
|
||||
using date::ceil;
|
||||
using date::January;
|
||||
@ -290,19 +472,25 @@ time_zone::get_info(date::local_time<Duration> tp) const
|
||||
if (start_rule_.ok())
|
||||
{
|
||||
auto y = year_month_day{floor<days>(tp)}.year();
|
||||
auto start = sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
|
||||
auto end = sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
|
||||
auto start = get_start(y);
|
||||
auto end = get_end(y);
|
||||
auto utcs = sys_seconds{floor<seconds>(tp - offset_).time_since_epoch()};
|
||||
auto utcd = sys_seconds{floor<seconds>(tp - (offset_ + save_)).time_since_epoch()};
|
||||
auto northern = start <= end;
|
||||
if ((utcs < start) != (utcd < start))
|
||||
{
|
||||
r.first.begin = sys_seconds{(end_rule_(y-years{1}) -
|
||||
(offset_ + save_)).time_since_epoch()};
|
||||
if (northern)
|
||||
r.first.begin = get_prev_end(y);
|
||||
else
|
||||
r.first.begin = end;
|
||||
r.first.end = start;
|
||||
r.first.offset = offset_;
|
||||
r.first.abbrev = std_abbrev_;
|
||||
r.second.begin = start;
|
||||
r.second.end = end;
|
||||
if (northern)
|
||||
r.second.end = end;
|
||||
else
|
||||
r.second.end = get_next_end(y);
|
||||
r.second.abbrev = dst_abbrev_;
|
||||
r.second.offset = offset_ + save_;
|
||||
r.second.save = ceil<minutes>(save_);
|
||||
@ -311,51 +499,29 @@ time_zone::get_info(date::local_time<Duration> tp) const
|
||||
}
|
||||
else if ((utcs < end) != (utcd < end))
|
||||
{
|
||||
r.first.begin = start;
|
||||
if (northern)
|
||||
r.first.begin = start;
|
||||
else
|
||||
r.first.begin = get_prev_start(y);
|
||||
r.first.end = end;
|
||||
r.first.offset = offset_ + save_;
|
||||
r.first.save = ceil<minutes>(save_);
|
||||
r.first.abbrev = dst_abbrev_;
|
||||
r.second.begin = end;
|
||||
r.second.end = sys_seconds{(start_rule_(y+years{1}) -
|
||||
offset_).time_since_epoch()};
|
||||
if (northern)
|
||||
r.second.end = get_next_start(y);
|
||||
else
|
||||
r.second.end = start;
|
||||
r.second.abbrev = std_abbrev_;
|
||||
r.second.offset = offset_;
|
||||
r.result = save_ > seconds{0} ? local_info::ambiguous
|
||||
: local_info::nonexistent;
|
||||
}
|
||||
else if (utcs < start)
|
||||
{
|
||||
r.first.begin = sys_seconds{(end_rule_(y-years{1}) -
|
||||
(offset_ + save_)).time_since_epoch()};
|
||||
r.first.end = start;
|
||||
r.first.offset = offset_;
|
||||
r.first.abbrev = std_abbrev_;
|
||||
}
|
||||
else if (utcs < end)
|
||||
{
|
||||
r.first.begin = start;
|
||||
r.first.end = end;
|
||||
r.first.offset = offset_ + save_;
|
||||
r.first.save = ceil<minutes>(save_);
|
||||
r.first.abbrev = dst_abbrev_;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.first.begin = end;
|
||||
r.first.end = sys_seconds{(start_rule_(y+years{1}) -
|
||||
offset_).time_since_epoch()};
|
||||
r.first.abbrev = std_abbrev_;
|
||||
r.first.offset = offset_;
|
||||
}
|
||||
}
|
||||
else // constant offset
|
||||
{
|
||||
r.first.begin = sys_days{year::min()/January/1};
|
||||
r.first.end = sys_days{year::max()/December/last};
|
||||
r.first.abbrev = std_abbrev_;
|
||||
r.first.offset = offset_;
|
||||
r.first = get_info(utcs);
|
||||
}
|
||||
else
|
||||
r.first = contant_offset();
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -366,6 +532,7 @@ time_zone::to_sys(date::local_time<Duration> tp) const
|
||||
using date::local_info;
|
||||
using date::sys_time;
|
||||
using date::ambiguous_local_time;
|
||||
using date::nonexistent_local_time;
|
||||
auto i = get_info(tp);
|
||||
if (i.result == local_info::nonexistent)
|
||||
throw nonexistent_local_time(tp, i);
|
||||
@ -416,6 +583,72 @@ operator<<(std::ostream& os, const time_zone& z)
|
||||
return os;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
time_zone::name() const
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
auto nm = std_abbrev_;
|
||||
auto print_offset = [](seconds off)
|
||||
{
|
||||
std::string nm;
|
||||
hh_mm_ss<seconds> offset{-off};
|
||||
if (offset.is_negative())
|
||||
nm += '-';
|
||||
nm += std::to_string(offset.hours().count());
|
||||
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
|
||||
{
|
||||
nm += ':';
|
||||
if (offset.minutes() < minutes{10})
|
||||
nm += '0';
|
||||
nm += std::to_string(offset.minutes().count());
|
||||
if (offset.seconds() != seconds{0})
|
||||
{
|
||||
nm += ':';
|
||||
if (offset.seconds() < seconds{10})
|
||||
nm += '0';
|
||||
nm += std::to_string(offset.seconds().count());
|
||||
}
|
||||
}
|
||||
return nm;
|
||||
};
|
||||
nm += print_offset(offset_);
|
||||
if (!dst_abbrev_.empty())
|
||||
{
|
||||
nm += dst_abbrev_;
|
||||
if (save_ != hours{1})
|
||||
nm += print_offset(offset_+save_);
|
||||
if (start_rule_.ok())
|
||||
{
|
||||
nm += ',';
|
||||
nm += start_rule_.to_string();
|
||||
nm += ',';
|
||||
nm += end_rule_.to_string();
|
||||
}
|
||||
}
|
||||
return nm;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
operator==(const time_zone& x, const time_zone& y)
|
||||
{
|
||||
return x.std_abbrev_ == y.std_abbrev_ &&
|
||||
x.dst_abbrev_ == y. dst_abbrev_ &&
|
||||
x.offset_ == y.offset_ &&
|
||||
x.save_ == y.save_ &&
|
||||
x.start_rule_ == y.start_rule_ &&
|
||||
x.end_rule_ == y.end_rule_;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
operator!=(const time_zone& x, const time_zone& y)
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
@ -428,7 +661,7 @@ throw_invalid(const string_t& s, unsigned i, const string_t& message)
|
||||
std::string(s) + '\n' +
|
||||
"\x1b[1;32m" +
|
||||
std::string(i, '~') + '^' +
|
||||
std::string(s.size()-i-1, '~') +
|
||||
std::string(i < s.size() ? s.size()-i-1 : 0, '~') +
|
||||
"\x1b[0m");
|
||||
}
|
||||
|
||||
@ -444,7 +677,7 @@ read_date(const string_t& s, unsigned i, rule& r)
|
||||
{
|
||||
++i;
|
||||
unsigned n;
|
||||
i = read_unsigned(s, i, 3, n);
|
||||
i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]");
|
||||
r.mode_ = rule::J;
|
||||
r.n_ = n;
|
||||
}
|
||||
@ -452,17 +685,17 @@ read_date(const string_t& s, unsigned i, rule& r)
|
||||
{
|
||||
++i;
|
||||
unsigned m;
|
||||
i = read_unsigned(s, i, 2, m);
|
||||
i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]");
|
||||
if (i == s.size() || s[i] != '.')
|
||||
throw_invalid(s, i, "Expected '.' after month");
|
||||
++i;
|
||||
unsigned n;
|
||||
i = read_unsigned(s, i, 1, n);
|
||||
i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]");
|
||||
if (i == s.size() || s[i] != '.')
|
||||
throw_invalid(s, i, "Expected '.' after weekday index");
|
||||
++i;
|
||||
unsigned wd;
|
||||
i = read_unsigned(s, i, 1, wd);
|
||||
i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]");
|
||||
r.mode_ = rule::M;
|
||||
r.m_ = month{m};
|
||||
r.wd_ = weekday{wd};
|
||||
@ -552,17 +785,17 @@ read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
|
||||
if (i == s.size())
|
||||
throw_invalid(s, i, "Expected to read unsigned time, but found end of string");
|
||||
unsigned x;
|
||||
i = read_unsigned(s, i, 2, x);
|
||||
i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]");
|
||||
t = hours{x};
|
||||
if (i != s.size() && s[i] == ':')
|
||||
{
|
||||
++i;
|
||||
i = read_unsigned(s, i, 2, x);
|
||||
i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]");
|
||||
t += minutes{x};
|
||||
if (i != s.size() && s[i] == ':')
|
||||
{
|
||||
++i;
|
||||
i = read_unsigned(s, i, 2, x);
|
||||
i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]");
|
||||
t += seconds{x};
|
||||
}
|
||||
}
|
||||
@ -571,10 +804,11 @@ read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
|
||||
|
||||
inline
|
||||
unsigned
|
||||
read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u)
|
||||
read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
|
||||
const string_t& message)
|
||||
{
|
||||
if (i == s.size() || !std::isdigit(s[i]))
|
||||
throw_invalid(s, i, "Expected to find a decimal digit");
|
||||
throw_invalid(s, i, message);
|
||||
u = static_cast<unsigned>(s[i] - '0');
|
||||
unsigned count = 1;
|
||||
for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count)
|
||||
|
@ -1707,9 +1707,13 @@ inline
|
||||
bool
|
||||
month_day::ok() const NOEXCEPT
|
||||
{
|
||||
CONSTDATA solar_hijri::day d[] =
|
||||
{31_d, 31_d, 31_d, 31_d, 31_d, 31_d, 30_d, 30_d, 30_d, 30_d, 30_d, 30_d};
|
||||
return m_.ok() && 1_d <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
|
||||
CONSTDATA solar_hijri::day d[] = {
|
||||
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
|
||||
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
|
||||
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30),
|
||||
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30)
|
||||
};
|
||||
return m_.ok() && solar_hijri::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
|
||||
}
|
||||
|
||||
CONSTCD11
|
||||
@ -1998,10 +2002,14 @@ inline
|
||||
day
|
||||
year_month_day_last::day() const NOEXCEPT
|
||||
{
|
||||
CONSTDATA solar_hijri::day d[] =
|
||||
{31_d, 31_d, 31_d, 31_d, 31_d, 31_d, 30_d, 30_d, 30_d, 30_d, 30_d, 29_d};
|
||||
CONSTDATA solar_hijri::day d[] = {
|
||||
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
|
||||
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
|
||||
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30),
|
||||
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(29)
|
||||
};
|
||||
return month() != esf || !y_.is_leap() ?
|
||||
d[static_cast<unsigned>(month())-1] : 30_d;
|
||||
d[static_cast<unsigned>(month()) - 1] : solar_hijri::day(30);
|
||||
}
|
||||
|
||||
CONSTCD14
|
||||
@ -2260,7 +2268,7 @@ year_month_day::ok() const NOEXCEPT
|
||||
{
|
||||
if (!(y_.ok() && m_.ok()))
|
||||
return false;
|
||||
return 1_d <= d_ && d_ <= (y_/m_/last).day();
|
||||
return solar_hijri::day(1) <= d_ && d_ <= (y_/m_/last).day();
|
||||
}
|
||||
|
||||
CONSTCD11
|
||||
|
@ -86,15 +86,6 @@ static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true,
|
||||
# ifdef _WIN32
|
||||
# error "USE_OS_TZDB can not be used on Windows"
|
||||
# endif
|
||||
# ifndef MISSING_LEAP_SECONDS
|
||||
# ifdef __APPLE__
|
||||
# define MISSING_LEAP_SECONDS 1
|
||||
# else
|
||||
# define MISSING_LEAP_SECONDS 0
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# define MISSING_LEAP_SECONDS 0
|
||||
#endif
|
||||
|
||||
#ifndef HAS_DEDUCTION_GUIDES
|
||||
@ -703,6 +694,11 @@ public:
|
||||
|
||||
private:
|
||||
template <class D, class T> friend class zoned_time;
|
||||
|
||||
template <class TimeZonePtr2>
|
||||
static
|
||||
TimeZonePtr2&&
|
||||
check(TimeZonePtr2&& p);
|
||||
};
|
||||
|
||||
using zoned_seconds = zoned_time<std::chrono::seconds>;
|
||||
@ -990,8 +986,6 @@ inline bool operator>=(const time_zone_link& x, const time_zone_link& y) {return
|
||||
|
||||
#endif // !USE_OS_TZDB
|
||||
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
|
||||
class leap_second
|
||||
{
|
||||
private:
|
||||
@ -1115,8 +1109,6 @@ operator>=(const sys_time<Duration>& x, const leap_second& y)
|
||||
|
||||
using leap = leap_second;
|
||||
|
||||
#endif // !MISSING_LEAP_SECONDS
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
namespace detail
|
||||
@ -1162,9 +1154,7 @@ struct tzdb
|
||||
#if !USE_OS_TZDB
|
||||
std::vector<time_zone_link> links;
|
||||
#endif
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
std::vector<leap_second> leap_seconds;
|
||||
#endif
|
||||
#if !USE_OS_TZDB
|
||||
std::vector<detail::Rule> rules;
|
||||
#endif
|
||||
@ -1221,31 +1211,31 @@ class tzdb_list
|
||||
public:
|
||||
~tzdb_list();
|
||||
tzdb_list() = default;
|
||||
tzdb_list(tzdb_list&& x) noexcept;
|
||||
tzdb_list(tzdb_list&& x) NOEXCEPT;
|
||||
|
||||
const tzdb& front() const noexcept {return *head_;}
|
||||
tzdb& front() noexcept {return *head_;}
|
||||
const tzdb& front() const NOEXCEPT {return *head_;}
|
||||
tzdb& front() NOEXCEPT {return *head_;}
|
||||
|
||||
class const_iterator;
|
||||
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
const_iterator begin() const NOEXCEPT;
|
||||
const_iterator end() const NOEXCEPT;
|
||||
|
||||
const_iterator cbegin() const noexcept;
|
||||
const_iterator cend() const noexcept;
|
||||
const_iterator cbegin() const NOEXCEPT;
|
||||
const_iterator cend() const NOEXCEPT;
|
||||
|
||||
const_iterator erase_after(const_iterator p) noexcept;
|
||||
const_iterator erase_after(const_iterator p) NOEXCEPT;
|
||||
|
||||
struct undocumented_helper;
|
||||
private:
|
||||
void push_front(tzdb* tzdb) noexcept;
|
||||
void push_front(tzdb* tzdb) NOEXCEPT;
|
||||
};
|
||||
|
||||
class tzdb_list::const_iterator
|
||||
{
|
||||
tzdb* p_ = nullptr;
|
||||
|
||||
explicit const_iterator(tzdb* p) noexcept : p_{p} {}
|
||||
explicit const_iterator(tzdb* p) NOEXCEPT : p_{p} {}
|
||||
public:
|
||||
const_iterator() = default;
|
||||
|
||||
@ -1255,20 +1245,20 @@ public:
|
||||
using pointer = const value_type*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
reference operator*() const noexcept {return *p_;}
|
||||
pointer operator->() const noexcept {return p_;}
|
||||
reference operator*() const NOEXCEPT {return *p_;}
|
||||
pointer operator->() const NOEXCEPT {return p_;}
|
||||
|
||||
const_iterator& operator++() noexcept {p_ = p_->next; return *this;}
|
||||
const_iterator operator++(int) noexcept {auto t = *this; ++(*this); return t;}
|
||||
const_iterator& operator++() NOEXCEPT {p_ = p_->next; return *this;}
|
||||
const_iterator operator++(int) NOEXCEPT {auto t = *this; ++(*this); return t;}
|
||||
|
||||
friend
|
||||
bool
|
||||
operator==(const const_iterator& x, const const_iterator& y) noexcept
|
||||
operator==(const const_iterator& x, const const_iterator& y) NOEXCEPT
|
||||
{return x.p_ == y.p_;}
|
||||
|
||||
friend
|
||||
bool
|
||||
operator!=(const const_iterator& x, const const_iterator& y) noexcept
|
||||
operator!=(const const_iterator& x, const const_iterator& y) NOEXCEPT
|
||||
{return !(x == y);}
|
||||
|
||||
friend class tzdb_list;
|
||||
@ -1276,28 +1266,28 @@ public:
|
||||
|
||||
inline
|
||||
tzdb_list::const_iterator
|
||||
tzdb_list::begin() const noexcept
|
||||
tzdb_list::begin() const NOEXCEPT
|
||||
{
|
||||
return const_iterator{head_};
|
||||
}
|
||||
|
||||
inline
|
||||
tzdb_list::const_iterator
|
||||
tzdb_list::end() const noexcept
|
||||
tzdb_list::end() const NOEXCEPT
|
||||
{
|
||||
return const_iterator{nullptr};
|
||||
}
|
||||
|
||||
inline
|
||||
tzdb_list::const_iterator
|
||||
tzdb_list::cbegin() const noexcept
|
||||
tzdb_list::cbegin() const NOEXCEPT
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
inline
|
||||
tzdb_list::const_iterator
|
||||
tzdb_list::cend() const noexcept
|
||||
tzdb_list::cend() const NOEXCEPT
|
||||
{
|
||||
return end();
|
||||
}
|
||||
@ -1328,7 +1318,7 @@ namespace detail
|
||||
template <class T>
|
||||
inline
|
||||
T*
|
||||
to_raw_pointer(T* p) noexcept
|
||||
to_raw_pointer(T* p) NOEXCEPT
|
||||
{
|
||||
return p;
|
||||
}
|
||||
@ -1336,7 +1326,7 @@ to_raw_pointer(T* p) noexcept
|
||||
template <class Pointer>
|
||||
inline
|
||||
auto
|
||||
to_raw_pointer(Pointer p) noexcept
|
||||
to_raw_pointer(Pointer p) NOEXCEPT
|
||||
-> decltype(detail::to_raw_pointer(p.operator->()))
|
||||
{
|
||||
return detail::to_raw_pointer(p.operator->());
|
||||
@ -1344,13 +1334,25 @@ to_raw_pointer(Pointer p) noexcept
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class Duration, class TimeZonePtr>
|
||||
template <class TimeZonePtr2>
|
||||
inline
|
||||
TimeZonePtr2&&
|
||||
zoned_time<Duration, TimeZonePtr>::check(TimeZonePtr2&& p)
|
||||
{
|
||||
if (detail::to_raw_pointer(p) == nullptr)
|
||||
throw std::runtime_error(
|
||||
"zoned_time constructed with a time zone pointer == nullptr");
|
||||
return std::forward<TimeZonePtr2>(p);
|
||||
}
|
||||
|
||||
template <class Duration, class TimeZonePtr>
|
||||
#if !defined(_MSC_VER) || (_MSC_VER > 1916)
|
||||
template <class T, class>
|
||||
#endif
|
||||
inline
|
||||
zoned_time<Duration, TimeZonePtr>::zoned_time()
|
||||
: zone_(zoned_traits<TimeZonePtr>::default_zone())
|
||||
: zone_(check(zoned_traits<TimeZonePtr>::default_zone()))
|
||||
{}
|
||||
|
||||
template <class Duration, class TimeZonePtr>
|
||||
@ -1359,15 +1361,15 @@ template <class T, class>
|
||||
#endif
|
||||
inline
|
||||
zoned_time<Duration, TimeZonePtr>::zoned_time(const sys_time<Duration>& st)
|
||||
: zone_(zoned_traits<TimeZonePtr>::default_zone())
|
||||
: zone_(check(zoned_traits<TimeZonePtr>::default_zone()))
|
||||
, tp_(st)
|
||||
{}
|
||||
|
||||
template <class Duration, class TimeZonePtr>
|
||||
inline
|
||||
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z)
|
||||
: zone_(std::move(z))
|
||||
{assert(detail::to_raw_pointer(zone_) != nullptr);}
|
||||
: zone_(check(std::move(z)))
|
||||
{}
|
||||
|
||||
#if HAS_STRING_VIEW
|
||||
|
||||
@ -1402,7 +1404,7 @@ zoned_time<Duration, TimeZonePtr>::zoned_time(const zoned_time<Duration2, TimeZo
|
||||
template <class Duration, class TimeZonePtr>
|
||||
inline
|
||||
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const sys_time<Duration>& st)
|
||||
: zone_(std::move(z))
|
||||
: zone_(check(std::move(z)))
|
||||
, tp_(st)
|
||||
{}
|
||||
|
||||
@ -1412,7 +1414,7 @@ template <class T, class>
|
||||
#endif
|
||||
inline
|
||||
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const local_time<Duration>& t)
|
||||
: zone_(std::move(z))
|
||||
: zone_(check(std::move(z)))
|
||||
, tp_(zone_->to_sys(t))
|
||||
{}
|
||||
|
||||
@ -1423,7 +1425,7 @@ template <class T, class>
|
||||
inline
|
||||
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const local_time<Duration>& t,
|
||||
choose c)
|
||||
: zone_(std::move(z))
|
||||
: zone_(check(std::move(z)))
|
||||
, tp_(zone_->to_sys(t, c))
|
||||
{}
|
||||
|
||||
@ -1432,7 +1434,7 @@ template <class Duration2, class TimeZonePtr2, class>
|
||||
inline
|
||||
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z,
|
||||
const zoned_time<Duration2, TimeZonePtr2>& zt)
|
||||
: zone_(std::move(z))
|
||||
: zone_(check(std::move(z)))
|
||||
, tp_(zt.tp_)
|
||||
{}
|
||||
|
||||
@ -1847,8 +1849,6 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const zoned_time<Duration, Tim
|
||||
return to_stream(os, fmt, t);
|
||||
}
|
||||
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
|
||||
class utc_clock
|
||||
{
|
||||
public:
|
||||
@ -2787,8 +2787,6 @@ to_gps_time(const tai_time<Duration>& t)
|
||||
return gps_clock::from_utc(tai_clock::to_utc(t));
|
||||
}
|
||||
|
||||
#endif // !MISSING_LEAP_SECONDS
|
||||
|
||||
} // namespace date
|
||||
|
||||
#endif // TZ_H
|
||||
|
@ -95,9 +95,9 @@ private:
|
||||
U& operator=(const pair& x);
|
||||
} u;
|
||||
|
||||
std::chrono::hours h_{};
|
||||
std::chrono::minutes m_{};
|
||||
std::chrono::seconds s_{};
|
||||
std::chrono::hours h_{0};
|
||||
std::chrono::minutes m_{0};
|
||||
std::chrono::seconds s_{0};
|
||||
tz zone_{tz::local};
|
||||
|
||||
public:
|
||||
@ -245,7 +245,7 @@ struct zonelet
|
||||
sys_seconds until_utc_;
|
||||
local_seconds until_std_;
|
||||
local_seconds until_loc_;
|
||||
std::chrono::minutes initial_save_{};
|
||||
std::chrono::minutes initial_save_{0};
|
||||
std::string initial_abbrev_;
|
||||
std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()};
|
||||
std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()};
|
||||
|
256
src/tz.cpp
256
src/tz.cpp
@ -89,6 +89,7 @@
|
||||
# include "date/ios.h"
|
||||
#else
|
||||
# define TARGET_OS_IPHONE 0
|
||||
# define TARGET_OS_SIMULATOR 0
|
||||
#endif
|
||||
|
||||
#if USE_OS_TZDB
|
||||
@ -140,7 +141,7 @@
|
||||
# endif // HAS_REMOTE_API
|
||||
#else // !_WIN32
|
||||
# include <unistd.h>
|
||||
# if !USE_OS_TZDB
|
||||
# if !USE_OS_TZDB && !defined(INSTALL)
|
||||
# include <wordexp.h>
|
||||
# endif
|
||||
# include <limits.h>
|
||||
@ -361,7 +362,11 @@ discover_tz_dir()
|
||||
throw runtime_error("discover_tz_dir failed to find zoneinfo\n");
|
||||
# else // __APPLE__
|
||||
# if TARGET_OS_IPHONE
|
||||
# if TARGET_OS_SIMULATOR
|
||||
return "/usr/share/zoneinfo";
|
||||
# else
|
||||
return "/var/db/timezone/zoneinfo";
|
||||
# endif
|
||||
# else
|
||||
CONSTDATA auto timezone = "/etc/localtime";
|
||||
if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0))
|
||||
@ -415,20 +420,20 @@ tzdb_list::~tzdb_list()
|
||||
}
|
||||
}
|
||||
|
||||
tzdb_list::tzdb_list(tzdb_list&& x) noexcept
|
||||
tzdb_list::tzdb_list(tzdb_list&& x) NOEXCEPT
|
||||
: head_{x.head_.exchange(nullptr)}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
tzdb_list::push_front(tzdb* tzdb) noexcept
|
||||
tzdb_list::push_front(tzdb* tzdb) NOEXCEPT
|
||||
{
|
||||
tzdb->next = head_;
|
||||
head_ = tzdb;
|
||||
}
|
||||
|
||||
tzdb_list::const_iterator
|
||||
tzdb_list::erase_after(const_iterator p) noexcept
|
||||
tzdb_list::erase_after(const_iterator p) NOEXCEPT
|
||||
{
|
||||
auto t = p.p_->next;
|
||||
p.p_->next = p.p_->next->next;
|
||||
@ -438,7 +443,7 @@ tzdb_list::erase_after(const_iterator p) noexcept
|
||||
|
||||
struct tzdb_list::undocumented_helper
|
||||
{
|
||||
static void push_front(tzdb_list& db_list, tzdb* tzdb) noexcept
|
||||
static void push_front(tzdb_list& db_list, tzdb* tzdb) NOEXCEPT
|
||||
{
|
||||
db_list.push_front(tzdb);
|
||||
}
|
||||
@ -460,6 +465,32 @@ get_tzdb_list()
|
||||
return tz_db;
|
||||
}
|
||||
|
||||
static
|
||||
std::string
|
||||
parse3(std::istream& in)
|
||||
{
|
||||
std::string r(3, ' ');
|
||||
ws(in);
|
||||
r[0] = static_cast<char>(in.get());
|
||||
r[1] = static_cast<char>(in.get());
|
||||
r[2] = static_cast<char>(in.get());
|
||||
return r;
|
||||
}
|
||||
|
||||
static
|
||||
unsigned
|
||||
parse_month(std::istream& in)
|
||||
{
|
||||
CONSTDATA char*const month_names[] =
|
||||
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
auto s = parse3(in);
|
||||
auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names;
|
||||
if (m >= std::end(month_names) - std::begin(month_names))
|
||||
throw std::runtime_error("oops: bad month name: " + s);
|
||||
return static_cast<unsigned>(++m);
|
||||
}
|
||||
|
||||
#if !USE_OS_TZDB
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -674,18 +705,6 @@ load_timezone_mappings_from_xml_file(const std::string& input_path)
|
||||
|
||||
// Parsing helpers
|
||||
|
||||
static
|
||||
std::string
|
||||
parse3(std::istream& in)
|
||||
{
|
||||
std::string r(3, ' ');
|
||||
ws(in);
|
||||
r[0] = static_cast<char>(in.get());
|
||||
r[1] = static_cast<char>(in.get());
|
||||
r[2] = static_cast<char>(in.get());
|
||||
return r;
|
||||
}
|
||||
|
||||
static
|
||||
unsigned
|
||||
parse_dow(std::istream& in)
|
||||
@ -699,20 +718,6 @@ parse_dow(std::istream& in)
|
||||
return static_cast<unsigned>(dow);
|
||||
}
|
||||
|
||||
static
|
||||
unsigned
|
||||
parse_month(std::istream& in)
|
||||
{
|
||||
CONSTDATA char*const month_names[] =
|
||||
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
auto s = parse3(in);
|
||||
auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names;
|
||||
if (m >= std::end(month_names) - std::begin(month_names))
|
||||
throw std::runtime_error("oops: bad month name: " + s);
|
||||
return static_cast<unsigned>(++m);
|
||||
}
|
||||
|
||||
static
|
||||
std::chrono::seconds
|
||||
parse_unsigned_time(std::istream& in)
|
||||
@ -2120,14 +2125,25 @@ time_zone::load_sys_info(std::vector<detail::transition>::const_iterator i) cons
|
||||
{
|
||||
using namespace std::chrono;
|
||||
assert(!transitions_.empty());
|
||||
assert(i != transitions_.begin());
|
||||
sys_info r;
|
||||
r.begin = i[-1].timepoint;
|
||||
r.end = i != transitions_.end() ? i->timepoint :
|
||||
sys_seconds(sys_days(year::max()/max_day));
|
||||
r.offset = i[-1].info->offset;
|
||||
r.save = i[-1].info->is_dst ? minutes{1} : minutes{0};
|
||||
r.abbrev = i[-1].info->abbrev;
|
||||
if (i != transitions_.begin())
|
||||
{
|
||||
r.begin = i[-1].timepoint;
|
||||
r.end = i != transitions_.end() ? i->timepoint :
|
||||
sys_seconds(sys_days(year::max()/max_day));
|
||||
r.offset = i[-1].info->offset;
|
||||
r.save = i[-1].info->is_dst ? minutes{1} : minutes{0};
|
||||
r.abbrev = i[-1].info->abbrev;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.begin = sys_days(year::min()/min_day);
|
||||
r.end = i+1 != transitions_.end() ? i[1].timepoint :
|
||||
sys_seconds(sys_days(year::max()/max_day));
|
||||
r.offset = i[0].info->offset;
|
||||
r.save = i[0].info->is_dst ? minutes{1} : minutes{0};
|
||||
r.abbrev = i[0].info->abbrev;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -2162,7 +2178,7 @@ time_zone::get_info_impl(local_seconds tp) const
|
||||
{
|
||||
i.second = load_sys_info(--tr);
|
||||
tps = sys_seconds{(tp - i.second.offset).time_since_epoch()};
|
||||
if (tps < i.second.end)
|
||||
if (tps < i.second.end && i.first.end != i.second.end)
|
||||
{
|
||||
i.result = local_info::ambiguous;
|
||||
std::swap(i.first, i.second);
|
||||
@ -2205,15 +2221,11 @@ operator<<(std::ostream& os, const time_zone& z)
|
||||
return os;
|
||||
}
|
||||
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
|
||||
leap_second::leap_second(const sys_seconds& s, detail::undocumented)
|
||||
: date_(s)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // !MISSING_LEAP_SECONDS
|
||||
|
||||
#else // !USE_OS_TZDB
|
||||
|
||||
time_zone::time_zone(const std::string& s, detail::undocumented)
|
||||
@ -2610,8 +2622,6 @@ operator<<(std::ostream& os, const time_zone& z)
|
||||
|
||||
#endif // !USE_OS_TZDB
|
||||
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const leap_second& x)
|
||||
{
|
||||
@ -2619,11 +2629,8 @@ operator<<(std::ostream& os, const leap_second& x)
|
||||
return os << x.date_ << " +";
|
||||
}
|
||||
|
||||
#endif // !MISSING_LEAP_SECONDS
|
||||
|
||||
#if USE_OS_TZDB
|
||||
|
||||
# ifdef __APPLE__
|
||||
static
|
||||
std::string
|
||||
get_version()
|
||||
@ -2632,12 +2639,99 @@ get_version()
|
||||
auto path = get_tz_dir() + string("/+VERSION");
|
||||
ifstream in{path};
|
||||
string version;
|
||||
in >> version;
|
||||
if (in.fail())
|
||||
throw std::runtime_error("Unable to get Timezone database version from " + path);
|
||||
return version;
|
||||
if (in)
|
||||
{
|
||||
in >> version;
|
||||
return version;
|
||||
}
|
||||
in.clear();
|
||||
in.open(get_tz_dir() + std::string(1, folder_delimiter) + "version");
|
||||
if (in)
|
||||
{
|
||||
in >> version;
|
||||
return version;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<leap_second>
|
||||
find_read_and_leap_seconds()
|
||||
{
|
||||
std::ifstream in(get_tz_dir() + std::string(1, folder_delimiter) + "leapseconds",
|
||||
std::ios_base::binary);
|
||||
if (in)
|
||||
{
|
||||
std::vector<leap_second> leap_seconds;
|
||||
std::string line;
|
||||
while (in)
|
||||
{
|
||||
std::getline(in, line);
|
||||
if (!line.empty() && line[0] != '#')
|
||||
{
|
||||
std::istringstream in(line);
|
||||
in.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
std::string word;
|
||||
in >> word;
|
||||
if (word == "Leap")
|
||||
{
|
||||
int y, m, d;
|
||||
in >> y;
|
||||
m = static_cast<int>(parse_month(in));
|
||||
in >> d;
|
||||
leap_seconds.push_back(leap_second(sys_days{year{y}/m/d} + days{1},
|
||||
detail::undocumented{}));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << line << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
return leap_seconds;
|
||||
}
|
||||
in.clear();
|
||||
in.open(get_tz_dir() + std::string(1, folder_delimiter) + "leap-seconds.list",
|
||||
std::ios_base::binary);
|
||||
if (in)
|
||||
{
|
||||
std::vector<leap_second> leap_seconds;
|
||||
std::string line;
|
||||
const auto offset = sys_days{1970_y/1/1}-sys_days{1900_y/1/1};
|
||||
while (in)
|
||||
{
|
||||
std::getline(in, line);
|
||||
if (!line.empty() && line[0] != '#')
|
||||
{
|
||||
std::istringstream in(line);
|
||||
in.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
using seconds = std::chrono::seconds;
|
||||
seconds::rep s;
|
||||
in >> s;
|
||||
if (s == 2272060800)
|
||||
continue;
|
||||
leap_seconds.push_back(leap_second(sys_seconds{seconds{s}} - offset,
|
||||
detail::undocumented{}));
|
||||
}
|
||||
}
|
||||
return leap_seconds;
|
||||
}
|
||||
in.clear();
|
||||
in.open(get_tz_dir() + std::string(1, folder_delimiter) + "right/UTC",
|
||||
std::ios_base::binary);
|
||||
if (in)
|
||||
{
|
||||
return load_just_leaps(in);
|
||||
}
|
||||
in.clear();
|
||||
in.open(get_tz_dir() + std::string(1, folder_delimiter) + "UTC",
|
||||
std::ios_base::binary);
|
||||
if (in)
|
||||
{
|
||||
return load_just_leaps(in);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
# endif
|
||||
|
||||
static
|
||||
std::unique_ptr<tzdb>
|
||||
@ -2666,6 +2760,7 @@ init_tzdb()
|
||||
strcmp(d->d_name, "iso3166.tab") == 0 ||
|
||||
strcmp(d->d_name, "right") == 0 ||
|
||||
strcmp(d->d_name, "+VERSION") == 0 ||
|
||||
strcmp(d->d_name, "version") == 0 ||
|
||||
strcmp(d->d_name, "zone.tab") == 0 ||
|
||||
strcmp(d->d_name, "zone1970.tab") == 0 ||
|
||||
strcmp(d->d_name, "tzdata.zi") == 0 ||
|
||||
@ -2693,28 +2788,8 @@ init_tzdb()
|
||||
}
|
||||
db->zones.shrink_to_fit();
|
||||
std::sort(db->zones.begin(), db->zones.end());
|
||||
# if !MISSING_LEAP_SECONDS
|
||||
std::ifstream in(get_tz_dir() + std::string(1, folder_delimiter) + "right/UTC",
|
||||
std::ios_base::binary);
|
||||
if (in)
|
||||
{
|
||||
in.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
db->leap_seconds = load_just_leaps(in);
|
||||
}
|
||||
else
|
||||
{
|
||||
in.clear();
|
||||
in.open(get_tz_dir() + std::string(1, folder_delimiter) +
|
||||
"UTC", std::ios_base::binary);
|
||||
if (!in)
|
||||
throw std::runtime_error("Unable to extract leap second information");
|
||||
in.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
db->leap_seconds = load_just_leaps(in);
|
||||
}
|
||||
# endif // !MISSING_LEAP_SECONDS
|
||||
# ifdef __APPLE__
|
||||
db->leap_seconds = find_read_and_leap_seconds();
|
||||
db->version = get_version();
|
||||
# endif
|
||||
return db;
|
||||
}
|
||||
|
||||
@ -2771,18 +2846,24 @@ file_exists(const std::string& filename)
|
||||
|
||||
// CURL tools
|
||||
|
||||
static
|
||||
int
|
||||
curl_global()
|
||||
{
|
||||
if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
|
||||
throw std::runtime_error("CURL global initialization failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct curl_global_init_and_cleanup
|
||||
{
|
||||
~curl_global_init_and_cleanup()
|
||||
{
|
||||
::curl_global_cleanup();
|
||||
}
|
||||
curl_global_init_and_cleanup()
|
||||
{
|
||||
if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
|
||||
throw std::runtime_error("CURL global initialization failed");
|
||||
}
|
||||
curl_global_init_and_cleanup(curl_global_init_and_cleanup const&) = delete;
|
||||
curl_global_init_and_cleanup& operator=(curl_global_init_and_cleanup const&) = delete;
|
||||
};
|
||||
|
||||
struct curl_deleter
|
||||
{
|
||||
void operator()(CURL* p) const
|
||||
@ -2797,8 +2878,7 @@ static
|
||||
std::unique_ptr<CURL, curl_deleter>
|
||||
curl_init()
|
||||
{
|
||||
static const auto curl_is_now_initiailized = curl_global();
|
||||
(void)curl_is_now_initiailized;
|
||||
static const curl_global_init_and_cleanup _{};
|
||||
return std::unique_ptr<CURL, curl_deleter>{::curl_easy_init()};
|
||||
}
|
||||
|
||||
@ -3289,8 +3369,8 @@ remote_download(const std::string& version, char* error_buffer)
|
||||
{
|
||||
auto mapping_file = get_download_mapping_file(version);
|
||||
result = download_to_file(
|
||||
"https://raw.githubusercontent.com/unicode-org/cldr/master/"
|
||||
"common/supplemental/windowsZones.xml",
|
||||
"https://raw.githubusercontent.com/unicode-org/cldr/master/"
|
||||
"common/supplemental/windowsZones.xml",
|
||||
mapping_file, download_file_options::text, error_buffer);
|
||||
}
|
||||
# endif // _WIN32
|
||||
@ -3564,11 +3644,9 @@ operator<<(std::ostream& os, const tzdb& db)
|
||||
os << "Version: " << db.version << "\n\n";
|
||||
for (const auto& x : db.zones)
|
||||
os << x << '\n';
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
os << '\n';
|
||||
for (const auto& x : db.leap_seconds)
|
||||
os << x << '\n';
|
||||
#endif // !MISSING_LEAP_SECONDS
|
||||
return os;
|
||||
}
|
||||
|
||||
|
@ -46,19 +46,19 @@ int
|
||||
main()
|
||||
{
|
||||
using namespace date::detail;
|
||||
static_assert(width<0>::value == 0, "");
|
||||
static_assert(width<1>::value == 0, "");
|
||||
static_assert(width<2>::value == 1, "");
|
||||
static_assert(width<3>::value == 19, "");
|
||||
static_assert(width<4>::value == 2, "");
|
||||
static_assert(width<5>::value == 1, "");
|
||||
static_assert(width<6>::value == 19, "");
|
||||
static_assert(width<7>::value == 19, "");
|
||||
static_assert(width<8>::value == 3, "");
|
||||
static_assert(width<9>::value == 19, "");
|
||||
static_assert(width<10>::value == 1, "");
|
||||
static_assert(width<100>::value == 2, "");
|
||||
static_assert(width<1000>::value == 3, "");
|
||||
static_assert(width<10000>::value == 4, "");
|
||||
static_assert(width<625>::value == 4, "");
|
||||
static_assert(width<0, 1>::value == 0, "");
|
||||
static_assert(width<1, 1>::value == 0, "");
|
||||
static_assert(width<1, 2>::value == 1, "");
|
||||
static_assert(width<1, 3>::value == 19, "");
|
||||
static_assert(width<1, 4>::value == 2, "");
|
||||
static_assert(width<1, 5>::value == 1, "");
|
||||
static_assert(width<1, 6>::value == 19, "");
|
||||
static_assert(width<1, 7>::value == 19, "");
|
||||
static_assert(width<1, 8>::value == 3, "");
|
||||
static_assert(width<1, 9>::value == 19, "");
|
||||
static_assert(width<1, 10>::value == 1, "");
|
||||
static_assert(width<1, 100>::value == 2, "");
|
||||
static_assert(width<1, 1000>::value == 3, "");
|
||||
static_assert(width<1, 10000>::value == 4, "");
|
||||
static_assert(width<756, 625>::value == 4, "");
|
||||
}
|
||||
|
@ -623,6 +623,21 @@ test_p()
|
||||
assert(!in.bad());
|
||||
assert(tp == sys_days{2016_y/12/11} + hours{23});
|
||||
}
|
||||
{
|
||||
std::istringstream in{"1986-12-01 01:01:01 pm"};
|
||||
sys_time<seconds> tp;
|
||||
in >> parse("%Y-%m-%d %I:%M:%S %p", tp);
|
||||
assert(!in.fail());
|
||||
assert(!in.bad());
|
||||
assert(tp == sys_days{1986_y/12/01} + hours{13} + minutes{01} + seconds{01});
|
||||
}
|
||||
{
|
||||
std::istringstream in{"1986-12-01 01:01:01"};
|
||||
sys_time<seconds> tp;
|
||||
in >> parse("%Y-%m-%d %I:%M:%S", tp);
|
||||
// The test will fail because %I needs the %p option to shows if it is AM or PM
|
||||
assert(in.fail());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
108
test/posix/ptz.pass.cpp
Normal file
108
test/posix/ptz.pass.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2021 Howard Hinnant
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Test Posix::time_zone
|
||||
|
||||
#include "tz.h"
|
||||
#include "ptz.h"
|
||||
#include <cassert>
|
||||
|
||||
bool
|
||||
is_equal(date::sys_info const& x, date::sys_info const& y)
|
||||
{
|
||||
return x.begin == y.begin &&
|
||||
x.end == y.end &&
|
||||
x.offset == y.offset &&
|
||||
x.save == y.save &&
|
||||
x.abbrev == y.abbrev;
|
||||
}
|
||||
|
||||
bool
|
||||
is_equal(date::local_info const& x, date::local_info const& y)
|
||||
{
|
||||
return x.result == y.result && is_equal(x.first, y.first)
|
||||
&& is_equal(x.second, y.second);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
|
||||
auto tzi = locate_zone("Australia/Sydney");
|
||||
Posix::time_zone tzp{"AEST-10AEDT,M10.1.0,M4.1.0/3"};
|
||||
auto tp = local_days{2021_y/1/1} + 0s;
|
||||
assert(tzp.get_info(tp).result == local_info::unique);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/10/Sunday[1]} + 2h + 30min;
|
||||
assert(tzp.get_info(tp).result == local_info::nonexistent);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/4/Sunday[1]} + 2h + 30min;
|
||||
assert(tzp.get_info(tp).result == local_info::ambiguous);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/7/1};
|
||||
assert(tzp.get_info(tp).result == local_info::unique);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
|
||||
tzi = locate_zone("America/New_York");
|
||||
tzp = Posix::time_zone{"EST5EDT,M3.2.0,M11.1.0"};
|
||||
tp = local_days{2021_y/1/1};
|
||||
assert(tzp.get_info(tp).result == local_info::unique);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/3/Sunday[2]} + 2h + 30min;
|
||||
assert(tzp.get_info(tp).result == local_info::nonexistent);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/11/Sunday[1]} + 1h + 30min;
|
||||
assert(tzp.get_info(tp).result == local_info::ambiguous);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/7/1};
|
||||
assert(tzp.get_info(tp).result == local_info::unique);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
|
||||
tzi = locate_zone("Europe/Dublin");
|
||||
tzp = Posix::time_zone{"IST-1GMT0,M10.5.0,M3.5.0/1"};
|
||||
tp = local_days{2021_y/1/1};
|
||||
assert(tzp.get_info(tp).result == local_info::unique);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/3/Sunday[last]} + 1h + 30min;
|
||||
assert(tzp.get_info(tp).result == local_info::nonexistent);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/10/Sunday[last]} + 1h + 30min;
|
||||
assert(tzp.get_info(tp).result == local_info::ambiguous);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
|
||||
tp = local_days{2021_y/7/1};
|
||||
assert(tzp.get_info(tp).result == local_info::unique);
|
||||
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
|
||||
}
|
36
test/tz_test/tzdb_list.pass.cpp
Normal file
36
test/tz_test/tzdb_list.pass.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2020 Howard Hinnant
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "tz.h"
|
||||
#include <type_traits>
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
using namespace date;
|
||||
static_assert( std::is_nothrow_destructible<tzdb_list>{}, "");
|
||||
static_assert( std::is_nothrow_default_constructible<tzdb_list>{}, "");
|
||||
static_assert(!std::is_copy_constructible<tzdb_list>{}, "");
|
||||
static_assert(!std::is_copy_assignable<tzdb_list>{}, "");
|
||||
static_assert( std::is_nothrow_move_constructible<tzdb_list>{}, "");
|
||||
static_assert(!std::is_move_assignable<tzdb_list>{}, "");
|
||||
}
|
Reference in New Issue
Block a user