171 Commits
v2.2 ... v2.4.1

Author SHA1 Message Date
e7e1482087 Remove BUILD_TZ_STATIC and replace with BUILD_SHARED_LIBS
* This is more standard CMake
* See https://cmake.org/cmake/help/v3.1/command/add_library.html
2018-03-19 18:04:34 +00:00
d6b95dc301 Remove TZ_CXX_STANDARD and instead use CMAKE_CXX_STANDARD
* See https://cmake.org/cmake/help/v3.1/variable/CMAKE_CXX_STANDARD.html?highlight=cmake_cxx_standard
2018-03-19 17:59:36 +00:00
700489e475 Introduced full weekday and month names
* The standardized version of this library has std::chrono::January
  and std::chrono::Sunday in place of std::chrono_literals::jan and
  std::chrono_literals::sun.  Compatible names added to namespace
  date to ease transition from this lib to std::chrono.  The old
  names are retained for backwards compatibility.
2018-03-18 18:27:04 -04:00
e6941697eb Validate TZdata failed with OS TZdata 2018-03-18 18:17:27 -04:00
c311db2f1a Clean up tz_test README 2018-03-18 18:14:40 -04:00
fffa52ac0e Cleanup and add to README.md 2018-03-18 18:14:40 -04:00
f105595f04 Use proper lib directory when installing on UNIX
Linux distributions use lib, lib32, and lib64 directories for library files,
symlinking them where needed. Let's follow distirbution rules by
utilising CMake module GNUInstallDirs.
2018-03-18 17:55:08 -04:00
20f0595b32 Update standardization progress 2018-03-17 10:57:45 -04:00
674a9e6953 Update README.md 2018-03-16 15:42:13 -04:00
bc8cf368e5 Update readme link to d0355r6 2018-03-14 22:50:18 -04:00
1e8ab50f82 Test modifications for standardization 2018-03-14 22:49:04 -04:00
38c5ca38bb Typo fixes in comment text 2018-03-04 18:26:07 -05:00
0bde4ba8c8 Make the parsing of minutes optional under the flag %z 2018-03-03 11:57:03 -05:00
afe61df277 Fix case of generated dateConfig so it will work on a case sensitive filesystem 2018-03-02 16:37:32 -05:00
43d8a4eab0 Range check minutes under parse 2018-03-02 09:03:41 -05:00
mwu
ca4036a4b0 Explicitly qualify std::string. Having a global scope string type shall not break the compilation anymore. 2018-02-23 21:17:26 -05:00
a1ceec19fe Workaround for MSVC 2018-02-20 11:51:28 -05:00
5524dd1ae8 Update README.md 2018-02-15 18:49:37 -05:00
4ada98d247 Enable testing in CMakeLists.txt
* Per instructions by @SlavSlavov in https://github.com/HowardHinnant/date/pull/278
2018-01-29 08:39:58 -05:00
040eed838b Augment path to ios.h 2018-01-26 19:53:51 -05:00
2acf403bcd Dont include ios.h unconditionally.
The include in tz.cpp was changed to only include the ios.h header
when compiling on apple platforms, as the documentation states that
only the four files date.h, tz.h, tz_private.h, and tz.cpp should be
required to use the time zone library.

Additionally, the ios.mm file was moved to src/ since it contains
C++ source code.
2018-01-24 20:24:10 -05:00
637e5d8007 Use target_compile_definitions
It's generally preferred to use [`target_compile_definitions`](https://floooh.github.io/2016/01/12/cmake-dependency-juggling.html) over `add_definitions`.
2018-01-21 12:33:42 -05:00
362cd8f27e Account for WIN32 2018-01-21 12:29:13 -05:00
a4ce4bc2d3 Use correct target
Also, fix the formatting in the generator expressions. For some reason, the previous
formatting caused this error:

  Target "date_interface" INTERFACE_INCLUDE_DIRECTORIES property contains
  path:

  ...

  which is prefixed in the source directory.
2018-01-21 12:29:13 -05:00
3e5a57467a Export a CMake config file
This will allow users to import the project via CMake methods, i.e `find_package` rather than doing it manually.
2018-01-21 12:29:13 -05:00
3b8372f4fa Avoid use of undeclared to_utc_time.
* Fixes https://github.com/HowardHinnant/date/issues/289
2018-01-18 15:55:58 -05:00
6941691de4 implement current_zone() for iOS using native API from iOSUtils 2018-01-12 14:59:01 -05:00
09b78ba92c bump msvc define to fix compile issue on MSVC 15.6 2018-01-12 10:51:05 -05:00
28c1c61ac2 Advertise that this works in C++17 2018-01-08 09:54:42 -05:00
55289f0d73 Make tz_dir a function-local static
* This solves initialization order issues detailed
  in https://github.com/HowardHinnant/date/issues/275
2018-01-05 19:26:44 -05:00
b7e58e193f Set standard or default to C++17
Changed cmake to default to c++17 unless TZ_CXX_STANDARD is set(e.g. to 11,14 or 17)
2018-01-05 18:45:29 -05:00
2b6ee6378c minutes fix 2018-01-05 18:42:48 -05:00
7d80d89a44 Allow specifying cxx standard to target
Not everyone can use C++17 even with compilers that support it
2017-12-31 16:42:29 -05:00
d9052cffa2 rename and mark-as-advanced the following cmake-variable used in function print_option:
CURR_${OPT} -> PRINT_OPTION_CURR_${OPT}
so that the date-project now has the following advanced cmake-variables:
 PRINT_OPTION_CURR_BUILD_TZ_STATIC
 PRINT_OPTION_CURR_USE_SYSTEM_TZ_DB
 PRINT_OPTION_CURR_USE_TZ_DB_IN_DOT
2017-12-26 21:00:29 +01:00
447687870f - Interface library name changed from date to date_interface
- Print state of set options
- cleanup on duplicate ${CMAKE_THREAD_LIBS_INIT}
2017-12-26 13:39:55 -05:00
dbd6e6e388 cmake: remove include_directories() command that is missing directory 2017-12-26 13:35:25 -05:00
9178193ad2 cmake: date as INTERFACE target, to enable automatic include_directory
(for cases where just date.h, but not "tz.h and its lib" are needed)
2017-12-26 13:35:25 -05:00
44c2515280 Use the highest possible C++ standard (#18) 2017-12-26 11:12:11 +02:00
0af7654764 Fix iOS build 2017-12-11 05:45:27 -06:00
1eeb4cd652 Prepare for the new definition of IST
* Irish Standard Time has a -1h save in the winter instead
  of a +1h save in the summer.
2017-12-08 13:08:40 -05:00
b49bdc3ca7 Add missing include <memory> for unique_ptr 2017-12-06 10:12:49 -05:00
c7e5a4d08e Add posix-style time zone example: ptz.h 2017-12-03 14:40:01 -05:00
5653e9e3a9 Fixes CURLE_SSL_CACERT (60) when downloading tzdata2017c.tar.gz 2017-12-01 12:14:41 -05:00
d3b8d4af8d use target_include_directories instead of include_directories 2017-12-01 11:18:59 -05:00
c2e139ef53 Replace realpath with readlink
* Appears to be more modern and Debian Stretch requires it.
2017-11-30 17:17:28 -05:00
a3e8f399c4 ifdef out clock_cast for VC-2017 and earlier
* Clients report it can't handle the C++11
* Triggered to be enabled on VC-number-next
* Deprecated API (to_xxx_time) rewritten to be independent of
  clock_cast.
* Whitespace changes to bring column length down to 90.
2017-11-30 15:40:37 -05:00
443a29df53 Improve range check on year
* The previous fix broke parsing of sub-date quantities such as
  durations.
2017-11-30 12:06:51 -05:00
1902b8e8fb Range check year on parse 2017-11-30 10:45:30 -05:00
c513a20691 Fix off-by-one bug in iso_week::year_lastweek_weekday
* The conversion from year_lastweek_weekday to sys_days
  and local_days had an off by one error.
* Added test for this case.
2017-11-30 10:28:14 -05:00
87ed7f83cf Correct IANA data download URL 2017-11-27 12:44:57 -05:00
f8cc62c396 Changed naming
so that it is parent folder name _pass or _fail (e.g.)
date_test_fail_<test_name>
and date_test_pass_<test_name>
As this should be easier to sort
2017-11-27 10:20:14 -05:00
2d2b65906a Set so that the pass tests are all tests that do not end in .fail.cpp 2017-11-27 10:20:14 -05:00
53629fa30c No longer hardwiring subfolders of test
Hid build errors on should fail tests
2017-11-27 10:20:14 -05:00
748a1fd5a9 Updated testing so that failures of the should fail tests will compile
but will return 1 in testing if they actually built
2017-11-26 23:22:29 -05:00
524517b369 Added scripts to do fail testing 2017-11-26 23:11:58 -05:00
98ae1e5241 part 1 of merge request 2017-11-26 23:09:46 -05:00
9b88763dbf Remove noexcept from tai_clock::now() and gps_clock::now()
*  These functions may try to initialize the tzdb which
   could fail.
2017-11-26 21:24:45 -05:00
f3741d68ff Removed OpenSSL requirement. Curl, if it supports SSL, will pull it in.
Tested on Ubuntu so far
2017-11-26 16:56:41 -05:00
080df4988f excluded testit from Windows 2017-11-26 16:56:41 -05:00
1f27fb7d42 Cannot set USE_OS_TZDB=1 on Windows 2017-11-26 16:56:41 -05:00
3e47883c41 Added compile option BUILD_STATIC that defaults to ON to build static
libraries.  Set to off to build shared
2017-11-26 16:56:41 -05:00
3a33cdca7d Fixed WORKING_DIR path on testit target to use project root as base 2017-11-26 16:56:41 -05:00
3c4f0b5ada Changed testing approach and added gitignore 2017-11-26 16:56:41 -05:00
c0a3e528a3 Typo 2017-11-26 16:56:41 -05:00
9c67d94f2f Started adding test building 2017-11-26 16:56:41 -05:00
9c39772731 Create CMakeList.txt
This is an implementation of cmake config relating to #18
2017-11-26 16:56:41 -05:00
15a63ec819 Updated on behalf of schmidt9 2017-11-26 14:41:40 -05:00
543315b700 Reversed order of arguments to clock_time_conversion.
Now the order of argument matches the clock_cast function.
The test only is_clock_castable trait still matches is_convertible
order of arguments.
2017-11-26 13:50:21 -05:00
7c69f1570d Changed invocation to match specification
Slight difference between invocation on lvalue of clock_time_conversion
object and prvalue.
2017-11-25 22:01:12 +01:00
dd91be668e Added deprecated functions test for real 2017-11-24 23:42:18 +01:00
b13c859ff1 Restored deprecated to_clock_time function.
Fixed the to_utc_time(const gps_time<Duration>& t) function
to correctly use clock_cast<utc_time>.

Added test to deprecated functions.
2017-11-24 23:37:32 +01:00
d4592bd497 Maked clock_time_conversion as const and used alias.
Marked operator() of all supplied clock_time_conversion overload
as const.
Changed input type to sys_time/utc_time alias when possible.
Used consitient nomencalture: st for sys_time, ut for utc_time
and tp for time in different clock.
2017-11-24 23:26:56 +01:00
9910f5fcc3 Moved clock_cast test to separate dir.
Fixed naqme of the to_sys_return_reference test, so it
is invoked.
2017-11-24 22:30:54 +01:00
58a4a9518a Mismatch in return type of converting function is now hard error.
Change the implementation, in the way that mismatch in return
type of the from/to_sys/utc functions (not returning time_point,
or returning time_point with inappropriate clock) leads to
hard error.

Added appropariate fail test for to_sys function, including:
* returning an int
* returning time_point of wrong clock
* reutrning reference to time_point
2017-11-24 22:23:50 +01:00
5a9b44a37a Implemented test for casting non-wall clocks and detecting that clocks cannot be casted.
Added test that detects if clock_cast<Dest>(Source) properly
SFINAEs if clock's are not castable, this includes test for
steady_clock that is not castable to any wall-clock.

Secondly added steady_based_clock based on steady_clock
(as name indicates), that clock_cast may be extended to clock
non-related to wall-time (sys/utc) using conversion traits.

Final example is pair of ambiguous clocks (amb1/amb2_clock) that can convert
to each other either using sys_clock or utc_clock.
Then the conversion from amb2 to amb1 is disambiguated
via trait specialization.
2017-11-24 18:08:00 +01:00
a9d2907fa1 Implemented test for custom clocks derived from sys_clock.
Created mil_clock and s2s_clock, that are both using to_sys/from_sys
function and are fully interoperable with existing clocks (including
ones based on utc).

Implemented an trait is used to provide direct conversion
from s2s_clock to mil_clock without converting to sys_clock
(conversion counter is used for this purpose).
2017-11-24 18:07:33 +01:00
bf5a4f3cd5 Implemented test for existing clocks.
Implemented test for conversions between existing clocks
(sys_clock, utc_clock, tai_clock, gps_clock) showing that
they can be used instead of to_clock_time functions.
2017-11-24 18:06:55 +01:00
e1099ef3ab Implemented clock_cast function.
Added an clock_time_conversion trait, that should be specialized
with SourceClock and DestClock respectively and provide an function
that convert time_point in SourceClock to equivalent time_point
in DestClock.

This function has following specializations:
1) <sys_clock, utc_clock> - convert sys_time to utc_time
2) <utc_clock, sys_clock> - convert utc_time to sys_time
3) <Clock, sys_clock>     - calls Clock::to_sys if it returns sys_time
4) <Clock, utc_clock>     - calls Clock::to_utc if it returns utc_time
5) <sys_clock, Clock>     - calls Clock::from_sys if it returns time_point<Clock>
5) <utc_clock, Clock>     - calls Clock::from_utc if it returns time_point<Clock>

Implemented an clock_cast<DestClock>(time_point<SourceClock, Dur> st), that
works as follow:
1) If DestClock is same as SourceClock, returns std
2) Otherwise, if clock_conversion<SourceClock, DestClock> available, uses it
3) Otherwise, if tries using clock_conversion<SourceClock, CommClock>
   and clock_conversion<CommClock, DestClock> for CommClock being utc_time
   or sys_time
4) Otherwise, tries using clock_conversion<SourceClock, utc_clock>
   and clock_conversion<sys_clock, DestinationClock> or reversed (firstly
   convert to sys_clock, then to utc_clock) to convert.
2017-11-24 17:55:15 +01:00
4614ebda4a Update to accomidate change of iana url #241 2017-11-23 20:25:00 -05:00
c286981b3b Fix improper application of GCC diagnostic suppression 2017-11-21 10:07:31 -05:00
9ca582d9da Silence GCC conversion warning for bitfields
* Used only in weekday_indexed.
2017-11-20 14:50:48 -05:00
f4b12ab023 Adjust HAS_CHRONO_ROUNDING macro for MSVC with v140_clang_c2 and LLVM-vs2014 2017-11-20 11:45:04 -05:00
bd51baf31e msvc token workaround 2017-11-19 18:52:56 -05:00
16439a8ce2 enhance tz_dir detection for buildroot with uclibc systems
* use /etc/TZ for timezone detection on buildroot with uclibc systems
2017-11-19 18:42:13 -05:00
d97bc984c7 Change default_zone from "UTC" to "Etc/UTC"
* zoned_traits<const time_zone*>::default_zone()
  should not depend on a Link, but on a Zone.
2017-11-19 18:17:09 -05:00
c9ef0a8f05 express reverse_bytes in an easy to optimize way
optimizes to single bswap instruction on gcc and clang
2017-11-19 17:52:17 -05:00
4832ea0ddb Move HAS_STRING_VIEW to date.h 2017-11-17 11:09:17 -05:00
ec514101a6 minor patches to build on Windows with MSVC v140_clang_c2 and LLVM-vs2014 2017-11-15 07:20:07 -05:00
517c0f2704 Update C++ standards proposal status 2017-11-12 06:27:50 -05:00
e12f7c66f0 Unconstrain make_zoned for VS-2015 2017-11-07 13:01:16 -07:00
3a5e8c9384 Silence clang warning 2017-10-30 13:48:09 -04:00
fa6529a2fc Allow zoned_time conversion among different TimeZonePtr types 2017-10-28 18:20:34 -04:00
4b73a42d02 Update README.md 2017-10-25 22:12:31 -04:00
25696b7fb3 Optimize to_stream for zoned_time
* Decrease the number of lookups in the database from 2 to 1.
2017-10-24 15:44:41 -04:00
9381e894a5 Specify exception constructors
* For nonexistent_local_time and ambiguous_local_time.
* Simplify the constructors.
* Make these exceptions usable for custom time zones.
2017-10-24 12:12:31 -04:00
5563d31b2e Update validation.cpp for new directory structure 2017-10-24 12:06:50 -04:00
202041e531 Add a workaround for a missing operator<< for gcc 5 on linux. See issue #205 for details. 2017-10-24 11:02:54 -04:00
0b7d9c6dbe Update README.md 2017-10-23 09:47:15 -04:00
66a97f907e to_stream sets failbit if required to supply a bad name:
* for an invalid month
* for an invalid weekday
2017-10-15 13:57:19 -04:00
94eb182256 Add wt to list of products using this library
Announced at CppCon-2017 by Roel Standaert.
2017-10-15 11:03:40 -04:00
f328d8c84a Have format set exceptions(failbit | badbit)
*  Be sure if something bad happens under the hood it is not silently
   swallowed.
2017-10-14 20:42:26 -04:00
8b9f0515b5 Replacing 0 with nullptr to avoid GCC warning
All the calls to `std::time_get::get` had `0` as end-of-range iterator.

E.g.

    auto& f = use_facet<time_get<CharT>>(is.getloc());
	// ...
    f.get(is, 0, is, err, &tm, command, fmt+1);
              ^

Using `nullptr` instead of `0` doesn't trigger the GCC 5.x warning:

> warning: zero as null pointer constant [-Wzero-as-null-pointer-constant]
2017-10-12 19:51:06 +02:00
ce975cadb0 Update Try-it-out link 2017-10-03 18:11:57 -04:00
2032fccbb7 Fix valgrind warning about uninitialized variable use in to_stream()
tm variable is not initialized in to_stream(), and valgrind warns about
"Conditional jump or move depends on uninitialised value(s)".

This is a false positive, as strftime always reads tm_hour, even if it
ends up never using it. To silence the warnings, initialize tm to zero.
2017-10-03 15:22:49 -04:00
2e213abb76 Silence clang static analyzer warnings 2017-10-03 12:45:09 -04:00
3acb299f3f Rename TZ_DB to tzdb
* Bring into alignment with proposal
* TZ_DB alias left behind for backwards compatibility
2017-09-30 14:48:25 -04:00
22a229af91 On macOS tz_dir is now discoverable at run time
* Apple changed the location of their IANA database in High Sierra,
  breaking current_zone().  Now on Apple the location of the IANA
  database is searched for at run time.
2017-09-26 09:47:24 -04:00
fc917fe303 Port testit to new directory structure 2017-09-25 19:28:04 -04:00
bff551b2a5 Convert Windows header file names to lower case 2017-09-23 19:55:56 -04:00
9f6c8d8c10 Update include paths for new directory structure 2017-09-22 20:10:58 -04:00
3c3ba68906 Proposal for an alternative directory structure 2017-09-22 20:03:16 -04:00
c5e58fd015 include <wordexp.h> is unnecessary when using USE_OS_TZDB 2017-09-10 13:50:51 -04:00
ef6d53595d Fix CHAR_BIT not found by including climits. 2017-09-09 19:05:54 -04:00
0f658db2cd Correct how width is counted for fractional seconds
* Include decimal point character in count.
2017-09-09 15:32:47 -04:00
481771ef5e Add support down to femtosecond precision
* Requires platform specific use of 128bit integral representation
  (e.g. std::chrono::duration<__int128_t, std::femto>).
2017-09-09 10:30:39 -04:00
5f01382e24 Enable current_zone() on FreeBSD 2017-09-08 20:47:38 -04:00
41563c46e8 Disabled some template constraints for vs since it does not work properly there 2017-09-07 19:41:47 -04:00
bba9aeafab Added shortened curl include path to support nuget package rmt_curl 2017-09-07 19:33:59 -04:00
4cb893c780 Enable DATE_BUILD_DLL for Linux and macOS 2017-09-05 20:07:13 -04:00
gm
c09d35534d Reformulate macros for static and shared linking on windows. 2017-09-05 19:59:57 -04:00
272d487b3d Add non-const front() to tzdb_list
* Needed when USE_OS_TZDB==1 && MISSING_LEAP_SECONDS==0
2017-08-31 17:45:42 -04:00
aad6010831 Add test for custom time zone support 2017-08-31 10:44:07 -04:00
gm
0707cc4932 NULL to nullptr change. 2017-08-27 21:13:47 -04:00
d3fcf00d55 Small exception safety improvement. 2017-08-27 21:13:47 -04:00
gm
2402a0bd25 undef somethings 2017-08-28 13:10:57 +12:00
873aa0515e Correct the fixes for VS-2017 that broke everyone else 2017-08-21 21:21:59 -04:00
49b50c43d9 Introduce the ONLY_C_LOCALE
*Eliminates dependance on on the time_get and time_put facets.
2017-08-18 22:56:03 -04:00
38c24b4090 Add fixes for VS-2017. 2017-08-18 22:55:31 -04:00
2515dfd1b5 Remove deleted weekday(int) constructor
* This was a failed experiment.
2017-08-15 13:44:16 -04:00
c3ab69ee0d Hard code year::min(), year::max() to [-32767, 32767]
* When year < 0, -year > 0 (always).
* These are the minimal limits C places on short.
* Reserving -32768 is useful in the implementation.
2017-08-15 12:42:27 -04:00
be871e6c85 Add is_clock 2017-08-13 22:57:36 -04:00
eced00fc1c Make tzdb_list::push_front private 2017-08-12 17:42:48 -04:00
859a50a70e Replace list<TZ_DB> with tzdb_list
* tzdb_list is a singly linked list with an atomic head
* push_front() and front() are thread safe.
2017-08-12 17:41:55 -04:00
80a142407a Add zoned_traits
* zoned_traits is SFINEA-friendly.

* zoned_traits is specialized on const time_zone*
  to call locate_zone.

* zoned_time accesses the database via zoned_traits<TimeZonePtr>.

* Clients with custom time zone databases can specialize zoned_traits.
2017-08-12 17:41:55 -04:00
d4d6eda861 Template zoned_time on TimeZonePtr
* TimeZonePtr defaults to const time_zone*.
* Adjust template deduction guides and make_zoned.
* Add HAS_STRING_VIEW
2017-08-12 17:41:55 -04:00
07ada69385 Expose a list<TZ_DB>
* Existing API always accesses the front of the list.
* Add locate_zone and current_zone member functions to TZ_DB.
* reload_tzdb() pushes a new database to the front of the list.
* get_tzdb_list() exposes the list<TZ_DB>&.
2017-08-12 17:41:55 -04:00
e7c3ca0e90 Fix compilation warnings for -DUSE_OS_TZDB.
tz.cpp:1617:24: error: unused parameter 't' [-Werror,-Wunused-parameter]
maybe_reverse_bytes(T& t, std::false_type)
                       ^
tz.cpp:2470:1: error: unused function 'get_version' [-Werror,-Wunused-function]
get_version()
^
tz.cpp: In lambda function:
tz.cpp:1922:84: error: declaration of ‘t’ shadows a previous local [-Werror=shadow]
                                        [](const sys_seconds& x, const transition& t)
                                                                                    ^
tz.cpp:1921:19: note: shadowed declaration is here
         for (auto t = std::upper_bound(transitions_.begin(), transitions_.end(), l,
                   ^
2017-08-12 16:59:23 -04:00
82de27d339 Fix bug in sys_time and local_time from_stream
* to handle microfortnights
2017-08-12 13:56:17 -04:00
922abf1299 Protect from Oracle sun macro 2017-08-06 00:30:27 -04:00
c4dcd5eb78 More Oracle fixes. 2017-08-05 20:27:15 -04:00
9c181a1440 Update tests for default constructible:
* weekday_indexed
* year_month_weekday
2017-08-05 16:31:18 -04:00
e2a38e600c Set constexpr rules to C++11 for Oracle 12.6 and earlier 2017-08-05 16:06:20 -04:00
4ae416f06a Add default constructor for weekday_indexed
* This enables the year_month_weekday default constructor.
2017-07-27 14:34:11 -04:00
5726b70bb7 Turn off constexpr when using clang + VS-2013 std::lib 2017-07-24 08:26:37 -04:00
e6b1e0fe58 Allow read_unsigned to read 0 digits
* Will allow reading "optional" integers.
2017-07-24 08:18:18 -04:00
170ebfd354 Another memory leaks fix 2017-07-22 12:00:52 -04:00
a5b77bb0fe add to copyright holder 2017-07-21 10:41:48 -04:00
5a53cb38a3 VS2013 doesn't accept exception specification for constructor default. Resolves #183 2017-07-21 10:41:48 -04:00
494fee4e1b User defined literals are not supported by VS2013, use explicit year constructor instead 2017-07-21 10:41:48 -04:00
a034eeed23 Have parse of %p set failbit if unsuccessful. 2017-07-19 15:26:29 -04:00
a1b19a2ed6 Silence some warnings. 2017-07-19 15:25:57 -04:00
496497d16e Fix memory leaks 2017-07-17 12:25:27 -04:00
a828109809 Suppress -Wunused-function warning 2017-07-13 09:49:38 -04:00
2129b813c9 Add www.safe.com to list of projects using date 2017-07-10 20:42:29 -04:00
c8d3cc14da Add zoned_time deduction guides 2017-07-06 20:49:53 -04:00
893cf51fd8 Add test for zoned_time 2017-07-06 20:49:28 -04:00
6067371127 Don't pass ill-formed strings to stold
* Avoid exceptions coming out of stold.
* from_stream should always set failbit instead of throw.
2017-07-06 11:54:55 -04:00
0c8b1f5967 Put expand_path back in when INSTALL is defined 2017-07-05 10:30:23 -04:00
16dd16e64e Silence warning on VS 2017-07-03 16:48:48 -04:00
ea0158c779 Move get_program_folder closer to where it is used 2017-07-03 16:45:11 -04:00
a0b8883763 Remove unused variables in load_timezone_mappings_from_xml_file 2017-07-03 16:43:01 -04:00
156bdf8bc6 Remove #ifdef in time_of_day
* Workaround for MSVC no longer needed.
2017-07-03 16:41:09 -04:00
2d1d8f2255 Put %Z back into default streaming for zoned_time
* It was removed by accident.
2017-07-01 23:00:34 -04:00
e0c962a8ce Silence unused variable warnings 2017-07-01 20:37:14 -04:00
7cbc4d8013 Also ignore leap-seconds.list
The tzdata package on Ubuntu includes /usr/share/zoneinfo/leap-
seconds.list. This is not a TZif file, so it should be ignored.
2017-06-29 10:07:35 -04:00
090b66beb8 Update README.md 2017-06-27 22:22:58 -04:00
791de2d9fc Update README.md 2017-06-27 22:22:09 -04:00
77a703afe2 Update to point to current standards proposal and R4 draft 2017-06-27 22:19:58 -04:00
bee4f27d4a Correct problems with load_timezone_mappings_from_xml_file
*  Move outside of HAS_REMOTE_API.

*  Ignore non-empty lines full of white space.
2017-06-26 22:03:01 -04:00
56cec17500 Add gratuitous zoned_time:: qualifier to operators
*  VS-2017 compatibility.
2017-06-26 21:33:34 -04:00
d359399090 Give zoned_time a default constructor 2017-06-26 21:19:29 -04:00
36 changed files with 6818 additions and 2580 deletions

194
.gitignore vendored Normal file
View File

@ -0,0 +1,194 @@
#ignore thumbnails created by windows
Thumbs.db
#Ignore files build by Visual Studio
*.obj
*.exe
*.pdb
*.user
*.aps
*.pch
*.vspscc
*_i.c
*_p.c
*.ncb
*.suo
*.tlb
*.tlh
*.bak
*.cache
*.ilk
*.log
[Bb]in
[Dd]ebug*/
*.lib
*.sbr
obj/
[Rr]elease*/
_ReSharper*/
[Tt]est[Rr]esult*
.idea/
*.opensdf
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific folders
*.sln.ide/
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Roslyn cache directories
*.ide/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
#NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# 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
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# If using the old MSBuild-Integrated Package Restore, uncomment this:
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
*.suo
*.vcxproj.filters
*.npp
CMakeFiles/*
nbproject/*
*.cd
*.cd
a.out
cmake-build-debug/*

164
CMakeLists.txt Normal file
View File

@ -0,0 +1,164 @@
cmake_minimum_required( VERSION 3.1.0 )
project( date_prj )
include( GNUInstallDirs )
find_package( Threads REQUIRED )
# Override by setting on CMake command line.
set( CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard whose features are requested.")
option( USE_SYSTEM_TZ_DB "Use the operating system's timezone database" OFF )
option( USE_TZ_DB_IN_DOT "Save the timezone database in the current folder" OFF )
option( BUILD_SHARED_LIBS "Build a shared version of library" OFF )
option( ENABLE_DATE_TESTING "Enable unit tests" ON )
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 "" )
mark_as_advanced(PRINT_OPTION_CURR_${OPT})
message( "# date: ${OPT} ${${OPT}}" )
endif( )
endfunction( )
print_option( USE_SYSTEM_TZ_DB )
print_option( USE_TZ_DB_IN_DOT )
print_option( BUILD_SHARED_LIBS )
print_option( ENABLE_DATE_TESTING )
set( HEADER_FOLDER "include" )
set( SOURCE_FOLDER "src" )
set( TEST_FOLDER "test" )
# This is needed so IDE's live MSVC show header files
set( HEADER_FILES
${HEADER_FOLDER}/date/chrono_io.h
${HEADER_FOLDER}/date/date.h
${HEADER_FOLDER}/date/ios.h
${HEADER_FOLDER}/date/islamic.h
${HEADER_FOLDER}/date/iso_week.h
${HEADER_FOLDER}/date/julian.h
${HEADER_FOLDER}/date/tz.h
${HEADER_FOLDER}/date/tz_private.h
)
add_library( tz ${HEADER_FILES} ${SOURCE_FOLDER}/tz.cpp )
if( USE_SYSTEM_TZ_DB )
target_compile_definitions( tz PRIVATE -DUSE_AUTOLOAD=0 )
target_compile_definitions( tz PRIVATE -DHAS_REMOTE_API=0 )
# cannot set USE_OS_TZDB to 1 on Windows
if( NOT WIN32 )
target_compile_definitions( tz PUBLIC -DUSE_OS_TZDB=1 )
endif( )
else( )
target_compile_definitions( tz PRIVATE -DUSE_AUTOLOAD=1 )
target_compile_definitions( tz PRIVATE -DHAS_REMOTE_API=1 )
target_compile_definitions( tz PUBLIC -DUSE_OS_TZDB=0 )
find_package( CURL REQUIRED )
include_directories( SYSTEM ${CURL_INCLUDE_DIRS} )
set( OPTIONAL_LIBRARIES ${CURL_LIBRARIES} )
endif( )
if( USE_TZ_DB_IN_DOT )
target_compile_definitions( tz PRIVATE -DINSTALL=. )
endif( )
target_link_libraries( tz ${CMAKE_THREAD_LIBS_INIT} ${OPTIONAL_LIBRARIES} )
# add include folders to the library and targets that consume it
target_include_directories(tz PUBLIC
$<BUILD_INTERFACE:
${CMAKE_CURRENT_SOURCE_DIR}/${HEADER_FOLDER}
>
$<INSTALL_INTERFACE:
include
>
)
add_library(date_interface INTERFACE) # an interface (not a library), to enable automatic include_directory (for when just date.h, but not "tz.h and its lib" are needed)
# add include folders to the INTERFACE and targets that consume it
target_include_directories(date_interface INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
$<INSTALL_INTERFACE:include>
)
if(WIN32 AND NOT CYGWIN)
set(DEF_INSTALL_CMAKE_DIR CMake)
else()
set(DEF_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/date)
endif()
install( TARGETS date_interface EXPORT dateConfig )
install( EXPORT dateConfig DESTINATION ${DEF_INSTALL_CMAKE_DIR} )
install( TARGETS tz
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) # This is for Windows
install( DIRECTORY ${HEADER_FOLDER}/ DESTINATION include/ )
if ( ENABLE_DATE_TESTING )
enable_testing( )
add_custom_target( testit COMMAND ${CMAKE_CTEST_COMMAND} )
add_dependencies( testit tz )
function( add_pass_tests TEST_GLOB TEST_PREFIX )
file( GLOB_RECURSE FILENAMES ${TEST_GLOB} )
include_directories( "${HEADER_FOLDER}/date" )
foreach( TEST_FILE ${FILENAMES} )
get_filename_component( TEST_NAME ${TEST_FILE} NAME_WE )
get_filename_component( TEST_EXT ${TEST_FILE} EXT )
if( NOT ${TEST_EXT} STREQUAL ".fail.cpp" )
set( PREFIX "${TEST_PREFIX}_pass_${TEST_NAME}" )
set( BIN_NAME ${PREFIX}_bin )
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 )
add_dependencies( testit ${BIN_NAME} )
endif( )
endforeach( )
endfunction( )
function( add_fail_tests TEST_GLOB TEST_PREFIX )
file( GLOB_RECURSE FILENAMES ${TEST_GLOB} )
foreach( TEST_FILE ${FILENAMES} )
get_filename_component( TEST_NAME ${TEST_FILE} NAME_WE )
get_filename_component( TEST_EXT ${TEST_FILE} EXT )
set( TEST_TYPE "_fail" )
set( PREFIX "${TEST_PREFIX}_fail_${TEST_NAME}" )
set( BIN_NAME ${PREFIX}_bin )
set( TST_NAME ${PREFIX}_test )
#target_compile_definitions( ${BIN_NAME} PRIVATE ${TST_NAME} )
set( TEST_BIN_NAME ${CMAKE_BINARY_DIR}/${BIN_NAME} )
add_custom_target( ${BIN_NAME}
COMMAND ${PROJECT_SOURCE_DIR}/compile_fail.sh ${TEST_BIN_NAME} ${CMAKE_CXX_COMPILER} -std=c++14 -L${CMAKE_BINARY_DIR}/ -ltz -I${PROJECT_SOURCE_DIR}/${HEADER_FOLDER}/date -o ${BIN_NAME} ${TEST_FILE}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT ${TST_NAME}
)
add_test( ${TST_NAME} "${PROJECT_SOURCE_DIR}/test_fail.sh" ${CMAKE_BINARY_DIR}/${BIN_NAME} WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/" )
#set_tests_properties( ${TST_NAME} PROPERTIES WILL_FAIL TRUE)
add_dependencies( testit ${BIN_NAME} )
endforeach( )
endfunction( )
file( GLOB children RELATIVE "${PROJECT_SOURCE_DIR}/${TEST_FOLDER}" "${PROJECT_SOURCE_DIR}/${TEST_FOLDER}/*" )
foreach( child ${children} )
if( IS_DIRECTORY "${PROJECT_SOURCE_DIR}/${TEST_FOLDER}/${child}" )
set( CUR_FOLDER "${PROJECT_SOURCE_DIR}/${TEST_FOLDER}/${child}" )
add_pass_tests( "${CUR_FOLDER}/*.cpp" ${child} )
if( NOT WIN32 )
add_fail_tests( "${CUR_FOLDER}/*.fail.cpp" ${child} )
endif( )
endif( )
endforeach( )
endif( )

View File

@ -1,39 +1,58 @@
# Date
[![Join the chat at https://gitter.im/HowardHinnant/date](https://badges.gitter.im/HowardHinnant/date.svg)](https://gitter.im/HowardHinnant/date?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<hr/>
**[Try it out on wandbox!](https://wandbox.org/permlink/n6NEh9nFCWLVgm1Q)**
---
This is actually several separate C++11/C++14 libraries:
**[Try it out on wandbox!](https://wandbox.org/permlink/vqwMyTphHJv5iXX7)**
1. `"date.h"` is a header-only library which builds upon `<chrono>`. It adds some new `duration` types, and new `time_point` types. It also adds "field" types such as `year_month_day` which is a struct `{year, month, day}`. And it provides convenient means to convert between the "field" types and the `time_point` types.
## Summary
This is actually several separate C++11/C++14/C++17 libraries:
1. `"date.h"` is a header-only library which builds upon `<chrono>`. It adds some new `duration` types, and new `time_point` types. It also adds "field" types such as `year_month_day` which is a struct `{year, month, day}`. And it provides convenient means to convert between the "field" types and the `time_point` types.
* Documentation: http://howardhinnant.github.io/date/date.html
* Video: https://www.youtube.com/watch?v=tzyGjOm8AKo
* Slides: http://schd.ws/hosted_files/cppcon2015/43/hinnant_dates.pdf
2. `"tz.h"` / `"tz.cpp"` are a timezone library built on top of the `"date.h"` library. This timezone library is a complete parser of the IANA timezone database. It provides for an easy way to access all of the data in this database, using the types from `"date.h"` and `<chrono>`. The IANA database also includes data on leap seconds, and this library provides utilities to compute with that information as well.
1. `"tz.h"` / `"tz.cpp"` are a timezone library built on top of the `"date.h"` library. This timezone library is a complete parser of the IANA timezone database. It provides for an easy way to access all of the data in this database, using the types from `"date.h"` and `<chrono>`. The IANA database also includes data on leap seconds, and this library provides utilities to compute with that information as well.
* Documentation: http://howardhinnant.github.io/date/tz.html
* Video: https://www.youtube.com/watch?v=Vwd3pduVGKY
* Slides: http://schd.ws/hosted_files/cppcon2016/0f/Welcome%20To%20The%20Time%20Zone%20-%20Howard%20Hinnant%20-%20CppCon%202016.pdf
3. `"iso_week.h"` is a header-only library built on top of the `"date.h"` library which implements the ISO week date calendar.
1. `"iso_week.h"` is a header-only library built on top of the `"date.h"` library which implements the ISO week date calendar.
* Documentation: http://howardhinnant.github.io/date/iso_week.html
4. `"julian.h"` is a header-only library built on top of the `"date.h"` library which implements a proleptic Julian calendar which is fully interoperable with everything above.
1. `"julian.h"` is a header-only library built on top of the `"date.h"` library which implements a proleptic Julian calendar which is fully interoperable with everything above.
* Documentation: http://howardhinnant.github.io/date/julian.html
5. `"islamic.h"` is a header-only library built on top of the `"date.h"` library which implements a proleptic Islamic calendar which is fully interoperable with everything above.
1. `"islamic.h"` is a header-only library built on top of the `"date.h"` library which implements a proleptic Islamic calendar which is fully interoperable with everything above.
* Documentation: http://howardhinnant.github.io/date/islamic.html
`"date.h"` and `"tz.h"` are now being proposed for standardization: http://howardhinnant.github.io/date/d0355r3.html
## Standardization
<hr/>
Slightly modified versions of `"date.h"` and `"tz.h"` were voted into the C++20 working draft at the Jacksonville FL meeting on 2018-03-17:
**Projects that use this project**
* http://howardhinnant.github.io/date/d0355r7.html
## Build & Test
If you would like to see your project listed here, and it isn't, please let me know. If your project is listed here and it shouldn't be, please let me know.
You will need [CMake](https://cmake.org/) and a recent C++ compiler. Here follows a guide of how to build and test using the CMake Makefile generator.
```bash
mkdir build
cd build
cmake ../
cmake --build . --target testit # Consider '-- -j4' for multithreading
```
## Projects using this library
* www.safe.com
* www.webtoolkit.eu/wt
If you would like your project (or product) on this list, just let me know.

16
compile_fail.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
export TEST_BIN_NAME=$1
#echo "Building ${TEST_BIN_NAME}"
shift 1
export BUILD_COMMAND=$@
#echo "Build command: ${BUILD_COMMAND}"
eval ${BUILD_COMMAND} >/dev/null 2>/dev/null
if [ $? -eq 0 ]; then
echo -ne "#!/bin/bash\nexit 1;" > ${TEST_BIN_NAME}
else
echo -ne "#!/bin/bash\nexit 0;" > ${TEST_BIN_NAME}
fi
chmod u+x ${TEST_BIN_NAME}
exit 0;

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@
{
std::string get_tzdata_path();
std::string get_current_timezone();
} // namespace iOSUtils
} // namespace date

View File

@ -1314,7 +1314,7 @@ inline
year_lastweek_weekday::operator sys_days() const NOEXCEPT
{
return sys_days(date::year{static_cast<int>(y_)}/date::dec/date::thu[date::last])
+ (mon - thu) - (mon - wd_);
+ (sun - thu) - (sun - wd_);
}
CONSTCD14
@ -1322,7 +1322,7 @@ inline
year_lastweek_weekday::operator local_days() const NOEXCEPT
{
return local_days(date::year{static_cast<int>(y_)}/date::dec/date::thu[date::last])
+ (mon - thu) - (mon - wd_);
+ (sun - thu) - (sun - wd_);
}
CONSTCD11

592
include/date/ptz.h Normal file
View File

@ -0,0 +1,592 @@
#ifndef PTZ_H
#define PTZ_H
// The MIT License (MIT)
//
// Copyright (c) 2017 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.
// This header allows Posix-style time zones as specified for TZ here:
// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
//
// Posix::time_zone can be constructed with a posix-style string and then used in
// a zoned_time like so:
//
// zoned_time<system_clock::duration, Posix::time_zone> zt{"EST5EDT,M3.2.0,M11.1.0",
// system_clock::now()};
// or:
//
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()};
//
// 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 <cctype>
#include <ostream>
#include <string>
namespace Posix
{
namespace detail
{
#if HAS_STRING_VIEW
using string_t = std::string_view;
#else // !HAS_STRING_VIEW
using string_t = std::string;
#endif // !HAS_STRING_VIEW
class rule;
void throw_invalid(const string_t& s, unsigned i, const string_t& message);
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);
class rule
{
enum {off, J, M, N};
date::month m_;
date::weekday wd_;
unsigned short n_ : 14;
unsigned short mode_ : 2;
std::chrono::duration<std::int32_t> time_ = std::chrono::hours{2};
public:
rule() : mode_(off) {}
bool ok() const {return mode_ != off;}
date::local_seconds operator()(date::year y) const;
friend std::ostream& operator<<(std::ostream& os, const rule& r);
friend unsigned read_date(const string_t& s, unsigned i, rule& r);
};
inline
date::local_seconds
rule::operator()(date::year y) const
{
using namespace date;
using sec = std::chrono::seconds;
date::local_seconds t;
switch (mode_)
{
case J:
t = local_days{y/jan/0} + days{n_ + (y.is_leap() && n_ > 59)} + sec{time_};
break;
case M:
t = (n_ == 5 ? local_days{y/m_/wd_[last]} : local_days{y/m_/wd_[n_]}) + sec{time_};
break;
case N:
t = local_days{y/jan/1} + days{n_} + sec{time_};
break;
default:
assert(!"rule called with bad mode");
}
return t;
}
inline
std::ostream&
operator<<(std::ostream& os, const rule& r)
{
switch (r.mode_)
{
case rule::J:
os << 'J' << r.n_ << date::format(" %T", r.time_);
break;
case rule::M:
if (r.n_ == 5)
os << r.m_/r.wd_[date::last];
else
os << r.m_/r.wd_[r.n_];
os << date::format(" %T", r.time_);
break;
case rule::N:
os << r.n_ << date::format(" %T", r.time_);
break;
default:
break;
}
return os;
}
} // namespace detail
class time_zone
{
std::string std_abbrev_;
std::string dst_abbrev_ = {};
std::chrono::seconds offset_;
std::chrono::seconds save_ = std::chrono::hours{1};
detail::rule start_rule_;
detail::rule end_rule_;
public:
explicit time_zone(const detail::string_t& name);
template <class Duration>
date::sys_info get_info(date::sys_time<Duration> st) const;
template <class Duration>
date::local_info get_info(date::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;
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;
template <class Duration>
date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_local(date::sys_time<Duration> tp) const;
friend std::ostream& operator<<(std::ostream& os, const time_zone& z);
const time_zone* operator->() const {return this;}
};
inline
time_zone::time_zone(const detail::string_t& s)
{
using namespace detail;
auto i = read_name(s, 0, std_abbrev_);
i = read_signed_time(s, i, offset_);
offset_ = -offset_;
if (i != s.size())
{
i = read_name(s, i, dst_abbrev_);
if (i != s.size())
{
if (s[i] != ',')
i = read_signed_time(s, i, save_);
if (i != s.size())
{
if (s[i] != ',')
throw_invalid(s, i, "Expecting end of string or ',' to start rule");
++i;
i = read_date(s, i, start_rule_);
if (i == s.size() || s[i] != ',')
throw_invalid(s, i, "Expecting ',' and then the ending rule");
++i;
i = read_date(s, i, end_rule_);
if (i != s.size())
throw_invalid(s, i, "Found unexpected trailing characters");
}
}
}
}
template <class Duration>
date::sys_info
time_zone::get_info(date::sys_time<Duration> st) const
{
using namespace date;
using namespace std::chrono;
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)
{
r.begin = start;
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else if (st < start)
{
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_;
}
}
else // constant offset
{
r.begin = sys_days{year::min()/jan/1};
r.end = sys_days{year::max()/dec/last};
r.abbrev = std_abbrev_;
}
return r;
}
template <class Duration>
date::local_info
time_zone::get_info(date::local_time<Duration> tp) const
{
using namespace date;
using namespace std::chrono;
local_info r{};
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 utcs = sys_seconds{floor<seconds>(tp - offset_).time_since_epoch()};
auto utcd = sys_seconds{floor<seconds>(tp - (offset_ + save_)).time_since_epoch()};
if ((utcs < start) != (utcd < 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_;
r.second.begin = start;
r.second.end = end;
r.second.abbrev = dst_abbrev_;
r.second.offset = offset_ + save_;
r.second.save = ceil<minutes>(save_);
r.result = save_ > seconds{0} ? local_info::nonexistent
: local_info::ambiguous;
}
else if ((utcs < end) != (utcd < 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_;
r.second.begin = end;
r.second.end = sys_seconds{(start_rule_(y+years{1}) -
offset_).time_since_epoch()};
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()/jan/1};
r.first.end = sys_days{year::max()/dec/last};
r.first.abbrev = std_abbrev_;
r.first.offset = 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
{
using namespace date;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
throw nonexistent_local_time(tp, i);
else if (i.result == local_info::ambiguous)
throw ambiguous_local_time(tp, i);
return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset;
}
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
{
using namespace date;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
{
return i.first.end;
}
else if (i.result == local_info::ambiguous)
{
if (z == choose::latest)
return sys_time<Duration>{tp.time_since_epoch()} - i.second.offset;
}
return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset;
}
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
{
using namespace date;
using namespace std::chrono;
using LT = local_time<typename std::common_type<Duration, seconds>::type>;
auto i = get_info(tp);
return LT{(tp + i.offset).time_since_epoch()};
}
inline
std::ostream&
operator<<(std::ostream& os, const time_zone& z)
{
using date::operator<<;
os << '{';
os << z.std_abbrev_ << ", " << z.dst_abbrev_ << date::format(", %T, ", z.offset_)
<< date::format("%T, [", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}";
return os;
}
namespace detail
{
inline
void
throw_invalid(const string_t& s, unsigned i, const string_t& message)
{
throw std::runtime_error(std::string("Invalid time_zone initializer.\n") +
std::string(message) + ":\n" +
s + '\n' +
"\x1b[1;32m" +
std::string(i, '~') + '^' +
std::string(s.size()-i-1, '~') +
"\x1b[0m");
}
inline
unsigned
read_date(const string_t& s, unsigned i, rule& r)
{
using namespace date;
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);
r.mode_ = rule::J;
r.n_ = n;
}
else if (s[i] == 'M')
{
++i;
unsigned m;
i = read_unsigned(s, i, 2, m);
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after month");
++i;
unsigned n;
i = read_unsigned(s, i, 1, n);
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after weekday index");
++i;
unsigned wd;
i = read_unsigned(s, i, 1, wd);
r.mode_ = rule::M;
r.m_ = month{m};
r.wd_ = weekday{wd};
r.n_ = n;
}
else if (std::isdigit(s[i]))
{
unsigned n;
i = read_unsigned(s, i, 3, n);
r.mode_ = rule::N;
r.n_ = n;
}
else
throw_invalid(s, i, "Expected 'J', 'M', or a digit to start rule");
if (i != s.size() && s[i] == '/')
{
++i;
std::chrono::seconds t;
i = read_unsigned_time(s, i, t);
r.time_ = t;
}
return i;
}
inline
unsigned
read_name(const string_t& s, unsigned i, std::string& name)
{
if (i == s.size())
throw_invalid(s, i, "Expected a name but found end of string");
if (s[i] == '<')
{
++i;
while (true)
{
if (i == s.size())
throw_invalid(s, i,
"Expected to find closing '>', but found end of string");
if (s[i] == '>')
break;
name.push_back(s[i]);
++i;
}
++i;
}
else
{
while (i != s.size() && std::isalpha(s[i]))
{
name.push_back(s[i]);
++i;
}
}
if (name.size() < 3)
throw_invalid(s, i, "Found name to be shorter than 3 characters");
return i;
}
inline
unsigned
read_signed_time(const string_t& s, unsigned i,
std::chrono::seconds& t)
{
if (i == s.size())
throw_invalid(s, i, "Expected to read signed time, but found end of string");
bool negative = false;
if (s[i] == '-')
{
negative = true;
++i;
}
else if (s[i] == '+')
++i;
i = read_unsigned_time(s, i, t);
if (negative)
t = -t;
return i;
}
inline
unsigned
read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
{
using namespace std::chrono;
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);
t = hours{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x);
t += minutes{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x);
t += seconds{x};
}
}
return i;
}
inline
unsigned
read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u)
{
if (i == s.size() || !std::isdigit(s[i]))
throw_invalid(s, i, "Expected to find a decimal digit");
u = static_cast<unsigned>(s[i] - '0');
unsigned count = 1;
for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count)
u = u * 10 + static_cast<unsigned>(s[i] - '0');
return i;
}
} // namespace detail
} // namespace Posix
namespace date
{
template <>
struct zoned_traits<Posix::time_zone>
{
#if HAS_STRING_VIEW
static
Posix::time_zone
locate_zone(std::string_view name)
{
return Posix::time_zone{name};
}
#else // !HAS_STRING_VIEW
static
Posix::time_zone
locate_zone(const std::string& name)
{
return Posix::time_zone{name};
}
static
Posix::time_zone
locate_zone(const char* name)
{
return Posix::time_zone{name};
}
#endif // !HAS_STRING_VIEW
};
} // namespace date
#endif // PTZ_H

2575
include/date/tz.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -291,6 +291,7 @@ struct transition
{
using namespace date;
using namespace std::chrono;
using date::operator<<;
os << t.timepoint << "Z ";
if (t.info->offset >= seconds{0})
os << '+';

405
ios.mm
View File

@ -1,405 +0,0 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Alexander Kormanovsky
//
// 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 "ios.h"
#if TARGET_OS_IPHONE
#include <Foundation/Foundation.h>
#include <iostream>
#include <zlib.h>
#include <sys/stat.h>
#ifndef TAR_DEBUG
# define TAR_DEBUG 0
#endif
#define INTERNAL_DIR "Library/tzdata"
#define TARGZ_EXTENSION "tar.gz"
#define TAR_BLOCK_SIZE 512
#define TAR_TYPE_POSITION 156
#define TAR_NAME_POSITION 0
#define TAR_NAME_SIZE 100
#define TAR_SIZE_POSITION 124
#define TAR_SIZE_SIZE 12
namespace date
{
namespace iOSUtils
{
struct TarInfo
{
char objType;
std::string objName;
int64_t realContentSize; // writable size without padding zeroes
int64_t blocksContentSize; // adjusted size to 512 bytes blocks
bool success;
};
char* convertCFStringRefPathToCStringPath(CFStringRef ref);
bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath);
TarInfo getTarObjectInfo(CFReadStreamRef readStream, int64_t location);
std::string getTarObject(CFReadStreamRef readStream, int64_t size);
bool writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data,
int64_t realContentSize);
std::string
date::iOSUtils::get_tzdata_path()
{
CFURLRef ref = CFCopyHomeDirectoryURL();
CFStringRef homePath = CFURLCopyPath(CFCopyHomeDirectoryURL());
std::string tzdata_path(std::string(convertCFStringRefPathToCStringPath(homePath)) +
INTERNAL_DIR);
if (access(tzdata_path.c_str(), F_OK) == 0)
{
#if TAR_DEBUG
printf("tzdata exists\n");
#endif
return tzdata_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(CFCopyHomeDirectoryURL(), archiveUrl, tzdata_path);
}
return tzdata_path;
}
char*
convertCFStringRefPathToCStringPath(CFStringRef ref)
{
CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref);
char *buffer = new char[bufferSize];
CFStringGetFileSystemRepresentation(ref, buffer, bufferSize);
return buffer;
}
bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath)
{
const char *TAR_TMP_PATH = "/tmp.tar";
// create Library path
CFStringRef libraryStr = CFStringCreateWithCString(NULL, "Library",
CFStringGetSystemEncoding());
CFURLRef libraryUrl = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
homeUrl, libraryStr,
false);
// create tzdata path
CFStringRef tzdataPathRef = CFStringCreateWithCString(NULL, INTERNAL_DIR,
CFStringGetSystemEncoding());
CFURLRef tzdataPathUrl = CFURLCreateCopyAppendingPathComponent(NULL, homeUrl,
tzdataPathRef, false);
// create src archive path
CFStringRef archivePath = CFURLCopyPath(archiveUrl);
gzFile tarFile = gzopen(convertCFStringRefPathToCStringPath(archivePath), "rb");
// create tar unpacking path
CFStringRef tarName = CFStringCreateWithCString(NULL, TAR_TMP_PATH,
CFStringGetSystemEncoding());
CFURLRef tarUrl = CFURLCreateCopyAppendingPathComponent(NULL, libraryUrl, tarName,
false);
const char *tarPath = convertCFStringRefPathToCStringPath(CFURLCopyPath(tarUrl));
// create tzdata directory
mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
// create stream
CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, tarUrl);
bool success = true;
if (!CFWriteStreamOpen(writeStream))
{
CFStreamError err = CFWriteStreamGetError(writeStream);
if (err.domain == kCFStreamErrorDomainPOSIX)
{
printf("kCFStreamErrorDomainPOSIX %i\n", err.error);
}
else if(err.domain == kCFStreamErrorDomainMacOSStatus)
{
printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error);
}
success = false;
}
if (!success)
{
remove(tarPath);
return false;
}
// ======= extract tar ========
unsigned int bufferLength = 1024 * 256; // 256Kb
void *buffer = malloc(bufferLength);
while (true)
{
int readBytes = gzread(tarFile, buffer, bufferLength);
if (readBytes > 0)
{
CFIndex writtenBytes = CFWriteStreamWrite(writeStream, (unsigned char*)buffer,
readBytes);
if (writtenBytes < 0)
{
CFStreamError err = CFWriteStreamGetError(writeStream);
printf("write stream error %i\n", err.error);
success = false;
break;
}
}
else if (readBytes == 0)
{
break;
}
else if (readBytes == -1)
{
printf("decompression failed\n");
success = false;
break;
}
else
{
printf("unexpected zlib state\n");
success = false;
break;
}
}
CFWriteStreamClose(writeStream);
CFRelease(writeStream);
free(buffer);
gzclose(tarFile);
if (!success)
{
remove(tarPath);
return false;
}
// ======== extract files =========
uint64_t location = 0; // Position in the file
// get file size
struct stat stat_buf;
int res = stat(tarPath, &stat_buf);
if (res != 0)
{
printf("error file size\n");
remove(tarPath);
return false;
}
int64_t tarSize = stat_buf.st_size;
// create read stream
CFReadStreamRef readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, tarUrl);
if (!CFReadStreamOpen(readStream))
{
CFStreamError err = CFReadStreamGetError(readStream);
if (err.domain == kCFStreamErrorDomainPOSIX)
{
printf("kCFStreamErrorDomainPOSIX %i", err.error);
}
else if(err.domain == kCFStreamErrorDomainMacOSStatus)
{
printf("kCFStreamErrorDomainMacOSStatus %i", err.error);
}
success = false;
}
if (!success)
{
CFRelease(readStream);
remove(tarPath);
return false;
}
int count = 0;
long size = 0;
// process files
while (location < tarSize)
{
TarInfo info = getTarObjectInfo(readStream, location);
if (!info.success || info.realContentSize == 0)
{
break; // something wrong or all files are read
}
switch (info.objType)
{
case '0': // file
case '\0': //
{
std::string obj = getTarObject(readStream, info.blocksContentSize);
#if TAR_DEBUG
size += info.realContentSize;
printf("#%i %s file size %lld written total %ld from %lld\n", ++count,
info.objName.c_str(), info.realContentSize, size, tarSize);
#endif
writeFile(tzdataPathUrl, info.objName, obj, info.realContentSize);
location += info.blocksContentSize;
break;
}
}
}
CFReadStreamClose(readStream);
CFRelease(readStream);
remove(tarPath);
return true;
}
TarInfo
getTarObjectInfo(CFReadStreamRef readStream, int64_t location)
{
int64_t length = TAR_BLOCK_SIZE;
uint8_t buffer[length];
char type;
char name[TAR_NAME_SIZE + 1];
char sizeBuf[TAR_SIZE_SIZE + 1];
CFIndex bytesRead;
bool avail = CFReadStreamHasBytesAvailable(readStream);
bytesRead = CFReadStreamRead(readStream, buffer, length);
if (bytesRead < 0)
{
CFStreamError err = CFReadStreamGetError(readStream);
printf("error reading tar object info %i", err.error);
return {false};
}
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);
int64_t realSize = strtol(sizeBuf, NULL, 8);
int64_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE));
return {type, std::string(name), realSize, blocksSize, true};
}
std::string
getTarObject(CFReadStreamRef readStream, int64_t size)
{
uint8_t buffer[size];
CFIndex bytesRead = CFReadStreamRead(readStream, buffer, size);
if (bytesRead < 0)
{
CFStreamError err = CFReadStreamGetError(readStream);
printf("error reading tar object info %i", err.error);
}
return std::string((char *)buffer);
}
bool
writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data,
int64_t realContentSize)
{
// create stream
CFStringRef fileNameRef = CFStringCreateWithCString(NULL, fileName.c_str(),
CFStringGetSystemEncoding());
CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, tzdataUrl, fileNameRef,
false);
CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, url);
// open stream
if (!CFWriteStreamOpen(writeStream))
{
CFStreamError err = CFWriteStreamGetError(writeStream);
if (err.domain == kCFStreamErrorDomainPOSIX)
{
printf("kCFStreamErrorDomainPOSIX %i\n", err.error);
}
else if(err.domain == kCFStreamErrorDomainMacOSStatus)
{
printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error);
}
CFRelease(writeStream);
return false;
}
// trim empty space
uint8_t trimmedData[realContentSize + 1];
memset(&trimmedData, '\0', realContentSize);
memcpy(&trimmedData, data.c_str(), realContentSize);
// write
CFIndex writtenBytes = CFWriteStreamWrite(writeStream, trimmedData, realContentSize);
if (writtenBytes < 0)
{
CFStreamError err = CFWriteStreamGetError(writeStream);
printf("write stream error %i\n", err.error);
}
CFWriteStreamClose(writeStream);
CFRelease(writeStream);
writeStream = NULL;
return true;
}
} // namespace iOSUtils
} // namespace date
#endif // TARGET_OS_IPHONE

337
src/ios.mm Normal file
View File

@ -0,0 +1,337 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Alexander Kormanovsky
//
// 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 "ios.h"
#if TARGET_OS_IPHONE
#include <Foundation/Foundation.h>
#include <fstream>
#include <zlib.h>
#include <sys/stat.h>
#ifndef TAR_DEBUG
# define TAR_DEBUG 0
#endif
#define INTERNAL_DIR "Library"
#define TZDATA_DIR "tzdata"
#define TARGZ_EXTENSION "tar.gz"
#define TAR_BLOCK_SIZE 512
#define TAR_TYPE_POSITION 156
#define TAR_NAME_POSITION 0
#define TAR_NAME_SIZE 100
#define TAR_SIZE_POSITION 124
#define TAR_SIZE_SIZE 12
namespace date
{
namespace iOSUtils
{
struct TarInfo
{
char objType;
std::string objName;
size_t realContentSize; // writable size without padding zeroes
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()
{
CFTimeZoneRef tzRef = CFTimeZoneCopySystem();
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()
{
CFURLRef homeUrlRef = CFCopyHomeDirectoryURL();
CFStringRef homePath = CFURLCopyPath(homeUrlRef);
std::string path(std::string(convertCFStringRefPathToCStringPath(homePath)) +
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
printf("tzdata dir exists\n");
#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)
{
CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref);
char *buffer = new char[bufferSize];
CFStringGetFileSystemRepresentation(ref, buffer, bufferSize);
auto result = std::string(buffer);
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);
}
else
if (readBytes == 0)
{
break;
}
else
if (readBytes == -1)
{
printf("decompression failed\n");
success = false;
break;
}
else
{
printf("unexpected zlib state\n");
success = false;
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);
if (res != 0)
{
printf("error file size\n");
remove(tarPath.c_str());
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
case '\0': //
{
std::string obj = getTarObject(is, info.blocksContentSize);
#if TAR_DEBUG
size += info.realContentSize;
printf("#%i %s file size %lld written total %ld from %lld\n", ++count,
info.objName.c_str(), info.realContentSize, size, tarSize);
#endif
writeFile(tzdataPath, info.objName, obj, info.realContentSize);
location += info.blocksContentSize;
break;
}
}
}
remove(tarPath.c_str());
return true;
}
TarInfo
getTarObjectInfo(std::ifstream &readStream)
{
int64_t length = TAR_BLOCK_SIZE;
char buffer[length];
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)
{
char buffer[size];
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
#endif // TARGET_OS_IPHONE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,187 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Tomasz Kamiński
//
// 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 <cassert>
#include <type_traits>
//used to count number of conversion
int conversions = 0;
//to/from impl
struct mil_clock
{
using duration = std::common_type_t<std::chrono::system_clock::duration, date::days>;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<mil_clock, duration>;
static constexpr date::sys_days epoch = date::year{2000}/date::month{0}/date::day{1};
template<typename Duration>
static
std::chrono::time_point<std::chrono::system_clock, std::common_type_t<Duration, date::days>>
to_sys(std::chrono::time_point<mil_clock, Duration> const& tp)
{
++conversions;
return epoch + tp.time_since_epoch();
}
template<typename Duration>
static
std::chrono::time_point<mil_clock, std::common_type_t<Duration, date::days>>
from_sys(std::chrono::time_point<std::chrono::system_clock, Duration> const& tp)
{
++conversions;
using res = std::chrono::time_point<mil_clock, std::common_type_t<Duration, date::days>>;
return res(tp - epoch);
}
static time_point now()
{
return from_sys(std::chrono::system_clock::now());
}
};
date::sys_days const mil_clock::epoch;
// traits example
struct s2s_clock
{
using duration = std::chrono::system_clock::duration;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<s2s_clock, duration>;
template<typename Duration>
static
std::chrono::time_point<std::chrono::system_clock, Duration>
to_sys(std::chrono::time_point<s2s_clock, Duration> const& tp)
{
++conversions;
return std::chrono::time_point<std::chrono::system_clock, Duration>(tp.time_since_epoch());
}
template<typename Duration>
static
std::chrono::time_point<s2s_clock, Duration>
from_sys(std::chrono::time_point<std::chrono::system_clock, Duration> const& tp)
{
++conversions;
return std::chrono::time_point<s2s_clock, Duration>(tp.time_since_epoch());
}
static time_point now()
{
return from_sys(std::chrono::system_clock::now());
}
};
namespace date
{
template<>
struct clock_time_conversion<mil_clock, s2s_clock>
{
template<typename Duration>
std::chrono::time_point<mil_clock, std::common_type_t<Duration, date::days>>
operator()(std::chrono::time_point<s2s_clock, Duration> const& tp)
{
++conversions;
using res = std::chrono::time_point<mil_clock, std::common_type_t<Duration, date::days>>;
return res(tp.time_since_epoch() - mil_clock::epoch.time_since_epoch());
}
};
}
int
main()
{
using namespace date;
using sys_clock = std::chrono::system_clock;
// self
{
sys_days st(1997_y/dec/12);
auto mt = mil_clock::from_sys(st);
assert(clock_cast<mil_clock>(mt) == mt);
}
// mil <-> sys
{
sys_days st(1997_y/dec/12);
auto mt = mil_clock::from_sys(st);
assert(clock_cast<mil_clock>(st) == mt);
assert(clock_cast<sys_clock>(mt) == st);
}
// mil <-> utc
{
sys_days st(1997_y/dec/12);
auto mt = mil_clock::from_sys(st);
auto ut = utc_clock::from_sys(st);
assert(clock_cast<mil_clock>(ut) == mt);
assert(clock_cast<utc_clock>(mt) == ut);
}
// mil <-> tai
{
sys_days st(1997_y/dec/12);
auto mt = mil_clock::from_sys(st);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
assert(clock_cast<tai_clock>(mt) == tt);
assert(clock_cast<mil_clock>(tt) == mt);
}
// mil <-> gps
{
sys_days st(1997_y/dec/12);
auto mt = mil_clock::from_sys(st);
auto ut = utc_clock::from_sys(st);
auto gt = gps_clock::from_utc(ut);
assert(clock_cast<gps_clock>(mt) == gt);
assert(clock_cast<mil_clock>(gt) == mt);
}
// s2s -> mil
{
sys_days st(1997_y/dec/12);
auto mt = mil_clock::from_sys(st);
auto s2t = s2s_clock::from_sys(st);
//direct trait conversion
conversions = 0;
assert(clock_cast<mil_clock>(s2t) == mt);
assert(conversions == 1);
//uses sys_clock
conversions = 0;
assert(clock_cast<s2s_clock>(mt) == s2t);
assert(conversions == 2);
}
}

View File

@ -0,0 +1,90 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Tomasz Kamiński
//
// 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 <cassert>
int
main()
{
using namespace date;
// sys <-> utc
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
assert(to_utc_time(st) == ut);
assert(to_sys_time(ut) == st);
}
// tai <-> utc
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
assert(to_tai_time(ut) == tt);
assert(to_utc_time(tt) == ut);
}
// tai <-> sys
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
assert(to_tai_time(st) == tt);
assert(to_sys_time(tt) == st);
}
// gps <-> utc
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto gt = gps_clock::from_utc(ut);
assert(to_gps_time(ut) == gt);
assert(to_utc_time(gt) == ut);
}
// gps <-> sys
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto gt = gps_clock::from_utc(ut);
assert(to_gps_time(st) == gt);
assert(to_sys_time(gt) == st);
}
// tai <-> gps
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
auto gt = gps_clock::from_utc(ut);
assert(to_gps_time(tt) == gt);
assert(to_tai_time(gt) == tt);
}
}

View File

@ -0,0 +1,247 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Tomasz Kamiński
//
// 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>
#include <cassert>
template<typename SourceClock, typename DestClock, typename = void>
struct is_clock_castable
: std::false_type
{};
template<typename SourceClock, typename DestClock>
struct is_clock_castable<SourceClock, DestClock, decltype(date::clock_cast<DestClock>(typename SourceClock::time_point()), void())>
: std::true_type
{};
//Clock based on steady clock, not related to wall time (sys_clock/utc_clock)
struct steady_based_clock
{
using duration = std::chrono::steady_clock::duration;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<steady_based_clock, duration>;
static time_point now()
{
return time_point(std::chrono::steady_clock::now().time_since_epoch());
}
};
//Traits that allow conversion between steady_clock and steady_based clock
//Does not use wall-time clocks as rally (sys/utc)
namespace date
{
template<>
struct clock_time_conversion<std::chrono::steady_clock, steady_based_clock>
{
template<typename Duration>
std::chrono::time_point<std::chrono::steady_clock, Duration>
operator()(std::chrono::time_point<steady_based_clock, Duration> const& tp) const
{
using res = std::chrono::time_point<std::chrono::steady_clock, Duration>;
return res(tp.time_since_epoch());
}
};
template<>
struct clock_time_conversion<steady_based_clock, std::chrono::steady_clock>
{
template<typename Duration>
std::chrono::time_point<steady_based_clock, Duration>
operator()(std::chrono::time_point<std::chrono::steady_clock, Duration> const& tp) const
{
using res = std::chrono::time_point<steady_based_clock, Duration>;
return res(tp.time_since_epoch());
}
};
}
//Ambigous clocks both providing to/from_sys and to/from_utc
//They are mock_ups just returning zero time_point
struct amb1_clock
{
using duration = std::chrono::seconds;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<amb1_clock>;
static time_point now()
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<std::chrono::system_clock, Duration>
to_sys(std::chrono::time_point<amb1_clock, Duration> const&)
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<amb1_clock, Duration>
from_sys(std::chrono::time_point<std::chrono::system_clock, Duration> const&)
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<date::utc_clock, Duration>
to_utc(std::chrono::time_point<amb1_clock, Duration> const&)
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<amb1_clock, Duration>
from_utc(std::chrono::time_point<date::utc_clock, Duration> const&)
{
return {};
}
};
struct amb2_clock
{
using duration = std::chrono::seconds;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<amb2_clock>;
static time_point now()
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<std::chrono::system_clock, Duration>
to_sys(std::chrono::time_point<amb2_clock, Duration> const&)
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<amb2_clock, Duration>
from_sys(std::chrono::time_point<std::chrono::system_clock, Duration> const&)
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<date::utc_clock, Duration>
to_utc(std::chrono::time_point<amb2_clock, Duration> const&)
{
return {};
}
template<typename Duration>
static
std::chrono::time_point<amb2_clock, Duration>
from_utc(std::chrono::time_point<date::utc_clock, Duration> const&)
{
return {};
}
};
namespace date
{
//Disambiguates that sys_clock is preffered
template<>
struct clock_time_conversion<amb1_clock, amb2_clock>
{
template<typename Duration>
std::chrono::time_point<amb1_clock, Duration>
operator()(std::chrono::time_point<amb2_clock, Duration> const& tp) const
{
return amb1_clock::from_sys(amb2_clock::to_sys(tp));
}
};
}
int
main()
{
using namespace date;
using namespace std::chrono;
using sys_clock = std::chrono::system_clock;
//steady_clock (must be different that sys_clock)
static_assert(is_clock_castable<steady_clock, steady_clock>::value, "steady_clock -> steady_clock");
static_assert(!is_clock_castable<steady_clock, sys_clock>::value, "steady_clock -> sys_clock");
static_assert(!is_clock_castable<sys_clock, steady_clock>::value, "sys_clock -> steady_clock");
static_assert(!is_clock_castable<steady_clock, utc_clock>::value, "steady_clock -> utc_clock");
static_assert(!is_clock_castable<utc_clock, steady_clock>::value, "utc_clock -> steady_clock");
static_assert(!is_clock_castable<steady_clock, tai_clock>::value, "steady_clock -> tai_clock");
static_assert(!is_clock_castable<tai_clock, steady_clock>::value, "tai_clock -> steady_clock");
//steady_based_clock (unrelated to sys_clock and utc_clocks)
static_assert(is_clock_castable<steady_based_clock, steady_based_clock>::value, "steady_based_clock -> steady_based_clock");
static_assert(!is_clock_castable<steady_based_clock, sys_clock>::value, "steady_based_clock -> sys_clock");
static_assert(!is_clock_castable<sys_clock, steady_based_clock>::value, "sys_clock -> steady_based_clock");
static_assert(!is_clock_castable<steady_based_clock, utc_clock>::value, "steady_based_clock -> utc_clock");
static_assert(!is_clock_castable<utc_clock, steady_based_clock>::value, "utc_clock -> steady_based_clock");
static_assert(!is_clock_castable<steady_based_clock, tai_clock>::value, "steady_based_clock -> tai_clock");
static_assert(!is_clock_castable<tai_clock, steady_based_clock>::value, "tai_clock -> steady_based_clock");
//steady_based <-> steady_clock
{
auto s1 = steady_clock::time_point(steady_clock::duration(200));
auto s2 = steady_based_clock::time_point(steady_based_clock::duration(200));
assert(clock_cast<steady_based_clock>(s1) == s2);
assert(clock_cast<steady_clock>(s2) == s1);
}
//ambX <-> sys/utc works as one rally can be used in each case, or one lead to quicker conversione
static_assert(is_clock_castable<amb1_clock, amb1_clock>::value, "amb1_clock -> amb1_clock");
static_assert(is_clock_castable<amb1_clock, sys_clock>::value, "amb1_clock -> sys_clock");
static_assert(is_clock_castable<sys_clock, amb1_clock>::value, "sys_clock -> amb1_clock");
static_assert(is_clock_castable<amb1_clock, utc_clock>::value, "amb1_clock -> utc_clock");
static_assert(is_clock_castable<utc_clock, amb1_clock>::value, "utc_clock -> amb1_clock");
static_assert(is_clock_castable<amb1_clock, tai_clock>::value, "amb1_clock -> tai_clock");
static_assert(is_clock_castable<tai_clock, amb1_clock>::value, "tai_clock -> amb1_clock");
static_assert(is_clock_castable<amb1_clock, tai_clock>::value, "amb1_clock -> tai_clock");
static_assert(is_clock_castable<gps_clock, amb1_clock>::value, "gps_clock -> amb1_clock");
static_assert(is_clock_castable<amb2_clock, amb2_clock>::value, "amb2_clock -> amb2_clock");
static_assert(is_clock_castable<amb2_clock, sys_clock>::value, "amb2_clock -> sys_clock");
static_assert(is_clock_castable<sys_clock, amb2_clock>::value, "sys_clock -> amb2_clock");
static_assert(is_clock_castable<amb2_clock, utc_clock>::value, "amb2_clock -> utc_clock");
static_assert(is_clock_castable<utc_clock, amb2_clock>::value, "utc_clock -> amb2_clock");
static_assert(is_clock_castable<amb2_clock, tai_clock>::value, "amb2_clock -> tai_clock");
static_assert(is_clock_castable<tai_clock, amb2_clock>::value, "tai_clock -> amb2_clock");
static_assert(is_clock_castable<amb2_clock, tai_clock>::value, "amb2_clock -> tai_clock");
static_assert(is_clock_castable<gps_clock, amb2_clock>::value, "gps_clock -> amb2_clock");
//amb1 -> amb2: ambigous because can either go trough sys_clock or utc_clock
static_assert(!is_clock_castable<amb1_clock, amb2_clock>::value, "amb1_clock -> amb2_clock");
//amb2 -> amb1: disambiguated via trait specialization
static_assert(is_clock_castable<amb2_clock, amb1_clock>::value, "amb2_clock -> amb1_clock");
}

View File

@ -0,0 +1,102 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Tomasz Kamiński
//
// 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 <cassert>
int
main()
{
using namespace date;
using sys_clock = std::chrono::system_clock;
// self
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
assert(clock_cast<sys_clock>(st) == st);
assert(clock_cast<utc_clock>(ut) == ut);
assert(clock_cast<tai_clock>(tt) == tt);
}
// sys <-> utc
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
assert(clock_cast<utc_clock>(st) == ut);
assert(clock_cast<sys_clock>(ut) == st);
}
// tai <-> utc
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
assert(clock_cast<tai_clock>(ut) == tt);
assert(clock_cast<utc_clock>(tt) == ut);
}
// tai <-> sys
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
assert(clock_cast<tai_clock>(st) == tt);
assert(clock_cast<sys_clock>(tt) == st);
}
// gps <-> utc
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto gt = gps_clock::from_utc(ut);
assert(clock_cast<gps_clock>(ut) == gt);
assert(clock_cast<utc_clock>(gt) == ut);
}
// gps <-> sys
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto gt = gps_clock::from_utc(ut);
assert(clock_cast<gps_clock>(st) == gt);
assert(clock_cast<sys_clock>(gt) == st);
}
// tai <-> gps
{
sys_days st(1997_y/dec/12);
auto ut = utc_clock::from_sys(st);
auto tt = tai_clock::from_utc(ut);
auto gt = gps_clock::from_utc(ut);
assert(clock_cast<gps_clock>(tt) == gt);
assert(clock_cast<tai_clock>(gt) == tt);
}
}

View File

@ -0,0 +1,49 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Tomasz Kamiński
//
// 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"
struct bad_clock
{
using duration = std::chrono::system_clock::duration;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<bad_clock, duration>;
template<typename Duration>
static
int
to_sys(std::chrono::time_point<bad_clock, Duration> const& tp)
{
return tp.time_since_epoch().count();
}
};
int
main()
{
using namespace date;
using sys_clock = std::chrono::system_clock;
auto bt = bad_clock::time_point();
clock_cast<sys_clock>(bt);
}

View File

@ -0,0 +1,51 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Tomasz Kamiński
//
// 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"
struct bad_clock
{
using duration = std::chrono::system_clock::duration;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<bad_clock, duration>;
template<typename Duration>
static
date::sys_time<Duration> const&
to_sys(std::chrono::time_point<bad_clock, Duration> const& tp)
{
static date::sys_time<Duration> val;
val = date::sys_time<Duration>(tp.time_since_epoch());
return val;
}
};
int
main()
{
using namespace date;
using sys_clock = std::chrono::system_clock;
auto bt = bad_clock::time_point();
clock_cast<sys_clock>(bt);
}

View File

@ -0,0 +1,49 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 Tomasz Kamiński
//
// 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"
struct bad_clock
{
using duration = std::chrono::system_clock::duration;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<bad_clock, duration>;
template<typename Duration>
static
date::utc_time<Duration>
to_sys(std::chrono::time_point<bad_clock, Duration> const& tp)
{
return utc_time<Duration>(tp.time_since_epoch());
}
};
int
main()
{
using namespace date;
using sys_clock = std::chrono::system_clock;
auto bt = bad_clock::time_point();
clock_cast<sys_clock>(bt);
}

View File

@ -94,7 +94,7 @@ main()
{
using D = decimal_format_seconds<milliseconds>;
static_assert(D::width == 3, "");
static_assert(is_same<D::precision, make_precision<D::width>::type>{}, "");
static_assert(is_same<D::precision, make_precision<D::rep, D::width>::type>{}, "");
D dfs{seconds{3}};
assert(dfs.seconds() == seconds{3});
assert(dfs.to_duration() == seconds{3});
@ -106,7 +106,7 @@ main()
{
using D = decimal_format_seconds<milliseconds>;
static_assert(D::width == 3, "");
static_assert(is_same<D::precision, make_precision<D::width>::type>{}, "");
static_assert(is_same<D::precision, make_precision<D::rep, D::width>::type>{}, "");
D dfs{milliseconds{3}};
assert(dfs.seconds() == seconds{0});
assert(dfs.to_duration() == milliseconds{3});
@ -118,7 +118,7 @@ main()
{
using D = decimal_format_seconds<microfortnights>;
static_assert(D::width == 4, "");
using S = make_precision<D::width>::type;
using S = make_precision<D::rep, D::width>::type;
static_assert(is_same<D::precision, S>{}, "");
D dfs{microfortnights{3}};
assert(dfs.seconds() == seconds{3});
@ -132,7 +132,7 @@ main()
using CT = common_type<seconds, microfortnights>::type;
using D = decimal_format_seconds<CT>;
static_assert(D::width == 4, "");
using S = make_precision<D::width>::type;
using S = make_precision<D::rep, D::width>::type;
static_assert(is_same<D::precision, S>{}, "");
D dfs{microfortnights{3}};
assert(dfs.seconds() == seconds{3});

View File

@ -40,24 +40,24 @@ main()
using namespace std;
using namespace std::chrono;
static_assert(make_precision<0>::width == 0, "");
static_assert(is_same<make_precision<0>::type, duration<int64_t, ratio<1, 1>>>{}, "");
static_assert(make_precision<int64_t, 0>::width == 0, "");
static_assert(is_same<make_precision<int64_t, 0>::type, duration<int64_t, ratio<1, 1>>>{}, "");
static_assert(make_precision<1>::width == 1, "");
static_assert(is_same<make_precision<1>::type, duration<int64_t, ratio<1, 10>>>{}, "");
static_assert(make_precision<int64_t, 1>::width == 1, "");
static_assert(is_same<make_precision<int64_t, 1>::type, duration<int64_t, ratio<1, 10>>>{}, "");
static_assert(make_precision<2>::width == 2, "");
static_assert(is_same<make_precision<2>::type, duration<int64_t, ratio<1, 100>>>{}, "");
static_assert(make_precision<int64_t, 2>::width == 2, "");
static_assert(is_same<make_precision<int64_t, 2>::type, duration<int64_t, ratio<1, 100>>>{}, "");
static_assert(make_precision<3>::width == 3, "");
static_assert(is_same<make_precision<3>::type, duration<int64_t, ratio<1, 1000>>>{}, "");
static_assert(make_precision<int64_t, 3>::width == 3, "");
static_assert(is_same<make_precision<int64_t, 3>::type, duration<int64_t, ratio<1, 1000>>>{}, "");
static_assert(make_precision<18>::width == 18, "");
static_assert(is_same<make_precision<18>::type, duration<int64_t, ratio<1, 1000000000000000000>>>{}, "");
static_assert(make_precision<int64_t, 18>::width == 18, "");
static_assert(is_same<make_precision<int64_t, 18>::type, duration<int64_t, ratio<1, 1000000000000000000>>>{}, "");
static_assert(make_precision<19>::width == 6, "");
static_assert(is_same<make_precision<19>::type, microseconds>{}, "");
static_assert(make_precision<int64_t, 19>::width == 6, "");
static_assert(is_same<make_precision<int64_t, 19>::type, microseconds>{}, "");
static_assert(make_precision<20>::width == 6, "");
static_assert(is_same<make_precision<20>::type, microseconds>{}, "");
static_assert(make_precision<int64_t, 20>::width == 6, "");
static_assert(is_same<make_precision<int64_t, 20>::type, microseconds>{}, "");
}

View File

@ -360,6 +360,12 @@ test_d()
assert(!in.bad());
assert(tp == 2016_y/12/9);
}
{
std::istringstream in{"2016 31 11"};
sys_days tp;
in >> parse("%Y %e %m", tp);
assert(in.fail());
}
}
void
@ -413,6 +419,12 @@ test_H()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/11} + hours{15});
}
{
std::istringstream in{"2016-12-11 24"};
sys_time<hours> tp;
in >> parse("%F %H", tp);
assert(in.fail());
}
}
void
@ -436,6 +448,12 @@ test_Ip()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/11} + hours{1});
}
{
std::istringstream in{"2016-12-11 13 am"};
sys_time<hours> tp;
in >> parse("%F %I %p", tp);
assert(in.fail());
}
}
void
@ -474,6 +492,12 @@ test_m()
assert(!in.bad());
assert(tp == 2016_y/9/12);
}
{
std::istringstream in{"2016 12 13"};
sys_days tp;
in >> parse("%Y %d %m", tp);
assert(in.fail());
}
}
void
@ -489,6 +513,12 @@ test_M()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/11} + minutes{15});
}
{
std::istringstream in{"2016-12-11 65"};
sys_time<minutes> tp;
in >> parse("%F %M", tp);
assert(in.fail());
}
}
void
@ -512,6 +542,12 @@ test_S()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/11} + seconds{15} + milliseconds{1});
}
{
std::istringstream in{"2016-12-11 60"};
sys_seconds tp;
in >> parse("%F %S", tp);
assert(in.fail());
}
}
void
@ -552,6 +588,24 @@ test_T()
assert(!in.bad());
assert(d == hours{15} + minutes{43} + seconds{22} + milliseconds{1});
}
{
std::istringstream in{"2016-12-11 24:43:22"};
sys_seconds tp;
in >> parse("%F %T", tp);
assert(in.fail());
}
{
std::istringstream in{"2016-12-11 15:60:22"};
sys_seconds tp;
in >> parse("%F %T", tp);
assert(in.fail());
}
{
std::istringstream in{"2016-12-11 15:43:60"};
sys_seconds tp;
in >> parse("%F %T", tp);
assert(in.fail());
}
}
void

View File

@ -23,6 +23,7 @@
// class weekday_indexed
// {
// public:
// weekday_indexed() = default;
// constexpr weekday_indexed(const date::weekday& wd, unsigned index) noexcept;
//
// constexpr date::weekday weekday() const noexcept;
@ -42,7 +43,7 @@
#include <type_traits>
static_assert( std::is_trivially_destructible<date::weekday_indexed>{}, "");
static_assert(!std::is_default_constructible<date::weekday_indexed>{}, "");
static_assert( std::is_default_constructible<date::weekday_indexed>{}, "");
static_assert( std::is_trivially_copy_constructible<date::weekday_indexed>{}, "");
static_assert( std::is_trivially_copy_assignable<date::weekday_indexed>{}, "");
static_assert( std::is_trivially_move_constructible<date::weekday_indexed>{}, "");

View File

@ -23,6 +23,7 @@
// class year_month_weekday
// {
// public:
// year_month_weekday() = default;
// constexpr year_month_weekday(const date::year& y, const date::month& m,
// const date::weekday_indexed& wdi) noexcept;
// constexpr year_month_weekday(const sys_days& dp) noexcept;
@ -83,7 +84,7 @@
#include <type_traits>
static_assert( std::is_trivially_destructible<date::year_month_weekday>{}, "");
static_assert(!std::is_default_constructible<date::year_month_weekday>{}, "");
static_assert( std::is_default_constructible<date::year_month_weekday>{}, "");
static_assert( std::is_trivially_copy_constructible<date::year_month_weekday>{}, "");
static_assert( std::is_trivially_copy_assignable<date::year_month_weekday>{}, "");
static_assert( std::is_trivially_move_constructible<date::year_month_weekday>{}, "");

View File

@ -115,4 +115,15 @@ main()
std::ostringstream os;
os << x0;
assert(os.str() == "2015-W last-Tue");
for (auto y = 1950_y; y <= 2050_y; ++y)
{
auto wd = mon;
do
{
auto x = y/last/wd;
assert(date::year_month_day{x} == date::year_month_day{year_weeknum_weekday{x}});
++wd;
} while (wd != mon);
}
}

View File

@ -48,7 +48,10 @@ if [ -z "$CXX_LANG" ]
then
CXX_LANG=c++14
fi
OPTIONS="-std=${CXX_LANG} $OPTIONS -I$ROOT -Wall"
OPTIONS="-std=${CXX_LANG} $OPTIONS -I$ROOT -Wall $ROOT/src/tz.cpp -lcurl"
echo $ROOT
HEADER_INCLUDE="-I$ROOT/include -I$ROOT/include/date"
case $TRIPLE in
*-*-mingw* | *-*-cygwin* | *-*-win*)

View File

@ -0,0 +1,81 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 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 custom time zone support
#include "tz.h"
class OffsetZone
{
std::chrono::minutes offset_;
public:
explicit OffsetZone(std::chrono::minutes offset)
: offset_{offset}
{}
template <class Duration>
auto
to_local(date::sys_time<Duration> tp) const
{
using namespace date;
using namespace std::chrono;
using LT = local_time<std::common_type_t<Duration, minutes>>;
return LT{(tp + offset_).time_since_epoch()};
}
template <class Duration>
auto
to_sys(date::local_time<Duration> tp) const
{
using namespace date;
using namespace std::chrono;
using ST = sys_time<std::common_type_t<Duration, minutes>>;
return ST{(tp - offset_).time_since_epoch()};
}
template <class Duration>
date::sys_info
get_info(date::sys_time<Duration> st) const
{
using namespace date;
using namespace std::chrono;
return {sys_seconds::min(), sys_seconds::max(), offset_,
minutes{0}, offset_ >= minutes{0} ? "+" + date::format("%H%M", offset_)
: "-" + date::format("%H%M", -offset_)};
}
const OffsetZone* operator->() const {return this;}
};
#include <cassert>
int
main()
{
using namespace date;
using namespace std::chrono;
auto now = system_clock::now();
auto offset = hours{-4};
zoned_time<system_clock::duration, OffsetZone> zt{OffsetZone{offset}, now};
assert(zt.get_local_time().time_since_epoch() == now.time_since_epoch() + offset);
}

View File

@ -1,21 +1,15 @@
To test: * Install tz.cpp by downloading the IANA timezone database
at: http://www.iana.org/time-zones You only need the data, not the
code.
# TZ Test
* Change the string `install` in tz.cpp to point to your downloaded
IANA database.
## How to Test
* Install tz.cpp by downloading the IANA timezone database at: http://www.iana.org/time-zones You only need the data, not the code.
* Change the string `install` in tz.cpp to point to your downloaded IANA database.
* Compile validate.cpp along with tz.cpp.
* Run the binary and direct the terminal output to a temporary file.
* Unzip the tzdata file that has the version corresponding to the IANA database you downloaded (e.g. tzdata2015f.txt.zip).
* Compare the unzipped txt file with the output of your validate test program. If they are identical, the test passes, else it fails.
* Unzip the tzdata file that has the version corresponding to the IANA
database you downloaded (e.g. tzdata2015f.txt.zip).
* Compare the unzipped txt file with the output of your validate test
program. If they are identical, the test passes, else it fails.
Miscellaneous:
## Miscellaneous
You can also compare one version of the tzdatabase with another using
these uncompressed text files. The text files contain for each
@ -25,4 +19,4 @@ versions change, minor updates to the set of these transitions are
typically made, typically due to changes in government policies.
The tests in this section will run much faster with optimizations
cranked up.
cranked up.

View File

@ -1,4 +1,4 @@
#include "tz.h"
#include "date/tz.h"
#include <iostream>
void

View File

@ -0,0 +1,464 @@
// The MIT License (MIT)
//
// Copyright (c) 2017 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.
// template <class Duration>
// class zoned_time
// {
// 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
// <
// std::is_convertible<sys_time<Duration2>,
// 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>&
// operator<<(std::basic_ostream<CharT, Traits>& os, const zoned_time<Duration1>& t);
// };
//
// using zoned_seconds = zoned_time<std::chrono::seconds>;
//
// template <class Duration1, class Duration2>
// inline
// bool
// operator!=(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);
//
// template <class Duration>
// zoned_time(sys_time<Duration>)
// -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>>;
//
// template <class Zone, class Duration>
// zoned_time(Zone, sys_time<Duration>)
// -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>>;
//
// template <class Zone, class Duration>
// zoned_time(Zone, local_time<Duration>, choose = choose::earliest)
// -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>>;
//
// template <class Zone, class Duration>
// zoned_time(Zone, zoned_time<Duration>, choose = choose::earliest)
// -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>>;
#include "tz.h"
#include <cassert>
#include <sstream>
#include <type_traits>
int
main()
{
using namespace std;
using namespace std::chrono;
using namespace date;
static_assert( is_nothrow_destructible<zoned_seconds>{}, "");
static_assert( is_default_constructible<zoned_seconds>{}, "");
static_assert( is_nothrow_copy_constructible<zoned_seconds>{}, "");
static_assert( is_nothrow_copy_assignable<zoned_seconds>{}, "");
static_assert( is_nothrow_move_constructible<zoned_seconds>{}, "");
static_assert( is_nothrow_move_assignable<zoned_seconds>{}, "");
static_assert(is_same<zoned_time<minutes>::duration, seconds>{}, "");
static_assert(is_same<zoned_seconds::duration, seconds>{}, "");
static_assert(is_same<zoned_time<milliseconds>::duration, milliseconds>{}, "");
// zoned_time();
{
zoned_seconds zt;
assert(zt.get_sys_time() == sys_seconds{});
assert(zt.get_time_zone()->name() == "Etc/UTC");
}
// zoned_time(const sys_time<Duration>& st);
{
static_assert(!is_convertible<sys_days, zoned_seconds>{}, "");
static_assert( is_constructible<zoned_seconds, sys_days>{}, "");
static_assert( is_convertible<sys_seconds, zoned_seconds>{}, "");
static_assert(!is_convertible<sys_time<milliseconds>, zoned_seconds>{}, "");
static_assert(!is_constructible<zoned_seconds, sys_time<milliseconds>>{}, "");
auto now = floor<seconds>(system_clock::now());
zoned_seconds zt = now;
assert(zt.get_sys_time() == now);
assert(zt.get_time_zone()->name() == "Etc/UTC");
}
// explicit zoned_time(const time_zone* z);
{
static_assert(!is_convertible<const time_zone*, zoned_seconds>{}, "");
static_assert( is_constructible<zoned_seconds, const time_zone*>{}, "");
zoned_seconds zt{locate_zone("America/New_York")};
assert(zt.get_sys_time() == sys_seconds{});
assert(zt.get_time_zone()->name() == "America/New_York");
}
// explicit zoned_time(std::string_view name);
{
static_assert(!is_convertible<std::string, zoned_seconds>{}, "");
static_assert( is_constructible<zoned_seconds, std::string>{}, "");
static_assert( is_constructible<zoned_seconds, const char*>{}, "");
static_assert( is_constructible<zoned_seconds, const char[3]>{}, "");
zoned_seconds zt{"America/New_York"};
assert(zt.get_sys_time() == sys_seconds{});
assert(zt.get_time_zone()->name() == "America/New_York");
}
// template <class Duration2,
// class = typename std::enable_if
// <
// std::is_convertible<sys_time<Duration2>,
// sys_time<Duration>>::value
// >::type>
// zoned_time(const zoned_time<Duration2>& zt) NOEXCEPT;
{
static_assert( is_convertible<zoned_time<days>, zoned_seconds>{}, "");
static_assert(!is_constructible<zoned_time<days>, zoned_seconds>{}, "");
zoned_time<days> zt1{"America/New_York", sys_days{2017_y/jul/5}};
zoned_seconds zt2 = zt1;
assert(zt2.get_sys_time() == sys_days{2017_y/jul/5});
assert(zt2.get_time_zone()->name() == "America/New_York");
}
// zoned_time(const time_zone* z, const local_time<Duration>& tp);
{
static_assert( is_constructible<zoned_seconds, const time_zone*, local_days>{}, "");
zoned_seconds zt = {locate_zone("America/New_York"), local_days{2017_y/jul/5}};
assert(zt.get_local_time() == local_days{2017_y/jul/5});
assert(zt.get_time_zone()->name() == "America/New_York");
try
{
zoned_seconds zt1 = {locate_zone("America/New_York"),
local_days{2017_y/mar/sun[2]} + hours{2} + minutes{15}};
assert(false);
}
catch(const nonexistent_local_time&)
{
}
try
{
zoned_seconds zt1 = {locate_zone("America/New_York"),
local_days{2017_y/nov/sun[1]} + hours{1} + minutes{15}};
assert(false);
}
catch(const ambiguous_local_time&)
{
}
}
// zoned_time(std::string_view name, const local_time<Duration>& tp);
{
static_assert( is_constructible<zoned_seconds, std::string, local_days>{}, "");
static_assert( is_constructible<zoned_seconds, const char*, local_days>{}, "");
zoned_seconds zt = {"America/New_York", local_days{2017_y/jul/5}};
assert(zt.get_local_time() == local_days{2017_y/jul/5});
assert(zt.get_time_zone()->name() == "America/New_York");
try
{
zoned_seconds zt1 = {"America/New_York",
local_days{2017_y/mar/sun[2]} + hours{2} + minutes{15}};
assert(false);
}
catch(const nonexistent_local_time&)
{
}
try
{
zoned_seconds zt1 = {"America/New_York",
local_days{2017_y/nov/sun[1]} + hours{1} + minutes{15}};
assert(false);
}
catch(const ambiguous_local_time&)
{
}
}
// zoned_time(const time_zone* z, const local_time<Duration>& tp, choose c);
{
static_assert( is_constructible<zoned_seconds, const time_zone*, local_days, choose>{}, "");
zoned_seconds zt = {locate_zone("America/New_York"),
local_days{2017_y/jul/5} + hours{2} + minutes{15},
choose::earliest};
assert(zt.get_sys_time() == sys_days{2017_y/jul/5} + hours{6} + minutes{15});
assert(zt.get_time_zone()->name() == "America/New_York");
zt = {locate_zone("America/New_York"),
local_days{2017_y/jul/5} + hours{2} + minutes{15},
choose::latest};
assert(zt.get_sys_time() == sys_days{2017_y/jul/5} + hours{6} + minutes{15});
assert(zt.get_time_zone()->name() == "America/New_York");
static_assert( is_constructible<zoned_seconds, const time_zone*, local_days, choose>{}, "");
zoned_seconds zt1 = {locate_zone("America/New_York"),
local_days{2017_y/mar/sun[2]} + hours{2} + minutes{15},
choose::earliest};
assert(zt1.get_sys_time() == sys_days{2017_y/mar/12} + hours{7});
assert(zt1.get_time_zone()->name() == "America/New_York");
zoned_seconds zt2 = {locate_zone("America/New_York"),
local_days{2017_y/mar/sun[2]} + hours{2} + minutes{15},
choose::latest};
assert(zt2.get_sys_time() == sys_days{2017_y/mar/12} + hours{7});
assert(zt2.get_time_zone()->name() == "America/New_York");
zoned_seconds zt3 = {locate_zone("America/New_York"),
local_days{2017_y/nov/sun[1]} + hours{1} + minutes{15},
choose::earliest};
assert(zt3.get_sys_time() == sys_days{2017_y/nov/5} + hours{5} + minutes{15});
assert(zt3.get_time_zone()->name() == "America/New_York");
zoned_seconds zt4 = {locate_zone("America/New_York"),
local_days{2017_y/nov/sun[1]} + hours{1} + minutes{15},
choose::latest};
assert(zt4.get_sys_time() == sys_days{2017_y/nov/5} + hours{6} + minutes{15});
assert(zt4.get_time_zone()->name() == "America/New_York");
}
// zoned_time(std::string_view name, const local_time<Duration>& tp, choose c);
{
static_assert( is_constructible<zoned_seconds, std::string, local_days, choose>{}, "");
static_assert( is_constructible<zoned_seconds, const char*, local_days, choose>{}, "");
zoned_seconds zt = {"America/New_York",
local_days{2017_y/jul/5} + hours{2} + minutes{15},
choose::earliest};
assert(zt.get_sys_time() == sys_days{2017_y/jul/5} + hours{6} + minutes{15});
assert(zt.get_time_zone()->name() == "America/New_York");
zt = {"America/New_York",
local_days{2017_y/jul/5} + hours{2} + minutes{15},
choose::latest};
assert(zt.get_sys_time() == sys_days{2017_y/jul/5} + hours{6} + minutes{15});
assert(zt.get_time_zone()->name() == "America/New_York");
static_assert( is_constructible<zoned_seconds, const time_zone*, local_days, choose>{}, "");
zoned_seconds zt1 = {"America/New_York",
local_days{2017_y/mar/sun[2]} + hours{2} + minutes{15},
choose::earliest};
assert(zt1.get_sys_time() == sys_days{2017_y/mar/12} + hours{7});
assert(zt1.get_time_zone()->name() == "America/New_York");
zoned_seconds zt2 = {"America/New_York",
local_days{2017_y/mar/sun[2]} + hours{2} + minutes{15},
choose::latest};
assert(zt2.get_sys_time() == sys_days{2017_y/mar/12} + hours{7});
assert(zt2.get_time_zone()->name() == "America/New_York");
zoned_seconds zt3 = {"America/New_York",
local_days{2017_y/nov/sun[1]} + hours{1} + minutes{15},
choose::earliest};
assert(zt3.get_sys_time() == sys_days{2017_y/nov/5} + hours{5} + minutes{15});
assert(zt3.get_time_zone()->name() == "America/New_York");
zoned_seconds zt4 = {"America/New_York",
local_days{2017_y/nov/sun[1]} + hours{1} + minutes{15},
choose::latest};
assert(zt4.get_sys_time() == sys_days{2017_y/nov/5} + hours{6} + minutes{15});
assert(zt4.get_time_zone()->name() == "America/New_York");
}
// zoned_time(const time_zone* z, const sys_time<Duration>& st);
{
static_assert( is_constructible<zoned_seconds, const time_zone*, sys_days>{}, "");
zoned_seconds zt = {locate_zone("America/New_York"),
sys_days{2017_y/jul/5} + hours{2} + minutes{15}};
assert(zt.get_sys_time() == sys_days{2017_y/jul/5} + hours{2} + minutes{15});
assert(zt.get_time_zone()->name() == "America/New_York");
}
// zoned_time(std::string_view name, const sys_time<Duration>& st);
{
static_assert( is_constructible<zoned_seconds, std::string, sys_days>{}, "");
static_assert( is_constructible<zoned_seconds, const char*, sys_days>{}, "");
zoned_seconds zt = {"America/New_York",
sys_days{2017_y/jul/5} + hours{2} + minutes{15}};
assert(zt.get_sys_time() == sys_days{2017_y/jul/5} + hours{2} + minutes{15});
assert(zt.get_time_zone()->name() == "America/New_York");
}
// zoned_time& operator=(const sys_time<Duration>& st);
{
static_assert( is_assignable<zoned_seconds, const sys_days&>{}, "");
zoned_seconds zt{"America/New_York"};
zt = sys_days{2017_y/jul/5};
assert(zt.get_sys_time() == sys_days{2017_y/jul/5});
assert(zt.get_time_zone()->name() == "America/New_York");
}
// zoned_time& operator=(const local_time<Duration>& st);
{
static_assert( is_assignable<zoned_seconds, const local_days&>{}, "");
zoned_seconds zt{"America/New_York"};
zt = local_days{2017_y/jul/5};
assert(zt.get_local_time() == local_days{2017_y/jul/5});
assert(zt.get_time_zone()->name() == "America/New_York");
try
{
zt = {"America/New_York",
local_days{2017_y/mar/sun[2]} + hours{2} + minutes{15}};
assert(false);
}
catch(const nonexistent_local_time&)
{
assert(zt.get_local_time() == local_days{2017_y/jul/5});
assert(zt.get_time_zone()->name() == "America/New_York");
}
try
{
zt = {"America/New_York",
local_days{2017_y/nov/sun[1]} + hours{1} + minutes{15}};
assert(false);
}
catch(const ambiguous_local_time&)
{
assert(zt.get_local_time() == local_days{2017_y/jul/5});
assert(zt.get_time_zone()->name() == "America/New_York");
}
}
// explicit operator sys_time<duration>() const;
{
static_assert(!is_convertible<zoned_seconds, sys_seconds>{}, "");
static_assert( is_constructible<sys_seconds, zoned_seconds>{}, "");
auto now = floor<seconds>(system_clock::now());
const zoned_seconds zt = {"America/New_York", now};
assert(sys_seconds{zt} == now);
}
// explicit operator local_time<duration>() const;
{
static_assert(!is_convertible<zoned_seconds, local_seconds>{}, "");
static_assert( is_constructible<local_seconds, zoned_seconds>{}, "");
auto now = local_days{2017_y/jul/5} + hours{23} + minutes{1} + seconds{48};
const zoned_seconds zt = {"America/New_York", now};
assert(local_seconds{zt} == now);
}
// const time_zone* get_time_zone() const;
{
const zoned_seconds zt{"America/New_York"};
assert(zt.get_time_zone() == locate_zone("America/New_York"));
}
// local_time<duration> get_local_time() const;
{
const zoned_seconds zt{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{9}};
assert(zt.get_local_time() == local_days{2017_y/jul/5} +
hours{23} + minutes{7} + seconds{9});
}
// sys_time<duration> get_sys_time() const;
{
const zoned_seconds zt{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{9}};
assert(zt.get_sys_time() == sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{9});
}
// sys_info get_info() const;
{
const zoned_seconds zt{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{9}};
auto info = zt.get_info();
assert(info.begin == sys_days{2017_y/mar/12} + hours{7});
assert(info.end == sys_days{2017_y/nov/5} + hours{6});
assert(info.offset == hours{-4});
assert(info.save != minutes{0});
assert(info.abbrev == "EDT");
}
// template <class Duration1, class Duration2>
// bool
// operator==(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);
{
const zoned_seconds zt{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{9}};
const zoned_seconds zt1{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{10}};
assert(zt == zt);
assert(!(zt == zt1));
}
// template <class Duration1, class Duration2>
// bool
// operator!=(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);
{
const zoned_seconds zt{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{9}};
const zoned_seconds zt1{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{10}};
assert(!(zt != zt));
assert(zt != zt1);
}
// template <class CharT, class Traits, class Duration1>
// std::basic_ostream<CharT, Traits>&
// operator<<(std::basic_ostream<CharT, Traits>& os, const zoned_time<Duration1>& t);
{
const zoned_seconds zt{"America/New_York", sys_days{2017_y/jul/6} +
hours{3} + minutes{7} + seconds{9}};
std::ostringstream test;
test << zt;
assert(test.str() == "2017-07-05 23:07:09 EDT");
}
}

10
test_fail.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
echo $1
eval $1
if [ $? -eq 0 ]; then
exit 0;
fi
exit 1;

1529
tz.h

File diff suppressed because it is too large Load Diff