forked from HowardHinnant/date
Compare commits
98 Commits
esp-idf-re
...
master
Author | SHA1 | Date | |
---|---|---|---|
6d7739e7e8 | |||
f94b8f36c6 | |||
a45ea7c17b | |||
0b336657b1 | |||
32ecb2ad24 | |||
d18e8b1653 | |||
b8d166b4b0 | |||
ca5727855b | |||
d0424a2518 | |||
8a1102fd08 | |||
632af88e80 | |||
28b7b23252 | |||
f079e3568c | |||
1a4f424659 | |||
5bdb7e6f31 | |||
e396108ee3 | |||
deec054564 | |||
dd8affc6de | |||
510a05429f | |||
361352673d | |||
447f5a30b9 | |||
fca69e308d | |||
7657ad7855 | |||
bbe2f51bc0 | |||
3e43210885 | |||
ac0c58d5da | |||
ab1dc3a5eb | |||
7c151cdb6a | |||
3286289bf6 | |||
cf8f25b183 | |||
dc9d161607 | |||
d2ddc5ea1e | |||
6219dd7e4d | |||
939031c38c | |||
51ce7e1310 | |||
155c6b9e76 | |||
1ead6715de | |||
f986299fbb | |||
706b1286e8 | |||
8a93211679 | |||
6b1c1b8b3a | |||
8f8336f42b | |||
cd3c57932f | |||
575fc23c3c | |||
0e65940a7f | |||
88a3b15126 | |||
5d225951ff | |||
ab37c362e3 | |||
0ef86f1ced | |||
cc4685a21e | |||
50acf3ffd8 | |||
15e0c84e42 | |||
28972d72b4 | |||
5f8c904231 | |||
c9169ea310 | |||
22ceabf205 | |||
c82b776f28 | |||
2c035f8def | |||
e6f4aed4d1 | |||
9ea5654c12 | |||
9d9161c978 | |||
3776e0f185 | |||
9e830536a0 | |||
655b249b8f | |||
529a09267f | |||
d9049ee697 | |||
2709deddd3 | |||
2e19c006e2 | |||
28b5106d4c | |||
e1aa4837e0 | |||
1ff7208036 | |||
b0adc54677 | |||
d57d764707 | |||
383214dea6 | |||
fb2554a7e0 | |||
8c126525cc | |||
8f95c598c9 | |||
417402ad35 | |||
38267fa1ef | |||
ebb5719cd7 | |||
2f4411312b | |||
e23c15bb36 | |||
052eebaf00 | |||
b49a7575eb | |||
6e921e1b1d | |||
39b4edf279 | |||
2ef74cb41a | |||
ac6ca2a095 | |||
ae017078c9 | |||
156e0a786e | |||
9a9a42db74 | |||
b5b765f928 | |||
77bd6b92a4 | |||
654b97091f | |||
b899774303 | |||
0e08b942c8 | |||
811be52e1c | |||
1c285d6545 |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
*zip -text -diff
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -135,7 +135,7 @@ publish/
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
140
CMakeLists.txt
140
CMakeLists.txt
@ -7,7 +7,7 @@
|
||||
include( FetchContent )
|
||||
FetchContent_Declare( date_src
|
||||
GIT_REPOSITORY https://github.com/HowardHinnant/date.git
|
||||
GIT_TAG v3.0.0 # adjust tag/branch/commit as needed
|
||||
GIT_TAG v3.0.1 # adjust tag/branch/commit as needed
|
||||
)
|
||||
FetchContent_MakeAvailable(date_src)
|
||||
...
|
||||
@ -17,13 +17,16 @@
|
||||
|
||||
cmake_minimum_required( VERSION 3.7 )
|
||||
|
||||
project( date VERSION 3.0.0 )
|
||||
project( date VERSION 3.0.4 )
|
||||
set(ABI_VERSION 3) # used as SOVERSION, increment when ABI changes
|
||||
|
||||
include( GNUInstallDirs )
|
||||
|
||||
get_directory_property( has_parent PARENT_DIRECTORY )
|
||||
|
||||
if (POLICY CMP0077)
|
||||
# Allow CMake 3.13+ to override options when using FetchContent / add_subdirectory.
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
endif ()
|
||||
|
||||
# Override by setting on CMake command line.
|
||||
set( CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard whose features are requested." )
|
||||
|
||||
@ -35,12 +38,17 @@ option( ENABLE_DATE_TESTING "Enable unit tests" OFF )
|
||||
option( DISABLE_STRING_VIEW "Disable string view" OFF )
|
||||
option( COMPILE_WITH_C_LOCALE "define ONLY_C_LOCALE=1" OFF )
|
||||
option( BUILD_TZ_LIB "build/install of TZ library" OFF )
|
||||
option( ENABLE_DATE_INSTALL "Enable install" ON )
|
||||
|
||||
if( ENABLE_DATE_TESTING AND NOT BUILD_TZ_LIB )
|
||||
message(WARNING "Testing requested, bug BUILD_TZ_LIB not ON - forcing the latter")
|
||||
message(WARNING "Testing requested, but BUILD_TZ_LIB not ON - forcing the latter")
|
||||
set (BUILD_TZ_LIB ON CACHE BOOL "required for testing" FORCE)
|
||||
endif( )
|
||||
|
||||
if( ENABLE_DATE_INSTALL )
|
||||
include( GNUInstallDirs )
|
||||
endif( )
|
||||
|
||||
function( print_option OPT )
|
||||
if ( NOT DEFINED PRINT_OPTION_CURR_${OPT} OR ( NOT PRINT_OPTION_CURR_${OPT} STREQUAL ${OPT} ) )
|
||||
set( PRINT_OPTION_CURR_${OPT} ${${OPT}} CACHE BOOL "" )
|
||||
@ -63,19 +71,37 @@ add_library( date INTERFACE )
|
||||
add_library( date::date ALIAS date )
|
||||
target_include_directories( date INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include> )
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> )
|
||||
# adding header sources just helps IDEs
|
||||
target_sources( date INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:include>/date/date.h
|
||||
# the rest of these are not currently part of the public interface of the library:
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/solar_hijri.h>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/islamic.h>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/iso_week.h>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/julian.h>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/date.h
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/solar_hijri.h
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/islamic.h
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/iso_week.h
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/julian.h
|
||||
)
|
||||
|
||||
set( APPLE_MOBILE_PLATFORM FALSE )
|
||||
if ( IOS
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "iOS"
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "visionOS"
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "watchOS"
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "tvOS"
|
||||
)
|
||||
set( APPLE_MOBILE_PLATFORM TRUE )
|
||||
endif()
|
||||
|
||||
set(TARGET_HEADERS
|
||||
include/date/date.h
|
||||
include/date/solar_hijri.h
|
||||
include/date/islamic.h
|
||||
include/date/iso_week.h
|
||||
include/date/julian.h
|
||||
)
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
|
||||
# public headers will get installed:
|
||||
set_target_properties( date PROPERTIES PUBLIC_HEADER include/date/date.h )
|
||||
set_target_properties( date PROPERTIES PUBLIC_HEADER "${TARGET_HEADERS}" )
|
||||
endif ()
|
||||
|
||||
# These used to be set with generator expressions,
|
||||
@ -95,6 +121,8 @@ else()
|
||||
endif()
|
||||
if ( DISABLE_STRING_VIEW )
|
||||
target_compile_definitions( date INTERFACE HAS_STRING_VIEW=0 -DHAS_DEDUCTION_GUIDES=0 )
|
||||
else()
|
||||
target_compile_definitions( date INTERFACE HAS_STRING_VIEW=1 )
|
||||
endif()
|
||||
|
||||
#[===================================================================[
|
||||
@ -102,16 +130,17 @@ endif()
|
||||
#]===================================================================]
|
||||
if( BUILD_TZ_LIB )
|
||||
add_library( date-tz )
|
||||
target_compile_definitions( date-tz PRIVATE BUILD_TZ_LIB=1 )
|
||||
target_sources( date-tz
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:include>/date/tz.h
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/tz.h
|
||||
PRIVATE
|
||||
include/date/tz_private.h
|
||||
src/tz.cpp )
|
||||
if ( IOS )
|
||||
if ( APPLE_MOBILE_PLATFORM )
|
||||
target_sources( date-tz
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:include>/date/ios.h
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/ios.h
|
||||
PRIVATE
|
||||
src/ios.mm )
|
||||
endif()
|
||||
@ -119,7 +148,7 @@ if( BUILD_TZ_LIB )
|
||||
target_link_libraries( date-tz PUBLIC date )
|
||||
target_include_directories( date-tz PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include> )
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> )
|
||||
|
||||
if ( USE_SYSTEM_TZ_DB OR MANUAL_TZ_DB )
|
||||
target_compile_definitions( date-tz PRIVATE AUTO_DOWNLOAD=0 HAS_REMOTE_API=0 )
|
||||
@ -139,7 +168,7 @@ if( BUILD_TZ_LIB )
|
||||
|
||||
set(TZ_HEADERS include/date/tz.h)
|
||||
|
||||
if( IOS )
|
||||
if( APPLE_MOBILE_PLATFORM )
|
||||
list(APPEND TZ_HEADERS include/date/ios.h)
|
||||
endif( )
|
||||
set_target_properties( date-tz PROPERTIES
|
||||
@ -153,54 +182,55 @@ if( BUILD_TZ_LIB )
|
||||
endif( )
|
||||
if( NOT USE_SYSTEM_TZ_DB AND NOT MANUAL_TZ_DB )
|
||||
find_package( CURL REQUIRED )
|
||||
target_include_directories( date-tz SYSTEM PRIVATE ${CURL_INCLUDE_DIRS} )
|
||||
target_link_libraries( date-tz PRIVATE ${CURL_LIBRARIES} )
|
||||
target_link_libraries( date-tz PRIVATE CURL::libcurl )
|
||||
endif( )
|
||||
endif( )
|
||||
|
||||
#[===================================================================[
|
||||
installation
|
||||
#]===================================================================]
|
||||
set( version_config "${CMAKE_CURRENT_BINARY_DIR}/dateConfigVersion.cmake" )
|
||||
if( ENABLE_DATE_INSTALL )
|
||||
set( version_config "${CMAKE_CURRENT_BINARY_DIR}/dateConfigVersion.cmake" )
|
||||
|
||||
include( CMakePackageConfigHelpers )
|
||||
write_basic_package_version_file( "${version_config}"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion )
|
||||
include( CMakePackageConfigHelpers )
|
||||
write_basic_package_version_file( "${version_config}"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion )
|
||||
|
||||
install( TARGETS date
|
||||
EXPORT dateConfig
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
|
||||
export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake )
|
||||
if (CMAKE_VERSION VERSION_LESS 3.15)
|
||||
install(
|
||||
FILES include/date/date.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
|
||||
endif ()
|
||||
install( TARGETS date
|
||||
EXPORT dateConfig
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
|
||||
export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake )
|
||||
if (CMAKE_VERSION VERSION_LESS 3.15)
|
||||
install(
|
||||
FILES ${TARGET_HEADERS}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
|
||||
endif ()
|
||||
|
||||
if( BUILD_TZ_LIB )
|
||||
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 date-tz NAMESPACE date:: APPEND FILE dateTargets.cmake )
|
||||
if( BUILD_TZ_LIB )
|
||||
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 date-tz NAMESPACE date:: APPEND FILE dateTargets.cmake )
|
||||
endif( )
|
||||
|
||||
if( WIN32 AND NOT CYGWIN)
|
||||
set( CONFIG_LOC CMake )
|
||||
else( )
|
||||
set( CONFIG_LOC "${CMAKE_INSTALL_LIBDIR}/cmake/date" )
|
||||
endif( )
|
||||
install( EXPORT dateConfig
|
||||
FILE dateTargets.cmake
|
||||
NAMESPACE date::
|
||||
DESTINATION ${CONFIG_LOC} )
|
||||
install (
|
||||
FILES cmake/dateConfig.cmake "${version_config}"
|
||||
DESTINATION ${CONFIG_LOC})
|
||||
endif( )
|
||||
|
||||
if( WIN32 AND NOT CYGWIN)
|
||||
set( CONFIG_LOC CMake )
|
||||
else( )
|
||||
set( CONFIG_LOC "${CMAKE_INSTALL_LIBDIR}/cmake/date" )
|
||||
endif( )
|
||||
install( EXPORT dateConfig
|
||||
FILE dateTargets.cmake
|
||||
NAMESPACE date::
|
||||
DESTINATION ${CONFIG_LOC} )
|
||||
install (
|
||||
FILES cmake/dateConfig.cmake "${version_config}"
|
||||
DESTINATION ${CONFIG_LOC})
|
||||
|
||||
#[===================================================================[
|
||||
testing
|
||||
#]===================================================================]
|
||||
|
@ -49,7 +49,7 @@ To use `"tz.h"`, there is a single source file (`src/tz.cpp`) that needs to be c
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
@ -66,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
|
||||
@ -80,5 +80,7 @@ cmake --build . --target testit # Consider '-- -j4' for multithreading
|
||||
* https://github.com/KomodoPlatform/atomicDEX-Pro
|
||||
* https://github.com/Kotlin/kotlinx-datetime
|
||||
* https://github.com/royalbee/jewish_date
|
||||
* https://github.com/apache/arrow/
|
||||
* https://lottopark.com
|
||||
|
||||
If you would like your project (or product) on this list, just let me know.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,10 +36,10 @@
|
||||
{
|
||||
namespace iOSUtils
|
||||
{
|
||||
|
||||
|
||||
std::string get_tzdata_path();
|
||||
std::string get_current_timezone();
|
||||
|
||||
|
||||
} // namespace iOSUtils
|
||||
} // namespace date
|
||||
|
||||
|
@ -758,8 +758,8 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last&
|
||||
inline namespace literals
|
||||
{
|
||||
|
||||
CONSTCD11 islamic::day operator "" _d(unsigned long long d) NOEXCEPT;
|
||||
CONSTCD11 islamic::year operator "" _y(unsigned long long y) NOEXCEPT;
|
||||
CONSTCD11 islamic::day operator ""_d(unsigned long long d) NOEXCEPT;
|
||||
CONSTCD11 islamic::year operator ""_y(unsigned long long y) NOEXCEPT;
|
||||
|
||||
} // inline namespace literals
|
||||
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
@ -1339,7 +1339,7 @@ inline namespace literals
|
||||
CONSTCD11
|
||||
inline
|
||||
islamic::day
|
||||
operator "" _d(unsigned long long d) NOEXCEPT
|
||||
operator ""_d(unsigned long long d) NOEXCEPT
|
||||
{
|
||||
return islamic::day{static_cast<unsigned>(d)};
|
||||
}
|
||||
@ -1347,7 +1347,7 @@ operator "" _d(unsigned long long d) NOEXCEPT
|
||||
CONSTCD11
|
||||
inline
|
||||
islamic::year
|
||||
operator "" _y(unsigned long long y) NOEXCEPT
|
||||
operator ""_y(unsigned long long y) NOEXCEPT
|
||||
{
|
||||
return islamic::year(static_cast<int>(y));
|
||||
}
|
||||
|
@ -148,6 +148,8 @@ public:
|
||||
year& operator+=(const years& y) NOEXCEPT;
|
||||
year& operator-=(const years& y) NOEXCEPT;
|
||||
|
||||
CONSTCD14 bool is_leap() const NOEXCEPT;
|
||||
|
||||
CONSTCD11 explicit operator int() const NOEXCEPT;
|
||||
CONSTCD11 bool ok() const NOEXCEPT;
|
||||
|
||||
@ -599,6 +601,17 @@ inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return t
|
||||
inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;}
|
||||
inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;}
|
||||
|
||||
CONSTCD14
|
||||
inline
|
||||
bool
|
||||
year::is_leap() const NOEXCEPT
|
||||
{
|
||||
const auto y = date::year{static_cast<int>(y_)};
|
||||
const auto s0 = sys_days((y-years{1})/12/date::thu[date::last]);
|
||||
const auto s1 = sys_days(y/12/date::thu[date::last]);
|
||||
return s1-s0 != days{7*52};
|
||||
}
|
||||
|
||||
CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;}
|
||||
CONSTCD11 inline bool year::ok() const NOEXCEPT {return min() <= *this && *this <= max();}
|
||||
|
||||
@ -734,7 +747,7 @@ inline namespace literals
|
||||
CONSTCD11
|
||||
inline
|
||||
iso_week::year
|
||||
operator "" _y(unsigned long long y) NOEXCEPT
|
||||
operator ""_y(unsigned long long y) NOEXCEPT
|
||||
{
|
||||
return iso_week::year(static_cast<int>(y));
|
||||
}
|
||||
@ -742,7 +755,7 @@ operator "" _y(unsigned long long y) NOEXCEPT
|
||||
CONSTCD11
|
||||
inline
|
||||
iso_week::weeknum
|
||||
operator "" _w(unsigned long long wn) NOEXCEPT
|
||||
operator ""_w(unsigned long long wn) NOEXCEPT
|
||||
{
|
||||
return iso_week::weeknum(static_cast<unsigned>(wn));
|
||||
}
|
||||
@ -1020,10 +1033,7 @@ inline
|
||||
weeknum
|
||||
year_lastweek::weeknum() const NOEXCEPT
|
||||
{
|
||||
const auto y = date::year{static_cast<int>(y_)};
|
||||
const auto s0 = sys_days((y-years{1})/12/date::thu[date::last]);
|
||||
const auto s1 = sys_days(y/12/date::thu[date::last]);
|
||||
return iso_week::weeknum(static_cast<unsigned>(date::trunc<weeks>(s1-s0).count()));
|
||||
return iso_week::weeknum(y_.is_leap() ? 53u : 52u);
|
||||
}
|
||||
|
||||
CONSTCD11 inline bool year_lastweek::ok() const NOEXCEPT {return y_.ok();}
|
||||
|
@ -758,8 +758,8 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last&
|
||||
inline namespace literals
|
||||
{
|
||||
|
||||
CONSTCD11 julian::day operator "" _d(unsigned long long d) NOEXCEPT;
|
||||
CONSTCD11 julian::year operator "" _y(unsigned long long y) NOEXCEPT;
|
||||
CONSTCD11 julian::day operator ""_d(unsigned long long d) NOEXCEPT;
|
||||
CONSTCD11 julian::year operator ""_y(unsigned long long y) NOEXCEPT;
|
||||
|
||||
// CONSTDATA julian::month jan{1};
|
||||
// CONSTDATA julian::month feb{2};
|
||||
@ -1333,7 +1333,7 @@ inline namespace literals
|
||||
CONSTCD11
|
||||
inline
|
||||
julian::day
|
||||
operator "" _d(unsigned long long d) NOEXCEPT
|
||||
operator ""_d(unsigned long long d) NOEXCEPT
|
||||
{
|
||||
return julian::day{static_cast<unsigned>(d)};
|
||||
}
|
||||
@ -1341,7 +1341,7 @@ operator "" _d(unsigned long long d) NOEXCEPT
|
||||
CONSTCD11
|
||||
inline
|
||||
julian::year
|
||||
operator "" _y(unsigned long long y) NOEXCEPT
|
||||
operator ""_y(unsigned long long y) NOEXCEPT
|
||||
{
|
||||
return julian::year(static_cast<int>(y));
|
||||
}
|
||||
@ -1655,10 +1655,10 @@ inline
|
||||
bool
|
||||
month_day::ok() const NOEXCEPT
|
||||
{
|
||||
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)
|
||||
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];
|
||||
}
|
||||
@ -1949,10 +1949,10 @@ inline
|
||||
day
|
||||
year_month_day_last::day() const NOEXCEPT
|
||||
{
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
@ -36,8 +36,24 @@
|
||||
// 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.
|
||||
// In C++17 CTAD simplifies this to:
|
||||
//
|
||||
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
|
||||
// zoned_time zt{tz, system_clock::now()};
|
||||
//
|
||||
// Extension to the Posix rules to allow a constant daylight saving offset:
|
||||
//
|
||||
// If the rule set is missing (everything starting with ','), then
|
||||
// there must be exactly one abbreviation (std or daylight) with
|
||||
// length 3 or greater, and that will be used as the constant offset. If
|
||||
// there are two, the std abbreviation is silently set to "", and the
|
||||
// result is constant daylight saving. If there are zero abbreviations
|
||||
// with no rule set, an exception is thrown.
|
||||
//
|
||||
// Example:
|
||||
// "EST5" yields a constant offset of -5h with 0h save and "EST abbreviation.
|
||||
// "5EDT" yields a constant offset of -4h with 1h save and "EDT" abbreviation.
|
||||
// "EST5EDT" and "5EDT4" are both equal to "5EDT".
|
||||
//
|
||||
// Note, Posix-style time zones are not recommended for all of the reasons described here:
|
||||
// https://stackoverflow.com/tags/timezone/info
|
||||
@ -46,6 +62,7 @@
|
||||
// have to have Posix time zones, you're welcome to use this one.
|
||||
|
||||
#include "date/tz.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
@ -269,8 +286,89 @@ public:
|
||||
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()};
|
||||
}
|
||||
|
||||
inline
|
||||
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;
|
||||
using date::days;
|
||||
using std::chrono::minutes;
|
||||
sys_info r;
|
||||
r.begin = sys_days{year::min()/January/1};
|
||||
r.end = sys_days{year::max()/December/last} + days{1} - std::chrono::seconds{1};
|
||||
if (std_abbrev_.size() > 0)
|
||||
{
|
||||
r.abbrev = std_abbrev_;
|
||||
r.offset = offset_;
|
||||
r.save = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
r.abbrev = dst_abbrev_;
|
||||
r.offset = offset_ + save_;
|
||||
r.save = date::ceil<minutes>(save_);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
time_zone::time_zone(const detail::string_t& s)
|
||||
{
|
||||
@ -278,11 +376,14 @@ time_zone::time_zone(const detail::string_t& s)
|
||||
using detail::read_signed_time;
|
||||
using detail::throw_invalid;
|
||||
auto i = read_name(s, 0, std_abbrev_);
|
||||
auto std_name_i = i;
|
||||
auto abbrev_name_i = i;
|
||||
i = read_signed_time(s, i, offset_);
|
||||
offset_ = -offset_;
|
||||
if (i != s.size())
|
||||
{
|
||||
i = read_name(s, i, dst_abbrev_);
|
||||
abbrev_name_i = i;
|
||||
if (i != s.size())
|
||||
{
|
||||
if (s[i] != ',')
|
||||
@ -305,6 +406,32 @@ time_zone::time_zone(const detail::string_t& s)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start_rule_.ok())
|
||||
{
|
||||
if (std_abbrev_.size() < 3)
|
||||
throw_invalid(s, std_name_i, "Zone with rules must have a std"
|
||||
" abbreviation of length 3 or greater");
|
||||
if (dst_abbrev_.size() < 3)
|
||||
throw_invalid(s, abbrev_name_i, "Zone with rules must have a daylight"
|
||||
" abbreviation of length 3 or greater");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dst_abbrev_.size() >= 3)
|
||||
{
|
||||
std_abbrev_.clear();
|
||||
}
|
||||
else if (std_abbrev_.size() < 3)
|
||||
{
|
||||
throw_invalid(s, std_name_i, "Zone must have at least one abbreviation"
|
||||
" of length 3 or greater");
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_abbrev_.clear();
|
||||
save_ = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Duration>
|
||||
@ -313,12 +440,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;
|
||||
@ -329,36 +454,65 @@ 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)
|
||||
if (st >= get_next_start(y))
|
||||
++y;
|
||||
else if (st < get_prev_end(y))
|
||||
--y;
|
||||
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();
|
||||
using seconds = std::chrono::seconds;
|
||||
assert(r.begin <= floor<seconds>(st) && floor<seconds>(st) <= r.end);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -371,7 +525,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;
|
||||
@ -384,19 +537,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_);
|
||||
@ -405,51 +564,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;
|
||||
}
|
||||
|
||||
@ -460,6 +597,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);
|
||||
@ -516,11 +654,22 @@ time_zone::name() const
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
auto nm = std_abbrev_;
|
||||
auto print_abbrev = [](std::string const& nm)
|
||||
{
|
||||
if (std::any_of(nm.begin(), nm.end(),
|
||||
[](char c)
|
||||
{
|
||||
return !std::isalpha(c);
|
||||
}))
|
||||
{
|
||||
return '<' + nm + '>';
|
||||
}
|
||||
return nm;
|
||||
};
|
||||
auto print_offset = [](seconds off)
|
||||
{
|
||||
std::string nm;
|
||||
hh_mm_ss<seconds> offset{-off};
|
||||
date::hh_mm_ss<seconds> offset{-off};
|
||||
if (offset.is_negative())
|
||||
nm += '-';
|
||||
nm += std::to_string(offset.hours().count());
|
||||
@ -540,10 +689,11 @@ time_zone::name() const
|
||||
}
|
||||
return nm;
|
||||
};
|
||||
auto nm = print_abbrev(std_abbrev_);
|
||||
nm += print_offset(offset_);
|
||||
if (!dst_abbrev_.empty())
|
||||
{
|
||||
nm += dst_abbrev_;
|
||||
nm += print_abbrev(dst_abbrev_);
|
||||
if (save_ != hours{1})
|
||||
nm += print_offset(offset_+save_);
|
||||
if (start_rule_.ok())
|
||||
@ -605,6 +755,8 @@ read_date(const string_t& s, unsigned i, rule& r)
|
||||
++i;
|
||||
unsigned n;
|
||||
i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]");
|
||||
if (!(1 <= n && n <= 365))
|
||||
throw_invalid(s, i-1, "Expected Julian day to be in the range [1, 365]");
|
||||
r.mode_ = rule::J;
|
||||
r.n_ = n;
|
||||
}
|
||||
@ -613,16 +765,22 @@ read_date(const string_t& s, unsigned i, rule& r)
|
||||
++i;
|
||||
unsigned m;
|
||||
i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]");
|
||||
if (!(1 <= m && m <= 12))
|
||||
throw_invalid(s, i-1, "Expected month to be in the range [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, "Expected to find week number [1, 5]");
|
||||
if (!(1 <= n && n <= 5))
|
||||
throw_invalid(s, i-1, "Expected week number to be in the range [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, "Expected to find day of week [0, 6]");
|
||||
if (wd > 6)
|
||||
throw_invalid(s, i-1, "Expected day of week to be in the range [0, 6]");
|
||||
r.mode_ = rule::M;
|
||||
r.m_ = month{m};
|
||||
r.wd_ = weekday{wd};
|
||||
@ -632,6 +790,8 @@ read_date(const string_t& s, unsigned i, rule& r)
|
||||
{
|
||||
unsigned n;
|
||||
i = read_unsigned(s, i, 3, n);
|
||||
if (n > 365)
|
||||
throw_invalid(s, i-1, "Expected Julian day to be in the range [0, 365]");
|
||||
r.mode_ = rule::N;
|
||||
r.n_ = n;
|
||||
}
|
||||
@ -676,8 +836,6 @@ read_name(const string_t& s, unsigned i, std::string& name)
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (name.size() < 3)
|
||||
throw_invalid(s, i, "Found name to be shorter than 3 characters");
|
||||
return i;
|
||||
}
|
||||
|
||||
@ -713,16 +871,22 @@ read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
|
||||
throw_invalid(s, i, "Expected to read unsigned time, but found end of string");
|
||||
unsigned x;
|
||||
i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]");
|
||||
if (x > 24)
|
||||
throw_invalid(s, i-1, "Expected hours to be in the range [0, 24]");
|
||||
t = hours{x};
|
||||
if (i != s.size() && s[i] == ':')
|
||||
{
|
||||
++i;
|
||||
i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]");
|
||||
if (x > 59)
|
||||
throw_invalid(s, i-1, "Expected minutes to be in the range [0, 59]");
|
||||
t += minutes{x};
|
||||
if (i != s.size() && s[i] == ':')
|
||||
{
|
||||
++i;
|
||||
i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]");
|
||||
if (x > 59)
|
||||
throw_invalid(s, i-1, "Expected seconds to be in the range [0, 59]");
|
||||
t += seconds{x};
|
||||
}
|
||||
}
|
||||
|
@ -792,8 +792,8 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last&
|
||||
inline namespace literals
|
||||
{
|
||||
|
||||
CONSTCD11 solar_hijri::day operator "" _d(unsigned long long d) NOEXCEPT;
|
||||
CONSTCD11 solar_hijri::year operator "" _y(unsigned long long y) NOEXCEPT;
|
||||
CONSTCD11 solar_hijri::day operator ""_d(unsigned long long d) NOEXCEPT;
|
||||
CONSTCD11 solar_hijri::year operator ""_y(unsigned long long y) NOEXCEPT;
|
||||
|
||||
} // inline namespace literals
|
||||
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
@ -1364,7 +1364,7 @@ inline namespace literals
|
||||
CONSTCD11
|
||||
inline
|
||||
solar_hijri::day
|
||||
operator "" _d(unsigned long long d) NOEXCEPT
|
||||
operator ""_d(unsigned long long d) NOEXCEPT
|
||||
{
|
||||
return solar_hijri::day{static_cast<unsigned>(d)};
|
||||
}
|
||||
@ -1372,7 +1372,7 @@ operator "" _d(unsigned long long d) NOEXCEPT
|
||||
CONSTCD11
|
||||
inline
|
||||
solar_hijri::year
|
||||
operator "" _y(unsigned long long y) NOEXCEPT
|
||||
operator ""_y(unsigned long long y) NOEXCEPT
|
||||
{
|
||||
return solar_hijri::year(static_cast<int>(y));
|
||||
}
|
||||
@ -1707,11 +1707,11 @@ inline
|
||||
bool
|
||||
month_day::ok() const NOEXCEPT
|
||||
{
|
||||
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)
|
||||
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];
|
||||
}
|
||||
@ -2002,11 +2002,11 @@ inline
|
||||
day
|
||||
year_month_day_last::day() const NOEXCEPT
|
||||
{
|
||||
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)
|
||||
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] : solar_hijri::day(30);
|
||||
|
@ -49,12 +49,12 @@
|
||||
|
||||
#ifndef HAS_REMOTE_API
|
||||
# if USE_OS_TZDB == 0
|
||||
# ifdef _WIN32
|
||||
# if defined _WIN32 || defined __ANDROID__
|
||||
# define HAS_REMOTE_API 0
|
||||
# else
|
||||
# define HAS_REMOTE_API 1
|
||||
# endif
|
||||
# else // HAS_REMOTE_API makes no since when using the OS timezone database
|
||||
# else // HAS_REMOTE_API makes no sense when using the OS timezone database
|
||||
# define HAS_REMOTE_API 0
|
||||
# endif
|
||||
#endif
|
||||
@ -139,6 +139,13 @@ namespace date
|
||||
|
||||
enum class choose {earliest, latest};
|
||||
|
||||
#if defined(BUILD_TZ_LIB)
|
||||
# if defined(ANDROID) || defined(__ANDROID__)
|
||||
struct tzdb;
|
||||
static std::unique_ptr<tzdb> init_tzdb();
|
||||
# endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
#endif // defined(BUILD_TZ_LIB)
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct undocumented;
|
||||
@ -231,8 +238,8 @@ nonexistent_local_time::make_msg(local_time<Duration> tp, const local_info& i)
|
||||
<< i.first.abbrev << " and\n"
|
||||
<< local_seconds{i.second.begin.time_since_epoch()} + i.second.offset << ' '
|
||||
<< i.second.abbrev
|
||||
<< " which are both equivalent to\n"
|
||||
<< i.first.end << " UTC";
|
||||
<< " which are both equivalent to\n";
|
||||
date::operator<<(os, i.first.end) << " UTC";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
@ -821,6 +828,12 @@ public:
|
||||
|
||||
#if !USE_OS_TZDB
|
||||
DATE_API void add(const std::string& s);
|
||||
#else
|
||||
# if defined(BUILD_TZ_LIB)
|
||||
# if defined(ANDROID) || defined(__ANDROID__)
|
||||
friend std::unique_ptr<tzdb> init_tzdb();
|
||||
# endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
# endif // defined(BUILD_TZ_LIB)
|
||||
#endif // !USE_OS_TZDB
|
||||
|
||||
private:
|
||||
@ -844,8 +857,11 @@ private:
|
||||
DATE_API void
|
||||
load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
|
||||
std::int32_t tzh_typecnt, std::int32_t tzh_charcnt);
|
||||
# if defined(ANDROID) || defined(__ANDROID__)
|
||||
void parse_from_android_tzdata(std::ifstream& inf, const std::size_t off);
|
||||
# endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
#else // !USE_OS_TZDB
|
||||
DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const;
|
||||
DATE_API sys_info get_info_impl(sys_seconds tp, int tz_int) const;
|
||||
DATE_API void adjust_infos(const std::vector<detail::Rule>& rules);
|
||||
DATE_API void parse_info(std::istream& in);
|
||||
#endif // !USE_OS_TZDB
|
||||
@ -1190,11 +1206,11 @@ struct tzdb
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
|
||||
#if HAS_STRING_VIEW
|
||||
const time_zone* locate_zone(std::string_view tz_name) const;
|
||||
DATE_API const time_zone* locate_zone(std::string_view tz_name) const;
|
||||
#else
|
||||
const time_zone* locate_zone(const std::string& tz_name) const;
|
||||
DATE_API const time_zone* locate_zone(const std::string& tz_name) const;
|
||||
#endif
|
||||
const time_zone* current_zone() const;
|
||||
DATE_API const time_zone* current_zone() const;
|
||||
};
|
||||
|
||||
using TZ_DB = tzdb;
|
||||
@ -1209,9 +1225,9 @@ class tzdb_list
|
||||
std::atomic<tzdb*> head_{nullptr};
|
||||
|
||||
public:
|
||||
~tzdb_list();
|
||||
DATE_API ~tzdb_list();
|
||||
tzdb_list() = default;
|
||||
tzdb_list(tzdb_list&& x) NOEXCEPT;
|
||||
DATE_API tzdb_list(tzdb_list&& x) NOEXCEPT;
|
||||
|
||||
const tzdb& front() const NOEXCEPT {return *head_;}
|
||||
tzdb& front() NOEXCEPT {return *head_;}
|
||||
@ -1224,7 +1240,7 @@ public:
|
||||
const_iterator cbegin() const NOEXCEPT;
|
||||
const_iterator cend() const NOEXCEPT;
|
||||
|
||||
const_iterator erase_after(const_iterator p) NOEXCEPT;
|
||||
DATE_API const_iterator erase_after(const_iterator p) NOEXCEPT;
|
||||
|
||||
struct undocumented_helper;
|
||||
private:
|
||||
|
@ -289,8 +289,7 @@ struct transition
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const transition& t)
|
||||
{
|
||||
using date::operator<<;
|
||||
os << t.timepoint << "Z ";
|
||||
date::operator<<(os, t.timepoint) << "Z ";
|
||||
if (t.info->offset >= std::chrono::seconds{0})
|
||||
os << '+';
|
||||
os << make_time(t.info->offset);
|
||||
|
110
src/ios.mm
110
src/ios.mm
@ -51,7 +51,7 @@ namespace date
|
||||
{
|
||||
namespace iOSUtils
|
||||
{
|
||||
|
||||
|
||||
struct TarInfo
|
||||
{
|
||||
char objType;
|
||||
@ -60,14 +60,14 @@ namespace date
|
||||
size_t blocksContentSize; // adjusted size to 512 bytes blocks
|
||||
bool success;
|
||||
};
|
||||
|
||||
|
||||
std::string convertCFStringRefPathToCStringPath(CFStringRef ref);
|
||||
bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath);
|
||||
TarInfo getTarObjectInfo(std::ifstream &readStream);
|
||||
std::string getTarObject(std::ifstream &readStream, int64_t size);
|
||||
bool writeFile(const std::string &tzdataPath, const std::string &fileName,
|
||||
const std::string &data, size_t realContentSize);
|
||||
|
||||
|
||||
std::string
|
||||
get_current_timezone()
|
||||
{
|
||||
@ -75,18 +75,18 @@ namespace date
|
||||
CFStringRef tzNameRef = CFTimeZoneGetName(tzRef);
|
||||
CFIndex bufferSize = CFStringGetLength(tzNameRef) + 1;
|
||||
char buffer[bufferSize];
|
||||
|
||||
|
||||
if (CFStringGetCString(tzNameRef, buffer, bufferSize, kCFStringEncodingUTF8))
|
||||
{
|
||||
CFRelease(tzRef);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
|
||||
CFRelease(tzRef);
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
get_tzdata_path()
|
||||
{
|
||||
@ -96,7 +96,7 @@ namespace date
|
||||
INTERNAL_DIR + "/" + TZDATA_DIR);
|
||||
std::string result_path(std::string(convertCFStringRefPathToCStringPath(homePath)) +
|
||||
INTERNAL_DIR);
|
||||
|
||||
|
||||
if (access(path.c_str(), F_OK) == 0)
|
||||
{
|
||||
#if TAR_DEBUG
|
||||
@ -104,34 +104,34 @@ namespace date
|
||||
#endif
|
||||
CFRelease(homeUrlRef);
|
||||
CFRelease(homePath);
|
||||
|
||||
|
||||
return result_path;
|
||||
}
|
||||
|
||||
|
||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||
CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION),
|
||||
NULL);
|
||||
|
||||
|
||||
if (CFArrayGetCount(paths) != 0)
|
||||
{
|
||||
// get archive path, assume there is no other tar.gz in bundle
|
||||
CFURLRef archiveUrl = static_cast<CFURLRef>(CFArrayGetValueAtIndex(paths, 0));
|
||||
CFStringRef archiveName = CFURLCopyPath(archiveUrl);
|
||||
archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL);
|
||||
|
||||
|
||||
extractTzdata(homeUrlRef, archiveUrl, path);
|
||||
|
||||
|
||||
CFRelease(archiveUrl);
|
||||
CFRelease(archiveName);
|
||||
}
|
||||
|
||||
|
||||
CFRelease(homeUrlRef);
|
||||
CFRelease(homePath);
|
||||
CFRelease(paths);
|
||||
|
||||
|
||||
return result_path;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
convertCFStringRefPathToCStringPath(CFStringRef ref)
|
||||
{
|
||||
@ -142,55 +142,55 @@ namespace date
|
||||
delete[] buffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath)
|
||||
{
|
||||
std::string TAR_TMP_PATH = "/tmp.tar";
|
||||
|
||||
|
||||
CFStringRef homeStringRef = CFURLCopyPath(homeUrl);
|
||||
auto homePath = convertCFStringRefPathToCStringPath(homeStringRef);
|
||||
CFRelease(homeStringRef);
|
||||
|
||||
|
||||
CFStringRef archiveStringRef = CFURLCopyPath(archiveUrl);
|
||||
auto archivePath = convertCFStringRefPathToCStringPath(archiveStringRef);
|
||||
CFRelease(archiveStringRef);
|
||||
|
||||
|
||||
// create Library path
|
||||
auto libraryPath = homePath + INTERNAL_DIR;
|
||||
|
||||
|
||||
// create tzdata path
|
||||
auto tzdataPath = libraryPath + "/" + TZDATA_DIR;
|
||||
|
||||
|
||||
// -- replace %20 with " "
|
||||
const std::string search = "%20";
|
||||
const std::string replacement = " ";
|
||||
size_t pos = 0;
|
||||
|
||||
|
||||
while ((pos = archivePath.find(search, pos)) != std::string::npos) {
|
||||
archivePath.replace(pos, search.length(), replacement);
|
||||
pos += replacement.length();
|
||||
}
|
||||
|
||||
|
||||
gzFile tarFile = gzopen(archivePath.c_str(), "rb");
|
||||
|
||||
|
||||
// create tar unpacking path
|
||||
auto tarPath = libraryPath + TAR_TMP_PATH;
|
||||
|
||||
|
||||
// create tzdata directory
|
||||
mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
|
||||
|
||||
// ======= extract tar ========
|
||||
|
||||
|
||||
std::ofstream os(tarPath.c_str(), std::ofstream::out | std::ofstream::app);
|
||||
unsigned int bufferLength = 1024 * 256; // 256Kb
|
||||
unsigned char *buffer = (unsigned char *)malloc(bufferLength);
|
||||
bool success = true;
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
int readBytes = gzread(tarFile, buffer, bufferLength);
|
||||
|
||||
|
||||
if (readBytes > 0)
|
||||
{
|
||||
os.write((char *) &buffer[0], readBytes);
|
||||
@ -214,21 +214,21 @@ namespace date
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
os.close();
|
||||
free(buffer);
|
||||
gzclose(tarFile);
|
||||
|
||||
|
||||
if (!success)
|
||||
{
|
||||
remove(tarPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ======== extract files =========
|
||||
|
||||
|
||||
uint64_t location = 0; // Position in the file
|
||||
|
||||
|
||||
// get file size
|
||||
struct stat stat_buf;
|
||||
int res = stat(tarPath.c_str(), &stat_buf);
|
||||
@ -239,20 +239,20 @@ namespace date
|
||||
return false;
|
||||
}
|
||||
int64_t tarSize = stat_buf.st_size;
|
||||
|
||||
|
||||
// create read stream
|
||||
std::ifstream is(tarPath.c_str(), std::ifstream::in | std::ifstream::binary);
|
||||
|
||||
|
||||
// process files
|
||||
while (location < tarSize)
|
||||
{
|
||||
TarInfo info = getTarObjectInfo(is);
|
||||
|
||||
|
||||
if (!info.success || info.realContentSize == 0)
|
||||
{
|
||||
break; // something wrong or all files are read
|
||||
}
|
||||
|
||||
|
||||
switch (info.objType)
|
||||
{
|
||||
case '0': // file
|
||||
@ -266,17 +266,17 @@ namespace date
|
||||
#endif
|
||||
writeFile(tzdataPath, info.objName, obj, info.realContentSize);
|
||||
location += info.blocksContentSize;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remove(tarPath.c_str());
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
TarInfo
|
||||
getTarObjectInfo(std::ifstream &readStream)
|
||||
{
|
||||
@ -285,22 +285,22 @@ namespace date
|
||||
char type;
|
||||
char name[TAR_NAME_SIZE + 1];
|
||||
char sizeBuf[TAR_SIZE_SIZE + 1];
|
||||
|
||||
|
||||
readStream.read(buffer, length);
|
||||
|
||||
|
||||
memcpy(&type, &buffer[TAR_TYPE_POSITION], 1);
|
||||
|
||||
|
||||
memset(&name, '\0', TAR_NAME_SIZE + 1);
|
||||
memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE);
|
||||
|
||||
|
||||
memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1);
|
||||
memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE);
|
||||
size_t realSize = strtol(sizeBuf, NULL, 8);
|
||||
size_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE));
|
||||
|
||||
|
||||
return {type, std::string(name), realSize, blocksSize, true};
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
getTarObject(std::ifstream &readStream, int64_t size)
|
||||
{
|
||||
@ -308,29 +308,29 @@ namespace date
|
||||
readStream.read(buffer, size);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
writeFile(const std::string &tzdataPath, const std::string &fileName, const std::string &data,
|
||||
size_t realContentSize)
|
||||
{
|
||||
std::ofstream os(tzdataPath + "/" + fileName, std::ofstream::out | std::ofstream::binary);
|
||||
|
||||
|
||||
if (!os) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// trim empty space
|
||||
char trimmedData[realContentSize + 1];
|
||||
memset(&trimmedData, '\0', realContentSize);
|
||||
memcpy(&trimmedData, data.c_str(), realContentSize);
|
||||
|
||||
|
||||
// write
|
||||
os.write(trimmedData, realContentSize);
|
||||
os.close();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace iOSUtils
|
||||
} // namespace date
|
||||
|
||||
|
624
src/tz.cpp
624
src/tz.cpp
@ -92,6 +92,27 @@
|
||||
# define TARGET_OS_SIMULATOR 0
|
||||
#endif
|
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
# include <sys/system_properties.h>
|
||||
# if USE_OS_TZDB
|
||||
# define MISSING_LEAP_SECONDS 1
|
||||
// from https://android.googlesource.com/platform/bionic/+/master/libc/tzcode/bionic.cpp
|
||||
static constexpr size_t ANDROID_TIMEZONE_NAME_LENGTH = 40;
|
||||
struct bionic_tzdata_header_t {
|
||||
char tzdata_version[12];
|
||||
std::int32_t index_offset;
|
||||
std::int32_t data_offset;
|
||||
std::int32_t final_offset;
|
||||
};
|
||||
struct index_entry_t {
|
||||
char buf[ANDROID_TIMEZONE_NAME_LENGTH];
|
||||
std::int32_t start;
|
||||
std::int32_t length;
|
||||
std::int32_t unused; // Was raw GMT offset; always 0 since tzdata2014f (L).
|
||||
};
|
||||
# endif // USE_OS_TZDB
|
||||
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
|
||||
#if USE_OS_TZDB
|
||||
# include <dirent.h>
|
||||
#endif
|
||||
@ -118,6 +139,9 @@
|
||||
// the current time zone. On Win32 windows.h provides a means to do it.
|
||||
// gcc/mingw supports unistd.h on Win32 but MSVC does not.
|
||||
|
||||
#ifdef __ANDROID__
|
||||
# define INSTALL .
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
# ifdef WINAPI_FAMILY
|
||||
# include <winapifamily.h>
|
||||
@ -170,9 +194,9 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
static CONSTDATA char folder_delimiter = '\\';
|
||||
#else // !_WIN32
|
||||
#elif !defined(ANDROID) && !defined(__ANDROID__)
|
||||
static CONSTDATA char folder_delimiter = '/';
|
||||
#endif // !_WIN32
|
||||
#endif // !defined(WIN32) && !defined(ANDROID) && !defined(__ANDROID__)
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ < 5
|
||||
// GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers
|
||||
@ -183,6 +207,36 @@ static CONSTDATA char folder_delimiter = '/';
|
||||
#if !USE_OS_TZDB
|
||||
|
||||
# ifdef _WIN32
|
||||
|
||||
static
|
||||
std::wstring
|
||||
convert_utf8_to_utf16(const std::string& s)
|
||||
{
|
||||
std::wstring out;
|
||||
const int size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
std::string msg = "Failed to determine required size when converting \"";
|
||||
msg += s;
|
||||
msg += "\" to UTF-16.";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
out.resize(size);
|
||||
const int check = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, &out[0], size);
|
||||
|
||||
if (size != check)
|
||||
{
|
||||
std::string msg = "Failed to convert \"";
|
||||
msg += s;
|
||||
msg += "\" to UTF-16.";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
# ifndef WINRT
|
||||
|
||||
namespace
|
||||
@ -267,6 +321,89 @@ get_download_folder()
|
||||
|
||||
# endif // !_WIN32
|
||||
|
||||
/*
|
||||
* This class is provided to mimic the following usage of `ifstream`:
|
||||
*
|
||||
* std::ifstream is(filename);
|
||||
*
|
||||
* file_streambuf ibuf(filename);
|
||||
* std::istream is(&ibuf);
|
||||
*
|
||||
* This is required because `ifstream` does not support opening files
|
||||
* containing wide characters on Windows. On Windows, `file_streambuf` uses
|
||||
* `file_open()` to convert the file name to UTF-16 before opening it with
|
||||
* `_wfopen()`.
|
||||
*
|
||||
* Note that this is not an exact re-implementation of `ifstream`,
|
||||
* but is enough for usage here.
|
||||
*
|
||||
* It is partially based on these two implementations:
|
||||
* - fdinbuf from http://www.josuttis.com/cppcode/fdstream.html
|
||||
* - stdiobuf https://stackoverflow.com/questions/12342542/convert-file-to-ifstream-c-android-ndk
|
||||
*
|
||||
* Apparently MSVC provides non-standard overloads of `ifstream` that support
|
||||
* a `const wchar_t*` file name, but MinGW does not https://stackoverflow.com/a/822032
|
||||
*/
|
||||
class file_streambuf
|
||||
: public std::streambuf
|
||||
{
|
||||
private:
|
||||
FILE* file_;
|
||||
static const int buffer_size_ = 1024;
|
||||
char buffer_[buffer_size_];
|
||||
|
||||
public:
|
||||
~file_streambuf()
|
||||
{
|
||||
if (file_)
|
||||
{
|
||||
::fclose(file_);
|
||||
}
|
||||
}
|
||||
file_streambuf(const file_streambuf&) = delete;
|
||||
file_streambuf& operator=(const file_streambuf&) = delete;
|
||||
|
||||
file_streambuf(const std::string& filename)
|
||||
: file_(file_open(filename))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual
|
||||
int_type
|
||||
underflow()
|
||||
{
|
||||
if (gptr() == egptr() && file_)
|
||||
{
|
||||
const size_t size = ::fread(buffer_, 1, buffer_size_, file_);
|
||||
setg(buffer_, buffer_, buffer_ + size);
|
||||
}
|
||||
return (gptr() == egptr())
|
||||
? traits_type::eof()
|
||||
: traits_type::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
private:
|
||||
FILE*
|
||||
file_open(const std::string& filename)
|
||||
{
|
||||
# ifdef _WIN32
|
||||
std::wstring wfilename = convert_utf8_to_utf16(filename);
|
||||
FILE* file = ::_wfopen(wfilename.c_str(), L"r");
|
||||
# else // !_WIN32
|
||||
FILE* file = ::fopen(filename.c_str(), "rb");
|
||||
# endif // _WIN32
|
||||
if (file == NULL)
|
||||
{
|
||||
std::string msg = "Error opening file \"";
|
||||
msg += filename;
|
||||
msg += "\".";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // !USE_OS_TZDB
|
||||
|
||||
namespace date
|
||||
@ -303,9 +440,9 @@ access_install()
|
||||
}
|
||||
|
||||
void
|
||||
set_install(const std::string& s)
|
||||
set_install(const std::string& install)
|
||||
{
|
||||
access_install() = s;
|
||||
access_install() = install;
|
||||
}
|
||||
|
||||
static
|
||||
@ -349,7 +486,18 @@ discover_tz_dir()
|
||||
{
|
||||
struct stat sb;
|
||||
using namespace std;
|
||||
# ifndef __APPLE__
|
||||
# if defined(ANDROID) || defined(__ANDROID__)
|
||||
CONSTDATA auto tz_dir_default = "/apex/com.android.tzdata/etc/tz";
|
||||
CONSTDATA auto tz_dir_fallback = "/system/usr/share/zoneinfo";
|
||||
|
||||
// Check updatable path first
|
||||
if(stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode))
|
||||
return tz_dir_default;
|
||||
else if(stat(tz_dir_fallback, &sb) == 0 && S_ISDIR(sb.st_mode))
|
||||
return tz_dir_fallback;
|
||||
else
|
||||
throw runtime_error("discover_tz_dir failed to find zoneinfo\n");
|
||||
# elif !defined(__APPLE__)
|
||||
CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo";
|
||||
CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc";
|
||||
|
||||
@ -372,9 +520,10 @@ discover_tz_dir()
|
||||
if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0))
|
||||
throw runtime_error("discover_tz_dir failed\n");
|
||||
string result;
|
||||
char rp[PATH_MAX+1] = {};
|
||||
if (readlink(timezone, rp, sizeof(rp)-1) > 0)
|
||||
result = string(rp);
|
||||
unique_ptr<char[]> rp(new char[sb.st_size]);
|
||||
const auto rp_length = readlink(timezone, rp.get(), sb.st_size);
|
||||
if (rp_length > 0)
|
||||
result = string(rp.get(), rp_length); // readlink doesn't null-terminate
|
||||
else
|
||||
throw system_error(errno, system_category(), "readlink() failed");
|
||||
auto i = result.find("zoneinfo");
|
||||
@ -406,7 +555,9 @@ get_tz_dir()
|
||||
static_assert(min_year <= max_year, "Configuration error");
|
||||
#endif
|
||||
|
||||
#if !defined(ANDROID) && !defined(__ANDROID__)
|
||||
static std::unique_ptr<tzdb> init_tzdb();
|
||||
#endif // !defined(ANDROID) && !defined(__ANDROID__)
|
||||
|
||||
tzdb_list::~tzdb_list()
|
||||
{
|
||||
@ -465,31 +616,67 @@ get_tzdb_list()
|
||||
return tz_db;
|
||||
}
|
||||
|
||||
#if !defined(ANDROID) && !defined(__ANDROID__)
|
||||
inline
|
||||
static
|
||||
std::string
|
||||
parse3(std::istream& in)
|
||||
char
|
||||
tolower(char c)
|
||||
{
|
||||
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;
|
||||
return static_cast<char>(std::tolower(c));
|
||||
}
|
||||
|
||||
inline
|
||||
static
|
||||
void
|
||||
tolower(std::string& s)
|
||||
{
|
||||
for (auto& c : s)
|
||||
c = tolower(c);
|
||||
}
|
||||
|
||||
inline
|
||||
static
|
||||
std::string
|
||||
get_alpha_word(std::istream& in)
|
||||
{
|
||||
ws(in);
|
||||
std::string s;
|
||||
while (!in.eof() && std::isalpha(in.peek()))
|
||||
s.push_back(static_cast<char>(in.get()));
|
||||
return s;
|
||||
}
|
||||
#endif // !defined(ANDROID) && !defined(__ANDROID__)
|
||||
|
||||
inline
|
||||
static
|
||||
bool
|
||||
is_prefix_of(std::string const& key, std::string const& value)
|
||||
{
|
||||
const size_t size = std::min(key.size(), value.size());
|
||||
return key.compare(0, size, value, 0, size) == 0;
|
||||
}
|
||||
|
||||
#if !defined(ANDROID) && !defined(__ANDROID__)
|
||||
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;
|
||||
static std::string const month_names[] =
|
||||
{"january", "february", "march", "april", "may", "june",
|
||||
"july", "august", "september", "october", "november", "december"};
|
||||
auto s = get_alpha_word(in);
|
||||
tolower(s);
|
||||
auto m = std::find_if(std::begin(month_names), std::end(month_names),
|
||||
[&s](std::string const& m)
|
||||
{
|
||||
return is_prefix_of(s, m);
|
||||
})
|
||||
- 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);
|
||||
}
|
||||
#endif // !defined(ANDROID) && !defined(__ANDROID__)
|
||||
|
||||
#if !USE_OS_TZDB
|
||||
|
||||
@ -559,15 +746,8 @@ load_timezone_mappings_from_xml_file(const std::string& input_path)
|
||||
std::vector<detail::timezone_mapping> mappings;
|
||||
std::string line;
|
||||
|
||||
std::ifstream is(input_path);
|
||||
if (!is.is_open())
|
||||
{
|
||||
// We don't emit file exceptions because that's an implementation detail.
|
||||
std::string msg = "Error opening time zone mapping file \"";
|
||||
msg += input_path;
|
||||
msg += "\".";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
file_streambuf ibuf(input_path);
|
||||
std::istream is(&ibuf);
|
||||
|
||||
auto error = [&input_path, &line_num](const char* info)
|
||||
{
|
||||
@ -697,7 +877,6 @@ load_timezone_mappings_from_xml_file(const std::string& input_path)
|
||||
}
|
||||
}
|
||||
|
||||
is.close();
|
||||
return mappings;
|
||||
}
|
||||
|
||||
@ -709,10 +888,16 @@ static
|
||||
unsigned
|
||||
parse_dow(std::istream& in)
|
||||
{
|
||||
CONSTDATA char*const dow_names[] =
|
||||
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
auto s = parse3(in);
|
||||
auto dow = std::find(std::begin(dow_names), std::end(dow_names), s) - dow_names;
|
||||
static std::string const dow_names[] =
|
||||
{"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||
auto s = get_alpha_word(in);
|
||||
tolower(s);
|
||||
auto dow = std::find_if(std::begin(dow_names), std::end(dow_names),
|
||||
[&s](std::string const& dow)
|
||||
{
|
||||
return is_prefix_of(s, dow);
|
||||
})
|
||||
- dow_names;
|
||||
if (dow >= std::end(dow_names) - std::begin(dow_names))
|
||||
throw std::runtime_error("oops: bad dow name: " + s);
|
||||
return static_cast<unsigned>(dow);
|
||||
@ -973,7 +1158,7 @@ detail::operator>>(std::istream& is, MonthDayTime& x)
|
||||
auto m = parse_month(is);
|
||||
if (!is.eof() && ws(is) && !is.eof() && is.peek() != '#')
|
||||
{
|
||||
if (is.peek() == 'l')
|
||||
if (tolower(is.peek()) == 'l')
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
is.get();
|
||||
@ -2039,6 +2224,9 @@ time_zone::load_data(std::istream& inf,
|
||||
void
|
||||
time_zone::init_impl()
|
||||
{
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
return;
|
||||
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
auto name = get_tz_dir() + ('/' + name_);
|
||||
@ -2164,7 +2352,7 @@ time_zone::get_info_impl(local_seconds tp) const
|
||||
{
|
||||
using namespace std::chrono;
|
||||
init();
|
||||
local_info i;
|
||||
local_info i{};
|
||||
i.result = local_info::unique;
|
||||
auto tr = upper_bound(transitions_.begin(), transitions_.end(), tp,
|
||||
[](const local_seconds& x, const transition& t)
|
||||
@ -2200,6 +2388,86 @@ time_zone::get_info_impl(local_seconds tp) const
|
||||
return i;
|
||||
}
|
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
void
|
||||
time_zone::parse_from_android_tzdata(std::ifstream& inf, const std::size_t off)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
if (!inf.is_open())
|
||||
throw std::runtime_error{"Unable to open tzdata"};
|
||||
std::size_t restorepos = inf.tellg();
|
||||
inf.seekg(off, inf.beg);
|
||||
load_header(inf);
|
||||
auto v = load_version(inf);
|
||||
std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
||||
tzh_timecnt, tzh_typecnt, tzh_charcnt;
|
||||
skip_reserve(inf);
|
||||
load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
||||
tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||
if (v == 0)
|
||||
{
|
||||
load_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(NDEBUG)
|
||||
inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
||||
tzh_ttisstdcnt + tzh_ttisgmtcnt);
|
||||
load_header(inf);
|
||||
auto v2 = load_version(inf);
|
||||
assert(v == v2);
|
||||
skip_reserve(inf);
|
||||
#else // defined(NDEBUG)
|
||||
inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt +
|
||||
tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15));
|
||||
#endif // defined(NDEBUG)
|
||||
load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt,
|
||||
tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||
load_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt);
|
||||
}
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
if (tzh_leapcnt > 0)
|
||||
{
|
||||
auto& leap_seconds = get_tzdb_list().front().leap_seconds;
|
||||
auto itr = leap_seconds.begin();
|
||||
auto l = itr->date();
|
||||
seconds leap_count{0};
|
||||
for (auto t = std::upper_bound(transitions_.begin(), transitions_.end(), l,
|
||||
[](const sys_seconds& x, const transition& ct)
|
||||
{
|
||||
return x < ct.timepoint;
|
||||
});
|
||||
t != transitions_.end(); ++t)
|
||||
{
|
||||
while (t->timepoint >= l)
|
||||
{
|
||||
++leap_count;
|
||||
if (++itr == leap_seconds.end())
|
||||
l = sys_days(max_year/max_day);
|
||||
else
|
||||
l = itr->date() + leap_count;
|
||||
}
|
||||
t->timepoint -= leap_count;
|
||||
}
|
||||
}
|
||||
#endif // !MISSING_LEAP_SECONDS
|
||||
auto b = transitions_.begin();
|
||||
auto i = transitions_.end();
|
||||
if (i != b)
|
||||
{
|
||||
for (--i; i != b; --i)
|
||||
{
|
||||
if (i->info->offset == i[-1].info->offset &&
|
||||
i->info->abbrev == i[-1].info->abbrev &&
|
||||
i->info->is_dst == i[-1].info->is_dst)
|
||||
i = transitions_.erase(i);
|
||||
}
|
||||
}
|
||||
inf.seekg(restorepos, inf.beg);
|
||||
}
|
||||
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const time_zone& z)
|
||||
{
|
||||
@ -2600,7 +2868,8 @@ operator<<(std::ostream& os, const time_zone& z)
|
||||
os.width(8);
|
||||
os << s.format_ << " ";
|
||||
os << s.until_year_ << ' ' << s.until_date_;
|
||||
os << " " << s.until_utc_ << " UTC";
|
||||
os << " ";
|
||||
date::operator<<(os, s.until_utc_) << " UTC";
|
||||
os << " " << s.until_std_ << " STD";
|
||||
os << " " << s.until_loc_;
|
||||
os << " " << make_time(s.initial_save_);
|
||||
@ -2625,20 +2894,19 @@ operator<<(std::ostream& os, const time_zone& z)
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const leap_second& x)
|
||||
{
|
||||
using namespace date;
|
||||
return os << x.date_ << " +";
|
||||
return date::operator<<(os, x.date_) << " +";
|
||||
}
|
||||
|
||||
#if USE_OS_TZDB
|
||||
|
||||
#if !defined(ANDROID) && !defined(__ANDROID__)
|
||||
static
|
||||
std::string
|
||||
get_version()
|
||||
{
|
||||
using namespace std;
|
||||
auto path = get_tz_dir() + string("/+VERSION");
|
||||
ifstream in{path};
|
||||
string version;
|
||||
auto path = get_tz_dir() + std::string("/+VERSION");
|
||||
std::ifstream in{path};
|
||||
std::string version;
|
||||
if (in)
|
||||
{
|
||||
in >> version;
|
||||
@ -2669,16 +2937,17 @@ find_read_and_leap_seconds()
|
||||
std::getline(in, line);
|
||||
if (!line.empty() && line[0] != '#')
|
||||
{
|
||||
std::istringstream in(line);
|
||||
in.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
std::istringstream iss(line);
|
||||
iss.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
std::string word;
|
||||
in >> word;
|
||||
if (word == "Leap")
|
||||
iss >> word;
|
||||
tolower(word);
|
||||
if (is_prefix_of(word, "leap"))
|
||||
{
|
||||
int y, m, d;
|
||||
in >> y;
|
||||
m = static_cast<int>(parse_month(in));
|
||||
in >> d;
|
||||
iss >> y;
|
||||
m = static_cast<int>(parse_month(iss));
|
||||
iss >> d;
|
||||
leap_seconds.push_back(leap_second(sys_days{year{y}/m/d} + days{1},
|
||||
detail::undocumented{}));
|
||||
}
|
||||
@ -2703,11 +2972,11 @@ find_read_and_leap_seconds()
|
||||
std::getline(in, line);
|
||||
if (!line.empty() && line[0] != '#')
|
||||
{
|
||||
std::istringstream in(line);
|
||||
in.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
std::istringstream iss(line);
|
||||
iss.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
using seconds = std::chrono::seconds;
|
||||
seconds::rep s;
|
||||
in >> s;
|
||||
iss >> s;
|
||||
if (s == 2272060800)
|
||||
continue;
|
||||
leap_seconds.push_back(leap_second(sys_seconds{seconds{s}} - offset,
|
||||
@ -2716,6 +2985,7 @@ find_read_and_leap_seconds()
|
||||
}
|
||||
return leap_seconds;
|
||||
}
|
||||
#if !MISSING_LEAP_SECONDS
|
||||
in.clear();
|
||||
in.open(get_tz_dir() + std::string(1, folder_delimiter) + "right/UTC",
|
||||
std::ios_base::binary);
|
||||
@ -2730,8 +3000,10 @@ find_read_and_leap_seconds()
|
||||
{
|
||||
return load_just_leaps(in);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
#endif // !defined(ANDROID) && !defined(__ANDROID__)
|
||||
|
||||
static
|
||||
std::unique_ptr<tzdb>
|
||||
@ -2739,6 +3011,38 @@ init_tzdb()
|
||||
{
|
||||
std::unique_ptr<tzdb> db(new tzdb);
|
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
auto path = get_tz_dir() + std::string("/tzdata");
|
||||
std::ifstream in{path};
|
||||
if (!in)
|
||||
throw std::runtime_error("Can not open " + path);
|
||||
bionic_tzdata_header_t hdr{};
|
||||
in.read(reinterpret_cast<char*>(&hdr), sizeof(bionic_tzdata_header_t));
|
||||
if (!is_prefix_of(hdr.tzdata_version, "tzdata") || hdr.tzdata_version[11] != 0)
|
||||
throw std::runtime_error("Malformed tzdata - invalid magic!");
|
||||
maybe_reverse_bytes(hdr.index_offset);
|
||||
maybe_reverse_bytes(hdr.data_offset);
|
||||
maybe_reverse_bytes(hdr.final_offset);
|
||||
if (hdr.index_offset > hdr.data_offset)
|
||||
throw std::runtime_error("Malformed tzdata - hdr.index_offset > hdr.data_offset!");
|
||||
const size_t index_size = hdr.data_offset - hdr.index_offset;
|
||||
if ((index_size % sizeof(index_entry_t)) != 0)
|
||||
throw std::runtime_error("Malformed tzdata - index size malformed!");
|
||||
//Iterate through zone index
|
||||
index_entry_t index_entry{};
|
||||
for (size_t idx = 0; idx < index_size; idx += sizeof(index_entry_t)) {
|
||||
in.read(reinterpret_cast<char*>(&index_entry), sizeof(index_entry_t));
|
||||
maybe_reverse_bytes(index_entry.start);
|
||||
maybe_reverse_bytes(index_entry.length);
|
||||
time_zone timezone{std::string(index_entry.buf),
|
||||
detail::undocumented{}};
|
||||
timezone.parse_from_android_tzdata(in, hdr.data_offset + index_entry.start);
|
||||
db->zones.emplace_back(std::move(timezone));
|
||||
}
|
||||
db->zones.shrink_to_fit();
|
||||
std::sort(db->zones.begin(), db->zones.end());
|
||||
db->version = std::string(hdr.tzdata_version).replace(0, 6, "");
|
||||
#else
|
||||
//Iterate through folders
|
||||
std::queue<std::string> subfolders;
|
||||
subfolders.emplace(get_tz_dir());
|
||||
@ -2763,6 +3067,7 @@ init_tzdb()
|
||||
strcmp(d->d_name, "version") == 0 ||
|
||||
strcmp(d->d_name, "zone.tab") == 0 ||
|
||||
strcmp(d->d_name, "zone1970.tab") == 0 ||
|
||||
strcmp(d->d_name, "zonenow.tab") == 0 ||
|
||||
strcmp(d->d_name, "tzdata.zi") == 0 ||
|
||||
strcmp(d->d_name, "leapseconds") == 0 ||
|
||||
strcmp(d->d_name, "leap-seconds.list") == 0 )
|
||||
@ -2790,6 +3095,7 @@ init_tzdb()
|
||||
std::sort(db->zones.begin(), db->zones.end());
|
||||
db->leap_seconds = find_read_and_leap_seconds();
|
||||
db->version = get_version();
|
||||
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
return db;
|
||||
}
|
||||
|
||||
@ -2836,7 +3142,8 @@ bool
|
||||
file_exists(const std::string& filename)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ::_access(filename.c_str(), 0) == 0;
|
||||
std::wstring wfilename = convert_utf8_to_utf16(filename);
|
||||
return ::_waccess(wfilename.c_str(), 0) == 0;
|
||||
#else
|
||||
return ::access(filename.c_str(), F_OK) == 0;
|
||||
#endif
|
||||
@ -2846,18 +3153,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
|
||||
@ -2872,8 +3185,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()};
|
||||
}
|
||||
|
||||
@ -3408,16 +3720,27 @@ std::string
|
||||
get_version(const std::string& path)
|
||||
{
|
||||
std::string version;
|
||||
std::ifstream infile(path + "version");
|
||||
if (infile.is_open())
|
||||
|
||||
std::string path_version = path + "version";
|
||||
|
||||
if (file_exists(path_version))
|
||||
{
|
||||
file_streambuf inbuf(path_version);
|
||||
std::istream infile(&inbuf);
|
||||
|
||||
infile >> version;
|
||||
|
||||
if (!infile.fail())
|
||||
return version;
|
||||
}
|
||||
else
|
||||
|
||||
std::string path_news = path + "NEWS";
|
||||
|
||||
if (file_exists(path_news))
|
||||
{
|
||||
infile.open(path + "NEWS");
|
||||
file_streambuf inbuf(path_news);
|
||||
std::istream infile(&inbuf);
|
||||
|
||||
while (infile)
|
||||
{
|
||||
infile >> version;
|
||||
@ -3428,6 +3751,7 @@ get_version(const std::string& path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unable to get Timezone database version from " + path);
|
||||
}
|
||||
|
||||
@ -3499,7 +3823,13 @@ init_tzdb()
|
||||
|
||||
for (const auto& filename : files)
|
||||
{
|
||||
std::ifstream infile(path + filename);
|
||||
std::string file_path = path + filename;
|
||||
if (!file_exists(file_path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
file_streambuf inbuf(file_path);
|
||||
std::istream infile(&inbuf);
|
||||
while (infile)
|
||||
{
|
||||
std::getline(infile, line);
|
||||
@ -3508,22 +3838,27 @@ init_tzdb()
|
||||
std::istringstream in(line);
|
||||
std::string word;
|
||||
in >> word;
|
||||
if (word == "Rule")
|
||||
if (word.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tolower(word);
|
||||
if (is_prefix_of(word, "rule"))
|
||||
{
|
||||
db->rules.push_back(Rule(line));
|
||||
continue_zone = false;
|
||||
}
|
||||
else if (word == "Link")
|
||||
else if (is_prefix_of(word, "link"))
|
||||
{
|
||||
db->links.push_back(time_zone_link(line));
|
||||
continue_zone = false;
|
||||
}
|
||||
else if (word == "Leap")
|
||||
else if (is_prefix_of(word, "leap"))
|
||||
{
|
||||
db->leap_seconds.push_back(leap_second(line, detail::undocumented{}));
|
||||
continue_zone = false;
|
||||
}
|
||||
else if (word == "Zone")
|
||||
else if (is_prefix_of(word, "zone"))
|
||||
{
|
||||
db->zones.push_back(time_zone(line, detail::undocumented{}));
|
||||
continue_zone = true;
|
||||
@ -3532,6 +3867,10 @@ init_tzdb()
|
||||
{
|
||||
db->zones.back().add(line);
|
||||
}
|
||||
else if (word.size() > 0 && word[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << line << '\n';
|
||||
@ -3577,6 +3916,67 @@ get_tzdb()
|
||||
return get_tzdb_list().front();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class recursion_limiter
|
||||
{
|
||||
unsigned depth_ = 0;
|
||||
unsigned limit_;
|
||||
|
||||
class restore_recursion_depth;
|
||||
|
||||
public:
|
||||
recursion_limiter(recursion_limiter const&) = delete;
|
||||
recursion_limiter& operator=(recursion_limiter const&) = delete;
|
||||
|
||||
explicit constexpr recursion_limiter(unsigned limit) noexcept;
|
||||
|
||||
restore_recursion_depth count();
|
||||
};
|
||||
|
||||
class recursion_limiter::restore_recursion_depth
|
||||
{
|
||||
recursion_limiter* rc_;
|
||||
|
||||
public:
|
||||
~restore_recursion_depth();
|
||||
restore_recursion_depth(restore_recursion_depth&&) = default;
|
||||
|
||||
explicit restore_recursion_depth(recursion_limiter* rc) noexcept;
|
||||
};
|
||||
|
||||
inline
|
||||
recursion_limiter::restore_recursion_depth::~restore_recursion_depth()
|
||||
{
|
||||
--(rc_->depth_);
|
||||
}
|
||||
|
||||
inline
|
||||
recursion_limiter::restore_recursion_depth::restore_recursion_depth(recursion_limiter* rc)
|
||||
noexcept
|
||||
: rc_{rc}
|
||||
{}
|
||||
|
||||
inline
|
||||
constexpr
|
||||
recursion_limiter::recursion_limiter(unsigned limit) noexcept
|
||||
: limit_{limit}
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
recursion_limiter::restore_recursion_depth
|
||||
recursion_limiter::count()
|
||||
{
|
||||
++depth_;
|
||||
if (depth_ > limit_)
|
||||
throw std::runtime_error("recursion limit of " +
|
||||
std::to_string(limit_) + " exceeded");
|
||||
return restore_recursion_depth{this};
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
const time_zone*
|
||||
#if HAS_STRING_VIEW
|
||||
tzdb::locate_zone(std::string_view tz_name) const
|
||||
@ -3584,6 +3984,10 @@ tzdb::locate_zone(std::string_view tz_name) const
|
||||
tzdb::locate_zone(const std::string& tz_name) const
|
||||
#endif
|
||||
{
|
||||
// If a link-to-link chain exceeds this limit, give up
|
||||
thread_local recursion_limiter rc{10};
|
||||
auto restore_count = rc.count();
|
||||
|
||||
auto zi = std::lower_bound(zones.begin(), zones.end(), tz_name,
|
||||
#if HAS_STRING_VIEW
|
||||
[](const time_zone& z, const std::string_view& nm)
|
||||
@ -3607,13 +4011,7 @@ tzdb::locate_zone(const std::string& tz_name) const
|
||||
});
|
||||
if (li != links.end() && li->name() == tz_name)
|
||||
{
|
||||
zi = std::lower_bound(zones.begin(), zones.end(), li->target(),
|
||||
[](const time_zone& z, const std::string& nm)
|
||||
{
|
||||
return z.name() < nm;
|
||||
});
|
||||
if (zi != zones.end() && zi->name() == li->target())
|
||||
return &*zi;
|
||||
return locate_zone(li->target());
|
||||
}
|
||||
#endif // !USE_OS_TZDB
|
||||
throw std::runtime_error(std::string(tz_name) + " not found in timezone database");
|
||||
@ -3789,10 +4187,12 @@ bool
|
||||
sniff_realpath(const char* timezone)
|
||||
{
|
||||
using namespace std;
|
||||
char rp[PATH_MAX+1] = {};
|
||||
if (realpath(timezone, rp) == nullptr)
|
||||
unique_ptr<char, decltype(free) *> rp(realpath(timezone, nullptr), free);
|
||||
if (rp.get() == nullptr)
|
||||
throw system_error(errno, system_category(), "realpath() failed");
|
||||
auto result = extract_tz_name(rp);
|
||||
auto result = extract_tz_name(rp.get());
|
||||
if (result.find("posix") == 0)
|
||||
return false;
|
||||
return result != "posixrules";
|
||||
}
|
||||
|
||||
@ -3819,18 +4219,24 @@ tzdb::current_zone() const
|
||||
{
|
||||
using namespace std;
|
||||
static const bool use_realpath = sniff_realpath(timezone);
|
||||
char rp[PATH_MAX+1] = {};
|
||||
if (use_realpath)
|
||||
{
|
||||
if (realpath(timezone, rp) == nullptr)
|
||||
unique_ptr<char, decltype(free) *> rp(realpath(timezone, nullptr), free);
|
||||
if (rp.get() == nullptr)
|
||||
throw system_error(errno, system_category(), "realpath() failed");
|
||||
return locate_zone(extract_tz_name(rp.get()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (readlink(timezone, rp, sizeof(rp)-1) <= 0)
|
||||
// +1 because st_size doesn't include the '\0' terminator
|
||||
const auto rp_size = sb.st_size + 1;
|
||||
unique_ptr<char[]> rp(new char[rp_size]);
|
||||
const auto rp_length = readlink(timezone, rp.get(), rp_size);
|
||||
if (rp_length <= 0)
|
||||
throw system_error(errno, system_category(), "readlink() failed");
|
||||
rp.get()[rp_length] = '\0'; // readlink doesn't null-terminate
|
||||
return locate_zone(extract_tz_name(rp.get()));
|
||||
}
|
||||
return locate_zone(extract_tz_name(rp));
|
||||
}
|
||||
}
|
||||
// On embedded systems e.g. buildroot with uclibc the timezone is linked
|
||||
@ -3849,9 +4255,10 @@ tzdb::current_zone() const
|
||||
if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
|
||||
using namespace std;
|
||||
string result;
|
||||
char rp[PATH_MAX+1] = {};
|
||||
if (readlink(timezone, rp, sizeof(rp)-1) > 0)
|
||||
result = string(rp);
|
||||
unique_ptr<char[]> rp(new char[sb.st_size]);
|
||||
const auto rp_length = readlink(timezone, rp.get(), sb.st_size);
|
||||
if (rp_length > 0)
|
||||
result = string(rp.get(), rp_length); // readlink doesn't null-terminate
|
||||
else
|
||||
throw system_error(errno, system_category(), "readlink() failed");
|
||||
|
||||
@ -3899,6 +4306,18 @@ tzdb::current_zone() const
|
||||
if (!result.empty())
|
||||
return locate_zone(result);
|
||||
#endif
|
||||
// Fall through to try other means.
|
||||
}
|
||||
{
|
||||
// On Android, it is not possible to use file based approach either,
|
||||
// we have to ask the value of `persist.sys.timezone` system property
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
char sys_timezone[PROP_VALUE_MAX];
|
||||
if (__system_property_get("persist.sys.timezone", sys_timezone) > 0)
|
||||
{
|
||||
return locate_zone(sys_timezone);
|
||||
}
|
||||
#endif // defined(ANDROID) || defined(__ANDROID__)
|
||||
// Fall through to try other means.
|
||||
}
|
||||
{
|
||||
@ -3914,13 +4333,32 @@ tzdb::current_zone() const
|
||||
auto p = result.find("ZONE=\"");
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
result.erase(p, p+6);
|
||||
result.erase(0, p+6);
|
||||
result.erase(result.rfind('"'));
|
||||
return locate_zone(result);
|
||||
}
|
||||
}
|
||||
// Fall through to try other means.
|
||||
}
|
||||
// On OpenWRT we need to check /etc/config/system
|
||||
// It will have a line with the following structure
|
||||
// ...
|
||||
// option zoneName 'Europe/Berlin'
|
||||
// ...
|
||||
{
|
||||
std::ifstream timezone_file("/etc/config/system");
|
||||
if (timezone_file.is_open())
|
||||
{
|
||||
for(std::string result; std::getline(timezone_file, result);) {
|
||||
std::string findStr = "option zoneName '";
|
||||
size_t startPos = result.find(findStr);
|
||||
if (startPos != std::string::npos) {
|
||||
size_t endPos = result.find("'", startPos + findStr.size());
|
||||
return locate_zone(result.substr(startPos + findStr.size(), endPos - startPos - findStr.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Could not get current timezone");
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ main()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::local_days, date::local_t, date::January, date::July, date::Sunday;
|
||||
|
||||
// self
|
||||
{
|
||||
|
@ -191,6 +191,7 @@ main()
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using sys_clock = std::chrono::system_clock;
|
||||
using local_t = date::local_t;
|
||||
|
||||
//steady_clock (must be different that sys_clock)
|
||||
static_assert(is_clock_castable<steady_clock, steady_clock>::value, "steady_clock -> steady_clock");
|
||||
|
@ -33,15 +33,15 @@
|
||||
// private:
|
||||
// std::chrono::seconds s_;
|
||||
// precision sub_s_;
|
||||
//
|
||||
//
|
||||
// public:
|
||||
// constexpr explicit decimal_format_seconds(const Duration& d) noexcept;
|
||||
//
|
||||
//
|
||||
// constexpr std::chrono::seconds& seconds() noexcept;
|
||||
// constexpr std::chrono::seconds seconds() const noexcept;
|
||||
// constexpr precision subseconds() const noexcept;
|
||||
// constexpr precision to_duration() const noexcept;
|
||||
//
|
||||
//
|
||||
// template <class CharT, class Traits>
|
||||
// friend
|
||||
// std::basic_ostream<CharT, Traits>&
|
||||
|
@ -36,7 +36,7 @@ void test_SI()
|
||||
// atto
|
||||
{
|
||||
duration<int, std::atto> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13as");
|
||||
os.str("");
|
||||
}
|
||||
@ -44,7 +44,7 @@ void test_SI()
|
||||
// femto
|
||||
{
|
||||
duration<int, std::femto> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13fs");
|
||||
os.str("");
|
||||
}
|
||||
@ -52,7 +52,7 @@ void test_SI()
|
||||
// pico
|
||||
{
|
||||
duration<int, std::pico> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13ps");
|
||||
os.str("");
|
||||
}
|
||||
@ -60,7 +60,7 @@ void test_SI()
|
||||
// nano
|
||||
{
|
||||
duration<int, std::nano> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13ns");
|
||||
os.str("");
|
||||
}
|
||||
@ -68,7 +68,7 @@ void test_SI()
|
||||
// mikro
|
||||
{
|
||||
duration<int, std::micro> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13\xC2\xB5s");
|
||||
os.str("");
|
||||
}
|
||||
@ -76,7 +76,7 @@ void test_SI()
|
||||
// milli
|
||||
{
|
||||
duration<int, std::milli> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13ms");
|
||||
os.str("");
|
||||
}
|
||||
@ -84,7 +84,7 @@ void test_SI()
|
||||
// centi
|
||||
{
|
||||
duration<int, std::centi> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13cs");
|
||||
os.str("");
|
||||
}
|
||||
@ -92,7 +92,7 @@ void test_SI()
|
||||
// deci
|
||||
{
|
||||
duration<int, std::deci> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13ds");
|
||||
os.str("");
|
||||
}
|
||||
@ -100,7 +100,7 @@ void test_SI()
|
||||
// seconds
|
||||
{
|
||||
duration<int> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13s");
|
||||
os.str("");
|
||||
}
|
||||
@ -108,7 +108,7 @@ void test_SI()
|
||||
// deca
|
||||
{
|
||||
duration<int, std::deca> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13das");
|
||||
os.str("");
|
||||
}
|
||||
@ -116,7 +116,7 @@ void test_SI()
|
||||
// hecto
|
||||
{
|
||||
duration<int, std::hecto> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13hs");
|
||||
os.str("");
|
||||
}
|
||||
@ -124,7 +124,7 @@ void test_SI()
|
||||
// kilo
|
||||
{
|
||||
duration<int, std::kilo> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13ks");
|
||||
os.str("");
|
||||
}
|
||||
@ -132,7 +132,7 @@ void test_SI()
|
||||
// mega
|
||||
{
|
||||
duration<int, std::mega> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13Ms");
|
||||
os.str("");
|
||||
}
|
||||
@ -140,7 +140,7 @@ void test_SI()
|
||||
// giga
|
||||
{
|
||||
duration<int, std::giga> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13Gs");
|
||||
os.str("");
|
||||
}
|
||||
@ -148,7 +148,7 @@ void test_SI()
|
||||
// tera
|
||||
{
|
||||
duration<int, std::tera> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13Ts");
|
||||
os.str("");
|
||||
}
|
||||
@ -156,7 +156,7 @@ void test_SI()
|
||||
// peta
|
||||
{
|
||||
duration<int, std::peta> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13Ps");
|
||||
os.str("");
|
||||
}
|
||||
@ -164,7 +164,7 @@ void test_SI()
|
||||
// femto
|
||||
{
|
||||
duration<int, std::exa> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13Es");
|
||||
os.str("");
|
||||
}
|
||||
@ -180,7 +180,7 @@ void test_calendar()
|
||||
// minutes
|
||||
{
|
||||
minutes d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13min");
|
||||
os.str("");
|
||||
}
|
||||
@ -188,7 +188,7 @@ void test_calendar()
|
||||
// hours
|
||||
{
|
||||
hours d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13h");
|
||||
os.str("");
|
||||
}
|
||||
@ -196,7 +196,7 @@ void test_calendar()
|
||||
// days
|
||||
{
|
||||
days d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13d");
|
||||
os.str("");
|
||||
}
|
||||
@ -212,7 +212,7 @@ void test_integral_scale()
|
||||
// ratio 123 / 1
|
||||
{
|
||||
duration<int, std::ratio<123, 1>> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[123]s");
|
||||
os.str("");
|
||||
}
|
||||
@ -220,7 +220,7 @@ void test_integral_scale()
|
||||
// ratio 100 / 4 = ratio 25 / 1
|
||||
{
|
||||
duration<int, std::ratio<25, 1>> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[25]s");
|
||||
os.str("");
|
||||
}
|
||||
@ -228,7 +228,7 @@ void test_integral_scale()
|
||||
// weeks = ratio 7 * 24 * 60 * 60 / 1 = ratio 604800 / 1
|
||||
{
|
||||
weeks d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[604800]s");
|
||||
os.str("");
|
||||
}
|
||||
@ -236,7 +236,7 @@ void test_integral_scale()
|
||||
// years = 146097/400 days = ratio 146097/400 * 24 * 60 * 60 = ratio 31556952 / 1
|
||||
{
|
||||
years d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[31556952]s");
|
||||
os.str("");
|
||||
}
|
||||
@ -244,7 +244,7 @@ void test_integral_scale()
|
||||
// months = 1/12 years = ratio 1/12 * 31556952 = ratio 2629746 / 1
|
||||
{
|
||||
months d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[2629746]s");
|
||||
os.str("");
|
||||
}
|
||||
@ -260,7 +260,7 @@ void test_ratio_scale()
|
||||
// ratio 1 / 2
|
||||
{
|
||||
duration<int, std::ratio<1, 2>> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[1/2]s");
|
||||
os.str("");
|
||||
}
|
||||
@ -268,7 +268,7 @@ void test_ratio_scale()
|
||||
// ratio 100 / 3
|
||||
{
|
||||
duration<int, std::ratio<100, 3>> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[100/3]s");
|
||||
os.str("");
|
||||
}
|
||||
@ -276,7 +276,7 @@ void test_ratio_scale()
|
||||
// ratio 100 / 6 = ratio 50 / 3
|
||||
{
|
||||
duration<int, std::ratio<100, 6>> d(13);
|
||||
os << d;
|
||||
date::operator<<(os, d);
|
||||
assert(os.str() == "13[50/3]s");
|
||||
os.str("");
|
||||
}
|
||||
|
@ -32,86 +32,86 @@ main()
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
std::ostringstream os;
|
||||
os << format("%C", sys_days{jun/1/20001});
|
||||
os << date::format("%C", sys_days{jun/1/20001});
|
||||
assert(os.str() == "200");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/20000});
|
||||
os << date::format("%C", sys_days{jun/1/20000});
|
||||
assert(os.str() == "200");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/19999});
|
||||
os << date::format("%C", sys_days{jun/1/19999});
|
||||
assert(os.str() == "199");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/2001});
|
||||
os << date::format("%C", sys_days{jun/1/2001});
|
||||
assert(os.str() == "20");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/2000});
|
||||
os << date::format("%C", sys_days{jun/1/2000});
|
||||
assert(os.str() == "20");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/1999});
|
||||
os << date::format("%C", sys_days{jun/1/1999});
|
||||
assert(os.str() == "19");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/101});
|
||||
os << date::format("%C", sys_days{jun/1/101});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/100});
|
||||
os << date::format("%C", sys_days{jun/1/100});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/99});
|
||||
os << date::format("%C", sys_days{jun/1/99});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/1});
|
||||
os << date::format("%C", sys_days{jun/1/1});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/0});
|
||||
os << date::format("%C", sys_days{jun/1/0});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-1});
|
||||
os << date::format("%C", sys_days{jun/1/-1});
|
||||
assert(os.str() == "-01");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-99});
|
||||
os << date::format("%C", sys_days{jun/1/-99});
|
||||
assert(os.str() == "-01");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-100});
|
||||
os << date::format("%C", sys_days{jun/1/-100});
|
||||
assert(os.str() == "-01");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-101});
|
||||
os << date::format("%C", sys_days{jun/1/-101});
|
||||
assert(os.str() == "-02");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-1999});
|
||||
os << date::format("%C", sys_days{jun/1/-1999});
|
||||
assert(os.str() == "-20");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-2000});
|
||||
os << date::format("%C", sys_days{jun/1/-2000});
|
||||
assert(os.str() == "-20");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-2001});
|
||||
os << date::format("%C", sys_days{jun/1/-2001});
|
||||
assert(os.str() == "-21");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-19999});
|
||||
os << date::format("%C", sys_days{jun/1/-19999});
|
||||
assert(os.str() == "-200");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-20000});
|
||||
os << date::format("%C", sys_days{jun/1/-20000});
|
||||
assert(os.str() == "-200");
|
||||
|
||||
os.str("");
|
||||
os << format("%C", sys_days{jun/1/-20001});
|
||||
os << date::format("%C", sys_days{jun/1/-20001});
|
||||
assert(os.str() == "-201");
|
||||
}
|
||||
|
@ -29,12 +29,11 @@ void
|
||||
test(const std::string& in_fmt, const std::string& input,
|
||||
const std::string& out_fmt, const std::string& output)
|
||||
{
|
||||
using namespace date;
|
||||
std::istringstream in{input};
|
||||
T t;
|
||||
in >> parse(in_fmt, t);
|
||||
assert(!in.fail());
|
||||
auto s = format(out_fmt, t);
|
||||
auto s = date::format(out_fmt, t);
|
||||
assert(s == output);
|
||||
}
|
||||
|
||||
|
@ -40,39 +40,41 @@ main()
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
std::ostringstream os;
|
||||
os << format("%F %T", sys_days{jan/1/year::min()});
|
||||
using date::year, date::last;
|
||||
|
||||
os << date::format("%F %T", sys_days{jan/1/year::min()});
|
||||
assert(os.str() == "-32767-01-01 00:00:00");
|
||||
os.str("");
|
||||
os << format("%F %T", sys_days{dec/last/year::max()});
|
||||
os << date::format("%F %T", sys_days{dec/last/year::max()});
|
||||
assert(os.str() == "32767-12-31 00:00:00");
|
||||
os.str("");
|
||||
os << format("%F %T", sys_days{dec/last/year::max()} + hours{23} + minutes{59} +
|
||||
os << date::format("%F %T", sys_days{dec/last/year::max()} + hours{23} + minutes{59} +
|
||||
seconds{59} + microseconds{999999});
|
||||
assert(os.str() == "32767-12-31 23:59:59.999999");
|
||||
os.str("");
|
||||
|
||||
os << format("%Y-%m-%d %H:%M:%S", sys_days{jan/1/year::min()});
|
||||
os << date::format("%Y-%m-%d %H:%M:%S", sys_days{jan/1/year::min()});
|
||||
assert(os.str() == "-32767-01-01 00:00:00");
|
||||
os.str("");
|
||||
os << format("%Y-%m-%d %H:%M:%S", sys_days{dec/last/year::max()});
|
||||
os << date::format("%Y-%m-%d %H:%M:%S", sys_days{dec/last/year::max()});
|
||||
assert(os.str() == "32767-12-31 00:00:00");
|
||||
os.str("");
|
||||
os << format("%Y-%m-%d %H:%M:%S", sys_days{dec/last/year::max()} + hours{23} +
|
||||
os << date::format("%Y-%m-%d %H:%M:%S", sys_days{dec/last/year::max()} + hours{23} +
|
||||
minutes{59} + seconds{59} + microseconds{999999});
|
||||
assert(os.str() == "32767-12-31 23:59:59.999999");
|
||||
os.str("");
|
||||
|
||||
os << format("%F %T", sys_days{jan/1/year::min()} + microfortnights{1});
|
||||
os << date::format("%F %T", sys_days{jan/1/year::min()} + microfortnights{1});
|
||||
assert(os.str() == "-32767-01-01 00:00:01.2096");
|
||||
os.str("");
|
||||
os << format("%F %T", sys_days{dec/last/year::max()} + microfortnights{1});
|
||||
os << date::format("%F %T", sys_days{dec/last/year::max()} + microfortnights{1});
|
||||
assert(os.str() == "32767-12-31 00:00:01.2096");
|
||||
os.str("");
|
||||
|
||||
os << format("%F", jan/1/year::min());
|
||||
os << date::format("%F", jan/1/year::min());
|
||||
assert(os.str() == "-32767-01-01");
|
||||
os.str("");
|
||||
os << format("%F", dec/last/year::max());
|
||||
os << date::format("%F", dec/last/year::max());
|
||||
assert(os.str() == "32767-12-31");
|
||||
os.str("");
|
||||
}
|
||||
|
@ -32,90 +32,90 @@ main()
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
std::ostringstream os;
|
||||
os << format("%y", sys_days{jun/1/20001});
|
||||
os << date::format("%y", sys_days{jun/1/20001});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/20000});
|
||||
os << date::format("%y", sys_days{jun/1/20000});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/19999});
|
||||
os << date::format("%y", sys_days{jun/1/19999});
|
||||
assert(os.str() == "99");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/2001});
|
||||
os << date::format("%y", sys_days{jun/1/2001});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/2000});
|
||||
os << date::format("%y", sys_days{jun/1/2000});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/1999});
|
||||
os << date::format("%y", sys_days{jun/1/1999});
|
||||
assert(os.str() == "99");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/101});
|
||||
os << date::format("%y", sys_days{jun/1/101});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/100});
|
||||
os << date::format("%y", sys_days{jun/1/100});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/99});
|
||||
os << date::format("%y", sys_days{jun/1/99});
|
||||
assert(os.str() == "99");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/1});
|
||||
os << date::format("%y", sys_days{jun/1/1});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/0});
|
||||
os << date::format("%y", sys_days{jun/1/0});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-1});
|
||||
os << date::format("%y", sys_days{jun/1/-1});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-99});
|
||||
os << date::format("%y", sys_days{jun/1/-99});
|
||||
assert(os.str() == "99");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-100});
|
||||
os << date::format("%y", sys_days{jun/1/-100});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-101});
|
||||
os << date::format("%y", sys_days{jun/1/-101});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-1999});
|
||||
os << date::format("%y", sys_days{jun/1/-1999});
|
||||
assert(os.str() == "99");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-2000});
|
||||
os << date::format("%y", sys_days{jun/1/-2000});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-2001});
|
||||
os << date::format("%y", sys_days{jun/1/-2001});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-19999});
|
||||
os << date::format("%y", sys_days{jun/1/-19999});
|
||||
assert(os.str() == "99");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-20000});
|
||||
os << date::format("%y", sys_days{jun/1/-20000});
|
||||
assert(os.str() == "00");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/-20001});
|
||||
os << date::format("%y", sys_days{jun/1/-20001});
|
||||
assert(os.str() == "01");
|
||||
|
||||
os.str("");
|
||||
os << format("%y", sys_days{jun/1/year::min()});
|
||||
os << date::format("%y", sys_days{jun/1/date::year::min()});
|
||||
assert(os.str() == "67");
|
||||
}
|
||||
|
@ -83,10 +83,11 @@ main()
|
||||
constexpr ConvertibleToMonths custom_month;
|
||||
constexpr ConvertibleToYears custom_year;
|
||||
constexpr ConvertibleToYearsAndMonths prefer_year;
|
||||
|
||||
|
||||
using date::last;
|
||||
|
||||
{
|
||||
constexpr year_month ym = 2001_y/feb;
|
||||
constexpr date::year_month ym = 2001_y/feb;
|
||||
CPP14_ASSERT(ym + one_month == 2001_y/mar);
|
||||
NOEXCEPT_ASSERT(ym + one_month);
|
||||
CPP14_ASSERT(one_month + ym == 2001_y/mar);
|
||||
@ -97,7 +98,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += one_month);
|
||||
CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= one_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + one_year == 2002_y/feb);
|
||||
NOEXCEPT_ASSERT(ym + one_year);
|
||||
CPP11_ASSERT(one_year + ym == 2002_y/feb);
|
||||
@ -141,7 +142,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += custom_month);
|
||||
CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= custom_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + custom_year == 2002_y/feb);
|
||||
NOEXCEPT_ASSERT(ym + custom_year);
|
||||
CPP11_ASSERT(custom_year + ym == 2002_y/feb);
|
||||
@ -166,7 +167,7 @@ main()
|
||||
}
|
||||
|
||||
{
|
||||
constexpr year_month_day ym = 2001_y/feb/10;
|
||||
constexpr date::year_month_day ym = 2001_y/feb/10;
|
||||
CPP14_ASSERT(ym + one_month == 2001_y/mar/10);
|
||||
NOEXCEPT_ASSERT(ym + one_month);
|
||||
CPP14_ASSERT(one_month + ym == 2001_y/mar/10);
|
||||
@ -177,7 +178,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += one_month);
|
||||
CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/10);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= one_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + one_year == 2002_y/feb/10);
|
||||
NOEXCEPT_ASSERT(ym + one_year);
|
||||
CPP11_ASSERT(one_year + ym == 2002_y/feb/10);
|
||||
@ -221,7 +222,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += custom_month);
|
||||
CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/10);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= custom_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + custom_year == 2002_y/feb/10);
|
||||
NOEXCEPT_ASSERT(ym + custom_year);
|
||||
CPP11_ASSERT(custom_year + ym == 2002_y/feb/10);
|
||||
@ -246,7 +247,7 @@ main()
|
||||
}
|
||||
|
||||
{
|
||||
constexpr year_month_day_last ym = 2001_y/feb/last;
|
||||
constexpr date::year_month_day_last ym = 2001_y/feb/last;
|
||||
CPP14_ASSERT(ym + one_month == 2001_y/mar/last);
|
||||
NOEXCEPT_ASSERT(ym + one_month);
|
||||
CPP14_ASSERT(one_month + ym == 2001_y/mar/last);
|
||||
@ -257,7 +258,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += one_month);
|
||||
CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/last);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= one_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + one_year == 2002_y/feb/last);
|
||||
NOEXCEPT_ASSERT(ym + one_year);
|
||||
CPP11_ASSERT(one_year + ym == 2002_y/feb/last);
|
||||
@ -301,7 +302,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += custom_month);
|
||||
CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/last);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= custom_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + custom_year == 2002_y/feb/last);
|
||||
NOEXCEPT_ASSERT(ym + custom_year);
|
||||
CPP11_ASSERT(custom_year + ym == 2002_y/feb/last);
|
||||
@ -326,7 +327,7 @@ main()
|
||||
}
|
||||
|
||||
{
|
||||
constexpr year_month_weekday ym = 2001_y/feb/fri[4];
|
||||
constexpr date::year_month_weekday ym = 2001_y/feb/fri[4];
|
||||
CPP14_ASSERT(ym + one_month == 2001_y/mar/fri[4]);
|
||||
NOEXCEPT_ASSERT(ym + one_month);
|
||||
CPP14_ASSERT(one_month + ym == 2001_y/mar/fri[4]);
|
||||
@ -337,7 +338,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += one_month);
|
||||
CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/fri[4]);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= one_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + one_year == 2002_y/feb/fri[4]);
|
||||
NOEXCEPT_ASSERT(ym + one_year);
|
||||
CPP11_ASSERT(one_year + ym == 2002_y/feb/fri[4]);
|
||||
@ -381,7 +382,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += custom_month);
|
||||
CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/fri[4]);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= custom_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + custom_year == 2002_y/feb/fri[4]);
|
||||
NOEXCEPT_ASSERT(ym + custom_year);
|
||||
CPP11_ASSERT(custom_year + ym == 2002_y/feb/fri[4]);
|
||||
@ -406,7 +407,7 @@ main()
|
||||
}
|
||||
|
||||
{
|
||||
constexpr year_month_weekday_last ym = 2001_y/feb/fri[last];
|
||||
constexpr date::year_month_weekday_last ym = 2001_y/feb/fri[last];
|
||||
CPP14_ASSERT(ym + one_month == 2001_y/mar/fri[last]);
|
||||
NOEXCEPT_ASSERT(ym + one_month);
|
||||
CPP14_ASSERT(one_month + ym == 2001_y/mar/fri[last]);
|
||||
@ -417,7 +418,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += one_month);
|
||||
CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/fri[last]);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= one_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + one_year == 2002_y/feb/fri[last]);
|
||||
NOEXCEPT_ASSERT(ym + one_year);
|
||||
CPP11_ASSERT(one_year + ym == 2002_y/feb/fri[last]);
|
||||
@ -461,7 +462,7 @@ main()
|
||||
NOEXCEPT_ASSERT(copy(ym) += custom_month);
|
||||
CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/fri[last]);
|
||||
NOEXCEPT_ASSERT(copy(ym) -= custom_month);
|
||||
|
||||
|
||||
CPP11_ASSERT(ym + custom_year == 2002_y/feb/fri[last]);
|
||||
NOEXCEPT_ASSERT(ym + custom_year);
|
||||
CPP11_ASSERT(custom_year + ym == 2002_y/feb/fri[last]);
|
||||
|
@ -204,6 +204,13 @@ test_c()
|
||||
assert(!in.bad());
|
||||
assert(tp == sys_days{2016_y/12/11} + hours{14} + minutes{2} + seconds{43});
|
||||
}
|
||||
{
|
||||
// can't parse negative years with "%c" directly
|
||||
std::istringstream in{"Sun Dec 11 14:02:43 -2016"};
|
||||
sys_seconds tp;
|
||||
in >> parse("%c", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -362,6 +369,30 @@ test_d()
|
||||
assert(!in.bad());
|
||||
assert(tp == 2016_y/12/9);
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016 +9 12"};
|
||||
sys_days tp;
|
||||
in >> parse("%Y %d %m", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016 +9 12"};
|
||||
sys_days tp;
|
||||
in >> parse("%Y %e %m", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016 -9 12"};
|
||||
sys_days tp;
|
||||
in >> parse("%Y %d %m", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016 -9 12"};
|
||||
sys_days tp;
|
||||
in >> parse("%Y %e %m", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016 31 11"};
|
||||
sys_days tp;
|
||||
@ -390,6 +421,7 @@ test_F()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::year_month_day;
|
||||
{
|
||||
std::istringstream in{"2016-12-13"};
|
||||
sys_days tp;
|
||||
@ -400,7 +432,7 @@ test_F()
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016-12-13"};
|
||||
year_month_day tp;
|
||||
year_month_day tp{};
|
||||
in >> parse("%F", tp);
|
||||
assert(!in.fail());
|
||||
assert(!in.bad());
|
||||
@ -413,6 +445,7 @@ test_H()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time;
|
||||
{
|
||||
std::istringstream in{"2016-12-11 15"};
|
||||
sys_time<hours> tp;
|
||||
@ -434,6 +467,7 @@ test_Ip()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time;
|
||||
{
|
||||
std::istringstream in{"2016-12-11 1 pm"};
|
||||
sys_time<hours> tp;
|
||||
@ -456,6 +490,12 @@ test_Ip()
|
||||
in >> parse("%F %I %p", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016-12-11 +1 pm"};
|
||||
sys_time<hours> tp;
|
||||
in >> parse("%F %I %p", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -500,6 +540,12 @@ test_m()
|
||||
in >> parse("%Y %d %m", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016-12-+3"};
|
||||
sys_days tp;
|
||||
in >> parse("%Y-%d-%m", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -507,6 +553,7 @@ test_M()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time;
|
||||
{
|
||||
std::istringstream in{"2016-12-11 15"};
|
||||
sys_time<minutes> tp;
|
||||
@ -528,6 +575,7 @@ test_S()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time;
|
||||
{
|
||||
std::istringstream in{"2016-12-11 15"};
|
||||
sys_seconds tp;
|
||||
@ -557,6 +605,7 @@ test_T()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time;
|
||||
{
|
||||
std::istringstream in{"2016-12-11 15:43:22"};
|
||||
sys_seconds tp;
|
||||
@ -615,6 +664,7 @@ test_p()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time;
|
||||
{
|
||||
std::istringstream in{"2016-12-11 11pm"};
|
||||
sys_time<hours> tp;
|
||||
@ -623,6 +673,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
|
||||
@ -729,6 +794,7 @@ test_z()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::local_seconds, date::local_days;
|
||||
{
|
||||
std::istringstream in{"2016-12-26 15:53:22 -0500"};
|
||||
sys_seconds tp;
|
||||
@ -753,6 +819,30 @@ test_z()
|
||||
assert(!in.bad());
|
||||
assert(tp == sys_days{2016_y/12/26} + hours{20} + minutes{53} + seconds{22});
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016-12-26 15:53:22 -+500"};
|
||||
sys_seconds tp;
|
||||
in >> parse("%F %T %z", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016-12-26 15:53:22 -+500"};
|
||||
sys_seconds tp;
|
||||
in >> parse("%F %T %Ez", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016-12-26 15:53:22 --500"};
|
||||
sys_seconds tp;
|
||||
in >> parse("%F %T %z", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
{
|
||||
std::istringstream in{"2016-12-26 15:53:22 --500"};
|
||||
sys_seconds tp;
|
||||
in >> parse("%F %T %Ez", tp);
|
||||
assert(in.fail());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -760,6 +850,7 @@ test_Z()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::local_seconds, date::local_days;
|
||||
{
|
||||
std::string a;
|
||||
std::istringstream in{"2016-12-26 15:53:22 word"};
|
||||
|
@ -79,16 +79,16 @@ main()
|
||||
static_assert(t1.to_duration() == hours{13}, "");
|
||||
#endif
|
||||
|
||||
auto t2 = t1;
|
||||
const auto t2 = t1;
|
||||
assert(t2.hours() == t1.hours());
|
||||
assert(t2.to_duration() == t1.to_duration());
|
||||
ostringstream os;
|
||||
os << t2;
|
||||
assert(os.str() == "13:00:00");
|
||||
auto h = make12(t2.hours());
|
||||
auto h = date::make12(t2.hours());
|
||||
os.str("");
|
||||
assert(h == hours{1});
|
||||
assert(t2.to_duration() == t1.to_duration());
|
||||
assert(!is_am(t2.hours()));
|
||||
assert(is_pm(t2.hours()));
|
||||
assert(!date::is_am(t2.hours()));
|
||||
assert(date::is_pm(t2.hours()));
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ main()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::year;
|
||||
|
||||
static_assert(year{2015} == 2015_y, "");
|
||||
static_assert(year{2015} != 2016_y, "");
|
||||
|
@ -75,6 +75,9 @@ test_arithmetic()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::year_month;
|
||||
using date::year;
|
||||
using date::month;
|
||||
|
||||
for (int y1 = 2010; y1 <= 2015; ++y1)
|
||||
{
|
||||
@ -114,6 +117,7 @@ void test_arithemtic_not_ok()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::year_month, date::month;
|
||||
|
||||
year_month ym{2018_y, month{14}};
|
||||
|
||||
@ -123,7 +127,7 @@ void test_arithemtic_not_ok()
|
||||
assert(ym - months{0} == ym2);
|
||||
assert(ym - ym2 == months{0});
|
||||
assert(ym2 - ym == months{0});
|
||||
|
||||
|
||||
auto ymc = ym;
|
||||
ymc += months{0};
|
||||
assert(ymc.ok());
|
||||
|
@ -56,7 +56,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::lastweek_weekday>{},
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::lastweek_weekday>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::lastweek_weekday>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::lastweek_weekday>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::lastweek_weekday,
|
||||
iso_week::weekday>{}, "");
|
||||
|
@ -82,7 +82,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::weekday>{}, "");
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::weekday>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::weekday>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::weekday>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::weekday, unsigned>{}, "");
|
||||
static_assert( std::is_nothrow_constructible<iso_week::weekday, iso_week::sys_days>{}, "");
|
||||
|
@ -70,7 +70,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::weeknum>{}, "");
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::weeknum>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::weeknum>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::weeknum>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::weeknum, unsigned>{}, "");
|
||||
static_assert(!std::is_convertible<unsigned, iso_week::weeknum>{}, "");
|
||||
|
@ -59,7 +59,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::weeknum_weekday>{}, "
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::weeknum_weekday>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::weeknum_weekday>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::weeknum_weekday>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::weeknum_weekday,
|
||||
iso_week::weeknum,
|
||||
|
@ -72,7 +72,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::year>{}, "");
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::year>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::year>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::year>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::year, int>{}, "");
|
||||
static_assert(!std::is_convertible<int, iso_week::year>{}, "");
|
||||
@ -84,6 +83,7 @@ int
|
||||
main()
|
||||
{
|
||||
using namespace iso_week;
|
||||
using iso_week::year;
|
||||
|
||||
static_assert(year{2015} == 2015_y, "");
|
||||
static_assert(int{year{2015}} == 2015, "");
|
||||
@ -93,6 +93,9 @@ main()
|
||||
static_assert(year{2015} <= 2015_y, "");
|
||||
static_assert(year{2016} >= 2015_y, "");
|
||||
|
||||
static_assert(year{2015}.is_leap(), "");
|
||||
static_assert(!year{2016}.is_leap(), "");
|
||||
|
||||
auto y = year{2014};
|
||||
assert(++y == year{2015});
|
||||
assert(y == year{2015});
|
||||
|
@ -64,7 +64,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::year_lastweek>{}, "")
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::year_lastweek>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::year_lastweek>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::year_lastweek>{}, "");
|
||||
|
||||
static_assert(std::is_nothrow_constructible<iso_week::year_lastweek,
|
||||
iso_week::year>{}, "");
|
||||
|
@ -65,7 +65,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::year_lastweek_weekday
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::year_lastweek_weekday>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::year_lastweek_weekday>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::year_lastweek_weekday>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::year_lastweek_weekday,
|
||||
iso_week::year, iso_week::weekday>{}, "");
|
||||
|
@ -65,7 +65,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::year_weeknum>{}, "");
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::year_weeknum>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::year_weeknum>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::year_weeknum>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::year_weeknum,
|
||||
iso_week::year,
|
||||
|
@ -69,7 +69,6 @@ static_assert( std::is_trivially_move_assignable<iso_week::year_weeknum_weekday>
|
||||
|
||||
static_assert(std::is_trivially_copyable<iso_week::year_weeknum_weekday>{}, "");
|
||||
static_assert(std::is_standard_layout<iso_week::year_weeknum_weekday>{}, "");
|
||||
static_assert(std::is_literal_type<iso_week::year_weeknum_weekday>{}, "");
|
||||
|
||||
static_assert( std::is_nothrow_constructible<iso_week::year_weeknum_weekday,
|
||||
iso_week::year, iso_week::weeknum,
|
||||
|
110
test/posix/ptz.pass.cpp
Normal file
110
test/posix/ptz.pass.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
// 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)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return x.begin == y.begin &&
|
||||
x.end == y.end &&
|
||||
x.offset == y.offset &&
|
||||
(x.save == minutes{0}) == (y.save == minutes{0}) &&
|
||||
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;
|
||||
using date::local_days, date::Sunday, date::last;
|
||||
|
||||
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)));
|
||||
}
|
@ -215,7 +215,7 @@ test_g() {
|
||||
static_assert(sizeof(ymdd)/sizeof(ymdd[0]) == sizeof(ymdh)/sizeof(ymdh[0]), "");
|
||||
static_assert(sizeof(ymdd)/sizeof(ymdd[0]) == sizeof(leaps)/sizeof(leaps[0]), "");
|
||||
|
||||
for (auto i = 0; i < sizeof(ymdd)/sizeof(ymdd[0]); ++i)
|
||||
for (auto i = 0u; i < sizeof(ymdd)/sizeof(ymdd[0]); ++i)
|
||||
{
|
||||
assert(solar_hijri::year_month_day{ymdd[i]} == ymdh[i]);
|
||||
assert(ymdd[i] == date::year_month_day{ymdh[i]});
|
||||
|
@ -46,8 +46,14 @@ fi
|
||||
|
||||
if [ -z "$CXX_LANG" ]
|
||||
then
|
||||
CXX_LANG=c++14
|
||||
CXX_LANG=c++17
|
||||
fi
|
||||
|
||||
if expr "$CXX" : ".*g++" >/dev/null
|
||||
then
|
||||
OPTIONS="$OPTIONS -pthread"
|
||||
fi
|
||||
|
||||
OPTIONS="-std=${CXX_LANG} $OPTIONS -I$ROOT -Wall $ROOT/src/tz.cpp -lcurl"
|
||||
|
||||
echo $ROOT
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using LT = local_time<std::common_type_t<Duration, minutes>>;
|
||||
using LT = date::local_time<std::common_type_t<Duration, minutes>>;
|
||||
return LT{(tp + offset_).time_since_epoch()};
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ public:
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using ST = sys_time<std::common_type_t<Duration, minutes>>;
|
||||
using ST = date::sys_time<std::common_type_t<Duration, minutes>>;
|
||||
return ST{(tp - offset_).time_since_epoch()};
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ test_info(const date::time_zone* zone, const date::sys_info& info)
|
||||
auto begin = info.begin;
|
||||
auto end = info.end - microseconds{1};
|
||||
auto mid = begin + (end - begin) /2;
|
||||
using sys_microseconds = sys_time<microseconds>;
|
||||
using sys_microseconds = date::sys_time<microseconds>;
|
||||
using zoned_microseconds = zoned_time<microseconds>;
|
||||
zoned_microseconds local{zone};
|
||||
|
||||
@ -106,7 +106,7 @@ tzmain()
|
||||
{
|
||||
std::cout << name << '\n';
|
||||
auto z = locate_zone(name);
|
||||
auto begin = sys_days(jan/1/year::min()) + seconds{0};
|
||||
auto begin = sys_days(jan/1/date::year::min()) + seconds{0};
|
||||
auto end = sys_days(jan/1/2035) + seconds{0};
|
||||
auto info = z->get_info(begin);
|
||||
std::cout << "Initially: ";
|
||||
@ -130,7 +130,7 @@ tzmain()
|
||||
info.save == prev_save)
|
||||
continue;
|
||||
auto dp = floor<days>(begin);
|
||||
auto ymd = year_month_day(dp);
|
||||
auto ymd = date::year_month_day(dp);
|
||||
auto time = make_time(begin - dp);
|
||||
std::cout << ymd << ' ' << time << "Z ";
|
||||
if (info.offset >= seconds{0})
|
||||
|
@ -25,12 +25,12 @@
|
||||
// {
|
||||
// public:
|
||||
// using duration = typename std::common_type<Duration, std::chrono::seconds>::type;
|
||||
//
|
||||
//
|
||||
// zoned_time();
|
||||
// zoned_time(const sys_time<Duration>& st);
|
||||
// explicit zoned_time(const time_zone* z);
|
||||
// explicit zoned_time(std::string_view name);
|
||||
//
|
||||
//
|
||||
// template <class Duration2,
|
||||
// class = typename std::enable_if
|
||||
// <
|
||||
@ -38,36 +38,36 @@
|
||||
// sys_time<Duration>>::value
|
||||
// >::type>
|
||||
// zoned_time(const zoned_time<Duration2>& zt) NOEXCEPT;
|
||||
//
|
||||
//
|
||||
// zoned_time(const time_zone* z, const local_time<Duration>& tp);
|
||||
// zoned_time(std::string_view name, const local_time<Duration>& tp);
|
||||
// zoned_time(const time_zone* z, const local_time<Duration>& tp, choose c);
|
||||
// zoned_time(std::string_view name, const local_time<Duration>& tp, choose c);
|
||||
//
|
||||
//
|
||||
// zoned_time(const time_zone* z, const zoned_time<Duration>& zt);
|
||||
// zoned_time(std::string_view name, const zoned_time<Duration>& zt);
|
||||
// zoned_time(const time_zone* z, const zoned_time<Duration>& zt, choose);
|
||||
// zoned_time(std::string_view name, const zoned_time<Duration>& zt, choose);
|
||||
//
|
||||
//
|
||||
// zoned_time(const time_zone* z, const sys_time<Duration>& st);
|
||||
// zoned_time(std::string_view name, const sys_time<Duration>& st);
|
||||
//
|
||||
//
|
||||
// zoned_time& operator=(const sys_time<Duration>& st);
|
||||
// zoned_time& operator=(const local_time<Duration>& ut);
|
||||
//
|
||||
//
|
||||
// explicit operator sys_time<duration>() const;
|
||||
// explicit operator local_time<duration>() const;
|
||||
//
|
||||
//
|
||||
// const time_zone* get_time_zone() const;
|
||||
// local_time<duration> get_local_time() const;
|
||||
// sys_time<duration> get_sys_time() const;
|
||||
// sys_info get_info() const;
|
||||
//
|
||||
//
|
||||
// template <class Duration1, class Duration2>
|
||||
// friend
|
||||
// bool
|
||||
// operator==(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);
|
||||
//
|
||||
//
|
||||
// template <class CharT, class Traits, class Duration1>
|
||||
// friend
|
||||
// std::basic_ostream<CharT, Traits>&
|
||||
@ -108,6 +108,7 @@ main()
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace date;
|
||||
using date::sys_time, date::local_days, date::local_seconds;
|
||||
static_assert( is_nothrow_destructible<zoned_seconds>{}, "");
|
||||
static_assert( is_default_constructible<zoned_seconds>{}, "");
|
||||
static_assert( is_nothrow_copy_constructible<zoned_seconds>{}, "");
|
||||
|
@ -34,6 +34,7 @@ void testDeductionFrom(Source&& s)
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time, date::local_days, date::local_time;
|
||||
|
||||
// No time point
|
||||
{
|
||||
@ -148,13 +149,14 @@ main()
|
||||
{
|
||||
using namespace date;
|
||||
using namespace std::chrono;
|
||||
using date::sys_time, date::local_days, date::local_time;
|
||||
|
||||
#if HAS_DEDUCTION_GUIDES
|
||||
// no arguments
|
||||
{
|
||||
zoned_time zt{};
|
||||
static_assert(std::is_same<decltype(zt), zoned_time<seconds>>::value, "");
|
||||
}
|
||||
}
|
||||
|
||||
// zoned_time
|
||||
{
|
||||
|
12
test_fail.sh
12
test_fail.sh
@ -1,10 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# show what is to be run
|
||||
echo $1
|
||||
eval $1
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
exit 0;
|
||||
fi
|
||||
exit 1;
|
||||
|
||||
# run the command
|
||||
eval $1 || exit 1 # if fails, return 1
|
||||
|
Reference in New Issue
Block a user