137 Commits

Author SHA1 Message Date
229c6876c6 compatible for c++11 but GCC < (#884) 2025-06-27 13:06:13 -04:00
0a1b72bf9d Port ptz.h to C++20 2025-06-27 12:57:16 -04:00
6d7739e7e8 Don't fail on nearly blank lines
If a line only contains tabs the word will be an empty string. And then
is_prefix_of does match on rule, beacuse it's substring of length 0 is
also an empty string. Subsequently the parsing of the rule fails with an
exception.
2025-06-05 11:07:49 -04:00
f94b8f36c6 Update CMakeLists.txt to 3.0.4 2025-05-28 13:53:02 -04:00
a45ea7c17b CMake: Fix Apple Mobile platforms detection 2025-03-21 13:07:15 -04:00
0b336657b1 Don't negate toff if we've already failed
This avoids calling `-` on `not_a_offset`, which is set to `minutes::min()`, which is typically the min of `long`. Due to two's complement, calling `-` on this minimum value is undefined behavior.
2025-03-17 10:55:37 -04:00
32ecb2ad24 Use ru{} in favor of rs{} where it makes sense 2025-03-11 16:32:38 -04:00
d18e8b1653 Address remaining 'whitespace in literal' upsetting clang++-20 2025-03-05 22:40:15 -05:00
b8d166b4b0 remove deprecated whitespace 2025-03-02 14:57:53 -05:00
ca5727855b fixed curl linkage 2025-01-14 10:51:48 -05:00
d0424a2518 test/solar_hijri_test/parse.pass.cpp: specify unsigned type in loop
This fixes the warning
```
parse.pass.cpp:218:22: warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
  218 |   for (auto i = 0; i < sizeof(ymdd)/sizeof(ymdd[0]); ++i)
      |                    ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
2025-01-09 17:47:58 -05:00
8a1102fd08 simplify test_fail.sh
the default `exit` value is 0 so we do not need to explicitly call it
2025-01-09 10:41:17 -05:00
632af88e80 test_fail.sh: call /bin/sh
This is a simple shell script using no bash functionalty.

Closes: https://github.com/HowardHinnant/date/issues/854
2025-01-09 10:41:17 -05:00
28b7b23252 Protect assert against overflow
Fixes #860
2024-12-06 18:34:35 -05:00
f079e3568c MSVC has different two-phase lookup rules than gcc and clang.
Trying to make everyone happy.
2024-11-22 11:02:12 -05:00
1a4f424659 Update README.md 2024-11-06 11:45:11 -05:00
5bdb7e6f31 Update project version 2024-10-20 14:14:05 -04:00
e396108ee3 Update project version 2024-10-19 09:52:15 +00:00
deec054564 Add missing DATE_API 2024-10-17 14:29:47 -04:00
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
bf79dd5a81 Update README.md 2021-01-11 20:05:18 -05:00
26fc2bd372 Fix read_long_double with respect to decimal_point:
*  Respect locale settings when ONLY_C_LOCALE=0
*  Do not respect locale settings when ONLY_C_LOCALE=1
Fixes #637
2021-01-05 21:14:41 -05:00
97246a638a Silence lossy conversion warning
Fixes #620
2020-11-10 08:41:30 -05:00
432bab81f9 Skip "version" file and USE_OS_TZDB to return "unknown" if no version found (#616)
In commit commit 0b72599bd4 there
was a change to read "version" in addition to "+VERSION", so that
file shold also be ignored when scanning/reading zonefiles.

Also before the above mentioed commit get_version() would return
"unknown" if not on __APPLE__ and the version file was not found.
Put back that behaviour, for all USE_OS_TZDB users.

This last change changes the behaviour for __APPLE__ from throwing
an exception to returning "unknown".

Co-authored-by: Lars Gullik Bjønnes <lbjonnes@cisco.com>
2020-10-27 10:08:30 -04:00
215cacff56 Change all uses of round to detail::round_i
Fixes #613

detail::round_i is the same as round when the To::rep
is integral, and is just an implicit conversion when
To::rep is floating point.
2020-10-25 20:16:07 -04:00
3cbfa4318f Put back handling DISABLE_STRING_VIEW (#609)
* Put back handling DISABLE_STRING_VIEW
* Add HAS_DEDUCTION_GUIDES to interface library target
2020-10-08 19:22:15 -04:00
115dd428cf Proposed fix for issue #161
Header include should follow the same preprocessor rules as function definition.
2020-10-08 11:47:09 +03:00
7848566815 Fix a parse error for Visual Studio 2020-10-02 12:53:16 -04:00
393b52f21b Update README.md 2020-09-19 10:15:51 -04:00
0b72599bd4 For USE_OS_TZDB, look for version number in the files
version and +VERSION
2020-09-11 16:00:18 -04:00
ba99134b8a For USE_OS_TZDB, look for leap seconds in the files
leapseconds and leap-seconds.list
2020-09-11 15:53:07 -04:00
5e18488899 Fix subtle bug in fractional_width 2020-09-05 16:34:04 -04:00
313189b0a8 Correct lingering references to bare "tz" libname: (#600)
Fixes: #599
2020-08-25 16:57:00 -04:00
057b441ceb Use LLONG_MAX instead of std::numeric_limits<long long>::max() when constexpr is absent 2020-08-21 17:09:51 -04:00
d7a0bf1fa7 replace noexcept with NOEXCEPT macro 2020-08-21 17:09:51 -04:00
8140d979cd fix brace value initialization for msvc 18 2020-08-21 17:09:51 -04:00
7990eae740 fix user defined literals for compatibility with msvc 18 2020-08-21 17:09:47 -04:00
1ec2ea0295 Add test/tz_test/tzdb_list.pass.cpp 2020-08-17 21:34:20 -04:00
658a3b9495 Fix crasher for early local times and USE_OS_TZDB=1 2020-08-17 21:34:20 -04:00
e7969c32e8 Add Kotlin's datetime library to the list of projects using this (#593) 2020-08-14 09:53:14 -04:00
569b2d6785 Improve error message while parsing posix timezone string 2020-08-02 10:56:37 -04:00
abe3ada04f Update README.md 2020-07-28 17:00:00 -04:00
9537addfc4 fix ONLY_C_LOCALE export from cmake (#590)
* fix ONLY_C_LOCALE export from cmake

* add some comments

* remove all generator expressions for target output

* cmake: fewer variables, make it easier to read
2020-07-22 19:03:42 -04:00
6952fb50a6 Correct the value for not_a_year 2020-07-08 14:20:31 -04:00
fe2f9c7eac Change constexpr to CONSTDATA 2020-07-07 15:01:45 -04:00
a6243ce56f set cmake proj ver to 3.0: (#584)
FIXES: #583
2020-06-22 11:48:09 -04:00
a55f1a103b Update README.md 2020-06-15 15:18:20 -04:00
d544e5af25 Throw exception if zoned_time is constructed with nullptr 2020-06-14 19:30:23 -04:00
cac99da8dc For traits in constexpr context use ::value
* Fixes #542
2020-06-02 21:08:57 -04:00
a088baf9a5 Update README.md 2020-05-26 11:47:28 -04:00
e6adff6754 [cmake] Rename tz library to date-tz:
Fixes: #426
2020-05-24 21:31:18 -04:00
d784766640 iOS Simulator support 2020-05-24 21:31:18 -04:00
9343e31599 Give Posix::time_zone equality comparison 2020-05-24 21:31:18 -04:00
f43c39fcf1 Add member function name() const to Posix::time_zone
* Returns the minimal string that uniquely identifies this
  time_zone.  That is, it takes advantage of all defaults.
2020-05-06 22:12:15 -04:00
e12095f26f Revert change for floating point reps in decimal_format_seconds 2020-04-18 09:24:53 -04:00
7d811743e0 Fix parse of second offset in posix time zone 2020-04-18 09:24:22 -04:00
a2fdba1adc __GNUC_MINOR -> __GNUC_MINOR__
* Fixes #560
2020-03-30 12:50:37 -04:00
48 changed files with 2447 additions and 797 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 2.4.2 # adjust tag/branch/commit as needed
GIT_TAG v3.0.1 # adjust tag/branch/commit as needed
)
FetchContent_MakeAvailable(date_src)
...
@ -17,12 +17,16 @@
cmake_minimum_required( VERSION 3.7 )
project( date VERSION 2.4.1 )
include( GNUInstallDirs )
project( date VERSION 3.0.4 )
set(ABI_VERSION 3) # used as SOVERSION, increment when ABI changes
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." )
@ -34,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 "" )
@ -62,116 +71,166 @@ add_library( date INTERFACE )
add_library( date::date ALIAS date )
target_include_directories( date INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include> )
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> )
# adding header sources just helps IDEs
target_sources( date INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:include>/date/date.h
# the rest of these are not currently part of the public interface of the library:
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/solar_hijri.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/islamic.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/iso_week.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include/date/julian.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/date.h
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/solar_hijri.h
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/islamic.h
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/iso_week.h
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/julian.h
)
set( APPLE_MOBILE_PLATFORM FALSE )
if ( IOS
OR CMAKE_SYSTEM_NAME STREQUAL "iOS"
OR CMAKE_SYSTEM_NAME STREQUAL "visionOS"
OR CMAKE_SYSTEM_NAME STREQUAL "watchOS"
OR CMAKE_SYSTEM_NAME STREQUAL "tvOS"
)
set( APPLE_MOBILE_PLATFORM TRUE )
endif()
set(TARGET_HEADERS
include/date/date.h
include/date/solar_hijri.h
include/date/islamic.h
include/date/iso_week.h
include/date/julian.h
)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
# public headers will get installed:
set_target_properties( date PROPERTIES PUBLIC_HEADER include/date/date.h )
set_target_properties( date PROPERTIES PUBLIC_HEADER "${TARGET_HEADERS}" )
endif ()
target_compile_definitions( date INTERFACE
#To workaround libstdc++ issue https://github.com/HowardHinnant/date/issues/388
ONLY_C_LOCALE=$<IF:$<BOOL:${COMPILE_WITH_C_LOCALE}>,1,0>
$<$<BOOL:${DISABLE_STRING_VIEW}>:HAS_STRING_VIEW=0> )
# These used to be set with generator expressions,
#
# ONLY_C_LOCALE=$<IF:$<BOOL:${COMPILE_WITH_C_LOCALE}>,1,0>
#
# which expand in the output target file to, e.g.
#
# ONLY_C_LOCALE=$<IF:$<BOOL:FALSE>,1,0>
#
# This string is then (somtimes?) not correctly interpreted.
if ( COMPILE_WITH_C_LOCALE )
# To workaround libstdc++ issue https://github.com/HowardHinnant/date/issues/388
target_compile_definitions( date INTERFACE ONLY_C_LOCALE=1 )
else()
target_compile_definitions( date INTERFACE ONLY_C_LOCALE=0 )
endif()
if ( DISABLE_STRING_VIEW )
target_compile_definitions( date INTERFACE HAS_STRING_VIEW=0 -DHAS_DEDUCTION_GUIDES=0 )
else()
target_compile_definitions( date INTERFACE HAS_STRING_VIEW=1 )
endif()
#[===================================================================[
tz (compiled) library
#]===================================================================]
if( BUILD_TZ_LIB )
add_library( tz )
target_sources( tz
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
$<$<BOOL:${IOS}>:$<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/tz.h
PRIVATE
include/date/tz_private.h
$<$<BOOL:${IOS}>:src/ios.mm>
src/tz.cpp )
add_library( date::tz ALIAS tz )
target_link_libraries( tz PUBLIC date )
target_include_directories( tz PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include> )
target_compile_definitions( tz
PRIVATE
AUTO_DOWNLOAD=$<IF:$<OR:$<BOOL:${USE_SYSTEM_TZ_DB}>,$<BOOL:${MANUAL_TZ_DB}>>,0,1>
HAS_REMOTE_API=$<IF:$<OR:$<BOOL:${USE_SYSTEM_TZ_DB}>,$<BOOL:${MANUAL_TZ_DB}>>,0,1>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${BUILD_SHARED_LIBS}>>:DATE_BUILD_DLL=1>
$<$<BOOL:${USE_TZ_DB_IN_DOT}>:INSTALL=.>
if ( APPLE_MOBILE_PLATFORM )
target_sources( date-tz
PUBLIC
USE_OS_TZDB=$<IF:$<AND:$<BOOL:${USE_SYSTEM_TZ_DB}>,$<NOT:$<BOOL:${WIN32}>>,$<NOT:$<BOOL:${MANUAL_TZ_DB}>>>,1,0>
INTERFACE
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${BUILD_SHARED_LIBS}>>:DATE_USE_DLL=1> )
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>/date/ios.h
PRIVATE
src/ios.mm )
endif()
add_library( date::date-tz ALIAS date-tz )
target_link_libraries( date-tz PUBLIC date )
target_include_directories( date-tz PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${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 )
else()
target_compile_definitions( date-tz PRIVATE AUTO_DOWNLOAD=1 HAS_REMOTE_API=1 )
endif()
if ( USE_SYSTEM_TZ_DB AND NOT WIN32 AND NOT MANUAL_TZ_DB )
target_compile_definitions( date-tz PRIVATE INSTALL=. PUBLIC USE_OS_TZDB=1 )
else()
target_compile_definitions( date-tz PUBLIC USE_OS_TZDB=0 )
endif()
if ( WIN32 AND BUILD_SHARED_LIBS )
target_compile_definitions( date-tz PUBLIC DATE_BUILD_DLL=1 )
endif()
set(TZ_HEADERS include/date/tz.h)
if( IOS )
if( APPLE_MOBILE_PLATFORM )
list(APPEND TZ_HEADERS include/date/ios.h)
endif( )
set_target_properties( tz PROPERTIES
set_target_properties( date-tz PROPERTIES
POSITION_INDEPENDENT_CODE ON
PUBLIC_HEADER "${TZ_HEADERS}"
VERSION "${PROJECT_VERSION}"
SOVERSION "${PROJECT_VERSION}" )
SOVERSION "${ABI_VERSION}" )
if( NOT MSVC )
find_package( Threads )
target_link_libraries( tz PUBLIC Threads::Threads )
target_link_libraries( date-tz PUBLIC Threads::Threads )
endif( )
if( NOT USE_SYSTEM_TZ_DB AND NOT MANUAL_TZ_DB )
find_package( CURL REQUIRED )
target_include_directories( tz SYSTEM PRIVATE ${CURL_INCLUDE_DIRS} )
target_link_libraries( tz PRIVATE ${CURL_LIBRARIES} )
target_link_libraries( date-tz PRIVATE CURL::libcurl )
endif( )
endif( )
#[===================================================================[
installation
#]===================================================================]
set( version_config "${CMAKE_CURRENT_BINARY_DIR}/dateConfigVersion.cmake" )
if( ENABLE_DATE_INSTALL )
set( version_config "${CMAKE_CURRENT_BINARY_DIR}/dateConfigVersion.cmake" )
include( CMakePackageConfigHelpers )
write_basic_package_version_file( "${version_config}"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion )
include( CMakePackageConfigHelpers )
write_basic_package_version_file( "${version_config}"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion )
install( TARGETS date
EXPORT dateConfig
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake )
if (CMAKE_VERSION VERSION_LESS 3.15)
install(
FILES include/date/date.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
endif ()
install( TARGETS date
EXPORT dateConfig
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake )
if (CMAKE_VERSION VERSION_LESS 3.15)
install(
FILES ${TARGET_HEADERS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date )
endif ()
if( BUILD_TZ_LIB )
install( TARGETS tz
EXPORT dateConfig
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # This is for Windows
export( TARGETS tz NAMESPACE date:: APPEND FILE dateTargets.cmake )
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
#]===================================================================]
@ -179,7 +238,7 @@ if( ENABLE_DATE_TESTING )
enable_testing( )
add_custom_target( testit COMMAND ${CMAKE_CTEST_COMMAND} )
add_dependencies( testit tz )
add_dependencies( testit date-tz )
function( add_pass_tests TEST_GLOB TEST_PREFIX )
file( GLOB_RECURSE FILENAMES ${TEST_GLOB} )
@ -193,7 +252,7 @@ if( ENABLE_DATE_TESTING )
set( TST_NAME ${PREFIX}_test )
add_executable( ${BIN_NAME} EXCLUDE_FROM_ALL ${TEST_FILE} )
add_test( ${TST_NAME} ${BIN_NAME} )
target_link_libraries( ${BIN_NAME} tz )
target_link_libraries( ${BIN_NAME} date-tz )
# HACK: because the test files don't use FQ includes:
target_include_directories( ${BIN_NAME} PRIVATE include/date )
add_dependencies( testit ${BIN_NAME} )
@ -222,7 +281,7 @@ if( ENABLE_DATE_TESTING )
${CMAKE_CXX_COMPILER}
-std=c++14
-L${CMAKE_BINARY_DIR}/
-ltz
-ldate-tz
-I${PROJECT_SOURCE_DIR}/include
-I${PROJECT_SOURCE_DIR}/include/date
-o ${BIN_NAME}

View File

@ -5,7 +5,7 @@
---
**[Try it out on wandbox!](https://wandbox.org/permlink/L8MwjzSSC3fXXrMd)**
**[Try it out on wandbox!](https://wandbox.org/permlink/oyXjibyF680HHoyS)**
## Summary
@ -43,6 +43,14 @@ Slightly modified versions of `"date.h"` and `"tz.h"` were voted into the C++20
## Build & Test
The recommended way to use any of these libraries besides `"tz.h"` is to just include it. These are header-only libraries (except `"tz.h"`).
To use `"tz.h"`, there is a single source file (`src/tz.cpp`) that needs to be compiled. Here are the recommended directions: https://howardhinnant.github.io/date/tz.html#Installation.
One can run tests by cd'ing into the `test` subdirectory and running `testit`. There are known failures on all platforms except for macOS. And even on macOS if C++11 is used. If any of these failures present problems for you, there exist workarounds.
Additionally there is _unsupported_ support for [vcpkg](https://github.com/Microsoft/vcpkg) and [CMake](https://cmake.org/). I don't personally use or maintain these systems as for me they cause more problems than they solve (for this small project). If you would like to contribute to these build systems please feel free to file a PR.
You can download and install Date using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
@ -58,7 +66,7 @@ You can optionally build using [CMake](https://cmake.org/). Here is a guide of h
```bash
mkdir build
cd build
cmake ../
cmake -DENABLE_DATE_TESTING=ON -DBUILD_TZ_LIB=ON ../
cmake --build . --target testit # Consider '-- -j4' for multithreading
```
## Projects using this library
@ -68,5 +76,11 @@ cmake --build . --target testit # Consider '-- -j4' for multithreading
* https://github.com/ViewTouch/viewtouch
* https://routinghub.com
* https://github.com/valhalla
* https://github.com/siodb/siodb
* https://github.com/KomodoPlatform/atomicDEX-Pro
* https://github.com/Kotlin/kotlinx-datetime
* https://github.com/royalbee/jewish_date
* https://github.com/apache/arrow/
* https://lottopark.com
If you would like your project (or product) on this list, just let me know.

View File

@ -1,8 +1,8 @@
include( CMakeFindDependencyMacro )
include( "${CMAKE_CURRENT_LIST_DIR}/dateTargets.cmake" )
if( NOT MSVC AND TARGET date::tz )
if( NOT MSVC AND TARGET date::date-tz )
find_dependency( Threads REQUIRED)
get_target_property( _tzill date::tz INTERFACE_LINK_LIBRARIES )
get_target_property( _tzill date::date-tz INTERFACE_LINK_LIBRARIES )
if( _tzill AND "${_tzill}" MATCHES "libcurl" )
find_dependency( CURL )
endif( )

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -758,8 +758,8 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last&
inline namespace literals
{
CONSTCD11 islamic::day operator "" _d(unsigned long long d) NOEXCEPT;
CONSTCD11 islamic::year operator "" _y(unsigned long long y) NOEXCEPT;
CONSTCD11 islamic::day operator ""_d(unsigned long long d) NOEXCEPT;
CONSTCD11 islamic::year operator ""_y(unsigned long long y) NOEXCEPT;
} // inline namespace literals
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
@ -1339,7 +1339,7 @@ inline namespace literals
CONSTCD11
inline
islamic::day
operator "" _d(unsigned long long d) NOEXCEPT
operator ""_d(unsigned long long d) NOEXCEPT
{
return islamic::day{static_cast<unsigned>(d)};
}
@ -1347,7 +1347,7 @@ operator "" _d(unsigned long long d) NOEXCEPT
CONSTCD11
inline
islamic::year
operator "" _y(unsigned long long y) NOEXCEPT
operator ""_y(unsigned long long y) NOEXCEPT
{
return islamic::year(static_cast<int>(y));
}

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();}
@ -734,7 +747,7 @@ inline namespace literals
CONSTCD11
inline
iso_week::year
operator "" _y(unsigned long long y) NOEXCEPT
operator ""_y(unsigned long long y) NOEXCEPT
{
return iso_week::year(static_cast<int>(y));
}
@ -742,7 +755,7 @@ operator "" _y(unsigned long long y) NOEXCEPT
CONSTCD11
inline
iso_week::weeknum
operator "" _w(unsigned long long wn) NOEXCEPT
operator ""_w(unsigned long long wn) NOEXCEPT
{
return iso_week::weeknum(static_cast<unsigned>(wn));
}
@ -1020,10 +1033,7 @@ inline
weeknum
year_lastweek::weeknum() const NOEXCEPT
{
const auto y = date::year{static_cast<int>(y_)};
const auto s0 = sys_days((y-years{1})/12/date::thu[date::last]);
const auto s1 = sys_days(y/12/date::thu[date::last]);
return iso_week::weeknum(static_cast<unsigned>(date::trunc<weeks>(s1-s0).count()));
return iso_week::weeknum(y_.is_leap() ? 53u : 52u);
}
CONSTCD11 inline bool year_lastweek::ok() const NOEXCEPT {return y_.ok();}

View File

@ -758,8 +758,8 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last&
inline namespace literals
{
CONSTCD11 julian::day operator "" _d(unsigned long long d) NOEXCEPT;
CONSTCD11 julian::year operator "" _y(unsigned long long y) NOEXCEPT;
CONSTCD11 julian::day operator ""_d(unsigned long long d) NOEXCEPT;
CONSTCD11 julian::year operator ""_y(unsigned long long y) NOEXCEPT;
// CONSTDATA julian::month jan{1};
// CONSTDATA julian::month feb{2};
@ -1333,7 +1333,7 @@ inline namespace literals
CONSTCD11
inline
julian::day
operator "" _d(unsigned long long d) NOEXCEPT
operator ""_d(unsigned long long d) NOEXCEPT
{
return julian::day{static_cast<unsigned>(d)};
}
@ -1341,7 +1341,7 @@ operator "" _d(unsigned long long d) NOEXCEPT
CONSTCD11
inline
julian::year
operator "" _y(unsigned long long y) NOEXCEPT
operator ""_y(unsigned long long y) NOEXCEPT
{
return julian::year(static_cast<int>(y));
}
@ -1655,9 +1655,12 @@ inline
bool
month_day::ok() const NOEXCEPT
{
CONSTDATA julian::day d[] =
{31_d, 29_d, 31_d, 30_d, 31_d, 30_d, 31_d, 31_d, 30_d, 31_d, 30_d, 31_d};
return m_.ok() && 1_d <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
CONSTDATA julian::day d[] = {
julian::day(31), julian::day(29), julian::day(31), julian::day(30),
julian::day(31), julian::day(30), julian::day(31), julian::day(31),
julian::day(30), julian::day(31), julian::day(30), julian::day(31)
};
return m_.ok() && julian::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
}
CONSTCD11
@ -1946,9 +1949,12 @@ inline
day
year_month_day_last::day() const NOEXCEPT
{
CONSTDATA julian::day d[] =
{31_d, 28_d, 31_d, 30_d, 31_d, 30_d, 31_d, 31_d, 30_d, 31_d, 30_d, 31_d};
return month() != feb || !y_.is_leap() ? d[static_cast<unsigned>(month())-1] : 29_d;
CONSTDATA julian::day d[] = {
julian::day(31), julian::day(28), julian::day(31), julian::day(30),
julian::day(31), julian::day(30), julian::day(31), julian::day(31),
julian::day(30), julian::day(31), julian::day(30), julian::day(31)
};
return month() != feb || !y_.is_leap() ? d[static_cast<unsigned>(month())-1] : julian::day(29);
}
CONSTCD14
@ -2190,7 +2196,7 @@ year_month_day::ok() const NOEXCEPT
{
if (!(y_.ok() && m_.ok()))
return false;
return 1_d <= d_ && d_ <= (y_/m_/last).day();
return julian::day(1) <= d_ && d_ <= (y_/m_/last).day();
}
CONSTCD11

View File

@ -36,17 +36,55 @@
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()};
//
// 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
//
// They are provided here as a non-trivial custom time zone example, and if you really
// have to have Posix time zones, you're welcome to use this one.
#include "date/tz.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <chrono>
#include <ostream>
#include <string>
#ifndef HAS_CHRONO_20
# if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 200100
# define HAS_CHRONO_20 0
# else
# define HAS_CHRONO_20 1
# endif
#endif
#if HAS_CHRONO_20
namespace chr = std::chrono;
# define HAS_STRING_VIEW 1
# include <format>
#else
# include "date/tz.h"
namespace chr = date;
#endif
namespace Posix
{
@ -70,14 +108,15 @@ unsigned read_date(const string_t& s, unsigned i, rule& r);
unsigned read_name(const string_t& s, unsigned i, std::string& name);
unsigned read_signed_time(const string_t& s, unsigned i, std::chrono::seconds& t);
unsigned read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t);
unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u);
unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
const string_t& message = string_t{});
class rule
{
enum {off, J, M, N};
date::month m_;
date::weekday wd_;
chr::month m_;
chr::weekday wd_;
unsigned short n_ : 14;
unsigned short mode_ : 2;
std::chrono::duration<std::int32_t> time_ = std::chrono::hours{2};
@ -86,22 +125,49 @@ public:
rule() : mode_(off) {}
bool ok() const {return mode_ != off;}
date::local_seconds operator()(date::year y) const;
chr::local_seconds operator()(chr::year y) const;
std::string to_string() const;
friend std::ostream& operator<<(std::ostream& os, const rule& r);
friend unsigned read_date(const string_t& s, unsigned i, rule& r);
friend bool operator==(const rule& x, const rule& y);
};
inline
date::local_seconds
rule::operator()(date::year y) const
bool
operator==(const rule& x, const rule& y)
{
using date::local_days;
using date::January;
using date::days;
using date::last;
if (x.mode_ != y.mode_)
return false;
switch (x.mode_)
{
case rule::J:
case rule::N:
return x.n_ == y.n_;
case rule::M:
return x.m_ == y.m_ && x.n_ == y.n_ && x.wd_ == y.wd_;
default:
return true;
}
}
inline
bool
operator!=(const rule& x, const rule& y)
{
return !(x == y);
}
inline
chr::local_seconds
rule::operator()(chr::year y) const
{
using chr::local_days;
using chr::January;
using chr::days;
using chr::last;
using sec = std::chrono::seconds;
date::local_seconds t;
chr::local_seconds t;
switch (mode_)
{
case J:
@ -119,6 +185,62 @@ rule::operator()(date::year y) const
return t;
}
inline
std::string
rule::to_string() const
{
using namespace std::chrono;
auto print_offset = [](seconds off)
{
std::string nm;
if (off != hours{2})
{
chr::hh_mm_ss<seconds> offset{off};
nm = '/';
nm += std::to_string(offset.hours().count());
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
{
nm += ':';
if (offset.minutes() < minutes{10})
nm += '0';
nm += std::to_string(offset.minutes().count());
if (offset.seconds() != seconds{0})
{
nm += ':';
if (offset.seconds() < seconds{10})
nm += '0';
nm += std::to_string(offset.seconds().count());
}
}
}
return nm;
};
std::string nm;
switch (mode_)
{
case rule::J:
nm = 'J';
nm += std::to_string(n_);
break;
case rule::M:
nm = 'M';
nm += std::to_string(static_cast<unsigned>(m_));
nm += '.';
nm += std::to_string(n_);
nm += '.';
nm += std::to_string(wd_.c_encoding());
break;
case rule::N:
nm = std::to_string(n_);
break;
default:
break;
}
nm += print_offset(time_);
return nm;
}
inline
std::ostream&
operator<<(std::ostream& os, const rule& r)
@ -126,17 +248,29 @@ operator<<(std::ostream& os, const rule& r)
switch (r.mode_)
{
case rule::J:
#if HAS_CHRONO_20
os << "J " << r.n_ << std::format("{:%T}", r.time_);
#else
os << 'J' << r.n_ << date::format(" %T", r.time_);
#endif
break;
case rule::M:
if (r.n_ == 5)
os << r.m_/r.wd_[date::last];
os << r.m_/r.wd_[chr::last];
else
os << r.m_/r.wd_[r.n_];
os << date::format(" %T", r.time_);
#if HAS_CHRONO_20
os << ' ' << std::format("{:%T}", r.time_);
#else
os << date::format(" %T", r.time_);
#endif
break;
case rule::N:
#if HAS_CHRONO_20
os << r.n_ << ' ' << std::format("{:%T}", r.time_);
#else
os << r.n_ << date::format(" %T", r.time_);
#endif
break;
default:
break;
@ -159,27 +293,112 @@ public:
explicit time_zone(const detail::string_t& name);
template <class Duration>
date::sys_info get_info(date::sys_time<Duration> st) const;
chr::sys_info get_info(chr::sys_time<Duration> st) const;
template <class Duration>
date::local_info get_info(date::local_time<Duration> tp) const;
chr::local_info get_info(chr::local_time<Duration> tp) const;
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(date::local_time<Duration> tp) const;
chr::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(chr::local_time<Duration> tp) const;
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(date::local_time<Duration> tp, date::choose z) const;
chr::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(chr::local_time<Duration> tp, chr::choose z) const;
template <class Duration>
date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_local(date::sys_time<Duration> tp) const;
chr::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_local(chr::sys_time<Duration> tp) const;
friend std::ostream& operator<<(std::ostream& os, const time_zone& z);
const time_zone* operator->() const {return this;}
std::string name() const;
friend bool operator==(const time_zone& x, const time_zone& y);
private:
chr::sys_seconds get_start(chr::year y) const;
chr::sys_seconds get_prev_start(chr::year y) const;
chr::sys_seconds get_next_start(chr::year y) const;
chr::sys_seconds get_end(chr::year y) const;
chr::sys_seconds get_prev_end(chr::year y) const;
chr::sys_seconds get_next_end(chr::year y) const;
chr::sys_info contant_offset() const;
};
inline
chr::sys_seconds
time_zone::get_start(chr::year y) const
{
return chr::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
}
inline
chr::sys_seconds
time_zone::get_prev_start(chr::year y) const
{
return chr::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()};
}
inline
chr::sys_seconds
time_zone::get_next_start(chr::year y) const
{
return chr::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()};
}
inline
chr::sys_seconds
time_zone::get_end(chr::year y) const
{
return chr::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
}
inline
chr::sys_seconds
time_zone::get_prev_end(chr::year y) const
{
return chr::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()};
}
inline
chr::sys_seconds
time_zone::get_next_end(chr::year y) const
{
return chr::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()};
}
inline
chr::sys_info
time_zone::contant_offset() const
{
using chr::year;
using chr::sys_info;
using chr::sys_days;
using chr::January;
using chr::December;
using chr::last;
using chr::days;
using std::chrono::minutes;
sys_info r;
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last} + days{1} - std::chrono::seconds{1};
if (std_abbrev_.size() > 0)
{
r.abbrev = std_abbrev_;
r.offset = offset_;
r.save = {};
}
else
{
r.abbrev = dst_abbrev_;
r.offset = offset_ + save_;
r.save = chr::ceil<minutes>(save_);
}
return r;
}
inline
time_zone::time_zone(const detail::string_t& s)
{
@ -187,15 +406,21 @@ 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] != ',')
{
i = read_signed_time(s, i, save_);
save_ = -save_ - offset_;
}
if (i != s.size())
{
if (s[i] != ',')
@ -211,98 +436,156 @@ 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>
date::sys_info
time_zone::get_info(date::sys_time<Duration> st) const
chr::sys_info
time_zone::get_info(chr::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;
using date::last;
using chr::sys_info;
using chr::year_month_day;
using chr::sys_days;
using chr::floor;
using chr::ceil;
using chr::days;
using chr::year;
using chr::January;
using chr::December;
using chr::last;
using std::chrono::minutes;
sys_info r{};
r.offset = offset_;
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(st)}.year();
auto start = sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
auto end = sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
if (start <= st && st < end)
if (st >= get_next_start(y))
++y;
else if (st < get_prev_end(y))
--y;
auto start = get_start(y);
auto end = get_end(y);
if (start <= end) // (northern hemisphere)
{
r.begin = start;
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
if (start <= st && st < end)
{
r.begin = start;
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else if (st < start)
{
r.begin = get_prev_end(y);
r.end = start;
r.abbrev = std_abbrev_;
}
else // st >= end
{
r.begin = end;
r.end = get_next_start(y);
r.abbrev = std_abbrev_;
}
}
else if (st < start)
else // end < start (southern hemisphere)
{
r.begin = sys_seconds{(end_rule_(y-years{1}) -
(offset_ + save_)).time_since_epoch()};
r.end = start;
r.abbrev = std_abbrev_;
}
else // st >= end
{
r.begin = end;
r.end = sys_seconds{(start_rule_(y+years{1}) - offset_).time_since_epoch()};
r.abbrev = std_abbrev_;
if (end <= st && st < start)
{
r.begin = end;
r.end = start;
r.abbrev = std_abbrev_;
}
else if (st < end)
{
r.begin = get_prev_start(y);
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else // st >= start
{
r.begin = start;
r.end = get_next_end(y);
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
}
}
else // constant offset
{
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last};
r.abbrev = std_abbrev_;
}
else
r = contant_offset();
using seconds = std::chrono::seconds;
assert(r.begin <= floor<seconds>(st) && floor<seconds>(st) <= r.end);
return r;
}
template <class Duration>
date::local_info
time_zone::get_info(date::local_time<Duration> tp) const
chr::local_info
time_zone::get_info(chr::local_time<Duration> tp) const
{
using date::local_info;
using date::year_month_day;
using date::days;
using date::sys_days;
using date::sys_seconds;
using date::years;
using date::year;
using date::ceil;
using date::January;
using date::December;
using date::last;
using chr::local_info;
using chr::year_month_day;
using chr::days;
using chr::sys_days;
using chr::sys_seconds;
using chr::year;
using chr::ceil;
using chr::January;
using chr::December;
using chr::last;
using std::chrono::seconds;
using std::chrono::minutes;
local_info r{};
using date::floor;
using chr::floor;
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(tp)}.year();
auto start = sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
auto end = sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
auto start = get_start(y);
auto end = get_end(y);
auto utcs = sys_seconds{floor<seconds>(tp - offset_).time_since_epoch()};
auto utcd = sys_seconds{floor<seconds>(tp - (offset_ + save_)).time_since_epoch()};
auto northern = start <= end;
if ((utcs < start) != (utcd < start))
{
r.first.begin = sys_seconds{(end_rule_(y-years{1}) -
(offset_ + save_)).time_since_epoch()};
if (northern)
r.first.begin = get_prev_end(y);
else
r.first.begin = end;
r.first.end = start;
r.first.offset = offset_;
r.first.abbrev = std_abbrev_;
r.second.begin = start;
r.second.end = end;
if (northern)
r.second.end = end;
else
r.second.end = get_next_end(y);
r.second.abbrev = dst_abbrev_;
r.second.offset = offset_ + save_;
r.second.save = ceil<minutes>(save_);
@ -311,61 +594,40 @@ 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;
}
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(date::local_time<Duration> tp) const
chr::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(chr::local_time<Duration> tp) const
{
using date::local_info;
using date::sys_time;
using date::ambiguous_local_time;
using chr::local_info;
using chr::sys_time;
using chr::ambiguous_local_time;
using chr::nonexistent_local_time;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
throw nonexistent_local_time(tp, i);
@ -375,12 +637,12 @@ time_zone::to_sys(date::local_time<Duration> tp) const
}
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(date::local_time<Duration> tp, date::choose z) const
chr::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(chr::local_time<Duration> tp, chr::choose z) const
{
using date::local_info;
using date::sys_time;
using date::choose;
using chr::local_info;
using chr::sys_time;
using chr::choose;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
{
@ -395,10 +657,10 @@ time_zone::to_sys(date::local_time<Duration> tp, date::choose z) const
}
template <class Duration>
date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_local(date::sys_time<Duration> tp) const
chr::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_local(chr::sys_time<Duration> tp) const
{
using date::local_time;
using chr::local_time;
using std::chrono::seconds;
using LT = local_time<typename std::common_type<Duration, seconds>::type>;
auto i = get_info(tp);
@ -409,13 +671,96 @@ inline
std::ostream&
operator<<(std::ostream& os, const time_zone& z)
{
using date::operator<<;
using chr::operator<<;
os << '{';
#if HAS_CHRONO_20
os << z.std_abbrev_ << ", " << z.dst_abbrev_ << ", " << std::format("{:%T, }", z.offset_)
<< std::format("{:%T, [}", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}";
#else
os << z.std_abbrev_ << ", " << z.dst_abbrev_ << date::format(", %T, ", z.offset_)
<< date::format("%T, [", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}";
#endif
return os;
}
inline
std::string
time_zone::name() const
{
using namespace chr;
using namespace std::chrono;
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;
chr::hh_mm_ss<seconds> offset{-off};
if (offset.is_negative())
nm += '-';
nm += std::to_string(offset.hours().count());
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
{
nm += ':';
if (offset.minutes() < minutes{10})
nm += '0';
nm += std::to_string(offset.minutes().count());
if (offset.seconds() != seconds{0})
{
nm += ':';
if (offset.seconds() < seconds{10})
nm += '0';
nm += std::to_string(offset.seconds().count());
}
}
return nm;
};
auto nm = print_abbrev(std_abbrev_);
nm += print_offset(offset_);
if (!dst_abbrev_.empty())
{
nm += print_abbrev(dst_abbrev_);
if (save_ != hours{1})
nm += print_offset(offset_+save_);
if (start_rule_.ok())
{
nm += ',';
nm += start_rule_.to_string();
nm += ',';
nm += end_rule_.to_string();
}
}
return nm;
}
inline
bool
operator==(const time_zone& x, const time_zone& y)
{
return x.std_abbrev_ == y.std_abbrev_ &&
x.dst_abbrev_ == y. dst_abbrev_ &&
x.offset_ == y.offset_ &&
x.save_ == y.save_ &&
x.start_rule_ == y.start_rule_ &&
x.end_rule_ == y.end_rule_;
}
inline
bool
operator!=(const time_zone& x, const time_zone& y)
{
return !(x == y);
}
namespace detail
{
@ -428,7 +773,7 @@ throw_invalid(const string_t& s, unsigned i, const string_t& message)
std::string(s) + '\n' +
"\x1b[1;32m" +
std::string(i, '~') + '^' +
std::string(s.size()-i-1, '~') +
std::string(i < s.size() ? s.size()-i-1 : 0, '~') +
"\x1b[0m");
}
@ -436,15 +781,17 @@ inline
unsigned
read_date(const string_t& s, unsigned i, rule& r)
{
using date::month;
using date::weekday;
using chr::month;
using chr::weekday;
if (i == s.size())
throw_invalid(s, i, "Expected rule but found end of string");
if (s[i] == 'J')
{
++i;
unsigned n;
i = read_unsigned(s, i, 3, n);
i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]");
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;
}
@ -452,17 +799,23 @@ read_date(const string_t& s, unsigned i, rule& r)
{
++i;
unsigned m;
i = read_unsigned(s, i, 2, m);
i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]");
if (!(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);
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);
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};
@ -472,6 +825,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;
}
@ -516,8 +871,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;
}
@ -552,17 +905,23 @@ read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
if (i == s.size())
throw_invalid(s, i, "Expected to read unsigned time, but found end of string");
unsigned x;
i = read_unsigned(s, i, 2, x);
i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]");
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);
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);
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};
}
}
@ -571,10 +930,11 @@ read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
inline
unsigned
read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u)
read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
const string_t& message)
{
if (i == s.size() || !std::isdigit(s[i]))
throw_invalid(s, i, "Expected to find a decimal digit");
throw_invalid(s, i, message);
u = static_cast<unsigned>(s[i] - '0');
unsigned count = 1;
for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count)
@ -586,7 +946,11 @@ read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u)
} // namespace Posix
#if HAS_CHRONO_20
namespace std::chrono
#else
namespace date
#endif
{
template <>
@ -622,6 +986,6 @@ struct zoned_traits<Posix::time_zone>
};
} // namespace date
} // namespace chr
#endif // PTZ_H

View File

@ -792,8 +792,8 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last&
inline namespace literals
{
CONSTCD11 solar_hijri::day operator "" _d(unsigned long long d) NOEXCEPT;
CONSTCD11 solar_hijri::year operator "" _y(unsigned long long y) NOEXCEPT;
CONSTCD11 solar_hijri::day operator ""_d(unsigned long long d) NOEXCEPT;
CONSTCD11 solar_hijri::year operator ""_y(unsigned long long y) NOEXCEPT;
} // inline namespace literals
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
@ -1364,7 +1364,7 @@ inline namespace literals
CONSTCD11
inline
solar_hijri::day
operator "" _d(unsigned long long d) NOEXCEPT
operator ""_d(unsigned long long d) NOEXCEPT
{
return solar_hijri::day{static_cast<unsigned>(d)};
}
@ -1372,7 +1372,7 @@ operator "" _d(unsigned long long d) NOEXCEPT
CONSTCD11
inline
solar_hijri::year
operator "" _y(unsigned long long y) NOEXCEPT
operator ""_y(unsigned long long y) NOEXCEPT
{
return solar_hijri::year(static_cast<int>(y));
}
@ -1707,9 +1707,13 @@ inline
bool
month_day::ok() const NOEXCEPT
{
CONSTDATA solar_hijri::day d[] =
{31_d, 31_d, 31_d, 31_d, 31_d, 31_d, 30_d, 30_d, 30_d, 30_d, 30_d, 30_d};
return m_.ok() && 1_d <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
CONSTDATA solar_hijri::day d[] = {
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30),
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30)
};
return m_.ok() && solar_hijri::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
}
CONSTCD11
@ -1998,10 +2002,14 @@ inline
day
year_month_day_last::day() const NOEXCEPT
{
CONSTDATA solar_hijri::day d[] =
{31_d, 31_d, 31_d, 31_d, 31_d, 31_d, 30_d, 30_d, 30_d, 30_d, 30_d, 29_d};
CONSTDATA solar_hijri::day d[] = {
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30),
solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(29)
};
return month() != esf || !y_.is_leap() ?
d[static_cast<unsigned>(month())-1] : 30_d;
d[static_cast<unsigned>(month()) - 1] : solar_hijri::day(30);
}
CONSTCD14
@ -2260,7 +2268,7 @@ year_month_day::ok() const NOEXCEPT
{
if (!(y_.ok() && m_.ok()))
return false;
return 1_d <= d_ && d_ <= (y_/m_/last).day();
return solar_hijri::day(1) <= d_ && d_ <= (y_/m_/last).day();
}
CONSTCD11

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
@ -86,15 +86,6 @@ static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true,
# ifdef _WIN32
# error "USE_OS_TZDB can not be used on Windows"
# endif
# ifndef MISSING_LEAP_SECONDS
# ifdef __APPLE__
# define MISSING_LEAP_SECONDS 1
# else
# define MISSING_LEAP_SECONDS 0
# endif
# endif
#else
# define MISSING_LEAP_SECONDS 0
#endif
#ifndef HAS_DEDUCTION_GUIDES
@ -148,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;
@ -240,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();
}
@ -703,6 +701,11 @@ public:
private:
template <class D, class T> friend class zoned_time;
template <class TimeZonePtr2>
static
TimeZonePtr2&&
check(TimeZonePtr2&& p);
};
using zoned_seconds = zoned_time<std::chrono::seconds>;
@ -825,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:
@ -848,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
@ -990,8 +1002,6 @@ inline bool operator>=(const time_zone_link& x, const time_zone_link& y) {return
#endif // !USE_OS_TZDB
#if !MISSING_LEAP_SECONDS
class leap_second
{
private:
@ -1115,8 +1125,6 @@ operator>=(const sys_time<Duration>& x, const leap_second& y)
using leap = leap_second;
#endif // !MISSING_LEAP_SECONDS
#ifdef _WIN32
namespace detail
@ -1162,9 +1170,7 @@ struct tzdb
#if !USE_OS_TZDB
std::vector<time_zone_link> links;
#endif
#if !MISSING_LEAP_SECONDS
std::vector<leap_second> leap_seconds;
#endif
#if !USE_OS_TZDB
std::vector<detail::Rule> rules;
#endif
@ -1200,11 +1206,11 @@ struct tzdb
#endif // defined(_MSC_VER) && (_MSC_VER < 1900)
#if HAS_STRING_VIEW
const time_zone* locate_zone(std::string_view tz_name) const;
DATE_API const time_zone* locate_zone(std::string_view tz_name) const;
#else
const time_zone* locate_zone(const std::string& tz_name) const;
DATE_API const time_zone* locate_zone(const std::string& tz_name) const;
#endif
const time_zone* current_zone() const;
DATE_API const time_zone* current_zone() const;
};
using TZ_DB = tzdb;
@ -1219,33 +1225,33 @@ class tzdb_list
std::atomic<tzdb*> head_{nullptr};
public:
~tzdb_list();
DATE_API ~tzdb_list();
tzdb_list() = default;
tzdb_list(tzdb_list&& x) noexcept;
DATE_API tzdb_list(tzdb_list&& x) NOEXCEPT;
const tzdb& front() const noexcept {return *head_;}
tzdb& front() noexcept {return *head_;}
const tzdb& front() const NOEXCEPT {return *head_;}
tzdb& front() NOEXCEPT {return *head_;}
class const_iterator;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator begin() const NOEXCEPT;
const_iterator end() const NOEXCEPT;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_iterator cbegin() const NOEXCEPT;
const_iterator cend() const NOEXCEPT;
const_iterator erase_after(const_iterator p) noexcept;
DATE_API const_iterator erase_after(const_iterator p) NOEXCEPT;
struct undocumented_helper;
private:
void push_front(tzdb* tzdb) noexcept;
void push_front(tzdb* tzdb) NOEXCEPT;
};
class tzdb_list::const_iterator
{
tzdb* p_ = nullptr;
explicit const_iterator(tzdb* p) noexcept : p_{p} {}
explicit const_iterator(tzdb* p) NOEXCEPT : p_{p} {}
public:
const_iterator() = default;
@ -1255,20 +1261,20 @@ public:
using pointer = const value_type*;
using difference_type = std::ptrdiff_t;
reference operator*() const noexcept {return *p_;}
pointer operator->() const noexcept {return p_;}
reference operator*() const NOEXCEPT {return *p_;}
pointer operator->() const NOEXCEPT {return p_;}
const_iterator& operator++() noexcept {p_ = p_->next; return *this;}
const_iterator operator++(int) noexcept {auto t = *this; ++(*this); return t;}
const_iterator& operator++() NOEXCEPT {p_ = p_->next; return *this;}
const_iterator operator++(int) NOEXCEPT {auto t = *this; ++(*this); return t;}
friend
bool
operator==(const const_iterator& x, const const_iterator& y) noexcept
operator==(const const_iterator& x, const const_iterator& y) NOEXCEPT
{return x.p_ == y.p_;}
friend
bool
operator!=(const const_iterator& x, const const_iterator& y) noexcept
operator!=(const const_iterator& x, const const_iterator& y) NOEXCEPT
{return !(x == y);}
friend class tzdb_list;
@ -1276,28 +1282,28 @@ public:
inline
tzdb_list::const_iterator
tzdb_list::begin() const noexcept
tzdb_list::begin() const NOEXCEPT
{
return const_iterator{head_};
}
inline
tzdb_list::const_iterator
tzdb_list::end() const noexcept
tzdb_list::end() const NOEXCEPT
{
return const_iterator{nullptr};
}
inline
tzdb_list::const_iterator
tzdb_list::cbegin() const noexcept
tzdb_list::cbegin() const NOEXCEPT
{
return begin();
}
inline
tzdb_list::const_iterator
tzdb_list::cend() const noexcept
tzdb_list::cend() const NOEXCEPT
{
return end();
}
@ -1328,7 +1334,7 @@ namespace detail
template <class T>
inline
T*
to_raw_pointer(T* p) noexcept
to_raw_pointer(T* p) NOEXCEPT
{
return p;
}
@ -1336,7 +1342,7 @@ to_raw_pointer(T* p) noexcept
template <class Pointer>
inline
auto
to_raw_pointer(Pointer p) noexcept
to_raw_pointer(Pointer p) NOEXCEPT
-> decltype(detail::to_raw_pointer(p.operator->()))
{
return detail::to_raw_pointer(p.operator->());
@ -1344,13 +1350,25 @@ to_raw_pointer(Pointer p) noexcept
} // namespace detail
template <class Duration, class TimeZonePtr>
template <class TimeZonePtr2>
inline
TimeZonePtr2&&
zoned_time<Duration, TimeZonePtr>::check(TimeZonePtr2&& p)
{
if (detail::to_raw_pointer(p) == nullptr)
throw std::runtime_error(
"zoned_time constructed with a time zone pointer == nullptr");
return std::forward<TimeZonePtr2>(p);
}
template <class Duration, class TimeZonePtr>
#if !defined(_MSC_VER) || (_MSC_VER > 1916)
template <class T, class>
#endif
inline
zoned_time<Duration, TimeZonePtr>::zoned_time()
: zone_(zoned_traits<TimeZonePtr>::default_zone())
: zone_(check(zoned_traits<TimeZonePtr>::default_zone()))
{}
template <class Duration, class TimeZonePtr>
@ -1359,15 +1377,15 @@ template <class T, class>
#endif
inline
zoned_time<Duration, TimeZonePtr>::zoned_time(const sys_time<Duration>& st)
: zone_(zoned_traits<TimeZonePtr>::default_zone())
: zone_(check(zoned_traits<TimeZonePtr>::default_zone()))
, tp_(st)
{}
template <class Duration, class TimeZonePtr>
inline
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z)
: zone_(std::move(z))
{assert(detail::to_raw_pointer(zone_) != nullptr);}
: zone_(check(std::move(z)))
{}
#if HAS_STRING_VIEW
@ -1402,7 +1420,7 @@ zoned_time<Duration, TimeZonePtr>::zoned_time(const zoned_time<Duration2, TimeZo
template <class Duration, class TimeZonePtr>
inline
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const sys_time<Duration>& st)
: zone_(std::move(z))
: zone_(check(std::move(z)))
, tp_(st)
{}
@ -1412,7 +1430,7 @@ template <class T, class>
#endif
inline
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const local_time<Duration>& t)
: zone_(std::move(z))
: zone_(check(std::move(z)))
, tp_(zone_->to_sys(t))
{}
@ -1423,7 +1441,7 @@ template <class T, class>
inline
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const local_time<Duration>& t,
choose c)
: zone_(std::move(z))
: zone_(check(std::move(z)))
, tp_(zone_->to_sys(t, c))
{}
@ -1432,7 +1450,7 @@ template <class Duration2, class TimeZonePtr2, class>
inline
zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z,
const zoned_time<Duration2, TimeZonePtr2>& zt)
: zone_(std::move(z))
: zone_(check(std::move(z)))
, tp_(zt.tp_)
{}
@ -1847,8 +1865,6 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const zoned_time<Duration, Tim
return to_stream(os, fmt, t);
}
#if !MISSING_LEAP_SECONDS
class utc_clock
{
public:
@ -2787,8 +2803,6 @@ to_gps_time(const tai_time<Duration>& t)
return gps_clock::from_utc(tai_clock::to_utc(t));
}
#endif // !MISSING_LEAP_SECONDS
} // namespace date
#endif // TZ_H

View File

@ -95,9 +95,9 @@ private:
U& operator=(const pair& x);
} u;
std::chrono::hours h_{};
std::chrono::minutes m_{};
std::chrono::seconds s_{};
std::chrono::hours h_{0};
std::chrono::minutes m_{0};
std::chrono::seconds s_{0};
tz zone_{tz::local};
public:
@ -245,7 +245,7 @@ struct zonelet
sys_seconds until_utc_;
local_seconds until_std_;
local_seconds until_loc_;
std::chrono::minutes initial_save_{};
std::chrono::minutes initial_save_{0};
std::string initial_abbrev_;
std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()};
std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()};
@ -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

File diff suppressed because it is too large Load Diff

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

@ -46,19 +46,19 @@ int
main()
{
using namespace date::detail;
static_assert(width<0>::value == 0, "");
static_assert(width<1>::value == 0, "");
static_assert(width<2>::value == 1, "");
static_assert(width<3>::value == 19, "");
static_assert(width<4>::value == 2, "");
static_assert(width<5>::value == 1, "");
static_assert(width<6>::value == 19, "");
static_assert(width<7>::value == 19, "");
static_assert(width<8>::value == 3, "");
static_assert(width<9>::value == 19, "");
static_assert(width<10>::value == 1, "");
static_assert(width<100>::value == 2, "");
static_assert(width<1000>::value == 3, "");
static_assert(width<10000>::value == 4, "");
static_assert(width<625>::value == 4, "");
static_assert(width<0, 1>::value == 0, "");
static_assert(width<1, 1>::value == 0, "");
static_assert(width<1, 2>::value == 1, "");
static_assert(width<1, 3>::value == 19, "");
static_assert(width<1, 4>::value == 2, "");
static_assert(width<1, 5>::value == 1, "");
static_assert(width<1, 6>::value == 19, "");
static_assert(width<1, 7>::value == 19, "");
static_assert(width<1, 8>::value == 3, "");
static_assert(width<1, 9>::value == 19, "");
static_assert(width<1, 10>::value == 1, "");
static_assert(width<1, 100>::value == 2, "");
static_assert(width<1, 1000>::value == 3, "");
static_assert(width<1, 10000>::value == 4, "");
static_assert(width<756, 625>::value == 4, "");
}

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

@ -204,6 +204,13 @@ test_c()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/11} + hours{14} + minutes{2} + seconds{43});
}
{
// can't parse negative years with "%c" directly
std::istringstream in{"Sun Dec 11 14:02:43 -2016"};
sys_seconds tp;
in >> parse("%c", tp);
assert(in.fail());
}
}
void
@ -362,6 +369,30 @@ test_d()
assert(!in.bad());
assert(tp == 2016_y/12/9);
}
{
std::istringstream in{"2016 +9 12"};
sys_days tp;
in >> parse("%Y %d %m", tp);
assert(in.fail());
}
{
std::istringstream in{"2016 +9 12"};
sys_days tp;
in >> parse("%Y %e %m", tp);
assert(in.fail());
}
{
std::istringstream in{"2016 -9 12"};
sys_days tp;
in >> parse("%Y %d %m", tp);
assert(in.fail());
}
{
std::istringstream in{"2016 -9 12"};
sys_days tp;
in >> parse("%Y %e %m", tp);
assert(in.fail());
}
{
std::istringstream in{"2016 31 11"};
sys_days tp;
@ -390,6 +421,7 @@ test_F()
{
using namespace date;
using namespace std::chrono;
using date::year_month_day;
{
std::istringstream in{"2016-12-13"};
sys_days tp;
@ -400,7 +432,7 @@ test_F()
}
{
std::istringstream in{"2016-12-13"};
year_month_day tp;
year_month_day tp{};
in >> parse("%F", tp);
assert(!in.fail());
assert(!in.bad());
@ -413,6 +445,7 @@ test_H()
{
using namespace date;
using namespace std::chrono;
using date::sys_time;
{
std::istringstream in{"2016-12-11 15"};
sys_time<hours> tp;
@ -434,6 +467,7 @@ test_Ip()
{
using namespace date;
using namespace std::chrono;
using date::sys_time;
{
std::istringstream in{"2016-12-11 1 pm"};
sys_time<hours> tp;
@ -456,6 +490,12 @@ test_Ip()
in >> parse("%F %I %p", tp);
assert(in.fail());
}
{
std::istringstream in{"2016-12-11 +1 pm"};
sys_time<hours> tp;
in >> parse("%F %I %p", tp);
assert(in.fail());
}
}
void
@ -500,6 +540,12 @@ test_m()
in >> parse("%Y %d %m", tp);
assert(in.fail());
}
{
std::istringstream in{"2016-12-+3"};
sys_days tp;
in >> parse("%Y-%d-%m", tp);
assert(in.fail());
}
}
void
@ -507,6 +553,7 @@ test_M()
{
using namespace date;
using namespace std::chrono;
using date::sys_time;
{
std::istringstream in{"2016-12-11 15"};
sys_time<minutes> tp;
@ -528,6 +575,7 @@ test_S()
{
using namespace date;
using namespace std::chrono;
using date::sys_time;
{
std::istringstream in{"2016-12-11 15"};
sys_seconds tp;
@ -557,6 +605,7 @@ test_T()
{
using namespace date;
using namespace std::chrono;
using date::sys_time;
{
std::istringstream in{"2016-12-11 15:43:22"};
sys_seconds tp;
@ -615,6 +664,7 @@ test_p()
{
using namespace date;
using namespace std::chrono;
using date::sys_time;
{
std::istringstream in{"2016-12-11 11pm"};
sys_time<hours> tp;
@ -623,6 +673,21 @@ test_p()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/11} + hours{23});
}
{
std::istringstream in{"1986-12-01 01:01:01 pm"};
sys_time<seconds> tp;
in >> parse("%Y-%m-%d %I:%M:%S %p", tp);
assert(!in.fail());
assert(!in.bad());
assert(tp == sys_days{1986_y/12/01} + hours{13} + minutes{01} + seconds{01});
}
{
std::istringstream in{"1986-12-01 01:01:01"};
sys_time<seconds> tp;
in >> parse("%Y-%m-%d %I:%M:%S", tp);
// The test will fail because %I needs the %p option to shows if it is AM or PM
assert(in.fail());
}
}
void
@ -729,6 +794,7 @@ test_z()
{
using namespace date;
using namespace std::chrono;
using date::local_seconds, date::local_days;
{
std::istringstream in{"2016-12-26 15:53:22 -0500"};
sys_seconds tp;
@ -753,6 +819,30 @@ test_z()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/26} + hours{20} + minutes{53} + seconds{22});
}
{
std::istringstream in{"2016-12-26 15:53:22 -+500"};
sys_seconds tp;
in >> parse("%F %T %z", tp);
assert(in.fail());
}
{
std::istringstream in{"2016-12-26 15:53:22 -+500"};
sys_seconds tp;
in >> parse("%F %T %Ez", tp);
assert(in.fail());
}
{
std::istringstream in{"2016-12-26 15:53:22 --500"};
sys_seconds tp;
in >> parse("%F %T %z", tp);
assert(in.fail());
}
{
std::istringstream in{"2016-12-26 15:53:22 --500"};
sys_seconds tp;
in >> parse("%F %T %Ez", tp);
assert(in.fail());
}
}
void
@ -760,6 +850,7 @@ test_Z()
{
using namespace date;
using namespace std::chrono;
using date::local_seconds, date::local_days;
{
std::string a;
std::istringstream in{"2016-12-26 15:53:22 word"};

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

@ -215,7 +215,7 @@ test_g() {
static_assert(sizeof(ymdd)/sizeof(ymdd[0]) == sizeof(ymdh)/sizeof(ymdh[0]), "");
static_assert(sizeof(ymdd)/sizeof(ymdd[0]) == sizeof(leaps)/sizeof(leaps[0]), "");
for (auto i = 0; i < sizeof(ymdd)/sizeof(ymdd[0]); ++i)
for (auto i = 0u; i < sizeof(ymdd)/sizeof(ymdd[0]); ++i)
{
assert(solar_hijri::year_month_day{ymdd[i]} == ymdh[i]);
assert(ymdd[i] == date::year_month_day{ymdh[i]});

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

@ -0,0 +1,36 @@
// The MIT License (MIT)
//
// Copyright (c) 2020 Howard Hinnant
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "tz.h"
#include <type_traits>
int
main()
{
using namespace date;
static_assert( std::is_nothrow_destructible<tzdb_list>{}, "");
static_assert( std::is_nothrow_default_constructible<tzdb_list>{}, "");
static_assert(!std::is_copy_constructible<tzdb_list>{}, "");
static_assert(!std::is_copy_assignable<tzdb_list>{}, "");
static_assert( std::is_nothrow_move_constructible<tzdb_list>{}, "");
static_assert(!std::is_move_assignable<tzdb_list>{}, "");
}

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
{

View File

@ -1,10 +1,6 @@
#!/bin/bash
#!/bin/sh
# show what is to be run
echo $1
eval $1
if [ $? -eq 0 ]; then
exit 0;
fi
exit 1;
# run the command
eval $1 || exit 1 # if fails, return 1