81 Commits

Author SHA1 Message Date
dd8affc6de Fix current_zone not working on buildroot+glibc target - refs #846
The layout for timezones, on these targets, is
`/usr/share/zoneinfo/posix/Europe/Paris` instead of
`/usr/share/zoneinfo/Europe/Paris`.

`/usr/share/zoneinfo/Europe/Paris` exists and is a symlink to
`/usr/share/zoneinfo/posix/Europe/Paris`.

`/etc/localtime` correctly links to `/usr/share/zoneinfo/Europe/Paris`,
so `readlink` must be used instead of `realpath`.

Signed-off-by: Julien Blanc <julien.blanc@tgcm.eu>
2024-10-02 10:11:57 -04:00
510a05429f Fix expected file format under Windows.
Make sure we handle "\r\n" correctly under Windows and Unix.
2024-09-30 13:59:57 -04:00
361352673d Include additional headers during cmake install 2024-09-30 11:54:39 -04:00
447f5a30b9 Fix unused functions on Android
... spotted with `-Wall`

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-09-30 11:00:51 -04:00
fca69e308d Ensure Android private functions dont show up
... for external consumers like tests

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-09-30 11:00:51 -04:00
7657ad7855 Fixed Android build - reasonable defaults for HAS_REMOTE_API and INSTALL to avoid including unavailable curl.h and wordexp.h 2024-09-29 20:30:45 -04:00
bbe2f51bc0 Fixed WINRT build - we need convert_utf8_to_utf16 in WINRT mode too 2024-09-29 20:30:45 -04:00
3e43210885 Implement USE_OS_TZDB for Android
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-09-29 16:21:36 -04:00
ac0c58d5da Stop using PATH_MAX
PATH_MAX isn't guaranteed to be defined in Posix environments; it is
only on systems that have a path length limit, and even in environments
where it is defined its usage can lead to issues.

To avoid using PATH_MAX, I've made two main changes:

- Where realpath() was used, I've changed the code to use its
  [POSIX.1-2008]'s new behaviour, where passing a null pointer as the
  resolved_name buffer results in realpath() to automatically allocate
  a buffer large enough to handle the given path, that is returned to
  the caller. This has been supported for a long time as a GNU libc
  extension before being standardized.
- Where readlink() was used, the size of the buffer was already
  determined when calling lstat(); the returned struct stat contains a
  st_size field, containing the number of bytes needed to store the
  symbolic link contents. This meant that to avoid using the tricky
  define I only needed to use a dynamically allocated buffer instead of
  a static one, of size stat.st_size (+1 when a null terminator is
  needed).

To make sure that memory is always freed, I've wrapped the new dynamic
allocations in an std::unique_ptr. The pointer returned by realpath()
must be freed with free(), so a unique_ptr with a custom deleter that
calls free() on destruction was used.

To read more about why PATH_MAX leads to buggy code I'd suggest reading
something like this: <https://eklitzke.org/path-max-is-tricky>.

[POSIX.1-2008]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
2024-09-28 16:56:16 -04:00
ab1dc3a5eb Fix description
Co-authored-by: Gerhard Olsson <6248932+gerhardol@users.noreply.github.com>
2024-09-28 16:41:52 -04:00
7c151cdb6a build: introduce ENABLE_DATE_INSTALL to allow user opt-out of install
When using `date` as a PRIVATE dependency, it is not required to install it. This flag give user using `date` with add_subdirectory the opportunity to disable install target Default behavior is conserved since ENABLE_DATE_INSTALL is ON
2024-09-28 16:41:52 -04:00
3286289bf6 build: use standard CMAKE_INSTALL_INCLUDEDIR for header install folder.
This gives user control over which folder header should be installed to instead of using hardcoded value `include/`.
Variable `CMAKE_INSTALL_INCLUDEDIR` is provided after a call to `include(GNUInstallDirs)`
More info can be found about usage in docs: https://cmake.org/cmake/help/latest/command/install.html
2024-09-28 16:41:22 -04:00
cf8f25b183 build(cmake): always mark HAS_STRING_VIEW as part…
…of the interface

If the library gets compiled with HAS_STRING_VIEW=1, consumers always
need to link to the functions using std::string_view, as they are the
only ones compiled into the shared library.

You can find a longer explanation here:
<https://github.com/HowardHinnant/date/pull/754#issuecomment-1361248007>
2024-09-28 16:40:52 -04:00
dc9d161607 detect current zone on OpenWRT systems 2024-09-28 16:31:51 -04:00
d2ddc5ea1e Update gitattributes of zip file to binary 2024-09-28 15:57:19 -04:00
6219dd7e4d Support tz databases from release 2023d and newer by adding new zonenow.tab file to ignore list
date's internal init_tzdb function iterates through the files provided
by the tz database, and init_tzdb contains a list of files that should
not be interpreted as being in TZif format.

The 2023d release of the tz database added a new file that is not in
TZif format, zonenow.tab. However, this file had not been added to the
ignore list.

This caused date to intepret the zonenow.tab file as a TZif file,
which caused assertions to fire in debug mode if the user attempted to
load its associated time_zone, causing load_header() to be invoked
on the file, which would find that it did not contain a 'TZif' magic
number.

This commit addresses the issue by adding the file to the ignore list.

The announcement on the tz database mailing list which describes the
addition of zonenow.tab can be found at this link:

https://mm.icann.org/pipermail/tz-announce/2023-December/000080.html
2024-09-28 15:56:36 -04:00
939031c38c Fix typo 2024-09-28 15:38:46 -04:00
51ce7e1310 A couple more fixes for parsing compliance
* Fixes: #839
2024-09-13 20:31:29 -04:00
155c6b9e76 Bring text parsing into compliance with the tzdata spec
* Fixes #836
2024-09-12 15:53:39 +01:00
1ead6715de Allow option override by FetchContent 2024-05-14 09:51:08 -04:00
f986299fbb support Android for current_zone() 2024-05-13 20:16:17 -04:00
706b1286e8 One more fix for issue #826 2024-05-10 16:44:22 -04:00
8a93211679 #826 Build fix for ambiguity with >= libc++ 17 2024-05-10 16:26:43 -04:00
6b1c1b8b3a Clean up restore_recursion_depth code. 2024-05-10 16:21:36 -04:00
8f8336f42b Add support for links to links 2024-04-19 14:58:58 -04:00
cd3c57932f Eliminate dead code.
* fixes #822
2024-04-15 10:03:38 -04:00
575fc23c3c Restrict mention of -Wstringop-overflow to gcc >= 11 2024-04-15 10:00:21 -04:00
0e65940a7f workaround for gcc bug 106757
see: https://github.com/HowardHinnant/date/issues/750
2024-03-16 17:40:55 -04:00
88a3b15126 Remove std::is_literal_type from tests.
* It was deprecated in C++17 and removed in C++20
2023-12-19 10:16:52 -05:00
5d225951ff Count sign as part of the character count : part 2
* Fix up some damage caused by previous commit.
2023-12-19 10:12:30 -05:00
ab37c362e3 Count sign as part of the character count. 2023-12-11 21:33:25 -05:00
0ef86f1ced Set not_a_century to something outside the domain of centuries for a year. 2023-12-11 21:32:25 -05:00
cc4685a21e Expose iso_week::year::is_leap() 2023-04-21 14:48:49 -04:00
50acf3ffd8 Fix compilation when MISSING_LEAP_SECONDS is set to true 2023-03-04 15:42:06 -05:00
15e0c84e42 Have Posix::time_zone::name() put <> around non-alpha abbreviations 2023-02-12 19:56:54 -05:00
28972d72b4 Guard against Posix::time_zone having transition near ...
beginning or end of year.
2023-02-12 19:56:54 -05:00
5f8c904231 Enable a permanent daylight saving rule for Posix::time_zone
* See comments in ptz.h for directions and examples.
2023-02-12 19:56:54 -05:00
c9169ea310 Fix comment typo in tz.h 2022-12-13 09:10:28 -05:00
22ceabf205 Fix tzdb parsing to handle new offset comments in 2022b 2022-08-12 10:30:35 -04:00
c82b776f28 Tweak sys_time streaming operator to work better year and
month precision time_points.
2022-08-12 10:29:10 -04:00
2c035f8def Fix abs(duration) to work with non-normalized ratios 2022-08-12 10:28:12 -04:00
e6f4aed4d1 Supply missing 'inline' 2022-04-24 22:27:54 -04:00
9ea5654c12 Sync get_info_impl() signatures 2022-02-16 20:23:27 -05:00
9d9161c978 Sync set_install() signatures 2022-02-16 20:23:27 -05:00
3776e0f185 Update Posix::time_zone comment with CTAD simplification 2022-01-26 19:44:06 -05:00
9e830536a0 Add range checking for numerical values in Posix::time_zone 2022-01-26 19:40:32 -05:00
655b249b8f test: use -pthread when $CXX is g++
Fixes https://github.com/HowardHinnant/date/issues/713
2021-11-02 18:55:01 -04:00
529a09267f Allow set_install() on Windows to use a file path containing Unicode
Anywhere we utilize that install file path, we carefully convert it to UTF-16 and utilize wide character helpers for file manipulation
2021-11-02 18:51:18 -04:00
d9049ee697 Fix current_zone for SLES
* Fixes #708
* Patch submitted by JaroslawMelzer
2021-10-15 09:03:33 -04:00
2709deddd3 fix -Wshadow warnings 2021-09-12 01:29:13 -06:00
2e19c006e2 cmake: update project version (#699) 2021-09-01 09:57:59 -04:00
Ans
28b5106d4c Fix uninitialized values (#698) 2021-09-01 09:44:26 -04:00
e1aa4837e0 Avoid signed integral overflow in to_stream
Fixes: #696
2021-08-31 17:43:44 -04:00
1ff7208036 Update README.md 2021-08-31 12:40:28 -04:00
b0adc54677 static_cast to silent warning implict conversion (#693)
That happen because `sizeof(long long unsigned int) == sizeof(long double)` in 32-bit architectures.
2021-08-17 11:35:13 -04:00
d57d764707 Round when parsing seconds instead of duration_cast. 2021-07-25 23:08:53 -04:00
383214dea6 Remove string_view overloads of parse
* They were rejected by the LWG
2021-07-25 23:01:30 -04:00
fb2554a7e0 Give gcc 4.8 a hand in constructing fields
Fixes #678
2021-06-02 18:23:54 -04:00
8c126525cc call from_stream explicitly from date namespace (#677)
Co-authored-by: Julian Zimmermann <Julian.Zimmermann@gti.de>
2021-05-31 12:54:11 -04:00
8f95c598c9 [Pedantic] Use named constants in place of literals 2021-05-27 21:20:36 -04:00
417402ad35 Remove trailing whitespace and add gitattributes (#672) 2021-05-24 08:22:43 -04:00
38267fa1ef Template basic_string_view on Traits in parse overloads 2021-05-20 08:46:42 -04:00
ebb5719cd7 Change default test mode to C++17 2021-05-19 14:43:19 -04:00
2f4411312b Add extra parse_manip constructor to avoid ambiguity 2021-05-19 14:36:09 -04:00
e23c15bb36 Add string_view overloads to parse functions 2021-05-19 14:13:33 -04:00
052eebaf00 When comparing sys_info in test...
only compare whether the saves are equal to 0
and not their actual values.

This allows one to compare against the binary database
which does not contain actual values of save.
2021-05-18 16:17:37 -04:00
b49a7575eb Zero initialize local_info in get_info
* Even when the result is unique, the second sys_info
  should be zero initialized.
2021-05-18 16:15:31 -04:00
6e921e1b1d Add tests for %I and %p format options 2021-04-16 21:26:33 -04:00
39b4edf279 Add flags in README to compile using cmake (#661) 2021-04-16 21:06:08 -04:00
2ef74cb41a If %I is used without %p, mark the ambiguity with failbit 2021-04-16 15:15:45 -04:00
ac6ca2a095 Add is_clock_v 2021-04-15 11:41:20 -04:00
ae017078c9 Fix Posix::time_zone for southern hemisphere
* The get_info functions were not prepared for the case where the start
  and end times of a rule are chronologically reversed for a year.  I.e.
  the daylight saving start comes later in the year than the daylight
  saving end.  When save is positive, this happens in the southern
  hemisphere (e.g. "Australia/Sydney" -> "AEST-10AEDT,M10.1.0,M4.1.0/3").
  It also happens in the nothern hemisphere when the save is negative
  (e.g. "Europe/Dublin" -> "IST-1GMT0,M10.5.0,M3.5.0/1").

* Added tests for Posix::time_zone.
2021-04-09 11:08:12 -04:00
156e0a786e Fix up namespace error in Posix::time_zone::to_sys 2021-04-07 20:33:01 -04:00
9a9a42db74 Enable streaming output operator for floating-point-based time_points 2021-04-06 17:22:22 -04:00
b5b765f928 Clean up calendrical streaming operators for invalid values 2021-03-31 16:15:53 -04:00
77bd6b92a4 Rattle github.io's cage 2021-03-22 14:59:59 -04:00
654b97091f Change default on C++17 to uncaught_exceptions
* By popular demand.
* If you need to undo this change, compile with
  -DHAS_UNCAUGHT_EXCEPTIONS=0.
2021-03-21 22:41:22 -04:00
b899774303 Run curl_global_cleanup only once per application 2021-03-11 19:58:34 -05:00
0e08b942c8 Correct the streaming output of year_month_day
* Invalid dates were printing out "invalid"
  too many times.
2021-03-11 19:50:14 -05:00
811be52e1c Add global cleanup to curl object destructor (#652)
Co-authored-by: Satish Bhat <sbhat@belvederetrading.com>
2021-03-01 17:00:11 -05:00
1c285d6545 Cast to unsigned char before calling toupper() (#648) 2021-02-24 12:49:46 -05:00
42 changed files with 1548 additions and 517 deletions

4
.gitattributes vendored Normal file
View 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
View File

@ -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

View File

@ -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.1 )
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,27 @@ 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(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 +111,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 +120,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 )
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 +138,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 )
@ -161,46 +180,48 @@ 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
#]===================================================================]

View File

@ -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,6 @@ 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/
If you would like your project (or product) on this list, just let me know.

View File

@ -136,7 +136,7 @@ namespace date
#endif
#ifndef HAS_UNCAUGHT_EXCEPTIONS
# if __cplusplus > 201703 || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define HAS_UNCAUGHT_EXCEPTIONS 1
# else
# define HAS_UNCAUGHT_EXCEPTIONS 0
@ -1004,6 +1004,8 @@ struct is_clock<T, std::void_t<decltype(T::now()), typename T::rep, typename T::
: std::true_type
{};
template<class T> inline constexpr bool is_clock_v = is_clock<T>::value;
#endif // HAS_VOID_T
//----------------+
@ -1020,6 +1022,7 @@ protected:
std::basic_ios<CharT, Traits>& is_;
CharT fill_;
std::ios::fmtflags flags_;
std::streamsize precision_;
std::streamsize width_;
std::basic_ostream<CharT, Traits>* tie_;
std::locale loc_;
@ -1029,6 +1032,7 @@ public:
{
is_.fill(fill_);
is_.flags(flags_);
is_.precision(precision_);
is_.width(width_);
is_.imbue(loc_);
is_.tie(tie_);
@ -1041,6 +1045,7 @@ public:
: is_(is)
, fill_(is.fill())
, flags_(is.flags())
, precision_(is.precision())
, width_(is.width(0))
, tie_(is.tie(nullptr))
, loc_(is.getloc())
@ -1311,7 +1316,7 @@ CONSTCD11
std::chrono::duration<Rep, Period>
abs(std::chrono::duration<Rep, Period> d)
{
return d >= d.zero() ? d : -d;
return d >= d.zero() ? d : static_cast<decltype(d)>(-d);
}
// round down
@ -1501,16 +1506,29 @@ operator-(const day& x, const days& y) NOEXCEPT
return x + -y;
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const day& d)
{
detail::save_ostream<CharT, Traits> _(os);
os.fill('0');
os.flags(std::ios::dec | std::ios::right);
os.width(2);
os << static_cast<unsigned>(d);
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
{
detail::low_level_fmt(os, d);
if (!d.ok())
os << " is not a valid day";
return os;
@ -1628,10 +1646,12 @@ operator-(const month& x, const months& y) NOEXCEPT
return x + -y;
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month& m)
{
if (m.ok())
{
@ -1639,7 +1659,20 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
os << format(os.getloc(), fmt, m);
}
else
os << static_cast<unsigned>(m) << " is not a valid month";
os << static_cast<unsigned>(m);
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
{
detail::low_level_fmt(os, m);
if (!m.ok())
os << " is not a valid month";
return os;
}
@ -1753,10 +1786,12 @@ operator-(const year& x, const years& y) NOEXCEPT
return year{static_cast<int>(x) - y.count()};
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year& y)
{
detail::save_ostream<CharT, Traits> _(os);
os.fill('0');
@ -1764,6 +1799,17 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
os.width(4 + (y < year{0}));
os.imbue(std::locale::classic());
os << static_cast<int>(y);
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
{
detail::low_level_fmt(os, y);
if (!y.ok())
os << " is not a valid year";
return os;
@ -1889,10 +1935,12 @@ operator-(const weekday& x, const days& y) NOEXCEPT
return x + -y;
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
{
if (wd.ok())
{
@ -1900,7 +1948,20 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
os << format(fmt, wd);
}
else
os << static_cast<unsigned>(wd.wd_) << " is not a valid weekday";
os << wd.c_encoding();
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
{
detail::low_level_fmt(os, wd);
if (!wd.ok())
os << " is not a valid weekday";
return os;
}
@ -2009,15 +2070,26 @@ weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCE
# pragma GCC diagnostic pop
#endif // __GNUC__
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
{
return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']';
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
{
os << wdi.weekday() << '[' << wdi.index();
if (!(1 <= wdi.index() && wdi.index() <= 5))
os << " is not a valid index";
os << ']';
detail::low_level_fmt(os, wdi);
if (!wdi.ok())
os << " is not a valid weekday_indexed";
return os;
}
@ -2067,12 +2139,27 @@ operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT
return !(x == y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
{
return low_level_fmt(os, wdl.weekday()) << "[last]";
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
{
return os << wdl.weekday() << "[last]";
detail::low_level_fmt(os, wdl);
if (!wdl.ok())
os << " is not a valid weekday_last";
return os;
}
CONSTCD11
@ -2247,12 +2334,28 @@ operator-(const year_month& ym, const years& dy) NOEXCEPT
return ym + -dy;
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
{
low_level_fmt(os, ym.year()) << '/';
return low_level_fmt(os, ym.month());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
{
return os << ym.year() << '/' << ym.month();
detail::low_level_fmt(os, ym);
if (!ym.ok())
os << " is not a valid year_month";
return os;
}
// month_day
@ -2332,12 +2435,28 @@ operator>=(const month_day& x, const month_day& y) NOEXCEPT
return !(x < y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day& md)
{
low_level_fmt(os, md.month()) << '/';
return low_level_fmt(os, md.day());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md)
{
return os << md.month() << '/' << md.day();
detail::low_level_fmt(os, md);
if (!md.ok())
os << " is not a valid month_day";
return os;
}
// month_day_last
@ -2394,12 +2513,27 @@ operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT
return !(x < y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
{
return low_level_fmt(os, mdl.month()) << "/last";
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
{
return os << mdl.month() << "/last";
detail::low_level_fmt(os, mdl);
if (!mdl.ok())
os << " is not a valid month_day_last";
return os;
}
// month_weekday
@ -2446,12 +2580,28 @@ operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT
return !(x == y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
{
low_level_fmt(os, mwd.month()) << '/';
return low_level_fmt(os, mwd.weekday_indexed());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
{
return os << mwd.month() << '/' << mwd.weekday_indexed();
detail::low_level_fmt(os, mwd);
if (!mwd.ok())
os << " is not a valid month_weekday";
return os;
}
// month_weekday_last
@ -2498,12 +2648,28 @@ operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT
return !(x == y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
{
low_level_fmt(os, mwdl.month()) << '/';
return low_level_fmt(os, mwdl.weekday_last());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
{
return os << mwdl.month() << '/' << mwdl.weekday_last();
detail::low_level_fmt(os, mwdl);
if (!mwdl.ok())
os << " is not a valid month_weekday_last";
return os;
}
// year_month_day_last
@ -2653,12 +2819,28 @@ operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
return !(x < y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
{
low_level_fmt(os, ymdl.year()) << '/';
return low_level_fmt(os, ymdl.month_day_last());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
{
return os << ymdl.year() << '/' << ymdl.month_day_last();
detail::low_level_fmt(os, ymdl);
if (!ymdl.ok())
os << " is not a valid year_month_day_last";
return os;
}
template<class>
@ -2889,12 +3071,13 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd)
os.fill('0');
os.flags(std::ios::dec | std::ios::right);
os.imbue(std::locale::classic());
os << ymd.year() << '-';
os << static_cast<int>(ymd.year()) << '-';
os.width(2);
os << static_cast<unsigned>(ymd.month()) << '-';
os << ymd.day();
os.width(2);
os << static_cast<unsigned>(ymd.day());
if (!ymd.ok())
os << " is not a valid date";
os << " is not a valid year_month_day";
return os;
}
@ -3130,8 +3313,12 @@ inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi)
{
return os << ymwdi.year() << '/' << ymwdi.month()
<< '/' << ymwdi.weekday_indexed();
detail::low_level_fmt(os, ymwdi.year()) << '/';
detail::low_level_fmt(os, ymwdi.month()) << '/';
detail::low_level_fmt(os, ymwdi.weekday_indexed());
if (!ymwdi.ok())
os << " is not a valid year_month_weekday";
return os;
}
template<class>
@ -3307,7 +3494,12 @@ inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl)
{
return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last();
detail::low_level_fmt(os, ymwdl.year()) << '/';
detail::low_level_fmt(os, ymwdl.month()) << '/';
detail::low_level_fmt(os, ymwdl.weekday_last());
if (!ymwdl.ok())
os << " is not a valid year_month_weekday_last";
return os;
}
template<class>
@ -3810,6 +4002,7 @@ public:
std::chrono::duration<rep> d = s_ + sub_s_;
if (d < std::chrono::seconds{10})
os << '0';
os.precision(width+6);
os << std::fixed << d.count();
return os;
}
@ -4000,9 +4193,7 @@ make24(std::chrono::hours h, bool is_pm) NOEXCEPT
template <class Duration>
using time_of_day = hh_mm_ss<Duration>;
template <class Rep, class Period,
class = typename std::enable_if
<!std::chrono::treat_as_floating_point<Rep>::value>::type>
template <class Rep, class Period>
CONSTCD11
inline
hh_mm_ss<std::chrono::duration<Rep, Period>>
@ -4015,9 +4206,8 @@ template <class CharT, class Traits, class Duration>
inline
typename std::enable_if
<
!std::chrono::treat_as_floating_point<typename Duration::rep>::value &&
std::ratio_less<typename Duration::period, days::period>::value
, std::basic_ostream<CharT, Traits>&
!std::is_convertible<Duration, days>::value,
std::basic_ostream<CharT, Traits>&
>::type
operator<<(std::basic_ostream<CharT, Traits>& os, const sys_time<Duration>& tp)
{
@ -4038,7 +4228,7 @@ inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const local_time<Duration>& ut)
{
return (os << sys_time<Duration>{ut.time_since_epoch()});
return (date::operator<<(os, sys_time<Duration>{ut.time_since_epoch()}));
}
namespace detail
@ -4597,7 +4787,11 @@ struct fields
hh_mm_ss<Duration> tod{};
bool has_tod = false;
#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ <= 409)
fields() : ymd{nanyear/0/0}, wd{8u}, tod{}, has_tod{false} {}
#else
fields() = default;
#endif
fields(year_month_day ymd_) : ymd(ymd_) {}
fields(weekday wd_) : wd(wd_) {}
@ -4779,7 +4973,7 @@ scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
is.setstate(std::ios::eofbit);
break;
}
auto c = static_cast<char>(toupper(ic));
auto c = static_cast<char>(toupper(static_cast<unsigned char>(ic)));
bool consume = false;
// For each keyword which might match, see if the indx character is c
// If a match if found, consume c
@ -4792,7 +4986,7 @@ scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
{
if (*st == might_match)
{
if (c == static_cast<char>(toupper((*ky)[indx])))
if (c == static_cast<char>(toupper(static_cast<unsigned char>((*ky)[indx]))))
{
consume = true;
if (ky->size() == indx+1)
@ -6019,8 +6213,13 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
const std::chrono::seconds* offset_sec = nullptr)
{
using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
auto ld = floor<days>(tp);
fields<CT> fds{year_month_day{ld}, hh_mm_ss<CT>{tp-local_seconds{ld}}};
auto ld = std::chrono::time_point_cast<days>(tp);
fields<CT> fds;
if (ld <= tp)
fds = fields<CT>{year_month_day{ld}, hh_mm_ss<CT>{tp-local_seconds{ld}}};
else
fds = fields<CT>{year_month_day{ld - days{1}},
hh_mm_ss<CT>{days{1} - (local_seconds{ld} - tp)}};
return to_stream(os, fmt, fds, abbrev, offset_sec);
}
@ -6033,8 +6232,13 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
using CT = typename std::common_type<Duration, seconds>::type;
const std::string abbrev("UTC");
CONSTDATA seconds offset{0};
auto sd = floor<days>(tp);
fields<CT> fds{year_month_day{sd}, hh_mm_ss<CT>{tp-sys_seconds{sd}}};
auto sd = std::chrono::time_point_cast<days>(tp);
fields<CT> fds;
if (sd <= tp)
fds = fields<CT>{year_month_day{sd}, hh_mm_ss<CT>{tp-sys_seconds{sd}}};
else
fds = fields<CT>{year_month_day{sd - days{1}},
hh_mm_ss<CT>{days{1} - (sys_seconds{sd} - tp)}};
return to_stream(os, fmt, fds, &abbrev, &offset);
}
@ -6147,7 +6351,10 @@ read_signed(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M =
if (('0' <= c && c <= '9') || c == '-' || c == '+')
{
if (c == '-' || c == '+')
{
(void)is.get();
--M;
}
auto x = static_cast<int>(read_unsigned(is, std::max(m, 1u), M));
if (!is.fail())
{
@ -6211,7 +6418,7 @@ read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned
is.setstate(std::ios::failbit);
return 0;
}
return i + f/std::pow(10.L, fcount);
return static_cast<long double>(i) + static_cast<long double>(f)/std::pow(10.L, fcount);
}
struct rs
@ -6320,7 +6527,14 @@ read(std::basic_istream<CharT, Traits>& is, int a0, Args&& ...args)
*e++ = static_cast<CharT>(CharT(u % 10) + CharT{'0'});
u /= 10;
} while (u > 0);
#if defined(__GNUC__) && __GNUC__ >= 11
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
std::reverse(buf, e);
#if defined(__GNUC__) && __GNUC__ >= 11
#pragma GCC diagnostic pop
#endif
for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p)
read(is, *p);
}
@ -6386,7 +6600,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
CONSTDATA int not_a_year = numeric_limits<short>::min();
CONSTDATA int not_a_2digit_year = 100;
CONSTDATA int not_a_century = not_a_year / 100;
CONSTDATA int not_a_century = numeric_limits<int>::min();
CONSTDATA int not_a_month = 0;
CONSTDATA int not_a_day = 0;
CONSTDATA int not_a_hour = numeric_limits<int>::min();
@ -6577,7 +6791,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
int tH;
int tM;
long double S;
long double S{};
read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
CharT{':'}, rld{S, 1, w});
checked_set(H, tH, not_a_hour, is);
@ -6657,7 +6871,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
int tH = not_a_hour;
int tM = not_a_minute;
long double S;
long double S{};
read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
CharT{':'}, rld{S, 1, w});
checked_set(H, tH, not_a_hour, is);
@ -7012,7 +7226,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
// "%I:%M:%S %p"
using dfs = detail::decimal_format_seconds<Duration>;
CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
long double S;
long double S{};
int tI = not_a_hour_12_value;
int tM = not_a_minute;
read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2},
@ -7068,7 +7282,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
{
using dfs = detail::decimal_format_seconds<Duration>;
CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
long double S;
long double S{};
read(is, rld{S, 1, width == -1 ? w : static_cast<unsigned>(width)});
checked_set(s, round_i<Duration>(duration<long double>{S}),
not_a_second, is);
@ -7102,7 +7316,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
int tH = not_a_hour;
int tM = not_a_minute;
long double S;
long double S{};
read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
CharT{':'}, rld{S, 1, w});
checked_set(H, tH, not_a_hour, is);
@ -7313,7 +7527,12 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
{
auto c = static_cast<char>(Traits::to_char_type(ic));
if (c == '-')
{
neg = true;
(void)is.get();
}
else if (c == '+')
(void)is.get();
}
if (modified == CharT{})
{
@ -7529,9 +7748,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) +
weeks{U-1} +
(weekday{static_cast<unsigned>(wd)} - Sunday);
if (Y == not_a_year)
Y = static_cast<int>(ymd_trial.year());
else if (year{Y} != ymd_trial.year())
if (year{Y} != ymd_trial.year())
goto broken;
if (m == not_a_month)
m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
@ -7548,9 +7765,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) +
weeks{W-1} +
(weekday{static_cast<unsigned>(wd)} - Monday);
if (Y == not_a_year)
Y = static_cast<int>(ymd_trial.year());
else if (year{Y} != ymd_trial.year())
if (year{Y} != ymd_trial.year())
goto broken;
if (m == not_a_month)
m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
@ -7565,11 +7780,11 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
if (j != not_a_doy && Y != not_a_year)
{
auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}};
if (m == 0)
if (m == not_a_month)
m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
goto broken;
if (d == 0)
if (d == not_a_day)
d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
goto broken;
@ -7655,6 +7870,8 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
goto broken;
}
}
else // I is ambiguous, AM or PM?
goto broken;
}
}
if (H != not_a_hour)
@ -7699,7 +7916,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year& y,
{
using CT = std::chrono::seconds;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.ymd.year().ok())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7715,7 +7932,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month& m,
{
using CT = std::chrono::seconds;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.ymd.month().ok())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7731,7 +7948,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, day& d,
{
using CT = std::chrono::seconds;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.ymd.day().ok())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7747,7 +7964,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, weekday& wd
{
using CT = std::chrono::seconds;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.wd.ok())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7763,7 +7980,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year_month&
{
using CT = std::chrono::seconds;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.ymd.month().ok())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7779,7 +7996,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month_day&
{
using CT = std::chrono::seconds;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.ymd.month().ok() || !fds.ymd.day().ok())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7795,7 +8012,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
{
using CT = std::chrono::seconds;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.ymd.ok())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7815,7 +8032,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
auto offptr = offset ? offset : &offset_local;
fields<CT> fds{};
fds.has_tod = true;
from_stream(is, fmt, fds, abbrev, offptr);
date::from_stream(is, fmt, fds, abbrev, offptr);
if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7833,7 +8050,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
using detail::round_i;
fields<CT> fds{};
fds.has_tod = true;
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
is.setstate(std::ios::failbit);
if (!is.fail())
@ -7850,12 +8067,13 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
{
using Duration = std::chrono::duration<Rep, Period>;
using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
using detail::round_i;
fields<CT> fds{};
from_stream(is, fmt, fds, abbrev, offset);
date::from_stream(is, fmt, fds, abbrev, offset);
if (!fds.has_tod)
is.setstate(std::ios::failbit);
if (!is.fail())
d = std::chrono::duration_cast<Duration>(fds.tod.to_duration());
d = round_i<Duration>(fds.tod.to_duration());
return is;
}
@ -7878,6 +8096,25 @@ public:
, offset_(offset)
{}
#if HAS_STRING_VIEW
parse_manip(const CharT* format, Parsable& tp,
std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
std::chrono::minutes* offset = nullptr)
: format_(format)
, tp_(tp)
, abbrev_(abbrev)
, offset_(offset)
{}
parse_manip(std::basic_string_view<CharT, Traits> format, Parsable& tp,
std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
std::chrono::minutes* offset = nullptr)
: format_(format)
, tp_(tp)
, abbrev_(abbrev)
, offset_(offset)
{}
#endif // HAS_STRING_VIEW
};
template <class Parsable, class CharT, class Traits, class Alloc>
@ -7885,14 +8122,14 @@ std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is,
const parse_manip<Parsable, CharT, Traits, Alloc>& x)
{
return from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_);
return date::from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_);
}
template <class Parsable, class CharT, class Traits, class Alloc>
inline
auto
parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp)
-> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
format.c_str(), tp),
parse_manip<Parsable, CharT, Traits, Alloc>{format, tp})
{
@ -7904,7 +8141,7 @@ inline
auto
parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
std::basic_string<CharT, Traits, Alloc>& abbrev)
-> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
format.c_str(), tp, &abbrev),
parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
{
@ -7916,7 +8153,7 @@ inline
auto
parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
std::chrono::minutes& offset)
-> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
format.c_str(), tp,
std::declval<std::basic_string<CharT, Traits, Alloc>*>(),
&offset),
@ -7930,7 +8167,7 @@ inline
auto
parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
-> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
format.c_str(), tp, &abbrev, &offset),
parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
{
@ -7943,7 +8180,7 @@ template <class Parsable, class CharT>
inline
auto
parse(const CharT* format, Parsable& tp)
-> decltype(from_stream(std::declval<std::basic_istream<CharT>&>(), format, tp),
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format, tp),
parse_manip<Parsable, CharT>{format, tp})
{
return {format, tp};
@ -7953,7 +8190,7 @@ template <class Parsable, class CharT, class Traits, class Alloc>
inline
auto
parse(const CharT* format, Parsable& tp, std::basic_string<CharT, Traits, Alloc>& abbrev)
-> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
tp, &abbrev),
parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
{
@ -7964,7 +8201,7 @@ template <class Parsable, class CharT>
inline
auto
parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset)
-> decltype(from_stream(std::declval<std::basic_istream<CharT>&>(), format,
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format,
tp, std::declval<std::basic_string<CharT>*>(), &offset),
parse_manip<Parsable, CharT>{format, tp, nullptr, &offset})
{
@ -7976,7 +8213,7 @@ inline
auto
parse(const CharT* format, Parsable& tp,
std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
-> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
-> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
tp, &abbrev, &offset),
parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
{

View File

@ -36,10 +36,10 @@
{
namespace iOSUtils
{
std::string get_tzdata_path();
std::string get_current_timezone();
} // namespace iOSUtils
} // namespace date

View File

@ -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();}
@ -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();}

View File

@ -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);
}

View File

@ -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,88 @@ 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 std::chrono::minutes;
sys_info r;
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last};
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 +375,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 +405,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 +439,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 +453,64 @@ 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();
assert(r.begin <= st && st < r.end);
return r;
}
@ -371,7 +523,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 +535,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 +562,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 +595,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 +652,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 +687,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 +753,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 +763,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 +788,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 +834,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 +869,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};
}
}

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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,23 @@ init_tzdb()
std::istringstream in(line);
std::string word;
in >> word;
if (word == "Rule")
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 +3863,10 @@ init_tzdb()
{
db->zones.back().add(line);
}
else if (word.size() > 0 && word[0] == '#')
{
continue;
}
else
{
std::cerr << line << '\n';
@ -3577,6 +3912,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 +3980,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 +4007,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 +4183,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 +4215,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 +4251,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 +4302,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 +4329,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");
}

View File

@ -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
{

View File

@ -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");

View File

@ -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>&

View File

@ -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("");
}

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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("");
}

View File

@ -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");
}

View File

@ -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]);

View File

@ -390,6 +390,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 +401,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 +414,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 +436,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;
@ -507,6 +510,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 +532,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 +562,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 +621,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 +630,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 +751,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;
@ -760,6 +783,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"};

View File

@ -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()));
}

View File

@ -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, "");

View File

@ -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());

View File

@ -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>{}, "");

View File

@ -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>{}, "");

View File

@ -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>{}, "");

View File

@ -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,

View File

@ -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});

View File

@ -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>{}, "");

View File

@ -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>{}, "");

View File

@ -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,

View File

@ -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
View 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)));
}

View File

@ -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

View File

@ -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()};
}

View File

@ -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})

View File

@ -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>{}, "");

View File

@ -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
{