diff --git a/.gitignore b/.gitignore index 3d2c70d..92acc15 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ *.user .idea/ *.log +.orig tests/ cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 096936d..75e18f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,8 +55,8 @@ find_package(Boost COMPONENTS if(APPLE) - include_directories(/usr/local/opt/openssl/include) - link_directories(/usr/local/opt/openssl/lib) + include_directories(/usr/local/opt/openssl/include) + link_directories(/usr/local/opt/openssl/lib) endif() @@ -116,16 +116,16 @@ set(LIBRARIES ssl) if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32) - set(LIBRARIES ${LIBRARIES} unwind) + set(LIBRARIES ${LIBRARIES} unwind) endif() if (WIN32) set(LIBRARIES ${LIBRARIES} - wsock32 - ntdll - ws2_32 - Iphlpapi - ) + wsock32 + ntdll + ws2_32 + Iphlpapi + ) else() set(LIBRARIES ${LIBRARIES} dl) endif() diff --git a/README.md b/README.md index 997fa8d..6cbb855 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,16 @@ Currently available Monero blockchain explorer websites have several limitations - track users activates through google analytics, - are closed sourced, - are not available as hidden services, - - provide only basic search capabilities, - do not support Monero testnet -In this example, these limitations are addressed by development of -an Onion Monero Blockchain Explorer. The example not only shows how to use Monero C++ libraries, - but also demonstrates how to use: - - [crow](https://github.com/ipkn/crow) - C++ micro web framework +In this example, these limitations are addressed by development of +an Onion Monero Blockchain Explorer. The example not only shows how to use +Monero C++ libraries, but also demonstrates how to use: + + - [crow](https://github.com/ipkn/crow) - C++ micro web framework - [mstch](https://github.com/no1msd/mstch) - C++ {{mustache}} templates - [json](https://github.com/nlohmann/json) - JSON for Modern C++ - - [date](https://github.com/HowardHinnant/date) - C++ date and time library - [fmt](https://github.com/fmtlib/fmt) - Small, safe and fast string formatting library ## Addresses @@ -25,34 +24,26 @@ an Onion Monero Blockchain Explorer. The example not only shows how to use Moner Tor users: - [http://dvwae436pd7nt4bc.onion](http://dvwae436pd7nt4bc.onion) - + Clearnet versions: - - [http://139.162.32.245:8081/](http://139.162.32.245:8081/) - bleeding edge, no https. - - [https://xmrchain.net/](https://xmrchain.net/) - https enabled. - - [https://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled. - - [http://blox.supportxmr.com/](http://blox.supportxmr.com/) - no https. - - [https://explorer.xmr.my/](https://explorer.xmr.my/) - https enabled. + + - [http://139.162.32.245:8081/](http://139.162.32.245:8081/) - bleeding edge version, no https. + - [https://explorer.xmr.my/](https://explorer.xmr.my/) - https enabled, up to date, but sometimes down. + - [https://xmrchain.net/](https://xmrchain.net/) - https enabled, most popular and very stable. + - [https://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled. + - [http://explore.MoneroWorld.com](http://explore.moneroworld.com) - same as the second one. Clearnet testnet Monero version: - - [http://139.162.32.245:8082/](http://139.162.32.245:8082/) - bleeding edge, no https. + - [http://139.162.32.245:8082/](http://139.162.32.245:8082/) - bleeding edge version, no https. - [https://testnet.xmrchain.com/](https://testnet.xmrchain.com/) - https enabled. - + + i2p users (main Monero network) - down for now: - [http://monerotools.i2p](http://monerotools.i2p) - -Clearnet testnet priority nodes: - - - 23.228.193.90 - /opt/monero/monerod --testnet --add-priority-node 23.228.193.90 - - 62.210.104.109 - /opt/monero/monerod --testnet --add-priority-node 62.210.104.109 - -Monero tor nodes: - - - 7kome2dwgre4wll6.onion - `/opt/monero/monero-wallet-cli --daemon-host 7kome2dwgre4wll6.onion` - - o6nvntbo3qsn36dm.onion - `/opt/monero/monero-wallet-cli --daemon-host o6nvntbo3qsn36dm.onion` - + ## Onion Monero Blockchain Explorer features The key features of the Onion Monero Blockchain Explorer are: @@ -71,9 +62,10 @@ The key features of the Onion Monero Blockchain Explorer are: - the only explorer showing number of amount output indices, - the only explorer supporting Monero testnet network, - the only explorer providing tx checker and pusher for online pushing of transactions, - - the only explorer allowing to inspect encrypted key images file and output files. - the only explorer able to estimate possible spendings based on address and viewkey. +## Prerequisite + ## Compilation on Ubuntu 16.04 ##### Compile latest Monero @@ -100,13 +92,12 @@ make ``` ##### Compile and run the explorer -Once the Monero is compiled and setup, the explorer can be downloaded and compiled +Once the Monero is compiles, the explorer can be downloaded and compiled as follows: ```bash # go to home folder if still in ~/monero cd ~ - # download the source code git clone https://github.com/moneroexamples/onion-monero-blockchain-explorer.git @@ -135,7 +126,6 @@ To run it: By default it will look for blockchain in its default location i.e., `~/.bitmonero/lmdb`. You can use `--bc-path` option if its in different location. - Example output: ```bash @@ -154,14 +144,30 @@ xmrblocks, start Onion Monero Blockchain Explorer: -h [ --help ] [=arg(=1)] (=0) produce help message -t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain --enable-pusher [=arg(=1)] (=0) enable pushing signed tx + --enable-mixin-details [=arg(=1)] (=0) + enable mixin details for key images, + e.g., timescale, mixin of mixins, in tx + context --enable-key-image-checker [=arg(=1)] (=0) enable key images file checker --enable-output-key-checker [=arg(=1)] (=0) enable outputs key file checker + --enable-mempool-cache arg (=1) enable caching txs in the mempool + --enable-json-api arg (=1) enable JSON REST api + --enable-tx-cache [=arg(=1)] (=0) enable caching of tx details + --show-cache-times [=arg(=1)] (=0) show times of getting data from cache + vs no cache + --enable-block-cache [=arg(=1)] (=0) enable caching of block details --enable-autorefresh-option [=arg(=1)] (=0) enable users to have the index page on autorefresh -p [ --port ] arg (=8081) default port + --testnet-url arg you can specifiy testnet url, if you + run it on mainet. link will show on + front page to testnet explorer + --mainnet-url arg you can specifiy mainnet url, if you + run it on testnet. link will show on + front page to mainnet explorer --no-blocks-on-index arg (=10) number of last blocks to be shown on index page -b [ --bc-path ] arg path to lmdb blockchain @@ -196,27 +202,301 @@ Note: Because we generated our own certificate, modern browsers will complain about it as they cant verify the signatures against any third party. So probably for any practical use need to have properly issued ssl certificates. -## Enable transaction pusher +## JSON API -By default, the tx pusher is disabled. The pushing will not work, but tx checking and inspecting will. +The explorer has JSON api. For the API, it uses conventions defined by [JSend](https://labs.omniti.com/labs/jsend). -To enable pushing the txs, use flag `--enable-pusher`, e.g.: +#### api/transaction/ ```bash -./xmrblocks --enable-pusher +curl -w "\n" -X GET "http://139.162.32.245:8081/api/transaction/6093260dbe79fd6277694d14789dc8718f1bd54457df8bab338c2efa3bb0f03d" ``` - -Note: There has been a number of issues with compatibility of tx's binary data between different Monero versions -and operating systems. Unless you are using latest development version of Monero and the explorer has been compiled -against the lastest version, pushing and checking unsigined and signed tx data - might not work due to incompatibilities in binary data. -## Example screenshot +```json +{ + "data": { + "block_height": 1268252, + "coinbase": 0, + "confirmations": 1, + "fee": 12517785574, + "inputs": [ + { + "amount": 0, + "key_image": "67838fd0ffd79f13e735830d3ec60412aed59e53e1f997feb6f73d088b949611" + } + ], + "outputs": [ + { + "amount": 0, + "public_key": "525779873776e4a42f517fd79b72e7c31c3ba03e730fc32287f6414fb702c1d7" + }, + { + "amount": 0, + "public_key": "e25f00fceb77af841d780b68647618812695b4ca6ebe338faba6e077f758ac30" + } + ], + "rct_type": 1, + "size": 13323000000000000, + "timestamp": 1489753456, + "timestamp_utc": "2017-03-17 12:24:16", + "tx_hash": "6093260dbe79fd6277694d14789dc8718f1bd54457df8bab338c2efa3bb0f03d", + "version": 2 + }, + "status": "success" +} +``` -![Onion Monero Blockchain Explorer](https://raw.githubusercontent.com/moneroexamples/onion-monero-blockchain-explorer/master/screenshot/screenshot_01.jpg) +#### api/transactions + +Transactions in last 25 blocks -## Other Monero examples +```bash +curl -w "\n" -X GET "http://139.162.32.245:8081/api/transactions" +``` + +Partial results shown: + +```json +{ + "data": { + "blocks": [ + { + "age": "33:16:49:53", + "height": 1268252, + "size": 105390000000000000, + "timestamp": 1489753456, + "timestamp_utc": "2017-03-17 12:24:16", + "txs": [ + { + "coinbase": true, + "mixin": 0, + "outputs": 8491554678365, + "rct_type": 0, + "tx_fee": 0, + "tx_hash": "7c4286f64544568265bb5418df84ae69afaa3567749210e46f8340c247f4803f", + "tx_size": 151000000000000, + "tx_version": 2 + }, + { + "coinbase": false, + "mixin": 5, + "outputs": 0, + "rct_type": 2, + "tx_fee": 17882516700, + "tx_hash": "2bfbccb918ee5f050808dd040ce03943b7315b81788e9cdee59cf86b557ba48c", + "tx_size": 19586000000000000, + "tx_version": 2 + } + ] + } + ], + "limit": 25, + "page": 0 + }, + "status": "success" +} +``` + +#### api/transactions?page=&limit= + + +```bash +curl -w "\n" -X GET "http://139.162.32.245:8081/api/transactions?page=2&limit=10" +``` + +Result analogical to the one above. + +#### api/block/ + + +```bash +curl -w "\n" -X GET "http://139.162.32.245:8081/api/block/1293257" +``` + +Partial results shown: + +```json +{ + "data": { + "block_height": 1293257, + "block_reward": 0, + "current_height": 1293264, + "hash": "9ef6bb8f9b8bd253fc6390e5c2cdc45c8ee99fad16447437108bf301fe6bd6e1", + "size": 141244, + "timestamp": 1492761974, + "timestamp_utc": "2017-04-21 08:06:14", + "txs": [ + { + "coinbase": true, + "extra": "018ae9560eb85d5ebd22d3beaed55c21d469eab430c5e3cac61b3fe2f5ad156770020800000001a9030800", + "mixin": 0, + "payment_id": "", + "payment_id8": "", + "rct_type": 0, + "tx_fee": 0, + "tx_hash": "3ff71b65bec34c9261e01a856e6a03594cf0472acf6b77db3f17ebd18eaa30bf", + "tx_size": 95, + "tx_version": 2, + "xmr_inputs": 0, + "xmr_outputs": 8025365394426 + } + ] + }, + "status": "success" +} +``` + + +#### api/mempool + + +```bash +curl -w "\n" -X GET "http://139.162.32.245:8081/api/mempool" +``` + +Partial results shown: + +```json +{ + "data": [ + { + "coinbase": false, + "extra": "02210001c32d313b74a859b904079c69dbc04ea6e37eddcf4aeb34e9400cc12831da5401b34082a9ff7476fe29a19fa6a1735a9c59db226b9ddcf715928aa71625b13062", + "mixin": 7, + "payment_id": "01c32d313b74a859b904079c69dbc04ea6e37eddcf4aeb34e9400cc12831da54", + "payment_id8": "", + "rct_type": 1, + "timestamp": 1492763220, + "timestamp_utc": "2017-04-21 08:27:00", + "tx_fee": 4083040000, + "tx_hash": "6751e0029558fdc4ab4528896529e32b2864c6ad43c5d8838c8ebe156ada0514", + "tx_size": 13224, + "tx_version": 2, + "xmr_inputs": 0, + "xmr_outputs": 0 + } + ], + "status": "success" +} +``` + +#### api/search/ + +```bash +curl -w "\n" -X GET "http://139.162.32.245:8081/api/search/1293669" +``` + +Partial results shown: + +```json +{ + "data": { + "block_height": 1293669, + "current_height": 1293670, + "hash": "5d55b8fabf85b0b4c959d66ad509eb92ddfe5c2b0e84e1760abcb090195c1913", + "size": 118026, + "timestamp": 1492815321, + "timestamp_utc": "2017-04-21 22:55:21", + "title": "block", + "txs": [ + { + "coinbase": true, + "extra": "01cb7fda09033a5fa06dc601b9295ef3790397cf3c645e958e34cf7ab699d2f5230208000000027f030200", + "mixin": 0, + "payment_id": "", + "payment_id8": "", + "rct_type": 0, + "tx_fee": 0, + "tx_hash": "479ba432f5c88736b438dd4446a11a13046a752d469f7828151f5c5b86be4e9a", + "tx_size": 95, + "tx_version": 2, + "xmr_inputs": 0, + "xmr_outputs": 7992697599717 + } + ] + }, + "status": "success" +} +``` + +#### api/outputs?txhash=&address=
&viewkey=&txprove=<0|1> + +For `txprove=0` we check which outputs belong to given address and corresponding viewkey. +For `txprove=1` we use to prove to the recipient that we sent them founds. +For this, we use recipient's address and our tx private key as a viewkey value, + i.e., `viewkey=` + +Checking outputs: + +```bash +# we use here official Monero project's donation address as an example +curl -w "\n" -X GET "http://139.162.32.245:8081/api/outputs?txhash=17049bc5f2d9fbca1ce8dae443bbbbed2fc02f1ee003ffdd0571996905faa831&address=44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A&viewkey=f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501&txprove=0" +``` + +```json +{ + "data": { + "address": "42f18fc61586554095b0799b5c4b6f00cdeb26a93b20540d366932c6001617b75db35109fbba7d5f275fef4b9c49e0cc1c84b219ec6ff652fda54f89f7f63c88", + "outputs": [ + { + "amount": 34980000000000, + "match": true, + "output_idx": 0, + "output_pubkey": "35d7200229e725c2bce0da3a2f20ef0720d242ecf88bfcb71eff2025c2501fdb" + }, + { + "amount": 0, + "match": false, + "output_idx": 1, + "output_pubkey": "44efccab9f9b42e83c12da7988785d6c4eb3ec6e7aa2ae1234e2f0f7cb9ed6dd" + } + ], + "tx_hash": "17049bc5f2d9fbca1ce8dae443bbbbed2fc02f1ee003ffdd0571996905faa831", + "tx_prove": false, + "viewkey": "f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501" + }, + "status": "success" +} +``` + +Proving transfer: + +We use recipient's address (i.e. not our address from which we sent xmr to recipient). +For the viewkey, we use `tx_private_key` (although the GET variable is still called `viewkey`) that we obtained by sending this txs. + +```bash +# this is for testnet transaction +curl -w "\n" -X GET "http://139.162.32.245:8082/api/outputs?txhash=94782a8c0aa8d8768afa0c040ef0544b63eb5148ca971a024ac402cad313d3b3&address=9wUf8UcPUtb2huK7RphBw5PFCyKosKxqtGxbcKBDnzTCPrdNfJjLjtuht87zhTgsffCB21qmjxjj18Pw7cBnRctcKHrUB7N&viewkey=e94b5bfc599d2f741d6f07e3ab2a83f915e96fb374dfb2cd3dbe730e34ecb40b&txprove=1" +``` + +```json +{ + "data": { + "address": "71bef5945b70bc0a31dbbe6cd0bd5884fe694bbfd18fff5f68f709438554fb88a51b1291e378e2f46a0155108782c242cc1be78af229242c36d4f4d1c4f72da2", + "outputs": [ + { + "amount": 1000000000000, + "match": true, + "output_idx": 0, + "output_pubkey": "c1bf4dd020b5f0ab70bd672d2f9e800ea7b8ab108b080825c1d6cfc0b7f7ee00" + }, + { + "amount": 0, + "match": false, + "output_idx": 1, + "output_pubkey": "8c61fae6ada2a103565dfdd307c7145b2479ddb1dab1eaadfa6c34db65d189d5" + } + ], + "tx_hash": "94782a8c0aa8d8768afa0c040ef0544b63eb5148ca971a024ac402cad313d3b3", + "tx_prove": true, + "viewkey": "e94b5bfc599d2f741d6f07e3ab2a83f915e96fb374dfb2cd3dbe730e34ecb40b" + }, + "status": "success" +} +``` + +## Other monero examples Other examples can be found on [github](https://github.com/moneroexamples?tab=repositories). Please know that some of the examples/repositories are not diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index d2baf80..282bc15 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -7,13 +7,11 @@ project(myext) set(SOURCE_HEADERS minicsv.h - format.h - ) + fmt/format.h) set(SOURCE_FILES fmt/format.cc - fmt/ostream.cc - date/tz.cpp) + fmt/ostream.cc) # make static library called libmyxrm # that we are going to link to diff --git a/ext/date/chrono_io.h b/ext/date/chrono_io.h deleted file mode 100644 index d66a4f2..0000000 --- a/ext/date/chrono_io.h +++ /dev/null @@ -1,668 +0,0 @@ -#ifndef CHRONO_IO_H -#define CHRONO_IO_H - -// The MIT License (MIT) -// -// Copyright (c) 2016 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. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that woud involve another several millennia of evolution). -// We did not mean to shout. - -#include -#include -#include -#include -#include -#include -#include - -namespace date -{ - -namespace detail -{ - -#if __cplusplus >= 201402 - -template -class string_literal -{ - CharT p_[N]; - -public: - using const_iterator = const CharT*; - - string_literal(string_literal const&) = default; - string_literal& operator=(string_literal const&) = delete; - - template > - constexpr string_literal(CharT c) noexcept - : p_{c} - { - } - - constexpr string_literal(const CharT(&a)[N]) noexcept - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template > - constexpr string_literal(const char(&a)[N]) noexcept - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template {}>> - constexpr string_literal(string_literal const& a) noexcept - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template > - constexpr string_literal(const string_literal& x, - const string_literal& y) noexcept - : p_{} - { - std::size_t i = 0; - for (; i < N1-1; ++i) - p_[i] = x[i]; - for (std::size_t j = 0; j < N2; ++j, ++i) - p_[i] = y[j]; - } - - constexpr const CharT* data() const noexcept {return p_;} - constexpr std::size_t size() const noexcept {return N-1;} - - constexpr const_iterator begin() const noexcept {return p_;} - constexpr const_iterator end() const noexcept {return p_ + N-1;} - - constexpr CharT const& operator[](std::size_t n) const noexcept - { - return p_[n]; - } - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const string_literal& s) - { - return os << s.p_; - } -}; - -template -constexpr -inline -string_literal, - N1 + N2 - 1> -operator+(const string_literal& x, const string_literal& y) noexcept -{ - using CharT = std::conditional_t; - return string_literal{string_literal{x}, - string_literal{y}}; -} - -template -constexpr -inline -string_literal -msl(const CharT(&a)[N]) noexcept -{ - return string_literal{a}; -} - -template {} || - std::is_same{} || - std::is_same{} || - std::is_same{}>> -constexpr -inline -string_literal -msl(CharT c) noexcept -{ - return string_literal{c}; -} - -constexpr -std::size_t -to_string_len(std::intmax_t i) -{ - std::size_t r = 0; - do - { - i /= 10; - ++r; - } while (i > 0); - return r; -} - -template -constexpr -inline -std::enable_if_t -< - N < 10, - string_literal -> -msl() noexcept -{ - return msl(char(N % 10 + '0')); -} - -template -constexpr -inline -std::enable_if_t -< - 10 <= N, - string_literal -> -msl() noexcept -{ - return msl() + msl(char(N % 10 + '0')); -} - -template -constexpr -inline -std::enable_if_t -< - std::ratio::type::den != 1, - string_literal::type::num) + - to_string_len(std::ratio::type::den) + 4> -> -msl(std::ratio) noexcept -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{'/'}) + - msl() + msl(CharT{']'}); -} - -template -constexpr -inline -std::enable_if_t -< - std::ratio::type::den == 1, - string_literal::type::num) + 3> -> -msl(std::ratio) noexcept -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{']'}); -} - -template -constexpr -inline -auto -msl(std::atto) noexcept -{ - return msl(CharT{'a'}); -} - -template -constexpr -inline -auto -msl(std::femto) noexcept -{ - return msl(CharT{'f'}); -} - -template -constexpr -inline -auto -msl(std::pico) noexcept -{ - return msl(CharT{'p'}); -} - -template -constexpr -inline -auto -msl(std::nano) noexcept -{ - return msl(CharT{'n'}); -} - -template -constexpr -inline -std::enable_if_t -< - std::is_same{}, - string_literal -> -msl(std::micro) noexcept -{ - return string_literal{"\xC2\xB5"}; -} - -template -constexpr -inline -std::enable_if_t -< - !std::is_same{}, - string_literal -> -msl(std::micro) noexcept -{ - return string_literal{CharT{static_cast('\xB5')}}; -} - -template -constexpr -inline -auto -msl(std::milli) noexcept -{ - return msl(CharT{'m'}); -} - -template -constexpr -inline -auto -msl(std::centi) noexcept -{ - return msl(CharT{'c'}); -} - -template -constexpr -inline -auto -msl(std::deci) noexcept -{ - return msl(CharT{'d'}); -} - -template -constexpr -inline -auto -msl(std::deca) noexcept -{ - return string_literal{"da"}; -} - -template -constexpr -inline -auto -msl(std::hecto) noexcept -{ - return msl(CharT{'h'}); -} - -template -constexpr -inline -auto -msl(std::kilo) noexcept -{ - return msl(CharT{'k'}); -} - -template -constexpr -inline -auto -msl(std::mega) noexcept -{ - return msl(CharT{'M'}); -} - -template -constexpr -inline -auto -msl(std::giga) noexcept -{ - return msl(CharT{'G'}); -} - -template -constexpr -inline -auto -msl(std::tera) noexcept -{ - return msl(CharT{'T'}); -} - -template -constexpr -inline -auto -msl(std::peta) noexcept -{ - return msl(CharT{'P'}); -} - -template -constexpr -inline -auto -msl(std::exa) noexcept -{ - return msl(CharT{'E'}); -} - -template -constexpr -auto -get_units(Period p) -{ - return msl(p) + string_literal{"s"}; -} - -template -constexpr -auto -get_units(std::ratio<1>) -{ - return string_literal{"s"}; -} - -template -constexpr -auto -get_units(std::ratio<60>) -{ - return string_literal{"min"}; -} - -template -constexpr -auto -get_units(std::ratio<3600>) -{ - return string_literal{"h"}; -} - -#else // __cplusplus < 201402 - -inline -std::string -to_string(std::uint64_t x) -{ - return std::to_string(x); -} - -template -std::basic_string -to_string(std::uint64_t x) -{ - auto y = std::to_string(x); - return std::basic_string(y.begin(), y.end()); -} - -template -constexpr -inline -typename std::enable_if -< - std::ratio::type::den != 1, - std::basic_string ->::type -msl(std::ratio) noexcept -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{'/'} + - to_string(R::den) + CharT{']'}; -} - -template -constexpr -inline -typename std::enable_if -< - std::ratio::type::den == 1, - std::basic_string ->::type -msl(std::ratio) noexcept -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{']'}; -} - -template -constexpr -inline -std::basic_string -msl(std::atto) noexcept -{ - return {'a'}; -} - -template -constexpr -inline -std::basic_string -msl(std::femto) noexcept -{ - return {'f'}; -} - -template -constexpr -inline -std::basic_string -msl(std::pico) noexcept -{ - return {'p'}; -} - -template -constexpr -inline -std::basic_string -msl(std::nano) noexcept -{ - return {'n'}; -} - -template -constexpr -inline -typename std::enable_if -< - std::is_same::value, - std::string ->::type -msl(std::micro) noexcept -{ - return "\xC2\xB5"; -} - -template -constexpr -inline -typename std::enable_if -< - !std::is_same::value, - std::basic_string ->::type -msl(std::micro) noexcept -{ - return {CharT(static_cast('\xB5'))}; -} - -template -constexpr -inline -std::basic_string -msl(std::milli) noexcept -{ - return {'m'}; -} - -template -constexpr -inline -std::basic_string -msl(std::centi) noexcept -{ - return {'c'}; -} - -template -constexpr -inline -std::basic_string -msl(std::deci) noexcept -{ - return {'d'}; -} - -template -constexpr -inline -std::basic_string -msl(std::deca) noexcept -{ - return {'d', 'a'}; -} - -template -constexpr -inline -std::basic_string -msl(std::hecto) noexcept -{ - return {'h'}; -} - -template -constexpr -inline -std::basic_string -msl(std::kilo) noexcept -{ - return {'k'}; -} - -template -constexpr -inline -std::basic_string -msl(std::mega) noexcept -{ - return {'M'}; -} - -template -constexpr -inline -std::basic_string -msl(std::giga) noexcept -{ - return {'G'}; -} - -template -constexpr -inline -std::basic_string -msl(std::tera) noexcept -{ - return {'T'}; -} - -template -constexpr -inline -std::basic_string -msl(std::peta) noexcept -{ - return {'P'}; -} - -template -constexpr -inline -std::basic_string -msl(std::exa) noexcept -{ - return {'E'}; -} - -template -std::basic_string -get_units(Period p) -{ - return msl(p) + CharT{'s'}; -} - -template -std::basic_string -get_units(std::ratio<1>) -{ - return {'s'}; -} - -template -std::basic_string -get_units(std::ratio<60>) -{ - return {'m', 'i', 'n'}; -} - -template -std::basic_string -get_units(std::ratio<3600>) -{ - return {'h'}; -} - -#endif // __cplusplus >= 201402 - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, - const std::chrono::duration& d) -{ - using namespace std::chrono; - return os << d.count() - << detail::get_units(typename Period::type{}); -} - -} // namespace date - -#endif // CHRONO_IO_H diff --git a/ext/date/date.h b/ext/date/date.h deleted file mode 100644 index 5ff8f01..0000000 --- a/ext/date/date.h +++ /dev/null @@ -1,4823 +0,0 @@ -#ifndef DATE_H -#define DATE_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016 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. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that woud involve another several millennia of evolution). -// We did not mean to shout. - -#include -#include -#if !(__cplusplus >= 201402) -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace date -{ - -//---------------+ -// Configuration | -//---------------+ - -// MSVC's constexpr support is still a WIP, even in VS2015. -// Fall back to a lesser mode to support it. -// TODO: Remove this or retest later once MSVC's constexpr improves. -#if defined(_MSC_VER) && ! defined(__clang__) -// MS cl compiler. -# define CONSTDATA const -# define CONSTCD11 -# define CONSTCD14 -# define NOEXCEPT _NOEXCEPT -#elif __cplusplus >= 201402 -// C++14 -# define CONSTDATA constexpr -# define CONSTCD11 constexpr -# define CONSTCD14 constexpr -# define NOEXCEPT noexcept -#else -// C++11 -# define CONSTDATA constexpr -# define CONSTCD11 constexpr -# define CONSTCD14 -# define NOEXCEPT noexcept -#endif - -//-----------+ -// Interface | -//-----------+ - -// durations - -using days = std::chrono::duration - , std::chrono::hours::period>>; - -using weeks = std::chrono::duration - , days::period>>; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -template - using sys_time = std::chrono::time_point; - -using sys_days = sys_time; -using sys_seconds = sys_time; - -struct local_t {}; - -template - using local_time = std::chrono::time_point; - -using local_seconds = local_time; -using local_days = local_time; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - day() = default; - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - month() = default; - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - year() = default; - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 year operator-() const NOEXCEPT; - CONSTCD11 year operator+() const NOEXCEPT; - - CONSTCD11 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT; - static CONSTCD11 year max() NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - weekday() = default; - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - explicit weekday(int) = delete; - CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - date::weekday wd_; - -public: - explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -// year_month - -class year_month -{ - date::year y_; - date::month m_; - -public: - year_month() = default; - CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - date::month m_; - date::day d_; - -public: - month_day() = default; - CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - date::month m_; - -public: - CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - date::month m_; - date::weekday_indexed wdi_; -public: - CONSTCD11 month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 month_weekday_last(const date::month& m, - const date::weekday_last& wd) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - date::year y_; - date::month m_; - date::day d_; - -public: - year_month_day() = default; - CONSTCD11 year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - date::year y_; - date::month_day_last mdl_; - -public: - CONSTCD11 year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT; - - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - date::year y_; - date::month m_; - date::weekday_indexed wdi_; - -public: - year_month_weekday() = default; - CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - date::year y_; - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, - const date::weekday_last& wdl) NOEXCEPT; - - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; - -// CONSTDATA date::month jan{1}; -// CONSTDATA date::month feb{2}; -// CONSTDATA date::month mar{3}; -// CONSTDATA date::month apr{4}; -// CONSTDATA date::month may{5}; -// CONSTDATA date::month jun{6}; -// CONSTDATA date::month jul{7}; -// CONSTDATA date::month aug{8}; -// CONSTDATA date::month sep{9}; -// CONSTDATA date::month oct{10}; -// CONSTDATA date::month nov{11}; -// CONSTDATA date::month dec{12}; -// -// CONSTDATA date::weekday sun{0u}; -// CONSTDATA date::weekday mon{1u}; -// CONSTDATA date::weekday tue{2u}; -// CONSTDATA date::weekday wed{3u}; -// CONSTDATA date::weekday thu{4u}; -// CONSTDATA date::weekday fri{5u}; -// CONSTDATA date::weekday sat{6u}; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -//----------------+ -// Implementation | -//----------------+ - -// utilities -namespace detail { - -template> -class save_stream -{ - std::basic_ostream& os_; - CharT fill_; - std::ios::fmtflags flags_; - std::locale loc_; - -public: - ~save_stream() - { - os_.fill(fill_); - os_.flags(flags_); - os_.imbue(loc_); - } - - save_stream(const save_stream&) = delete; - save_stream& operator=(const save_stream&) = delete; - - explicit save_stream(std::basic_ostream& os) - : os_(os) - , fill_(os.fill()) - , flags_(os.flags()) - , loc_(os.getloc()) - {} -}; - -#ifdef __GNUC__ -// GCC complains about __int128 with -pedantic or -pedantic-errors -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" -#endif - -template -struct choose_trunc_type -{ - static const int digits = std::numeric_limits::digits; - using type = typename std::conditional - < - digits < 32, - std::int32_t, - typename std::conditional - < - digits < 64, - std::int64_t, -#ifdef __SIZEOF_INT128__ - __int128 -#else - std::int64_t -#endif - >::type - >::type; -}; - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -template -CONSTCD11 -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - return t; -} - -template -CONSTCD14 -inline -typename std::enable_if -< - std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - using namespace std; - using I = typename choose_trunc_type::type; - CONSTDATA auto digits = numeric_limits::digits; - static_assert(digits < numeric_limits::digits, ""); - CONSTDATA auto max = I{1} << (digits-1); - CONSTDATA auto min = -max; - const auto negative = t < T{0}; - if (min <= t && t <= max && t != 0 && t == t) - { - t = static_cast(static_cast(t)); - if (t == 0 && negative) - t = -t; - } - return t; -} - -} // detail - -// trunc towards zero -template -CONSTCD11 -inline -To -trunc(const std::chrono::duration& d) -{ - return To{detail::trunc(std::chrono::duration_cast(d).count())}; -} - -#ifndef HAS_CHRONO_ROUNDING -# if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 -# define HAS_CHRONO_ROUNDING 1 -# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 -# define HAS_CHRONO_ROUNDING 1 -# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 -# define HAS_CHRONO_ROUNDING 1 -# else -# define HAS_CHRONO_ROUNDING 0 -# endif -#endif // HAS_CHRONO_ROUNDING - -#if HAS_CHRONO_ROUNDING == 0 - -// round down -template -CONSTCD14 -inline -To -floor(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t > d) - return t - To{1}; - return t; -} - -// round to nearest, to even on tie -template -CONSTCD14 -inline -To -round(const std::chrono::duration& d) -{ - auto t0 = floor(d); - auto t1 = t0 + To{1}; - if (t1 == To{0} && t0 < To{0}) - t1 = -t1; - auto diff0 = d - t0; - auto diff1 = t1 - d; - if (diff0 == diff1) - { - if (t0 - trunc(t0/2)*2 == To{0}) - return t0; - return t1; - } - if (diff0 < diff1) - return t0; - return t1; -} - -// round up -template -CONSTCD14 -inline -To -ceil(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t < d) - return t + To{1}; - return t; -} - -template ::is_signed - >::type> -CONSTCD11 -std::chrono::duration -abs(std::chrono::duration d) -{ - return d >= d.zero() ? d : -d; -} - -// round down -template -CONSTCD11 -inline -std::chrono::time_point -floor(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{floor(tp.time_since_epoch())}; -} - -// round to nearest, to even on tie -template -CONSTCD11 -inline -std::chrono::time_point -round(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{round(tp.time_since_epoch())}; -} - -// round up -template -CONSTCD11 -inline -std::chrono::time_point -ceil(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{ceil(tp.time_since_epoch())}; -} - -#else // HAS_CHRONO_ROUNDING == 1 - -using std::chrono::floor; -using std::chrono::ceil; -using std::chrono::round; -using std::chrono::abs; - -#endif // HAS_CHRONO_ROUNDING - -// trunc towards zero -template -CONSTCD11 -inline -std::chrono::time_point -trunc(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{trunc(tp.time_since_epoch())}; -} - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - detail::save_stream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) - 1 + y.count(); - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - switch (static_cast(m)) - { - case 1: - os << "Jan"; - break; - case 2: - os << "Feb"; - break; - case 3: - os << "Mar"; - break; - case 4: - os << "Apr"; - break; - case 5: - os << "May"; - break; - case 6: - os << "Jun"; - break; - case 7: - os << "Jul"; - break; - case 8: - os << "Aug"; - break; - case 9: - os << "Sep"; - break; - case 10: - os << "Oct"; - break; - case 11: - os << "Nov"; - break; - case 12: - os << "Dec"; - break; - default: - os << static_cast(m) << " is not a valid month"; - break; - } - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} -CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} -CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} - -CONSTCD11 -inline -bool -year::is_leap() const NOEXCEPT -{ - return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} -CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} - -CONSTCD11 -inline -year -year::min() NOEXCEPT -{ - return year{std::numeric_limits::min()}; -} - -CONSTCD11 -inline -year -year::max() NOEXCEPT -{ - return year{std::numeric_limits::max()}; -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - detail::save_stream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os << static_cast(y); - return os; -} - -// weekday - -CONSTCD11 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - return static_cast(static_cast( - z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd)) - {} - -CONSTCD11 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD11 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 -inline -weekday::operator unsigned() const NOEXCEPT -{ - return static_cast(wd_); -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const diff = static_cast(x) - static_cast(y); - return days{diff <= 6 ? diff : diff + 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - switch (static_cast(wd)) - { - case 0: - os << "Sun"; - break; - case 1: - os << "Mon"; - break; - case 2: - os << "Tue"; - break; - case 3: - os << "Wed"; - break; - case 4: - os << "Thu"; - break; - case 5: - os << "Fri"; - break; - case 6: - os << "Sat"; - break; - default: - os << static_cast(wd) << " is not a valid weekday"; - break; - } - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -date::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return date::day{static_cast(d)}; -} - -CONSTCD11 -inline -date::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return date::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA date::last_spec last{}; - -CONSTDATA date::month jan{1}; -CONSTDATA date::month feb{2}; -CONSTDATA date::month mar{3}; -CONSTDATA date::month apr{4}; -CONSTDATA date::month may{5}; -CONSTDATA date::month jun{6}; -CONSTDATA date::month jul{7}; -CONSTDATA date::month aug{8}; -CONSTDATA date::month sep{9}; -CONSTDATA date::month oct{10}; -CONSTDATA date::month nov{11}; -CONSTDATA date::month dec{12}; - -CONSTDATA date::weekday sun{0u}; -CONSTDATA date::weekday mon{1u}; -CONSTDATA date::weekday tue{2u}; -CONSTDATA date::weekday wed{3u}; -CONSTDATA date::weekday thu{4u}; -CONSTDATA date::weekday fri{5u}; -CONSTDATA date::weekday sat{6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return date::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd))) - , index_(static_cast(index)) - {} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return os << wdi.weekday() << '[' << wdi.index() << ']'; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - return os << wdl.weekday() << "[last]"; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - return os << ym.year() << '/' << ym.month(); -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(29), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - return os << md.month() << '/' << md.day(); -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - return os << mdl.month() << "/last"; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - return os << mwd.month() << '/' << mwd.weekday_indexed(); -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - return os << mwdl.month() << '/' << mwdl.weekday_last(); -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(28), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return month() != feb || !y_.is_leap() ? - d[static_cast(month()) - 1] : date::day{29}; -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - return os << ymdl.year() << '/' << ymdl.month_day_last(); -} - -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const y = static_cast(y_) - (m_ <= feb); - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era = (y >= 0 ? y : y-399) / 400; - auto const yoe = static_cast(y - era * 400); // [0, 399] - auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] - auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] - return days{era * 146097 + static_cast(doe) - 719468}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - detail::save_stream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os << ymd.year() << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os << ymd.day(); - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const z = dp.count() + 719468; - auto const era = (z >= 0 ? z : z - 146096) / 146097; - auto const doe = static_cast(z - era * 146097); // [0, 146096] - auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] - auto const y = static_cast(yoe) + era * 400; - auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] - auto const mp = (5*doy + 2)/153; // [0, 11] - auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] - auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] - return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; -} - -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - date::weekday(static_cast(y_/m_/1)) + days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = date::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - return os << ymwdi.year() << '/' << ymwdi.month() - << '/' << ymwdi.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const date::year& y, - const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -// time_of_day - -enum {am = 1, pm}; - -namespace detail -{ - -enum class classify -{ - not_valid, - hour, - minute, - second, - subsecond -}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - -template -struct classify_duration -{ - static CONSTDATA classify value = - Duration{1} >= days{1} ? classify::not_valid : - Duration{1} >= std::chrono::hours{1} ? classify::hour : - Duration{1} >= std::chrono::minutes{1} ? classify::minute : - Duration{1} >= std::chrono::seconds{1} ? classify::second : - classify::subsecond; -}; - -#else - -template -struct classify_duration -{ - static CONSTDATA classify value = - std::ratio_greater_equal< - typename Duration::period, - days::period >::value - ? classify::not_valid : - std::ratio_greater_equal< - typename Duration::period, - std::chrono::hours::period>::value - ? classify::hour : - std::ratio_greater_equal< - typename Duration::period, - std::chrono::minutes::period>::value - ? classify::minute : - std::ratio_greater_equal< - typename Duration::period, - std::chrono::seconds::period>::value - ? classify::second : - classify::subsecond; -}; - -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -class time_of_day_base -{ -protected: - std::chrono::hours h_; - unsigned char mode_; - - enum {is24hr}; - - CONSTCD11 time_of_day_base(std::chrono::hours h, unsigned m) NOEXCEPT - : h_(h) - , mode_(static_cast(m)) - {} - - CONSTCD14 void make24() NOEXCEPT; - CONSTCD14 void make12() NOEXCEPT; - - CONSTCD14 std::chrono::hours to24hr() const; -}; - -CONSTCD14 -inline -std::chrono::hours -time_of_day_base::to24hr() const -{ - auto h = h_; - if (mode_ == am || mode_ == pm) - { - CONSTDATA auto h12 = std::chrono::hours(12); - if (mode_ == pm) - { - if (h != h12) - h = h + h12; - } - else if (h == h12) - h = std::chrono::hours(0); - } - return h; -} - -CONSTCD14 -inline -void -time_of_day_base::make24() NOEXCEPT -{ - h_ = to24hr(); - mode_ = is24hr; -} - -CONSTCD14 -inline -void -time_of_day_base::make12() NOEXCEPT -{ - if (mode_ == is24hr) - { - CONSTDATA auto h12 = std::chrono::hours(12); - if (h_ >= h12) - { - if (h_ > h12) - h_ = h_ - h12; - mode_ = pm; - } - else - { - if (h_ == std::chrono::hours(0)) - h_ = h12; - mode_ = am; - } - } -} - -template ::value> -class time_of_day_storage; - -template -class time_of_day_storage, detail::classify::hour> - : private detail::time_of_day_base -{ - using base = detail::time_of_day_base; - -public: - using precision = std::chrono::hours; - - CONSTCD11 explicit time_of_day_storage(std::chrono::hours since_midnight) NOEXCEPT - : base(since_midnight, is24hr) - {} - - CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, unsigned md) NOEXCEPT - : base(h, md) - {} - - CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} - CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} - - CONSTCD14 explicit operator precision() const NOEXCEPT - { - return to24hr(); - } - - CONSTCD14 precision to_duration() const NOEXCEPT - { - return static_cast(*this); - } - - CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} - CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const time_of_day_storage& t) - { - using namespace std; - detail::save_stream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - if (t.mode_ != am && t.mode_ != pm) - os.width(2); - os << t.h_.count(); - switch (t.mode_) - { - case time_of_day_storage::is24hr: - os << "00"; - break; - case am: - os << "am"; - break; - case pm: - os << "pm"; - break; - } - return os; - } -}; - -template -class time_of_day_storage, detail::classify::minute> - : private detail::time_of_day_base -{ - using base = detail::time_of_day_base; - - std::chrono::minutes m_; - -public: - using precision = std::chrono::minutes; - - CONSTCD11 explicit time_of_day_storage(std::chrono::minutes since_midnight) NOEXCEPT - : base(std::chrono::duration_cast(since_midnight), is24hr) - , m_(since_midnight - h_) - {} - - CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, - unsigned md) NOEXCEPT - : base(h, md) - , m_(m) - {} - - CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} - CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} - CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} - - CONSTCD14 explicit operator precision() const NOEXCEPT - { - return to24hr() + m_; - } - - CONSTCD14 precision to_duration() const NOEXCEPT - { - return static_cast(*this); - } - - CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} - CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const time_of_day_storage& t) - { - using namespace std; - detail::save_stream _(os); - if (static_cast(t) < std::chrono::hours{0}) - os << '-'; - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - if (t.mode_ != am && t.mode_ != pm) - os.width(2); - os << std::abs(t.h_.count()) << ':'; - os.width(2); - os << std::abs(t.m_.count()); - switch (t.mode_) - { - case am: - os << "am"; - break; - case pm: - os << "pm"; - break; - } - return os; - } -}; - -template -class time_of_day_storage, detail::classify::second> - : private detail::time_of_day_base -{ - using base = detail::time_of_day_base; - - std::chrono::minutes m_; - std::chrono::seconds s_; - -public: - using precision = std::chrono::seconds; - - CONSTCD11 explicit time_of_day_storage(std::chrono::seconds since_midnight) NOEXCEPT - : base(std::chrono::duration_cast(since_midnight), is24hr) - , m_(std::chrono::duration_cast(since_midnight - h_)) - , s_(since_midnight - h_ - m_) - {} - - CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, - std::chrono::seconds s, unsigned md) NOEXCEPT - : base(h, md) - , m_(m) - , s_(s) - {} - - CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} - CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} - CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} - CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} - - CONSTCD14 explicit operator precision() const NOEXCEPT - { - return to24hr() + s_ + m_; - } - - CONSTCD14 precision to_duration() const NOEXCEPT - { - return static_cast(*this); - } - - CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} - CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const time_of_day_storage& t) - { - using namespace std; - detail::save_stream _(os); - if (static_cast(t) < std::chrono::hours{0}) - os << '-'; - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - if (t.mode_ != am && t.mode_ != pm) - os.width(2); - os << std::abs(t.h_.count()) << ':'; - os.width(2); - os << std::abs(t.m_.count()) << ':'; - os.width(2); - os << std::abs(t.s_.count()); - switch (t.mode_) - { - case am: - os << "am"; - break; - case pm: - os << "pm"; - break; - } - return os; - } -}; - -template -class time_of_day_storage, detail::classify::subsecond> - : private detail::time_of_day_base -{ -public: - using precision = std::chrono::duration; - -private: - using base = detail::time_of_day_base; - - std::chrono::minutes m_; - std::chrono::seconds s_; - precision sub_s_; - -public: - CONSTCD11 explicit time_of_day_storage(precision since_midnight) NOEXCEPT - : base(std::chrono::duration_cast(since_midnight), is24hr) - , m_(std::chrono::duration_cast(since_midnight - h_)) - , s_(std::chrono::duration_cast(since_midnight - h_ - m_)) - , sub_s_(since_midnight - h_ - m_ - s_) - {} - - CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, - std::chrono::seconds s, precision sub_s, - unsigned md) NOEXCEPT - : base(h, md) - , m_(m) - , s_(s) - , sub_s_(sub_s) - {} - - CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} - CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} - CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} - CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} - CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} - - CONSTCD14 explicit operator precision() const NOEXCEPT - { - return to24hr() + s_ + sub_s_ + m_; - } - - CONSTCD14 precision to_duration() const NOEXCEPT - { - return static_cast(*this); - } - - CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} - CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const time_of_day_storage& t) - { - using namespace std; - detail::save_stream _(os); - if (static_cast(t) < std::chrono::hours{0}) - os << '-'; - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - if (t.mode_ != am && t.mode_ != pm) - os.width(2); - os << std::abs(t.h_.count()) << ':'; - os.width(2); - os << std::abs(t.m_.count()) << ':'; - os.width(2); - os << std::abs(t.s_.count()) - << use_facet>(os.getloc()).decimal_point(); - os.imbue(locale{}); -#if __cplusplus >= 201402 - CONSTDATA auto cl10 = ceil_log10(Period::den); - using scale = std::ratio_multiply>; - os.width(cl10); - os << std::abs(t.sub_s_.count()) * scale::num / scale::den; -#else // __cplusplus >= 201402 - // inefficient sub-optimal run-time mess, but gets the job done - const unsigned long long cl10 = - static_cast(std::ceil(log10(Period::den))); - const auto p10 = std::pow(10., cl10); - os.width(cl10); - os << static_cast(std::abs(t.sub_s_.count()) - * Period::num * p10 / Period::den); -#endif // __cplusplus >= 201402 - switch (t.mode_) - { - case am: - os << "am"; - break; - case pm: - os << "pm"; - break; - } - return os; - } - -private: -#if __cplusplus >= 201402 - CONSTCD11 static int ceil_log10(unsigned long long i) NOEXCEPT - { - --i; - int n = 0; - if (i >= 10000000000000000) {i /= 10000000000000000; n += 16;} - if (i >= 100000000) {i /= 100000000; n += 8;} - if (i >= 10000) {i /= 10000; n += 4;} - if (i >= 100) {i /= 100; n += 2;} - if (i >= 10) {i /= 10; n += 1;} - if (i >= 1) {i /= 10; n += 1;} - return n; - } - - CONSTCD11 static unsigned long long pow10(unsigned y) NOEXCEPT - { - CONSTDATA unsigned long long p10[] = - { - 1ull, - 10ull, - 100ull, - 1000ull, - 10000ull, - 100000ull, - 1000000ull, - 10000000ull, - 100000000ull, - 1000000000ull, - 10000000000ull, - 100000000000ull, - 1000000000000ull, - 10000000000000ull, - 100000000000000ull, - 1000000000000000ull, - 10000000000000000ull, - 100000000000000000ull, - 1000000000000000000ull, - 10000000000000000000ull - }; - return p10[y]; - } -#endif // __cplusplus >= 201402 -}; - -} // namespace detail - -template -class time_of_day - : public detail::time_of_day_storage -{ - using base = detail::time_of_day_storage; -public: -#if !(defined(_MSC_VER) && !defined(__clang__)) - // C++11 - using base::base; -#else - // MS cl compiler workaround. - template - explicit time_of_day(Args&& ...args) - : base(std::forward(args)...) - {} -#endif -}; - -template ::value>::type> -CONSTCD11 -inline -time_of_day> -make_time(const std::chrono::duration& d) -{ - return time_of_day>(d); -} - -CONSTCD11 -inline -time_of_day -make_time(const std::chrono::hours& h, unsigned md) -{ - return time_of_day(h, md); -} - -CONSTCD11 -inline -time_of_day -make_time(const std::chrono::hours& h, const std::chrono::minutes& m, - unsigned md) -{ - return time_of_day(h, m, md); -} - -CONSTCD11 -inline -time_of_day -make_time(const std::chrono::hours& h, const std::chrono::minutes& m, - const std::chrono::seconds& s, unsigned md) -{ - return time_of_day(h, m, s, md); -} - -template >::value>::type> -CONSTCD11 -inline -time_of_day> -make_time(const std::chrono::hours& h, const std::chrono::minutes& m, - const std::chrono::seconds& s, const std::chrono::duration& sub_s, - unsigned md) -{ - return time_of_day>(h, m, s, sub_s, md); -} - -template -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value && - std::ratio_less::value - , std::basic_ostream& ->::type -operator<<(std::basic_ostream& os, const sys_time& tp) -{ - auto const dp = floor(tp); - return os << year_month_day(dp) << ' ' << make_time(tp-dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const sys_days& dp) -{ - return os << year_month_day(dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& ut) -{ - return os << sys_time{ut.time_since_epoch()}; -} - -// format - -namespace detail -{ - -template -std::basic_string -format(const std::locale& loc, std::basic_string fmt, - const local_time& tp, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr) -{ - // Handle these specially - // %S append fractional seconds if tp has precision finer than seconds - // %T append fractional seconds if tp has precision finer than seconds - // %z replace with offset from zone on +/-hhmm format - // %Ez, %Oz replace with offset from zone on +/-hh:mm format - // %Z replace with abbreviation from zone - - using namespace std; - using namespace std::chrono; - auto command = false; - auto modified = false; - for (std::size_t i = 0; i < fmt.size(); ++i) - { - switch (fmt[i]) - { - case '%': - command = true; - modified = false; - break; - case 'O': - case 'E': - modified = true; - break; - case 'S': - case 'T': - if (command && !modified && ratio_less>::value) - { - basic_ostringstream os; - os.imbue(loc); - os << make_time(tp - floor(tp)); - auto s = os.str(); - s.erase(0, 8); - fmt.insert(i+1, s); - i += s.size() - 1; - } - command = false; - modified = false; - break; - case 'z': - if (command) - { - if (offset_sec == nullptr) - throw std::runtime_error("Can not format local_time with %z"); - else - { - auto offset = duration_cast(*offset_sec); - basic_ostringstream os; - os.imbue(loc); - if (offset >= minutes{0}) - os << '+'; - os << make_time(offset); - auto s = os.str(); - if (!modified) - s.erase(s.find(':'), 1); - fmt.replace(i - 1 - modified, 2 + modified, s); - i += 3; - } - } - command = false; - modified = false; - break; - case 'Z': - if (command && !modified) - { - if (abbrev == nullptr) - throw std::runtime_error("Can not format local_time with %Z"); - else - { - fmt.replace(i - 1, 2, - std::basic_string(abbrev->begin(), abbrev->end())); - i += abbrev->size() - 1; - } - } - command = false; - modified = false; - break; - default: - command = false; - modified = false; - break; - } - } - auto& f = use_facet>(loc); - basic_ostringstream os; - os.imbue(loc); - auto ld = floor(tp); - auto ymd = year_month_day{ld}; - auto hms = make_time(floor(tp - ld)); - std::tm tm{}; - tm.tm_sec = static_cast(hms.seconds().count()); - tm.tm_min = static_cast(hms.minutes().count()); - tm.tm_hour = static_cast(hms.hours().count()); - tm.tm_mday = static_cast(static_cast(ymd.day())); - tm.tm_mon = static_cast(static_cast(ymd.month()) - 1); - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(static_cast(weekday{ld})); - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - f.put(os, os, os.fill(), &tm, fmt.data(), fmt.data() + fmt.size()); - return os.str(); -} - -} // namespace detail - -template -inline -std::basic_string -format(const std::locale& loc, std::basic_string fmt, - const local_time& tp) -{ - return detail::format(loc, std::move(fmt), tp); -} - -template -inline -std::basic_string -format(std::basic_string fmt, const local_time& tp) -{ - return detail::format(std::locale{}, std::move(fmt), tp); -} - -template -inline -std::basic_string -format(const std::locale& loc, std::basic_string fmt, - const sys_time& tp) -{ - const std::string abbrev("UTC"); - CONSTDATA std::chrono::seconds offset{0}; - return detail::format(loc, std::move(fmt), - local_time{tp.time_since_epoch()}, &abbrev, &offset); -} - -template -inline -std::basic_string -format(std::basic_string fmt, const sys_time& tp) -{ - const std::string abbrev("UTC"); - CONSTDATA std::chrono::seconds offset{0}; - return detail::format(std::locale{}, std::move(fmt), - local_time{tp.time_since_epoch()}, &abbrev, &offset); -} - -// const CharT* formats - -template -inline -std::basic_string -format(const std::locale& loc, const CharT* fmt, const local_time& tp) -{ - return detail::format(loc, std::basic_string(fmt), tp); -} - -template -inline -std::basic_string -format(const CharT* fmt, const local_time& tp) -{ - return detail::format(std::locale{}, std::basic_string(fmt), tp); -} - -template -inline -std::basic_string -format(const std::locale& loc, const CharT* fmt, const sys_time& tp) -{ - const std::string abbrev("UTC"); - CONSTDATA std::chrono::seconds offset{0}; - return detail::format(loc, std::basic_string(fmt), - local_time{tp.time_since_epoch()}, - &abbrev, &offset); -} - -template -inline -std::basic_string -format(const CharT* fmt, const sys_time& tp) -{ - const std::string abbrev("UTC"); - CONSTDATA std::chrono::seconds offset{0}; - return detail::format(std::locale{}, std::basic_string(fmt), - local_time{tp.time_since_epoch()}, - &abbrev, &offset); -} - -// parse - -namespace detail -{ - -template -void -parse(std::basic_istream& is, - const std::basic_string& format, local_time& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using namespace std; - using namespace std::chrono; - typename basic_istream::sentry ok{is}; - if (ok) - { - auto& f = use_facet>(is.getloc()); - ios_base::iostate err = ios_base::goodbit; - std::tm tm{}; - Duration subseconds{}; - std::basic_string temp_abbrev; - minutes temp_offset{}; - - auto b = format.data(); - auto i = b; - auto e = b + format.size(); - auto command = false; - auto modified = false; - for (; i < e && 0 == (err & ios_base::failbit); ++i) - { - switch (*i) - { - case '%': - command = true; - modified = false; - break; - case 'F': - if (command && !modified) - { - f.get(is, 0, is, err, &tm, b, i-1); - b = i+1; - if ((err & ios_base::failbit) == 0) - { - const CharT ymd[] = {'%', 'Y', '-', '%', 'm', '-', '%', 'd'}; - f.get(is, 0, is, err, &tm, ymd, ymd+8); - } - } - command = false; - modified = false; - break; - case 'O': - case 'E': - modified = true; - break; - case 'T': - case 'S': - if (command && !modified) - { - f.get(is, 0, is, err, &tm, b, i-1); - if (err & ios_base::failbit) - { - command = modified = false; - break; // break the switch/case - } - b = i+1; - if (*i == 'T') - { - const CharT hm[] = {'%', 'H', ':', '%', 'M', ':'}; - f.get(is, 0, is, err, &tm, hm, hm+6); - if (err & ios_base::failbit) - { - command = modified = false; - break; // break the switch/case - } - } - if (ratio_less>::value) - { - auto decimal_point = Traits::to_int_type( - use_facet>(is.getloc()).decimal_point()); - string buf; - while (true) - { - auto k = is.peek(); - if (Traits::eq_int_type(k, Traits::eof())) - break; - if (Traits::eq_int_type(k, decimal_point)) - { - buf += '.'; - decimal_point = Traits::eof(); - is.get(); - } - else - { - auto c = static_cast(Traits::to_char_type(k)); - if (isdigit(c)) - { - buf += c; - is.get(); - } - else - { - break; - } - } - }; - if (!buf.empty()) - subseconds = round(duration{stod(buf)}); - else - err |= ios_base::failbit; - } - else - { - const CharT hm[] = {'%', 'S'}; - f.get(is, 0, is, err, &tm, hm, hm+2); - } - } - command = false; - modified = false; - break; - case 'z': - if (command) - { - f.get(is, 0, is, err, &tm, b, i-1-modified); - b = i+1; - if ((err & ios_base::failbit) == 0) - { - CharT sign{}; - is >> sign; - if (!is.fail() && (sign == '+' || sign == '-')) - { - char h1, h0, m1, m0; - char colon = ':'; - h1 = static_cast(is.get()); - h0 = static_cast(is.get()); - if (modified) - { - if (h0 == ':') - { - colon = h0; - h0 = h1; - h1 = '0'; - } - else - colon = static_cast(is.get()); - } - m1 = static_cast(is.get()); - m0 = static_cast(is.get()); - if (!is.fail() && std::isdigit(h1) && std::isdigit(h0) - && std::isdigit(m1) && std::isdigit(m0) - && colon == ':') - { - temp_offset = 10*hours{h1 - '0'} + hours{h0 - '0'} + - 10*minutes{m1 - '0'} + minutes{m0 - '0'}; - if (sign == '-') - temp_offset = -temp_offset; - } - else - err |= ios_base::failbit; - } - else - err |= ios_base::failbit; - } - } - command = false; - modified = false; - break; - case 'Z': - if (command && !modified) - { - f.get(is, 0, is, err, &tm, b, i-1); - b = i+1; - if ((err & ios_base::failbit) == 0) - { - is >> temp_abbrev; - if (is.fail()) - err |= ios_base::failbit; - } - } - command = false; - modified = false; - break; - default: - command = false; - modified = false; - break; - } - } - if ((err & ios_base::failbit) == 0) - { - if (b < e) - f.get(is, 0, is, err, &tm, b, e); - if ((err & ios_base::failbit) == 0) - { - using namespace std::chrono; - tp = floor(local_days(year{tm.tm_year + 1900}/ - (tm.tm_mon+1)/ - (tm.tm_mday)) + - subseconds + seconds{tm.tm_sec} + - minutes{tm.tm_min} + hours{tm.tm_hour}); - if (abbrev != nullptr) - *abbrev = std::move(temp_abbrev); - if (offset != nullptr) - *offset = temp_offset; - } - } - is.setstate(err); - } - else - is.setstate(ios_base::failbit); -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, local_time& tp, - std::chrono::minutes* offset) -{ - parse(is, format, tp, static_cast*>(nullptr), offset); -} - -template > -struct parse_local_manip -{ - const std::basic_string format_; - local_time& tp_; - std::basic_string* abbrev_; - std::chrono::minutes* offset_; - -public: - parse_local_manip(std::basic_string format, - local_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(std::move(format)) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} - -}; - -template -std::basic_istream& -operator>>(std::basic_istream& is, - const parse_local_manip& x) -{ - parse(is, x.format_, x.tp_, x.abbrev_, x.offset_); - return is; -} - -template > -struct parse_sys_manip -{ - const std::basic_string format_; - sys_time& tp_; - std::basic_string* abbrev_; - std::chrono::minutes* offset_; - -public: - parse_sys_manip(std::basic_string format, - sys_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(std::move(format)) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} - -}; - -template -std::basic_istream& -operator>>(std::basic_istream& is, - const parse_sys_manip& x) -{ - std::chrono::minutes offset{}; - auto offptr = x.offset_ ? x.offset_ : &offset; - local_time lt; - parse(is, x.format_, lt, x.abbrev_, offptr); - if (!is.fail()) - x.tp_ = sys_time{floor(lt - *offptr).time_since_epoch()}; - return is; -} - -} // namespace detail - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, sys_time& tp) -{ - std::chrono::minutes offset{}; - local_time lt; - detail::parse(is, format, lt, &offset); - if (!is.fail()) - tp = sys_time{floor(lt - offset).time_since_epoch()}; -} - -template -inline -detail::parse_sys_manip -parse(const std::basic_string& format, sys_time& tp) -{ - return {format, tp}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, sys_time& tp, - std::basic_string& abbrev) -{ - std::chrono::minutes offset{}; - local_time lt; - detail::parse(is, format, lt, &abbrev, &offset); - if (!is.fail()) - tp = sys_time{floor(lt - offset).time_since_epoch()}; -} - -template -inline -detail::parse_sys_manip -parse(const std::basic_string& format, sys_time& tp, - std::basic_string& abbrev) -{ - return {format, tp, &abbrev}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, sys_time& tp, - std::chrono::minutes& offset) -{ - local_time lt; - detail::parse(is, format, lt, &offset); - if (!is.fail()) - tp = sys_time{floor(lt - offset).time_since_epoch()}; -} - -template -inline -detail::parse_sys_manip -parse(const std::basic_string& format, sys_time& tp, - std::chrono::minutes& offset) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, sys_time& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) -{ - local_time lt; - detail::parse(is, format, lt, &abbrev, &offset); - if (!is.fail()) - tp = sys_time{floor(lt - offset).time_since_epoch()}; -} - -template -inline -detail::parse_sys_manip -parse(const std::basic_string& format, sys_time& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) -{ - return {format, tp, &abbrev, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, sys_time& tp, - std::chrono::minutes& offset, std::basic_string& abbrev) -{ - local_time lt; - detail::parse(is, format, lt, &abbrev, &offset); - if (!is.fail()) - tp = sys_time{floor(lt - offset).time_since_epoch()}; -} - -template -inline -detail::parse_sys_manip -parse(const std::basic_string& format, sys_time& tp, - std::chrono::minutes& offset, std::basic_string& abbrev) -{ - return {format, tp, &abbrev, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, local_time& tp) -{ - detail::parse(is, format, tp); -} - -template -inline -detail::parse_local_manip -parse(const std::basic_string& format, local_time& tp) -{ - return {format, tp}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, local_time& tp, - std::basic_string& abbrev) -{ - detail::parse(is, format, tp, &abbrev); -} - -template -inline -detail::parse_local_manip -parse(const std::basic_string& format, local_time& tp, - std::basic_string& abbrev) -{ - return {format, tp, &abbrev}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, local_time& tp, - std::chrono::minutes& offset) -{ - detail::parse(is, format, tp, &offset); -} - -template -inline -detail::parse_local_manip -parse(const std::basic_string& format, local_time& tp, - std::chrono::minutes& offset) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, local_time& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) -{ - detail::parse(is, format, tp, &abbrev, &offset); -} - -template -inline -detail::parse_local_manip -parse(const std::basic_string& format, local_time& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) -{ - return {format, tp, &abbrev, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, - const std::basic_string& format, local_time& tp, - std::chrono::minutes& offset, std::basic_string& abbrev) -{ - detail::parse(is, format, tp, &abbrev, &offset); -} - -template -inline -detail::parse_local_manip -parse(const std::basic_string& format, local_time& tp, - std::chrono::minutes& offset, std::basic_string& abbrev) -{ - return {format, tp, &abbrev, &offset}; -} - -// const CharT* formats - -template -inline -void -parse(std::basic_istream& is, const CharT* format, sys_time& tp) -{ - parse(is, std::basic_string(format), tp); -} - -template -inline -detail::parse_sys_manip -parse(const CharT* format, sys_time& tp) -{ - return {format, tp}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, sys_time& tp, - std::basic_string& abbrev) -{ - parse(is, std::basic_string(format), tp, abbrev); -} - -template -inline -detail::parse_sys_manip -parse(const CharT* format, sys_time& tp, - std::basic_string& abbrev) -{ - return {format, tp, &abbrev}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, sys_time& tp, - std::chrono::minutes& offset) -{ - parse(is, std::basic_string(format), tp, offset); -} - -template -inline -detail::parse_sys_manip -parse(const CharT* format, sys_time& tp, std::chrono::minutes& offset) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, sys_time& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) -{ - parse(is, std::basic_string(format), tp, abbrev, offset); -} - -template -inline -detail::parse_sys_manip -parse(const CharT* format, sys_time& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) -{ - return {format, tp, &abbrev, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, sys_time& tp, - std::chrono::minutes& offset, std::basic_string& abbrev) -{ - parse(is, std::basic_string(format), tp, abbrev, offset); -} - -template -inline -detail::parse_sys_manip -parse(const CharT* format, sys_time& tp, - std::chrono::minutes& offset, std::basic_string& abbrev) -{ - return {format, tp, &abbrev, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, - local_time& tp) -{ - parse(is, std::basic_string(format), tp); -} - -template -inline -detail::parse_local_manip -parse(const CharT* format, local_time& tp) -{ - return {format, tp}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, - local_time& tp, std::basic_string& abbrev) -{ - parse(is, std::basic_string(format), tp, abbrev); -} - -template -inline -detail::parse_local_manip -parse(const CharT* format, local_time& tp, - std::basic_string& abbrev) -{ - return {format, tp, &abbrev}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, - local_time& tp, std::chrono::minutes& offset) -{ - parse(is, std::basic_string(format), tp, offset); -} - -template -inline -detail::parse_local_manip -parse(const CharT* format, local_time& tp, std::chrono::minutes& offset) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, - local_time& tp, std::basic_string& abbrev, - std::chrono::minutes& offset) -{ - parse(is, std::basic_string(format), tp, abbrev, offset); -} - -template -inline -detail::parse_local_manip -parse(const CharT* format, local_time& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) -{ - return {format, tp, &abbrev, &offset}; -} - -template -inline -void -parse(std::basic_istream& is, const CharT* format, - local_time& tp, std::chrono::minutes& offset, - std::basic_string& abbrev) -{ - parse(is, std::basic_string(format), tp, abbrev, offset); -} - -template -inline -detail::parse_local_manip -parse(const CharT* format, local_time& tp, std::chrono::minutes& offset, - std::basic_string& abbrev) -{ - return {format, tp, &abbrev, &offset}; -} - -} // namespace date - -#endif // DATE_H diff --git a/ext/date/ios.h b/ext/date/ios.h deleted file mode 100644 index 3f791bd..0000000 --- a/ext/date/ios.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// ios.h -// DateTimeLib -// -// 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. - -#ifndef ios_hpp -#define ios_hpp - -#if __APPLE__ -# include -# if TARGET_OS_IPHONE -# include - - namespace date - { - namespace iOSUtils - { - - std::string get_tzdata_path(); - - } // namespace iOSUtils - } // namespace date - -# endif // TARGET_OS_IPHONE -#else // !__APPLE__ -# define TARGET_OS_IPHONE 0 -#endif // !__APPLE__ -#endif // ios_hpp diff --git a/ext/date/ios.mm b/ext/date/ios.mm deleted file mode 100644 index ec95302..0000000 --- a/ext/date/ios.mm +++ /dev/null @@ -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 - -#include -#include -#include - -#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(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 diff --git a/ext/date/julian.h b/ext/date/julian.h deleted file mode 100644 index 696be7c..0000000 --- a/ext/date/julian.h +++ /dev/null @@ -1,3046 +0,0 @@ -#ifndef JULIAN_H -#define JULIAN_H - -// The MIT License (MIT) -// -// Copyright (c) 2016 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. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that woud involve another several millennia of evolution). -// We did not mean to shout. - -#include "date.h" - -namespace julian -{ - -// durations - -using days = date::days; - -using weeks = date::weeks; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -using sys_days = date::sys_days; -using local_days = date::local_days; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT; - static CONSTCD11 year max() NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - explicit weekday(int) = delete; - CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - CONSTCD11 weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - julian::weekday wd_; - -public: - explicit CONSTCD11 weekday_last(const julian::weekday& wd) NOEXCEPT; - - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -// year_month - -class year_month -{ - julian::year y_; - julian::month m_; - -public: - CONSTCD11 year_month(const julian::year& y, const julian::month& m) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - julian::month m_; - julian::day d_; - -public: - CONSTCD11 month_day(const julian::month& m, const julian::day& d) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - julian::month m_; - -public: - CONSTCD11 explicit month_day_last(const julian::month& m) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - julian::month m_; - julian::weekday_indexed wdi_; -public: - CONSTCD11 month_weekday(const julian::month& m, - const julian::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - julian::month m_; - julian::weekday_last wdl_; - -public: - CONSTCD11 month_weekday_last(const julian::month& m, - const julian::weekday_last& wd) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - julian::year y_; - julian::month m_; - julian::day d_; - -public: - CONSTCD11 year_month_day(const julian::year& y, const julian::month& m, - const julian::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - julian::year y_; - julian::month_day_last mdl_; - -public: - CONSTCD11 year_month_day_last(const julian::year& y, - const julian::month_day_last& mdl) NOEXCEPT; - - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 julian::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - julian::year y_; - julian::month m_; - julian::weekday_indexed wdi_; - -public: - CONSTCD11 year_month_weekday(const julian::year& y, const julian::month& m, - const julian::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - julian::year y_; - julian::month m_; - julian::weekday_last wdl_; - -public: - CONSTCD11 year_month_weekday_last(const julian::year& y, const julian::month& m, - const julian::weekday_last& wdl) NOEXCEPT; - - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 julian::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 julian::year operator "" _y(unsigned long long y) NOEXCEPT; - -// CONSTDATA julian::month jan{1}; -// CONSTDATA julian::month feb{2}; -// CONSTDATA julian::month mar{3}; -// CONSTDATA julian::month apr{4}; -// CONSTDATA julian::month may{5}; -// CONSTDATA julian::month jun{6}; -// CONSTDATA julian::month jul{7}; -// CONSTDATA julian::month aug{8}; -// CONSTDATA julian::month sep{9}; -// CONSTDATA julian::month oct{10}; -// CONSTDATA julian::month nov{11}; -// CONSTDATA julian::month dec{12}; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -//----------------+ -// Implementation | -//----------------+ - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - date::detail::save_stream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) - 1 + y.count(); - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - switch (static_cast(m)) - { - case 1: - os << "Jan"; - break; - case 2: - os << "Feb"; - break; - case 3: - os << "Mar"; - break; - case 4: - os << "Apr"; - break; - case 5: - os << "May"; - break; - case 6: - os << "Jun"; - break; - case 7: - os << "Jul"; - break; - case 8: - os << "Aug"; - break; - case 9: - os << "Sep"; - break; - case 10: - os << "Oct"; - break; - case 11: - os << "Nov"; - break; - case 12: - os << "Dec"; - break; - default: - os << static_cast(m) << " is not a valid month"; - break; - } - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} - -CONSTCD11 -inline -bool -year::is_leap() const NOEXCEPT -{ - return y_ % 4 == 0; -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} -CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} - -CONSTCD11 -inline -year -year::min() NOEXCEPT -{ - return year{std::numeric_limits::min()}; -} - -CONSTCD11 -inline -year -year::max() NOEXCEPT -{ - return year{std::numeric_limits::max()}; -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - date::detail::save_stream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os << static_cast(y); - return os; -} - -// weekday - -CONSTCD11 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - return static_cast(static_cast( - z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd)) - {} - -CONSTCD11 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD11 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 -inline -weekday::operator unsigned() const NOEXCEPT -{ - return static_cast(wd_); -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const diff = static_cast(x) - static_cast(y); - return days{diff <= 6 ? diff : diff + 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - switch (static_cast(wd)) - { - case 0: - os << "Sun"; - break; - case 1: - os << "Mon"; - break; - case 2: - os << "Tue"; - break; - case 3: - os << "Wed"; - break; - case 4: - os << "Thu"; - break; - case 5: - os << "Fri"; - break; - case 6: - os << "Sat"; - break; - default: - os << static_cast(wd) << " is not a valid weekday"; - break; - } - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -julian::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return julian::day{static_cast(d)}; -} - -CONSTCD11 -inline -julian::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return julian::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA julian::last_spec last{}; - -CONSTDATA julian::month jan{1}; -CONSTDATA julian::month feb{2}; -CONSTDATA julian::month mar{3}; -CONSTDATA julian::month apr{4}; -CONSTDATA julian::month may{5}; -CONSTDATA julian::month jun{6}; -CONSTDATA julian::month jul{7}; -CONSTDATA julian::month aug{8}; -CONSTDATA julian::month sep{9}; -CONSTDATA julian::month oct{10}; -CONSTDATA julian::month nov{11}; -CONSTDATA julian::month dec{12}; - -CONSTDATA julian::weekday sun{0u}; -CONSTDATA julian::weekday mon{1u}; -CONSTDATA julian::weekday tue{2u}; -CONSTDATA julian::weekday wed{3u}; -CONSTDATA julian::weekday thu{4u}; -CONSTDATA julian::weekday fri{5u}; -CONSTDATA julian::weekday sat{6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return julian::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd))) - , index_(static_cast(index)) - {} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return os << wdi.weekday() << '[' << wdi.index() << ']'; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline julian::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const julian::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - return os << wdl.weekday() << "[last]"; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const julian::year& y, const julian::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - return os << ym.year() << '/' << ym.month(); -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const julian::month& m, const julian::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline julian::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline julian::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA julian::day d[] = - {31_d, 29_d, 31_d, 30_d, 31_d, 30_d, 31_d, 31_d, 30_d, 31_d, 30_d, 31_d}; - return m_.ok() && 1_d <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - return os << md.month() << '/' << md.day(); -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const julian::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - return os << mdl.month() << "/last"; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const julian::month& m, - const julian::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - return os << mwd.month() << '/' << mwd.weekday_indexed(); -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const julian::month& m, - const julian::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - return os << mwdl.month() << '/' << mwdl.weekday_last(); -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const julian::year& y, - const julian::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA julian::day d[] = - {31_d, 28_d, 31_d, 30_d, 31_d, 30_d, 31_d, 31_d, 30_d, 31_d, 30_d, 31_d}; - return month() != feb || !y_.is_leap() ? d[static_cast(month())-1] : 29_d; -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - return os << ymdl.year() << '/' << ymdl.month_day_last(); -} - -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const julian::year& y, const julian::month& m, - const julian::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const y = static_cast(y_) - (m_ <= feb); - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era = (y >= 0 ? y : y-3) / 4; - auto const yoe = static_cast(y - era * 4); // [0, 3] - auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] - auto const doe = yoe * 365 + doy; // [0, 1460] - return days{era * 1461 + static_cast(doe) - 719470}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return 1_d <= d_ && d_ <= (y_/m_/last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - date::detail::save_stream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os << ymd.year() << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os << ymd.day(); - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const z = dp.count() + 719470; - auto const era = (z >= 0 ? z : z - 1460) / 1461; - auto const doe = static_cast(z - era * 1461); // [0, 1460] - auto const yoe = (doe - doe/1460) / 365; // [0, 3] - auto const y = static_cast(yoe) + era * 4; - auto const doy = doe - 365*yoe; // [0, 365] - auto const mp = (5*doy + 2)/153; // [0, 11] - auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] - auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] - return year_month_day{julian::year{y + (m <= 2)}, julian::month(m), julian::day(d)}; -} - -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const julian::year& y, const julian::month& m, - const julian::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - julian::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = julian::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - julian::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - return os << ymwdi.year() << '/' << ymwdi.month() - << '/' << ymwdi.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const julian::year& y, - const julian::month& m, - const julian::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (julian::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -} // namespace julian - -#endif // JULIAN_H diff --git a/ext/date/tz.cpp b/ext/date/tz.cpp deleted file mode 100644 index e3c4da7..0000000 --- a/ext/date/tz.cpp +++ /dev/null @@ -1,3120 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016 Howard Hinnant -// Copyright (c) 2015 Ville Voutilainen -// 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. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that woud involve another several millennia of evolution). -// We did not mean to shout. - -#ifdef _WIN32 -// Windows.h will be included directly and indirectly (e.g. by curl). -// We need to define these macros to prevent Windows.h bringing in -// more than we need and do it eearly so Windows.h doesn't get included -// without these macros having been defined. -// min/max macrosinterfere with the C++ versions. -#ifndef NOMINMAX -#define NOMINMAX -#endif -// We don't need all that Windows has to offer. -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif // _WIN32 - -// None of this happens with the MS SDK (at least VS14 which I tested), but: -// Compiling with mingw, we get "error: 'KF_FLAG_DEFAULT' was not declared in this scope." -// and error: 'SHGetKnownFolderPath' was not declared in this scope.". -// It seems when using mingw NTDDI_VERSION is undefined and that -// causes KNOWN_FOLDER_FLAG and the KF_ flags to not get defined. -// So we must define NTDDI_VERSION to get those flags on mingw. -// The docs say though here: -// https://msdn.microsoft.com/en-nz/library/windows/desktop/aa383745(v=vs.85).aspx -// that "If you define NTDDI_VERSION, you must also define _WIN32_WINNT." -// So we declare we require Vista or greater. -#ifdef __MINGW32__ - -#ifndef NTDDI_VERSION -#define NTDDI_VERSION 0x06000000 -#define _WIN32_WINNT _WIN32_WINNT_VISTA -#elif NTDDI_VERSION < 0x06000000 -#warning "If this fails to compile NTDDI_VERSION may be to low. See comments above." -#endif -// But once we define the values above we then get this linker error: -// "tz.cpp:(.rdata$.refptr.FOLDERID_Downloads[.refptr.FOLDERID_Downloads]+0x0): " -// "undefined reference to `FOLDERID_Downloads'" -// which #include cures see: -// https://support.microsoft.com/en-us/kb/130869 -#include -// But with included, the error moves on to: -// error: 'FOLDERID_Downloads' was not declared in this scope -// Which #include cures. -#include - -#endif // __MINGW32__ - -#include -#endif // _WIN32 - -#include "tz_private.h" -#include "ios.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#endif // _WIN32 - -// unistd.h is used on some platforms as part of the the means to get -// the current time zone. On Win32 Windows.h provides a means to do it. -// gcc/mingw supports unistd.h on Win32 but MSVC does not. - -#ifdef _WIN32 -# include // _unlink etc. -# include // CoTaskFree, ShGetKnownFolderPath etc. -# if HAS_REMOTE_API -# include // _mkdir -# include // ShFileOperation etc. -# endif // HAS_REMOTE_API -#else // !WIN32 -# include -# include -# if !USE_SHELL_API -# include -# include -# include -# include -# include -# include -# endif //!USE_SHELL_API -#endif // !WIN32 - - -#if HAS_REMOTE_API -// Note curl includes windows.h so we must include curl AFTER definitions of things -// that effect windows.h such as NOMINMAX. -#include -#endif - -#ifdef _WIN32 -static CONSTDATA char folder_delimiter = '\\'; - -namespace -{ - struct task_mem_deleter - { - void operator()(wchar_t buf[]) - { - if (buf != nullptr) - CoTaskMemFree(buf); - } - }; - using co_task_mem_ptr = std::unique_ptr; -} - -// We might need to know certain locations even if not using the remote API, -// so keep these routines out of that block for now. -static -std::string -get_known_folder(const GUID& folderid) -{ - std::string folder; - PWSTR pfolder = nullptr; - HRESULT hr = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, NULL, &pfolder); - if (SUCCEEDED(hr)) - { - co_task_mem_ptr folder_ptr(pfolder); - folder = std::string(folder_ptr.get(), folder_ptr.get() + wcslen(folder_ptr.get())); - } - return folder; -} - -// Usually something like "c:\Program Files". -static -std::string -get_program_folder() -{ - return get_known_folder(FOLDERID_ProgramFiles); -} - -// Usually something like "c:\Users\username\Downloads". -static -std::string -get_download_folder() -{ - return get_known_folder(FOLDERID_Downloads); -} - -#else // !_WIN32 - -static CONSTDATA char folder_delimiter = '/'; - -static -std::string -expand_path(std::string path) -{ -#if TARGET_OS_IPHONE - return date::iOSUtils::get_tzdata_path(); -#else - ::wordexp_t w{}; - ::wordexp(path.c_str(), &w, 0); - assert(w.we_wordc == 1); - path = w.we_wordv[0]; - ::wordfree(&w); - return path; -#endif -} - -#endif // !_WIN32 - -namespace date -{ -// +---------------------+ -// | Begin Configuration | -// +---------------------+ - -using namespace detail; - -static std::string get_install() -{ -#ifdef _WIN32 - std::string install = get_download_folder(); - install += folder_delimiter; - install += "tzdata"; -#else - std::string install = expand_path("~/Downloads/tzdata"); -#endif - return install; -} - -#ifndef INSTALL - -static const std::string install = get_install(); - -#else // INSTALL - -#define STRINGIZEIMP(x) #x -#define STRINGIZE(x) STRINGIZEIMP(x) - -static const std::string install = STRINGIZE(INSTALL) + - std::string(1, folder_delimiter) + "tzdata"; - -#endif // INSTALL - -static -std::string -get_download_gz_file(const std::string& version) -{ - auto file = install + version + ".tar.gz"; - return file; -} - -static const std::vector files = -{ - "africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe", - "pacificnew", "northamerica", "southamerica", "systemv", "leapseconds" -}; - -// These can be used to reduce the range of the database to save memory -CONSTDATA auto min_year = date::year::min(); -CONSTDATA auto max_year = date::year::max(); - -CONSTDATA auto min_day = date::jan/1; -CONSTDATA auto max_day = date::dec/31; - -// +-------------------+ -// | End Configuration | -// +-------------------+ - -namespace detail -{ -struct undocumented {explicit undocumented() = default;}; -} - -#ifndef _MSC_VER -static_assert(min_year <= max_year, "Configuration error"); -#endif - -#ifdef TIMEZONE_MAPPING - -namespace // Put types in an anonymous name space. -{ - -// A simple type to manage RAII for key handles and to -// implement the trivial registry interface we need. -// Not intended to be general-purpose. -class reg_key -{ -private: - // Note there is no value documented to be an invalid handle value. - // Not NULL nor INVALID_HANDLE_VALUE. We must rely on is_open. - HKEY m_key = nullptr; - bool m_is_open = false; -public: - ~reg_key() - { - close(); - } - - reg_key() = default; - reg_key(const reg_key&) = delete; - reg_key& operator=(const reg_key&) = delete; - - HKEY handle() - { - return m_key; - } - - bool is_open() const - { - return m_is_open; - } - - LONG open(const wchar_t* key_name) - { - LONG result; - result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &m_key); - if (result == ERROR_SUCCESS) - m_is_open = true; - return result; - } - - LONG close() - { - if (m_is_open) - { - auto result = RegCloseKey(m_key); - assert(result == ERROR_SUCCESS); - if (result == ERROR_SUCCESS) - { - m_is_open = false; - m_key = nullptr; - } - return result; - } - return ERROR_SUCCESS; - } - - // WARNING: this function is not a general-purpose function. - // It has a hard-coded value size limit that should be sufficient for our use cases. - bool get_string(const wchar_t* key_name, std::string& value, std::wstring_convert>& converter) - { - value.clear(); - wchar_t value_buffer[256]; - // in/out parameter. Documentation say that size is a count of bytes not chars. - DWORD size = sizeof(value_buffer) - sizeof(value_buffer[0]); - DWORD tzi_type = REG_SZ; - if (RegQueryValueExW(handle(), key_name, nullptr, &tzi_type, - reinterpret_cast(value_buffer), &size) == ERROR_SUCCESS) - { - // Function does not guarantee to null terminate. - value_buffer[size/sizeof(value_buffer[0])] = L'\0'; - value = converter.to_bytes(value_buffer); - return true; - } - return false; - } - - bool get_binary(const wchar_t* key_name, void* value, int value_size) - { - DWORD size = value_size; - DWORD type = REG_BINARY; - if (RegQueryValueExW(handle(), key_name, nullptr, &type, - reinterpret_cast(value), &size) == ERROR_SUCCESS - && (int) size == value_size) - return true; - return false; - } -}; - -} // anonymous namespace - -static -std::string -get_download_mapping_file(const std::string& version) -{ - auto file = install + version + "windowsZones.xml"; - return file; -} - -// Parse this XML file: -// http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml -// The parsing method is designed to be simple and quick. It is not overly -// forgiving of change but it should diagnose basic format issues. -// See timezone_mapping structure for more info. -static -std::vector -load_timezone_mappings_from_xml_file(const std::string& input_path) -{ - std::size_t line_num = 0; - std::vector mappings; - std::string line; - - std::ifstream is(input_path); - if (!is.is_open()) - { - // We don't emit file exceptions because that's an implementation detail. - std::string msg = "Error opening time zone mapping file \""; - msg += input_path; - msg += "\"."; - throw std::runtime_error(msg); - } - - auto error = [&input_path, &line_num](const char* info) - { - std::string msg = "Error loading time zone mapping file \""; - msg += input_path; - msg += "\" at line "; - msg += std::to_string(line_num); - msg += ": "; - msg += info; - throw std::runtime_error(msg); - }; - // [optional space]a="b" - auto read_attribute = [&line_num, &line, &error] - (const char* name, std::string& value, std::size_t startPos) - ->std::size_t - { - value.clear(); - // Skip leading space before attribute name. - std::size_t spos = line.find_first_not_of(' ', startPos); - if (spos == std::string::npos) - spos = startPos; - // Assume everything up to next = is the attribute name - // and that an = will always delimit that. - std::size_t epos = line.find('=', spos); - if (epos == std::string::npos) - error("Expected \'=\' right after attribute name."); - std::size_t name_len = epos - spos; - // Expect the name we find matches the name we expect. - if (line.compare(spos, name_len, name) != 0) - { - std::string msg; - msg = "Expected attribute name \'"; - msg += name; - msg += "\' around position "; - msg += std::to_string(spos); - msg += " but found something else."; - error(msg.c_str()); - } - ++epos; // Skip the '=' that is after the attribute name. - spos = epos; - if (spos < line.length() && line[spos] == '\"') - ++spos; // Skip the quote that is before the attribute value. - else - { - std::string msg = "Expected '\"' to begin value of attribute \'"; - msg += name; - msg += "\'."; - error(msg.c_str()); - } - epos = line.find('\"', spos); - if (epos == std::string::npos) - { - std::string msg = "Expected '\"' to end value of attribute \'"; - msg += name; - msg += "\'."; - error(msg.c_str()); - } - // Extract everything in between the quotes. Note no escaping is done. - std::size_t value_len = epos - spos; - value.assign(line, spos, value_len); - ++epos; // Skip the quote that is after the attribute value; - return epos; - }; - - // Quick but not overly forgiving XML mapping file processing. - bool mapTimezonesOpenTagFound = false; - bool mapTimezonesCloseTagFound = false; - bool mapZoneOpenTagFound = false; - bool mapTZoneCloseTagFound = false; - std::size_t mapZonePos = std::string::npos; - std::size_t mapTimezonesPos = std::string::npos; - CONSTDATA char mapTimeZonesOpeningTag[] = { ""); - mapTimezonesCloseTagFound = (mapTimezonesPos != std::string::npos); - if (!mapTimezonesCloseTagFound) - { - std::size_t commentPos = line.find(" " << x.target_; -} - -// leap - -leap::leap(const std::string& s, detail::undocumented) -{ - using namespace date; - std::istringstream in(s); - in.exceptions(std::ios::failbit | std::ios::badbit); - std::string word; - int y; - MonthDayTime date; - in >> word >> y >> date; - date_ = date.to_time_point(year(y)); -} - -std::ostream& -operator<<(std::ostream& os, const leap& x) -{ - using namespace date; - return os << x.date_ << " +"; -} - -static -bool -file_exists(const std::string& filename) -{ -#ifdef _WIN32 - return ::_access(filename.c_str(), 0) == 0; -#else - return ::access(filename.c_str(), F_OK) == 0; -#endif -} - -#if HAS_REMOTE_API - -// CURL tools - -static -int -curl_global() -{ - if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0) - throw std::runtime_error("CURL global initialization failed"); - return 0; -} - -static const auto curl_delete = [](CURL* p) {::curl_easy_cleanup(p);}; - -static -std::unique_ptr -curl_init() -{ - static const auto curl_is_now_initiailized = curl_global(); - (void)curl_is_now_initiailized; - return std::unique_ptr{::curl_easy_init(), curl_delete}; -} - -static -bool -download_to_string(const std::string& url, std::string& str) -{ - str.clear(); - auto curl = curl_init(); - if (!curl) - return false; - std::string version; - curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); - curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb, - void* userp) -> std::size_t - { - auto& str = *static_cast(userp); - auto realsize = size * nmemb; - str.append(contents, realsize); - return realsize; - }; - curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str); - auto res = curl_easy_perform(curl.get()); - return (res == CURLE_OK); -} - -namespace -{ - enum class download_file_options { binary, text }; -} - -static -bool -download_to_file(const std::string& url, const std::string& local_filename, - download_file_options opts) -{ - auto curl = curl_init(); - if (!curl) - return false; - curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); - curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb, - void* userp) -> std::size_t - { - auto& of = *static_cast(userp); - auto realsize = size * nmemb; - of.write(contents, realsize); - return realsize; - }; - curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb); - decltype(curl_easy_perform(curl.get())) res; - { - std::ofstream of(local_filename, - opts == download_file_options::binary ? - std::ofstream::out | std::ofstream::binary : - std::ofstream::out); - of.exceptions(std::ios::badbit); - curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &of); - res = curl_easy_perform(curl.get()); - } - return res == CURLE_OK; -} - -std::string -remote_version() -{ - std::string version; - std::string str; - if (download_to_string("http://www.iana.org/time-zones", str)) - { - CONSTDATA char db[] = "/time-zones/repository/releases/tzdata"; - CONSTDATA auto db_size = sizeof(db) - 1; - auto p = str.find(db, 0, db_size); - const int ver_str_len = 5; - if (p != std::string::npos && p + (db_size + ver_str_len) <= str.size()) - version = str.substr(p + db_size, ver_str_len); - } - return version; -} - -bool -remote_download(const std::string& version) -{ - assert(!version.empty()); - auto url = "http://www.iana.org/time-zones/repository/releases/tzdata" + version + - ".tar.gz"; - bool result = download_to_file(url, get_download_gz_file(version), - download_file_options::binary); -#ifdef TIMEZONE_MAPPING - if (result) - { - auto mapping_file = get_download_mapping_file(version); - result = download_to_file("http://unicode.org/repos/cldr/trunk/common/" - "supplemental/windowsZones.xml", - mapping_file, download_file_options::text); - } -#endif - return result; -} - -// TODO! Using system() create a process and a console window. -// This is useful to see what errors may occur but is slow and distracting. -// Consider implementing this functionality more directly, such as -// using _mkdir and CreateProcess etc. -// But use the current means now as matches Unix implementations and while -// in proof of concept / testing phase. -// TODO! Use eventually. -static -bool -remove_folder_and_subfolders(const std::string& folder) -{ -#ifdef _WIN32 -# if USE_SHELL_API - // Delete the folder contents by deleting the folder. - std::string cmd = "rd /s /q \""; - cmd += folder; - cmd += '\"'; - return std::system(cmd.c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - // Create a buffer containing the path to delete. It must be terminated - // by two nuls. Who designs these API's... - std::vector from; - from.assign(folder.begin(), folder.end()); - from.push_back('\0'); - from.push_back('\0'); - SHFILEOPSTRUCT fo{}; // Zero initialize. - fo.wFunc = FO_DELETE; - fo.pFrom = from.data(); - fo.fFlags = FOF_NO_UI; - int ret = SHFileOperation(&fo); - if (ret == 0 && !fo.fAnyOperationsAborted) - return true; - return false; -# endif // !USE_SHELL_API -#else // !WIN32 -# if USE_SHELL_API - return std::system(("rm -R " + folder).c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - struct dir_deleter { - dir_deleter() {} - void operator()(DIR* d) const - { - if (d != nullptr) - { - int result = closedir(d); - assert(result == 0); - } - } - }; - using closedir_ptr = std::unique_ptr; - - std::string filename; - struct stat statbuf; - std::size_t folder_len = folder.length(); - struct dirent* p = nullptr; - - closedir_ptr d(opendir(folder.c_str())); - bool r = d.get() != nullptr; - while (r && (p=readdir(d.get())) != nullptr) - { - if (strcmp(p->d_name, ".") == 0 || strcmp(p->d_name, "..") == 0) - continue; - - // + 2 for path delimiter and nul terminator. - std::size_t buf_len = folder_len + strlen(p->d_name) + 2; - filename.resize(buf_len); - std::size_t path_len = static_cast( - snprintf(&filename[0], buf_len, "%s/%s", folder.c_str(), p->d_name)); - assert(path_len == buf_len - 1); - filename.resize(path_len); - - if (stat(filename.c_str(), &statbuf) == 0) - r = S_ISDIR(statbuf.st_mode) - ? remove_folder_and_subfolders(filename) - : unlink(filename.c_str()) == 0; - } - d.reset(); - - if (r) - r = rmdir(folder.c_str()) == 0; - - return r; -# endif // !USE_SHELL_API -#endif // !WIN32 -} - -static -bool -make_directory(const std::string& folder) -{ -#ifdef _WIN32 -# if USE_SHELL_API - // Re-create the folder. - std::string cmd = "mkdir \""; - cmd += folder; - cmd += '\"'; - return std::system(cmd.c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return _mkdir(folder.c_str()) == 0; -# endif // !USE_SHELL_API -#else // !WIN32 -# if USE_SHELL_API - return std::system(("mkdir " + folder).c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return mkdir(folder.c_str(), 0777) == 0; -# endif // !USE_SHELL_API -#endif -} - -static -bool -delete_file(const std::string& file) -{ -#ifdef _WIN32 -# if USE_SHELL_API - std::string cmd = "del \""; - cmd += file; - cmd += '\"'; - return std::system(cmd.c_str()) == 0; -# else // !USE_SHELL_API - return _unlink(file.c_str()) == 0; -# endif // !USE_SHELL_API -#else // !WIN32 -# if USE_SHELL_API - return std::system(("rm " + file).c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return unlink(file.c_str()) == 0; -# endif // !USE_SHELL_API -#endif // !WIN32 -} - -#ifdef TIMEZONE_MAPPING - -static -bool -move_file(const std::string& from, const std::string& to) -{ -#ifdef _WIN32 -# if USE_SHELL_API - std::string cmd = "move \""; - cmd += from; - cmd += "\" \""; - cmd += to; - cmd += '\"'; - return std::system(cmd.c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return !!::MoveFile(from.c_str(), to.c_str()); -# endif // !USE_SHELL_API -#else // !WIN32 -# if USE_SHELL_API - return std::system(("mv " + from + " " + to).c_str()) == EXIT_SUCCESS; -# else - return rename(from, to) == 0); -# endif -#endif // !WIN32 -} - -#endif // TIMEZONE_MAPPING - -#ifdef _WIN32 - -// Note folder can and usually does contain spaces. -static -std::string -get_unzip_program() -{ - std::string path; - - // 7-Zip appears to note its location in the registry. - // If that doesn't work, fall through and take a guess, but it will likely be wrong. - HKEY hKey = nullptr; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\7-Zip", 0, KEY_READ, &hKey) == ERROR_SUCCESS) - { - char value_buffer[MAX_PATH + 1]; // fyi 260 at time of writing. - // in/out parameter. Documentation say that size is a count of bytes not chars. - DWORD size = sizeof(value_buffer) - sizeof(value_buffer[0]); - DWORD tzi_type = REG_SZ; - // Testing shows Path key value is "C:\Program Files\7-Zip\" i.e. always with trailing \. - bool got_value = (RegQueryValueExA(hKey, "Path", nullptr, &tzi_type, - reinterpret_cast(value_buffer), &size) == ERROR_SUCCESS); - RegCloseKey(hKey); // Close now incase of throw later. - if (got_value) - { - // Function does not guarantee to null terminate. - value_buffer[size / sizeof(value_buffer[0])] = '\0'; - path = value_buffer; - if (!path.empty()) - { - path += "7z.exe"; - return path; - } - } - } - path += get_program_folder(); - path += folder_delimiter; - path += "7-Zip\\7z.exe"; - return path; -} - -#if !USE_SHELL_API -static -int -run_program(const std::string& command) -{ - STARTUPINFO si{}; - si.cb = sizeof(si); - PROCESS_INFORMATION pi{}; - - // Allegedly CreateProcess overwrites the command line. Ugh. - std::string mutable_command(command); - if (CreateProcess(nullptr, &mutable_command[0], - nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) - { - WaitForSingleObject(pi.hProcess, INFINITE); - DWORD exit_code; - bool got_exit_code = !!GetExitCodeProcess(pi.hProcess, &exit_code); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - // Not 100% sure about this still active thing is correct, - // but I'm going with it because I *think* WaitForSingleObject might - // return in some cases without INFINITE-ly waiting. - // But why/wouldn't GetExitCodeProcess return false in that case? - if (got_exit_code && exit_code != STILL_ACTIVE) - return static_cast(exit_code); - } - return EXIT_FAILURE; -} -#endif // !USE_SHELL_API - -static -std::string -get_download_tar_file(const std::string& version) -{ - auto file = install; - file += folder_delimiter; - file += "tzdata"; - file += version; - file += ".tar"; - return file; -} - -static -bool -extract_gz_file(const std::string& version, const std::string& gz_file, - const std::string& dest_folder) -{ - auto unzip_prog = get_unzip_program(); - bool unzip_result = false; - // Use the unzip program to extract the tar file from the archive. - - // Aim to create a string like: - // "C:\Program Files\7-Zip\7z.exe" x "C:\Users\SomeUser\Downloads\tzdata2016d.tar.gz" - // -o"C:\Users\SomeUser\Downloads\tzdata" - std::string cmd; - cmd = '\"'; - cmd += unzip_prog; - cmd += "\" x \""; - cmd += gz_file; - cmd += "\" -o\""; - cmd += dest_folder; - cmd += '\"'; - -#if USE_SHELL_API - // When using shelling out with std::system() extra quotes are required around the - // whole command. It's weird but neccessary it seems, see: - // http://stackoverflow.com/q/27975969/576911 - - cmd = "\"" + cmd + "\""; - if (std::system(cmd.c_str()) == EXIT_SUCCESS) - unzip_result = true; -#else // !USE_SHELL_API - if (run_program(cmd) == EXIT_SUCCESS) - unzip_result = true; -#endif // !USE_SHELL_API - if (unzip_result) - delete_file(gz_file); - - // Use the unzip program extract the data from the tar file that was - // just extracted from the archive. - auto tar_file = get_download_tar_file(version); - cmd = '\"'; - cmd += unzip_prog; - cmd += "\" x \""; - cmd += tar_file; - cmd += "\" -o\""; - cmd += install; - cmd += '\"'; -#if USE_SHELL_API - cmd = "\"" + cmd + "\""; - if (std::system(cmd.c_str()) == EXIT_SUCCESS) - unzip_result = true; -#else // !USE_SHELL_API - if (run_program(cmd) == EXIT_SUCCESS) - unzip_result = true; -#endif // !USE_SHELL_API - - if (unzip_result) - delete_file(tar_file); - - return unzip_result; -} - -#else // !_WIN32 - -#if !USE_SHELL_API -static -int -run_program(const char* prog, const char*const args[]) -{ - pid_t pid = fork(); - if (pid == -1) // Child failed to start. - return EXIT_FAILURE; - - if (pid != 0) - { - // We are in the parent. Child started. Wait for it. - pid_t ret; - int status; - while ((ret = waitpid(pid, &status, 0)) == -1) - { - if (errno != EINTR) - break; - } - if (ret != -1) - { - if (WIFEXITED(status)) - return WEXITSTATUS(status); - } - printf("Child issues!\n"); - - return EXIT_FAILURE; // Not sure what status of child is. - } - else // We are in the child process. Start the program the parent wants to run. - { - - if (execv(prog, const_cast(args)) == -1) // Does not return. - { - perror("unreachable 0\n"); - _Exit(127); - } - printf("unreachable 2\n"); - } - printf("unreachable 2\n"); - // Unreachable. - assert(false); - exit(EXIT_FAILURE); - return EXIT_FAILURE; -} -#endif // !USE_SHELL_API - -static -bool -extract_gz_file(const std::string&, const std::string& gz_file, const std::string&) -{ -#if USE_SHELL_API - bool unzipped = std::system(("tar -xzf " + gz_file + " -C " + install).c_str()) == EXIT_SUCCESS; -#else // !USE_SHELL_API - const char prog[] = {"/usr/bin/tar"}; - const char*const args[] = - { - prog, "-xzf", gz_file.c_str(), "-C", install.c_str(), nullptr - }; - bool unzipped = (run_program(prog, args) == EXIT_SUCCESS); -#endif // !USE_SHELL_API - if (unzipped) - { - delete_file(gz_file); - return true; - } - return false; -} - -#endif // !_WIN32 - -bool -remote_install(const std::string& version) -{ - auto success = false; - assert(!version.empty()); - - auto gz_file = get_download_gz_file(version); - if (file_exists(gz_file)) - { - if (file_exists(install)) - remove_folder_and_subfolders(install); - if (make_directory(install)) - { - if (extract_gz_file(version, gz_file, install)) - success = true; -#ifdef TIMEZONE_MAPPING - auto mapping_file_source = get_download_mapping_file(version); - auto mapping_file_dest = install; - mapping_file_dest += folder_delimiter; - mapping_file_dest += "windowsZones.xml"; - if (!move_file(mapping_file_source, mapping_file_dest)) - success = false; -#endif - } - } - return success; -} -#endif // HAS_REMOTE_API - -static -std::string -get_version(const std::string& path) -{ - std::ifstream infile(path + "NEWS"); - std::string version; - while (infile) - { - infile >> version; - if (version == "Release") - { - infile >> version; - return version; - } - } - throw std::runtime_error("Unable to get Timezone database version from " + path); -} - -static -TZ_DB -init_tzdb() -{ - using namespace date; - const std::string path = install + folder_delimiter; - std::string line; - bool continue_zone = false; - TZ_DB db; - -#if AUTO_DOWNLOAD - if (!file_exists(install)) - { - auto rv = remote_version(); - if (!rv.empty() && remote_download(rv)) - { - if (!remote_install(rv)) - { - std::string msg = "Timezone database version \""; - msg += rv; - msg += "\" did not install correctly to \""; - msg += install; - msg += "\""; - throw std::runtime_error(msg); - } - } - if (!file_exists(install)) - { - std::string msg = "Timezone database not found at \""; - msg += install; - msg += "\""; - throw std::runtime_error(msg); - } - db.version = get_version(path); - } - else - { - db.version = get_version(path); - auto rv = remote_version(); - if (!rv.empty() && db.version != rv) - { - if (remote_download(rv)) - { - remote_install(rv); - db.version = get_version(path); - } - } - } -#else // !AUTO_DOWNLOAD - if (!file_exists(install)) - { - std::string msg = "Timezone database not found at \""; - msg += install; - msg += "\""; - throw std::runtime_error(msg); - } - db.version = get_version(path); -#endif // !AUTO_DOWNLOAD - - for (const auto& filename : files) - { - std::ifstream infile(path + filename); - while (infile) - { - std::getline(infile, line); - if (!line.empty() && line[0] != '#') - { - std::istringstream in(line); - std::string word; - in >> word; - if (word == "Rule") - { - db.rules.push_back(Rule(line)); - continue_zone = false; - } - else if (word == "Link") - { - db.links.push_back(link(line)); - continue_zone = false; - } - else if (word == "Leap") - { - db.leaps.push_back(leap(line, detail::undocumented{})); - continue_zone = false; - } - else if (word == "Zone") - { - db.zones.push_back(time_zone(line, detail::undocumented{})); - continue_zone = true; - } - else if (line[0] == '\t' && continue_zone) - { - db.zones.back().add(line); - } - else - { - std::cerr << line << '\n'; - } - } - } - } - std::sort(db.rules.begin(), db.rules.end()); - Rule::split_overlaps(db.rules); - std::sort(db.zones.begin(), db.zones.end()); -#if !LAZY_INIT - for (auto& z : db.zones) - z.adjust_infos(db.rules); -#endif - db.zones.shrink_to_fit(); - std::sort(db.links.begin(), db.links.end()); - db.links.shrink_to_fit(); - std::sort(db.leaps.begin(), db.leaps.end()); - db.leaps.shrink_to_fit(); - -#ifdef TIMEZONE_MAPPING - std::string mapping_file = path + "windowsZones.xml"; - db.mappings = load_timezone_mappings_from_xml_file(mapping_file); - sort_zone_mappings(db.mappings); - get_windows_timezone_info(db.native_zones); -#endif // TIMEZONE_MAPPING - - return db; -} - -static -TZ_DB& -access_tzdb() -{ - static TZ_DB tz_db; - return tz_db; -} - -const TZ_DB& -reload_tzdb() -{ -#if AUTO_DOWNLOAD - auto const& v = access_tzdb().version; - if (!v.empty() && v == remote_version()) - return access_tzdb(); -#endif - return access_tzdb() = init_tzdb(); -} - -const TZ_DB& -get_tzdb() -{ - static const TZ_DB& ref = access_tzdb() = init_tzdb(); - return ref; -} - -const time_zone* -locate_zone(const std::string& tz_name) -{ - const auto& db = get_tzdb(); - auto zi = std::lower_bound(db.zones.begin(), db.zones.end(), tz_name, - [](const time_zone& z, const std::string& nm) - { - return z.name() < nm; - }); - if (zi == db.zones.end() || zi->name() != tz_name) - { - auto li = std::lower_bound(db.links.begin(), db.links.end(), tz_name, - [](const link& z, const std::string& nm) - { - return z.name() < nm; - }); - if (li != db.links.end() && li->name() == tz_name) - { - zi = std::lower_bound(db.zones.begin(), db.zones.end(), li->target(), - [](const time_zone& z, const std::string& nm) - { - return z.name() < nm; - }); - if (zi != db.zones.end() && zi->name() == li->target()) - return &*zi; - } - throw std::runtime_error(tz_name + " not found in timezone database"); - } - return &*zi; -} - -std::ostream& -operator<<(std::ostream& os, const TZ_DB& db) -{ - os << "Version: " << db.version << '\n'; - std::string title("--------------------------------------------" - "--------------------------------------------\n" - "Name ""Start Y ""End Y " - "Beginning ""Offset " - "Designator\n" - "--------------------------------------------" - "--------------------------------------------\n"); - int count = 0; - for (const auto& x : db.rules) - { - if (count++ % 50 == 0) - os << title; - os << x << '\n'; - } - os << '\n'; - title = std::string("---------------------------------------------------------" - "--------------------------------------------------------\n" - "Name ""Offset " - "Rule ""Abrev ""Until\n" - "---------------------------------------------------------" - "--------------------------------------------------------\n"); - count = 0; - for (const auto& x : db.zones) - { - if (count++ % 10 == 0) - os << title; - os << x << '\n'; - } - os << '\n'; - title = std::string("---------------------------------------------------------" - "--------------------------------------------------------\n" - "Alias ""To\n" - "---------------------------------------------------------" - "--------------------------------------------------------\n"); - count = 0; - for (const auto& x : db.links) - { - if (count++ % 45 == 0) - os << title; - os << x << '\n'; - } - os << '\n'; - title = std::string("---------------------------------------------------------" - "--------------------------------------------------------\n" - "Leap second on\n" - "---------------------------------------------------------" - "--------------------------------------------------------\n"); - os << title; - for (const auto& x : db.leaps) - os << x << '\n'; - return os; -} - -// ----------------------- - -#ifdef _WIN32 - -const time_zone* -current_zone() -{ -#ifdef TIMEZONE_MAPPING - TIME_ZONE_INFORMATION tzi{}; - DWORD tz_result = ::GetTimeZoneInformation(&tzi); - if (tz_result == TIME_ZONE_ID_INVALID) - { - auto error_code = ::GetLastError(); // Store this quick before it gets overwritten. - throw std::runtime_error("GetTimeZoneInformation failed: " - + get_win32_message(error_code)); - } - std::wstring_convert> converter; - std::string standard_name(converter.to_bytes(tzi.StandardName)); - auto tz = find_native_timezone_by_standard_name(standard_name); - if (!tz) - { - std::string msg; - msg = "current_zone() failed: "; - msg += standard_name; - msg += " was not found in the Windows Time Zone registry"; - throw std::runtime_error( msg ); - } - std::string standard_tzid; - if (!native_to_standard_timezone_name(tz->timezone_id, standard_tzid)) - { - std::string msg; - msg = "current_zone() failed: A mapping from the Windows Time Zone id \""; - msg += tz->timezone_id; - msg += "\" was not found in the time zone mapping database."; - throw std::runtime_error(msg); - } - return date::locate_zone(standard_tzid); -#else // !TIMEZONE_MAPPING - // Currently Win32 requires iana <--> windows tz name mappings - // for this function to work. - // TODO! we should really support TIMEZONE_MAPPINGS=0 on Windows, - // And in this mode we should read the current iana timezone from a file. - // This would allow the TZ library do be used by apps that don't care - // about Windows standard names just iana names. - // This would allow the xml dependency to be dropped and none of - // the name mapping functions would be needed. - throw std::runtime_error("current_zone not implemented."); -#endif // !TIMEZONE_MAPPING -} - -#else // !WIN32 - -const time_zone* -current_zone() -{ - // On some OS's a file called /etc/localtime may - // exist and it may be either a real file - // containing time zone details or a symlink to such a file. - // On MacOS and BSD Unix if this file is a symlink it - // might resolve to a path like this: - // "/usr/share/zoneinfo/America/Los_Angeles" - // If it does, we try to determine the current - // timezone from the remainder of the path by removing the prefix - // and hoping the rest resolves to valid timezone. - // It may not always work though. If it doesn't then an - // exception will be thrown by local_timezone. - // The path may also take a relative form: - // "../usr/share/zoneinfo/America/Los_Angeles". - struct stat sb; - CONSTDATA auto timezone = "/etc/localtime"; - if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) - { - std::string result(sb.st_size, '\0'); - auto sz = readlink(timezone, &result.front(), result.size()); - if (sz == -1) - throw std::runtime_error("readlink failure"); - result.resize(sz); - const char zonepath[] = "/usr/share/zoneinfo/"; - const std::size_t zonepath_len = sizeof(zonepath)/sizeof(zonepath[0])-1; - const std::size_t pos = result.find(zonepath); - if (pos != result.npos) - result.erase(0, zonepath_len+pos); - return locate_zone(result); - } - { - // On some versions of some linux distro's (e.g. Ubuntu), - // the current timezone might be in the first line of - // the /etc/timezone file. - std::ifstream timezone_file("/etc/timezone"); - if (timezone_file.is_open()) - { - std::string result; - std::getline(timezone_file, result); - if (!result.empty()) - return locate_zone(result); - } - // Fall through to try other means. - } - { - // On some versions of some linux distro's (e.g. Red Hat), - // the current timezone might be in the first line of - // the /etc/sysconfig/clock file as: - // ZONE="US/Eastern" - std::ifstream timezone_file("/etc/sysconfig/clock"); - std::string result; - while (timezone_file) - { - std::getline(timezone_file, result); - auto p = result.find("ZONE=\""); - if (p != std::string::npos) - { - result.erase(p, p+6); - result.erase(result.rfind('"')); - return locate_zone(result); - } - } - // Fall through to try other means. - } - throw std::runtime_error("Could not get current timezone"); -} - -#endif // !WIN32 - -#if defined(TZ_TEST) && defined(TIMEZONE_MAPPING) - -const time_zone* -locate_native_zone(const std::string& native_tz_name) -{ - std::string standard_tz_name; - if (!native_to_standard_timezone_name(native_tz_name, standard_tz_name)) - { - std::string msg; - msg = "locate_native_zone() failed: A mapping from the native/Windows Time Zone id \""; - msg += native_tz_name; - msg += "\" was not found in the time zone mapping database."; - throw std::runtime_error(msg); - } - return locate_zone(standard_tz_name); -} - -#endif // TZ_TEST && TIMEZONE_MAPPING - -} // namespace date diff --git a/ext/date/tz.h b/ext/date/tz.h deleted file mode 100644 index 49cb805..0000000 --- a/ext/date/tz.h +++ /dev/null @@ -1,1345 +0,0 @@ -#ifndef TZ_H -#define TZ_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016 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. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that woud involve another several millennia of evolution). -// We did not mean to shout. - -// Get more recent database at http://www.iana.org/time-zones - -// The notion of "current timezone" is something the operating system is expected to "just -// know". How it knows this is system specific. It's often a value set by the user at OS -// intallation time and recorded by the OS somewhere. On Linux and Mac systems the current -// timezone name is obtained by looking at the name or contents of a particular file on -// disk. On Windows the current timzeone name comes from the registry. In either method, -// there is no guarantee that the "native" current timezone name obtained will match any -// of the "Standard" names in this library's "database". On Linux, the names usually do -// seem to match so mapping functions to map from native to "Standard" are typically not -// required. On Windows, the names are never "Standard" so mapping is always required. -// Technically any OS may use the mapping process but currently only Windows does use it. - -#ifdef _WIN32 -# ifndef TIMEZONE_MAPPING -# define TIMEZONE_MAPPING 1 -# endif -#else -# ifdef TIMEZONE_MAPPING -# error "Timezone mapping is not required or not implemented for this platform." -# endif -#endif - -#ifndef LAZY_INIT -# define LAZY_INIT 1 -#endif - -#ifndef HAS_REMOTE_API -# ifdef _WIN32 -# define HAS_REMOTE_API 0 -# else -# define HAS_REMOTE_API 1 -# endif -#endif - -#ifndef AUTO_DOWNLOAD -# define AUTO_DOWNLOAD HAS_REMOTE_API -#endif - -static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, - "AUTO_DOWNLOAD can not be turned on without HAS_REMOTE_API"); - -#ifndef USE_SHELL_API -# define USE_SHELL_API 1 -#endif - -#include "date.h" - -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#include "tz_private.h" -#endif - -#include -#include -#include -#include -#include -#if LAZY_INIT -# include -# include -#endif -#include -#include -#include -#include -#include -#include -#include - -namespace date -{ - -enum class choose {earliest, latest}; - -namespace detail -{ - struct undocumented; -} - -class nonexistent_local_time - : public std::runtime_error -{ -public: - template - nonexistent_local_time(local_time tp, local_seconds first, - const std::string& first_abbrev, local_seconds last, - const std::string& last_abbrev, sys_seconds time_sys); - -private: - template - static - std::string - make_msg(local_time tp, - local_seconds first, const std::string& first_abbrev, - local_seconds last, const std::string& last_abbrev, - sys_seconds time_sys); -}; - -template -inline -nonexistent_local_time::nonexistent_local_time(local_time tp, - local_seconds first, - const std::string& first_abbrev, - local_seconds last, - const std::string& last_abbrev, - sys_seconds time_sys) - : std::runtime_error(make_msg(tp, first, first_abbrev, last, last_abbrev, time_sys)) - {} - -template -std::string -nonexistent_local_time::make_msg(local_time tp, local_seconds first, - const std::string& first_abbrev, local_seconds last, - const std::string& last_abbrev, sys_seconds time_sys) -{ - using namespace date; - std::ostringstream os; - os << tp << " is in a gap between\n" - << first << ' ' << first_abbrev << " and\n" - << last << ' ' << last_abbrev - << " which are both equivalent to\n" - << time_sys << " UTC"; - return os.str(); -} - -class ambiguous_local_time - : public std::runtime_error -{ -public: - template - ambiguous_local_time(local_time tp, std::chrono::seconds first_offset, - const std::string& first_abbrev, - std::chrono::seconds second_offset, - const std::string& second_abbrev); - -private: - template - static - std::string - make_msg(local_time tp, - std::chrono::seconds first_offset, const std::string& first_abbrev, - std::chrono::seconds second_offset, const std::string& second_abbrev); -}; - -template -inline -ambiguous_local_time::ambiguous_local_time( - local_time tp, - std::chrono::seconds first_offset, - const std::string& first_abbrev, - std::chrono::seconds second_offset, - const std::string& second_abbrev) - : std::runtime_error(make_msg(tp, first_offset, first_abbrev, second_offset, - second_abbrev)) - {} - -template -std::string -ambiguous_local_time::make_msg(local_time tp, - std::chrono::seconds first_offset, - const std::string& first_abbrev, - std::chrono::seconds second_offset, - const std::string& second_abbrev) -{ - using namespace date; - std::ostringstream os; - os << tp << " is ambiguous. It could be\n" - << tp << ' ' << first_abbrev << " == " - << tp - first_offset << " UTC or\n" - << tp << ' ' << second_abbrev << " == " - << tp - second_offset << " UTC"; - return os.str(); -} - -namespace detail { class Rule; } - -struct sys_info -{ - sys_seconds begin; - sys_seconds end; - std::chrono::seconds offset; - std::chrono::minutes save; - std::string abbrev; -}; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const sys_info& r) -{ - os << r.begin << '\n'; - os << r.end << '\n'; - os << make_time(r.offset) << "\n"; - os << make_time(r.save) << "\n"; - os << r.abbrev << '\n'; - return os; -} - -struct local_info -{ - enum {unique, nonexistent, ambiguous} result; - sys_info first; - sys_info second; -}; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_info& r) -{ - if (r.result == local_info::nonexistent) - os << "nonexistent between\n"; - else if (r.result == local_info::ambiguous) - os << "ambiguous between\n"; - os << r.first; - if (r.result != local_info::unique) - { - os << "and\n"; - os << r.second; - } - return os; -} - -class time_zone; - -template -class zoned_time -{ - const time_zone* zone_; - sys_time tp_; - -public: - zoned_time(const sys_time& st); - explicit zoned_time(const time_zone* z); - explicit zoned_time(const std::string& name); - - template , - sys_time>::value - >::type> - zoned_time(const zoned_time& zt) NOEXCEPT; - - zoned_time(const time_zone* z, const local_time& tp); - zoned_time(const std::string& name, const local_time& tp); - zoned_time(const time_zone* z, const local_time& tp, choose c); - zoned_time(const std::string& name, const local_time& tp, choose c); - - zoned_time(const time_zone* z, const zoned_time& zt); - zoned_time(const std::string& name, const zoned_time& zt); - zoned_time(const time_zone* z, const zoned_time& zt, choose); - zoned_time(const std::string& name, const zoned_time& zt, choose); - - zoned_time(const time_zone* z, const sys_time& st); - zoned_time(const std::string& name, const sys_time& st); - - zoned_time& operator=(const sys_time& st); - zoned_time& operator=(const local_time& ut); - - operator sys_time() const; - explicit operator local_time() const; - - const time_zone* get_time_zone() const; - local_time get_local_time() const; - sys_time get_sys_time() const; - sys_info get_info() const; - - template - friend - bool - operator==(const zoned_time& x, const zoned_time& y); - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const zoned_time& t); - -private: - template friend class zoned_time; - - static_assert(std::is_convertible::value, - "zoned_time must have a precision of seconds or finer"); -}; - -using zoned_seconds = zoned_time; - -template -inline -bool -operator==(const zoned_time& x, const zoned_time& y) -{ - return x.zone_ == y.zone_ && x.tp_ == y.tp_; -} - -template -inline -bool -operator!=(const zoned_time& x, const zoned_time& y) -{ - return !(x == y); -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -namespace detail { struct zonelet; } -#endif - -class time_zone -{ -private: - - std::string name_; - std::vector zonelets_; -#if LAZY_INIT - std::unique_ptr adjusted_; -#endif - -public: -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - time_zone(time_zone&&) = default; - time_zone& operator=(time_zone&&) = default; -#else // defined(_MSC_VER) && (_MSC_VER < 1900) - time_zone(time_zone&& src); - time_zone& operator=(time_zone&& src); -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - - explicit time_zone(const std::string& s, detail::undocumented); - - const std::string& name() const NOEXCEPT; - - template sys_info get_info(sys_time st) const; - template local_info get_info(local_time tp) const; - - template - sys_time::type> - to_sys(local_time tp) const; - - template - sys_time::type> - to_sys(local_time tp, choose z) const; - - template - local_time::type> - to_local(sys_time tp) const; - - friend bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT; - friend bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT; - friend std::ostream& operator<<(std::ostream& os, const time_zone& z); - - void add(const std::string& s); - void adjust_infos(const std::vector& rules); - -private: - sys_info get_info_impl(sys_seconds tp) const; - local_info get_info_impl(local_seconds tp) const; - sys_info get_info_impl(sys_seconds tp, int timezone) const; - - void parse_info(std::istream& in); - - template - sys_time::type> - to_sys_impl(local_time tp, choose z, std::false_type) const; - template - sys_time::type> - to_sys_impl(local_time tp, choose, std::true_type) const; -}; - -#if defined(_MSC_VER) && (_MSC_VER < 1900) - -inline -time_zone::time_zone(time_zone&& src) - : name_(std::move(src.name_)) - , zonelets_(std::move(src.zonelets_)) -#if LAZY_INIT - , adjusted_(std::move(src.adjusted_)) -#endif - {} - -inline -time_zone& -time_zone::operator=(time_zone&& src) -{ - name_ = std::move(src.name_); - zonelets_ = std::move(src.zonelets_); -#if LAZY_INIT - adjusted_ = std::move(src.adjusted_); -#endif - return *this; -} - -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - -inline -const std::string& -time_zone::name() const NOEXCEPT -{ - return name_; -} - -template -inline -sys_info -time_zone::get_info(sys_time st) const -{ - using namespace std::chrono; - return get_info_impl(floor(st)); -} - -template -inline -local_info -time_zone::get_info(local_time tp) const -{ - using namespace std::chrono; - return get_info_impl(floor(tp)); -} - -template -inline -sys_time::type> -time_zone::to_sys(local_time tp) const -{ - return to_sys_impl(tp, choose{}, std::true_type{}); -} - -template -inline -sys_time::type> -time_zone::to_sys(local_time tp, choose z) const -{ - return to_sys_impl(tp, z, std::false_type{}); -} - -template -inline -local_time::type> -time_zone::to_local(sys_time tp) const -{ - using LT = local_time::type>; - auto i = get_info(tp); - return LT{(tp + i.offset).time_since_epoch()}; -} - -inline bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ == y.name_;} -inline bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ < y.name_;} - -inline bool operator!=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x == y);} -inline bool operator> (const time_zone& x, const time_zone& y) NOEXCEPT {return y < x;} -inline bool operator<=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(y < x);} -inline bool operator>=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x < y);} - -template -sys_time::type> -time_zone::to_sys_impl(local_time tp, choose z, std::false_type) const -{ - using namespace date; - using namespace std::chrono; - 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{tp.time_since_epoch()} - i.second.offset; - } - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -template -sys_time::type> -time_zone::to_sys_impl(local_time tp, choose, std::true_type) const -{ - using namespace date; - using namespace std::chrono; - auto i = get_info(tp); - if (i.result == local_info::nonexistent) - { - auto prev_end = local_seconds{i.first.end.time_since_epoch()} + - i.first.offset; - auto next_begin = local_seconds{i.second.begin.time_since_epoch()} + - i.second.offset; - throw nonexistent_local_time(tp, prev_end, i.first.abbrev, - next_begin, i.second.abbrev, i.first.end); - } - else if (i.result == local_info::ambiguous) - { - throw ambiguous_local_time(tp, i.first.offset, i.first.abbrev, - i.second.offset, i.second.abbrev); - } - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -class link -{ -private: - std::string name_; - std::string target_; -public: - explicit link(const std::string& s); - - const std::string& name() const {return name_;} - const std::string& target() const {return target_;} - - friend bool operator==(const link& x, const link& y) {return x.name_ == y.name_;} - friend bool operator< (const link& x, const link& y) {return x.name_ < y.name_;} - - friend std::ostream& operator<<(std::ostream& os, const link& x); -}; - -inline bool operator!=(const link& x, const link& y) {return !(x == y);} -inline bool operator> (const link& x, const link& y) {return y < x;} -inline bool operator<=(const link& x, const link& y) {return !(y < x);} -inline bool operator>=(const link& x, const link& y) {return !(x < y);} - -class leap -{ -private: - sys_seconds date_; - -public: - explicit leap(const std::string& s, detail::undocumented); - - sys_seconds date() const {return date_;} - - friend bool operator==(const leap& x, const leap& y) {return x.date_ == y.date_;} - friend bool operator< (const leap& x, const leap& y) {return x.date_ < y.date_;} - - template - friend - bool - operator==(const leap& x, const sys_time& y) - { - return x.date_ == y; - } - - template - friend - bool - operator< (const leap& x, const sys_time& y) - { - return x.date_ < y; - } - - template - friend - bool - operator< (const sys_time& x, const leap& y) - { - return x < y.date_; - } - - friend std::ostream& operator<<(std::ostream& os, const leap& x); -}; - -inline bool operator!=(const leap& x, const leap& y) {return !(x == y);} -inline bool operator> (const leap& x, const leap& y) {return y < x;} -inline bool operator<=(const leap& x, const leap& y) {return !(y < x);} -inline bool operator>=(const leap& x, const leap& y) {return !(x < y);} - -template -inline -bool -operator==(const sys_time& x, const leap& y) -{ - return y == x; -} - -template -inline -bool -operator!=(const leap& x, const sys_time& y) -{ - return !(x == y); -} - -template -inline -bool -operator!=(const sys_time& x, const leap& y) -{ - return !(x == y); -} - -template -inline -bool -operator> (const leap& x, const sys_time& y) -{ - return y < x; -} - -template -inline -bool -operator> (const sys_time& x, const leap& y) -{ - return y < x; -} - -template -inline -bool -operator<=(const leap& x, const sys_time& y) -{ - return !(y < x); -} - -template -inline -bool -operator<=(const sys_time& x, const leap& y) -{ - return !(y < x); -} - -template -inline -bool -operator>=(const leap& x, const sys_time& y) -{ - return !(x < y); -} - -template -inline -bool -operator>=(const sys_time& x, const leap& y) -{ - return !(x < y); -} - -#ifdef TIMEZONE_MAPPING - -namespace detail -{ - -// The time zone mapping is modelled after this data file: -// http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml -// and the field names match the element names from the mapZone element -// of windowsZones.xml. -// The website displays this file here: -// http://www.unicode.org/cldr/charts/latest/supplemental/zone_tzid.html -// The html view is sorted before being displayed but is otherwise the same -// There is a mapping between the os centric view (in this case windows) -// the html displays uses and the generic view the xml file. -// That mapping is this: -// display column "windows" -> xml field "other". -// display column "region" -> xml field "territory". -// display column "tzid" -> xml field "type". -// This structure uses the generic terminology because it could be -// used to to support other os/native name conversions, not just windows, -// and using the same generic names helps retain the connection to the -// origin of the data that we are using. -struct timezone_mapping -{ - timezone_mapping(const char* other, const char* territory, const char* type) - : other(other), territory(territory), type(type) - { - } - timezone_mapping() = default; - std::string other; - std::string territory; - std::string type; -}; - -struct timezone_info -{ - timezone_info() = default; - std::string timezone_id; - std::string standard_name; -}; - -} // detail - -#endif // TIMEZONE_MAPPING - -struct TZ_DB -{ - std::string version; - std::vector zones; - std::vector links; - std::vector leaps; - std::vector rules; -#ifdef TIMEZONE_MAPPING - // TODO! These need some protection. - std::vector mappings; - std::vector native_zones; -#endif - - TZ_DB() = default; -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - TZ_DB(TZ_DB&&) = default; - TZ_DB& operator=(TZ_DB&&) = default; -#else // defined(_MSC_VER) || (_MSC_VER >= 1900) - TZ_DB(TZ_DB&& src) - : - version(std::move(src.version)), - zones(std::move(src.zones)), - links(std::move(src.links)), - leaps(std::move(src.leaps)), - rules(std::move(src.rules)) -#ifdef TIMEZONE_MAPPING - , - mappings(std::move(src.mappings)), - native_zones(std::move(src.native_zones)) -#endif - {} - - TZ_DB& operator=(TZ_DB&& src) - { - version = std::move(src.version); - zones = std::move(src.zones); - links = std::move(src.links); - leaps = std::move(src.leaps); - rules = std::move(src.rules); -#ifdef TIMEZONE_MAPPING - mappings = std::move(src.mappings); - native_zones = std::move(src.native_zones); -#endif - return *this; - } -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) -}; - -std::ostream& -operator<<(std::ostream& os, const TZ_DB& db); - -const TZ_DB& get_tzdb(); -const TZ_DB& reload_tzdb(); - -#if HAS_REMOTE_API -std::string remote_version(); -bool remote_download(const std::string& version); -bool remote_install(const std::string& version); -#endif - -const time_zone* locate_zone(const std::string& tz_name); -#ifdef TZ_TEST -# if _WIN32 -const time_zone* locate_native_zone(const std::string& native_tz_name); -# endif // _WIN32 -#endif // TZ_TEST -const time_zone* current_zone(); - -// zoned_time - -template -inline -zoned_time::zoned_time(const sys_time& st) - : zone_(locate_zone("UTC")) - , tp_(st) - {} - -template -inline -zoned_time::zoned_time(const time_zone* z) - : zone_(z) - {assert(zone_ != nullptr);} - -template -inline -zoned_time::zoned_time(const std::string& name) - : zoned_time(locate_zone(name)) - {} - -template -inline -zoned_time::zoned_time(const time_zone* z, const local_time& t) - : zone_(z) - , tp_(z->to_sys(t)) - {} - -template -inline -zoned_time::zoned_time(const std::string& name, const local_time& t) - : zoned_time(locate_zone(name), t) - {} - -template -inline -zoned_time::zoned_time(const time_zone* z, const local_time& t, - choose c) - : zone_(z) - , tp_(z->to_sys(t, c)) - {} - -template -inline -zoned_time::zoned_time(const std::string& name, const local_time& t, - choose c) - : zoned_time(locate_zone(name), t, c) - {} - -template -template -inline -zoned_time::zoned_time(const zoned_time& zt) NOEXCEPT - : zone_(zt.zone_) - , tp_(zt.tp_) - {} - -template -inline -zoned_time::zoned_time(const time_zone* z, const zoned_time& zt) - : zone_(z) - , tp_(zt.tp_) - {} - -template -inline -zoned_time::zoned_time(const std::string& name, const zoned_time& zt) - : zoned_time(locate_zone(name), zt) - {} - -template -inline -zoned_time::zoned_time(const time_zone* z, const zoned_time& zt, choose) - : zoned_time(z, zt) - {} - -template -inline -zoned_time::zoned_time(const std::string& name, - const zoned_time& zt, choose c) - : zoned_time(locate_zone(name), zt, c) - {} - -template -inline -zoned_time::zoned_time(const time_zone* z, const sys_time& st) - : zone_(z) - , tp_(st) - {} - -template -inline -zoned_time::zoned_time(const std::string& name, const sys_time& st) - : zoned_time(locate_zone(name), st) - {} - - -template -inline -zoned_time& -zoned_time::operator=(const sys_time& st) -{ - tp_ = st; - return *this; -} - -template -inline -zoned_time& -zoned_time::operator=(const local_time& ut) -{ - tp_ = zone_->to_sys(ut); - return *this; -} - -template -inline -zoned_time::operator local_time() const -{ - return get_local_time(); -} - -template -inline -zoned_time::operator sys_time() const -{ - return get_sys_time(); -} - -template -inline -const time_zone* -zoned_time::get_time_zone() const -{ - return zone_; -} - -template -inline -local_time -zoned_time::get_local_time() const -{ - return zone_->to_local(tp_); -} - -template -inline -sys_time -zoned_time::get_sys_time() const -{ - return tp_; -} - -template -inline -sys_info -zoned_time::get_info() const -{ - return zone_->get_info(tp_); -} - -// make_zoned_time - -template -inline -zoned_time::type> -make_zoned(const sys_time& tp) -{ - return {tp}; -} - -template -inline -zoned_time::type> -make_zoned(const time_zone* zone, const local_time& tp) -{ - return {zone, tp}; -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const local_time& tp) -{ - return {name, tp}; -} - -template -inline -zoned_time::type> -make_zoned(const time_zone* zone, const local_time& tp, choose c) -{ - return {zone, tp, c}; -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const local_time& tp, choose c) -{ - return {name, tp, c}; -} - -template -inline -zoned_time::type> -make_zoned(const time_zone* zone, const zoned_time& zt) -{ - return {zone, zt}; -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const zoned_time& zt) -{ - return {name, zt}; -} - -template -inline -zoned_time::type> -make_zoned(const time_zone* zone, const zoned_time& zt, choose c) -{ - return {zone, zt, c}; -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const zoned_time& zt, choose c) -{ - return {name, zt, c}; -} - -template -inline -zoned_time::type> -make_zoned(const time_zone* zone, const sys_time& st) -{ - return {zone, st}; -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const sys_time& st) -{ - return {name, st}; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const zoned_time& t) -{ - auto i = t.zone_->get_info(t.tp_); - auto lt = t.tp_ + i.offset; - return os << lt << ' ' << i.abbrev; -} - -class utc_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static CONSTDATA bool is_steady = false; - - static time_point now(); -}; - -template - using utc_time = std::chrono::time_point; - -using utc_seconds = utc_time; - -template -inline -utc_time::type> -to_utc_time(const sys_time& st) -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - auto const& leaps = get_tzdb().leaps; - auto const lt = std::upper_bound(leaps.begin(), leaps.end(), st); - return utc_time{st.time_since_epoch() + seconds{lt-leaps.begin()}}; -} - -template -inline -sys_time::type> -to_sys_time(const utc_time& ut) -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - auto const& leaps = get_tzdb().leaps; - auto tp = sys_time{ut.time_since_epoch()}; - if (tp >= leaps.front()) - { - auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); - tp -= seconds{lt-leaps.begin()}; - if (tp < lt[-1]) - { - if (tp >= lt[-1].date() - seconds{1}) - tp = lt[-1].date() - duration{1}; - else - tp += seconds{1}; - } - } - return tp; -} - -inline -utc_clock::time_point -utc_clock::now() -{ - using namespace std::chrono; - return to_utc_time(system_clock::now()); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const utc_time& t) -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - auto const& leaps = get_tzdb().leaps; - auto tp = sys_time{t.time_since_epoch()}; - if (tp >= leaps.front()) - { - auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); - tp -= seconds{lt-leaps.begin()}; - if (tp < lt[-1]) - { - if (tp >= lt[-1].date() - seconds{1}) - { - auto const dp = floor(tp); - auto time = make_time(tp-dp); - time.seconds() += seconds{1}; - return os << year_month_day(dp) << ' ' << time; - } - else - tp += seconds{1}; - } - } - return os << tp; -} - -// tai_clock - -class tai_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static const bool is_steady = false; - - static time_point now() NOEXCEPT; -}; - -template - using tai_time = std::chrono::time_point; - -using tai_seconds = tai_time; - -template -inline -utc_time::type> -to_utc_time(const tai_time& t) NOEXCEPT -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - return utc_time{t.time_since_epoch()} - - (sys_days{year{1970}/jan/1} - sys_days{year{1958}/jan/1} + seconds{10}); -} - -template -inline -tai_time::type> -to_tai_time(const utc_time& t) NOEXCEPT -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - return tai_time{t.time_since_epoch()} + - (sys_days{year{1970}/jan/1} - sys_days{year{1958}/jan/1} + seconds{10}); -} - -template -inline -tai_time::type> -to_tai_time(const sys_time& t) -{ - return to_tai_time(to_utc_time(t)); -} - -inline -tai_clock::time_point -tai_clock::now() NOEXCEPT -{ - using namespace std::chrono; - return to_tai_time(system_clock::now()); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const tai_time& t) -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - auto tp = sys_time{t.time_since_epoch()} - - (sys_days{year{1970}/jan/1} - sys_days{year{1958}/jan/1}); - return os << tp; -} - -// gps_clock - -class gps_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static const bool is_steady = false; - - static time_point now() NOEXCEPT; -}; - -template - using gps_time = std::chrono::time_point; - -using gps_seconds = gps_time; - -template -inline -utc_time::type> -to_utc_time(const gps_time& t) NOEXCEPT -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - return utc_time{t.time_since_epoch()} + - (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1970}/jan/1} + seconds{9}); -} - -template -inline -gps_time::type> -to_gps_time(const utc_time& t) -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - return gps_time{t.time_since_epoch()} - - (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1970}/jan/1} + seconds{9}); -} - -template -inline -gps_time::type> -to_gps_time(const sys_time& t) -{ - return to_gps_time(to_utc_time(t)); -} - -inline -gps_clock::time_point -gps_clock::now() NOEXCEPT -{ - using namespace std::chrono; - return to_gps_time(system_clock::now()); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const gps_time& t) -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - auto tp = sys_time{t.time_since_epoch()} + - (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1970}/jan/1}); - return os << tp; -} - -template -inline -sys_time::type> -to_sys_time(const tai_time& t) -{ - return to_sys_time(to_utc_time(t)); -} - -template -inline -sys_time::type> -to_sys_time(const gps_time& t) -{ - return to_sys_time(to_utc_time(t)); -} - -template -inline -tai_time::type> -to_tai_time(const gps_time& t) NOEXCEPT -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - return tai_time{t.time_since_epoch()} + - (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1958}/jan/1} + seconds{19}); -} - -template -inline -gps_time::type> -to_gps_time(const tai_time& t) NOEXCEPT -{ - using namespace std::chrono; - using duration = typename std::common_type::type; - return gps_time{t.time_since_epoch()} - - (sys_days{year{1980}/jan/sun[1]} - sys_days{year{1958}/jan/1} + seconds{19}); -} - -// format - -template -inline -std::basic_string -format(const std::locale& loc, std::basic_string fmt, - const zoned_time& tp) -{ - auto const info = tp.get_info(); - return detail::format(loc, std::move(fmt), tp.get_local_time(), - &info.abbrev, &info.offset); -} - -template -inline -std::basic_string -format(std::basic_string fmt, const zoned_time& tp) -{ - auto const info = tp.get_info(); - return detail::format(std::locale{}, std::move(fmt), tp.get_local_time(), - &info.abbrev, &info.offset); -} - -// const CharT* formats - -template -inline -std::basic_string -format(const std::locale& loc, const CharT* fmt, const zoned_time& tp) -{ - auto const info = tp.get_info(); - return detail::format(loc, std::basic_string(fmt), tp.get_local_time(), - &info.abbrev, &info.offset); -} - -template -inline -std::basic_string -format(const CharT* fmt, const zoned_time& tp) -{ - auto const info = tp.get_info(); - return detail::format(std::locale{}, std::basic_string(fmt), - tp.get_local_time(), &info.abbrev, &info.offset); -} - -} // namespace date - -#endif // TZ_H diff --git a/ext/date/tz_private.h b/ext/date/tz_private.h deleted file mode 100644 index e796e9c..0000000 --- a/ext/date/tz_private.h +++ /dev/null @@ -1,265 +0,0 @@ -#ifndef TZ_PRIVATE_H -#define TZ_PRIVATE_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016 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. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that woud involve another several millennia of evolution). -// We did not mean to shout. - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -#include "tz.h" -#else -#include "date.h" -#include -#endif - -namespace date -{ - -namespace detail -{ - -enum class tz {utc, local, standard}; - -//forward declare to avoid warnings in gcc 6.2 -class MonthDayTime; -std::istream& operator>>(std::istream& is, MonthDayTime& x); -std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); - - -class MonthDayTime -{ -private: - struct pair - { -#if defined(_MSC_VER) && (_MSC_VER < 1900) - pair() : month_day_(date::jan / 1), weekday_(0U) {} - - pair(const date::month_day& month_day, const date::weekday& weekday) - : month_day_(month_day), weekday_(weekday) {} -#endif - - date::month_day month_day_; - date::weekday weekday_; - }; - - enum Type {month_day, month_last_dow, lteq, gteq}; - - Type type_{month_day}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - union U -#else - struct U -#endif - { - date::month_day month_day_; - date::month_weekday_last month_weekday_last_; - pair month_day_weekday_; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - U() : month_day_{date::jan/1} {} -#else - U() : - month_day_(date::jan/1), - month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U))) - {} - -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - - U& operator=(const date::month_day& x); - U& operator=(const date::month_weekday_last& x); - U& operator=(const pair& x); - } u; - - std::chrono::hours h_{0}; - std::chrono::minutes m_{0}; - std::chrono::seconds s_{0}; - tz zone_{tz::local}; - -public: - MonthDayTime() = default; - MonthDayTime(local_seconds tp, tz timezone); - MonthDayTime(const date::month_day& md, tz timezone); - - date::day day() const; - date::month month() const; - tz zone() const {return zone_;} - - void canonicalize(date::year y); - - sys_seconds - to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const; - sys_days to_sys_days(date::year y) const; - - sys_seconds to_time_point(date::year y) const; - int compare(date::year y, const MonthDayTime& x, date::year yx, - std::chrono::seconds offset, std::chrono::minutes prev_save) const; - - friend std::istream& operator>>(std::istream& is, MonthDayTime& x); - friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); -}; - -// A Rule specifies one or more set of datetimes without using an offset. -// Multiple dates are specified with multiple years. The years in effect -// go from starting_year_ to ending_year_, inclusive. starting_year_ <= -// ending_year_. save_ is ineffect for times from the specified time -// onward, including the specified time. When the specified time is -// local, it uses the save_ from the chronologically previous Rule, or if -// there is none, 0. - -//forward declare to avoid warnings in gcc 6.2 -class Rule; -bool operator==(const Rule& x, const Rule& y); -bool operator<(const Rule& x, const Rule& y); -bool operator==(const Rule& x, const date::year& y); -bool operator<(const Rule& x, const date::year& y); -bool operator==(const date::year& x, const Rule& y); -bool operator<(const date::year& x, const Rule& y); -bool operator==(const Rule& x, const std::string& y); -bool operator<(const Rule& x, const std::string& y); -bool operator==(const std::string& x, const Rule& y); -bool operator<(const std::string& x, const Rule& y); -std::ostream& operator<<(std::ostream& os, const Rule& r); - -class Rule -{ -private: - std::string name_; - date::year starting_year_{0}; - date::year ending_year_{0}; - MonthDayTime starting_at_; - std::chrono::minutes save_{0}; - std::string abbrev_; - -public: - Rule() = default; - explicit Rule(const std::string& s); - Rule(const Rule& r, date::year starting_year, date::year ending_year); - - const std::string& name() const {return name_;} - const std::string& abbrev() const {return abbrev_;} - - const MonthDayTime& mdt() const {return starting_at_;} - const date::year& starting_year() const {return starting_year_;} - const date::year& ending_year() const {return ending_year_;} - const std::chrono::minutes& save() const {return save_;} - - static void split_overlaps(std::vector& rules); - - friend bool operator==(const Rule& x, const Rule& y); - friend bool operator<(const Rule& x, const Rule& y); - friend bool operator==(const Rule& x, const date::year& y); - friend bool operator<(const Rule& x, const date::year& y); - friend bool operator==(const date::year& x, const Rule& y); - friend bool operator<(const date::year& x, const Rule& y); - friend bool operator==(const Rule& x, const std::string& y); - friend bool operator<(const Rule& x, const std::string& y); - friend bool operator==(const std::string& x, const Rule& y); - friend bool operator<(const std::string& x, const Rule& y); - - friend std::ostream& operator<<(std::ostream& os, const Rule& r); - -private: - date::day day() const; - date::month month() const; - static void split_overlaps(std::vector& rules, std::size_t i, std::size_t& e); - static bool overlaps(const Rule& x, const Rule& y); - static void split(std::vector& rules, std::size_t i, std::size_t k, - std::size_t& e); -}; - -inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);} -inline bool operator> (const Rule& x, const Rule& y) {return y < x;} -inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);} - -inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);} -inline bool operator> (const Rule& x, const date::year& y) {return y < x;} -inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);} - -inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);} -inline bool operator> (const date::year& x, const Rule& y) {return y < x;} -inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);} - -inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);} -inline bool operator> (const Rule& x, const std::string& y) {return y < x;} -inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);} - -inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);} -inline bool operator> (const std::string& x, const Rule& y) {return y < x;} -inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);} - -struct zonelet -{ - enum tag {has_rule, has_save, is_empty}; - - std::chrono::seconds gmtoff_; - tag tag_ = has_rule; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - union U -#else - struct U -#endif - { - std::string rule_; - std::chrono::minutes save_; - - ~U() {} - U() {} - U(const U&) {} - U& operator=(const U&) = delete; - } u; - - std::string format_; - date::year until_year_{0}; - MonthDayTime until_date_; - sys_seconds until_utc_; - local_seconds until_std_; - local_seconds until_loc_; - std::chrono::minutes initial_save_{}; - std::string initial_abbrev_; - std::pair first_rule_{nullptr, date::year::min()}; - std::pair last_rule_{nullptr, date::year::max()}; - - ~zonelet(); - zonelet(); - zonelet(const zonelet& i); - zonelet& operator=(const zonelet&) = delete; -}; - -} // namespace detail - -} // namespace date - -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#include "tz.h" -#endif - -#endif // TZ_PRIVATE_H diff --git a/ext/mstch/include/mstch/mstch.hpp b/ext/mstch/include/mstch/mstch.hpp index 68a3cfd..2427b38 100644 --- a/ext/mstch/include/mstch/mstch.hpp +++ b/ext/mstch/include/mstch/mstch.hpp @@ -94,7 +94,7 @@ class lambda_t { } using node = boost::make_recursive_variant< - std::nullptr_t, std::string, int, double, bool, uint64_t, uint32_t, + std::nullptr_t, std::string, int, double, bool, uint64_t, int64_t, uint32_t, internal::lambda_t, std::shared_ptr>, std::map, diff --git a/ext/mstch/src/visitor/render_node.hpp b/ext/mstch/src/visitor/render_node.hpp index 88f115f..23535f3 100644 --- a/ext/mstch/src/visitor/render_node.hpp +++ b/ext/mstch/src/visitor/render_node.hpp @@ -38,6 +38,12 @@ class render_node: public boost::static_visitor { return ss.str(); } + std::string operator()(const int64_t& value) const { + std::stringstream ss; + ss << value; + return ss.str(); + } + std::string operator()(const uint32_t& value) const { std::stringstream ss; ss << value; diff --git a/ext/vpetrigocaches/cache.hpp b/ext/vpetrigocaches/cache.hpp new file mode 100644 index 0000000..c543ecd --- /dev/null +++ b/ext/vpetrigocaches/cache.hpp @@ -0,0 +1,138 @@ +#ifndef CACHE_HPP +#define CACHE_HPP + +#include +#include +#include +#include +#include +#include "cache_policy.hpp" + +namespace caches +{ + +// Base class for caching algorithms +template > +class fixed_sized_cache +{ + public: + + using iterator = typename std::unordered_map::iterator; + + using const_iterator = + typename std::unordered_map::const_iterator; + + using operation_guard = typename std::lock_guard; + + fixed_sized_cache( + size_t max_size, + const Policy& policy = Policy()) + : max_cache_size{max_size}, + cache_policy(policy) + { + if (max_cache_size == 0) + { + max_cache_size = std::numeric_limits::max(); + } + } + + void Put(const Key& key, const Value& value) + { + operation_guard{safe_op}; + auto elem_it = FindElem(key); + + if (elem_it == cache_items_map.end()) + { + // add new element to the cache + if (Size() + 1 > max_cache_size) + { + auto disp_candidate_key = cache_policy.ReplCandidate(); + + Erase(disp_candidate_key); + } + + Insert(key, value); + } + else + { + // update previous value + Update(key, value); + } + } + + bool Contains(const Key& key) + { + operation_guard{safe_op}; + auto elem_it = FindElem(key); + return elem_it != cache_items_map.end(); + } + + const Value& Get(const Key& key) const + { + operation_guard{safe_op}; + auto elem_it = FindElem(key); + + if (elem_it == cache_items_map.end()) + { + throw std::range_error{"No such element in the cache"}; + } + cache_policy.Touch(key); + + return elem_it->second; + } + + const size_t Size() const + { + operation_guard{safe_op}; + + return cache_items_map.size(); + } + + // return a key of a displacement candidate + void Clear() + { + operation_guard{safe_op}; + + cache_policy.Clear(); + cache_items_map.clear(); + } + + protected: + + void Insert(const Key& key, const Value& value) + { + cache_policy.Insert(key); + cache_items_map.emplace(std::make_pair(key, value)); + } + + void Erase(const Key& key) + { + cache_policy.Erase(key); + cache_items_map.erase(key); + } + + void Update(const Key& key, const Value& value) + { + cache_policy.Touch(key); + cache_items_map[key] = value; + } + + const_iterator FindElem(const Key& key) const + { + return cache_items_map.find(key); + } + + +private: + + std::unordered_map cache_items_map; + + mutable Policy cache_policy; + mutable std::mutex safe_op; + + size_t max_cache_size; + +}; +} + +#endif // CACHE_HPP diff --git a/ext/vpetrigocaches/cache_policy.hpp b/ext/vpetrigocaches/cache_policy.hpp new file mode 100644 index 0000000..19cf815 --- /dev/null +++ b/ext/vpetrigocaches/cache_policy.hpp @@ -0,0 +1,77 @@ +#ifndef CACHE_POLICY_HPP +#define CACHE_POLICY_HPP + +#include + +namespace caches +{ + +template + +class ICachePolicy +{ + public: + + virtual ~ICachePolicy() {} + + // handle element insertion in a cache + virtual void Insert(const Key& key) = 0; + + // handle request to the key-element in a cache + virtual void Touch(const Key& key) = 0; + + // handle element deletion from a cache + virtual void Erase(const Key& key) = 0; + + // return a key of a replacement candidate + virtual const Key& ReplCandidate() const = 0; + + // clear the cache + virtual void Clear() = 0; + +}; + +template +class NoCachePolicy : public ICachePolicy +{ + public: + + NoCachePolicy() = default; + + ~NoCachePolicy() override = default; + + void Insert(const Key& key) override + { + key_storage.emplace(key); + } + + void Touch(const Key& key) override + { + // do not do anything + } + + void Erase(const Key& key) override + { + key_storage.erase(key); + } + + // return a key of a displacement candidate + const Key& ReplCandidate() const override + { + return *key_storage.crbegin(); + } + + // return a key of a displacement candidate + void Clear() override + { + key_storage.clear(); + } + + private: + + std::unordered_set key_storage; +}; + +} // namespace caches + +#endif // CACHE_POLICY_HPP diff --git a/ext/vpetrigocaches/fifo_cache_policy.hpp b/ext/vpetrigocaches/fifo_cache_policy.hpp new file mode 100644 index 0000000..fa17757 --- /dev/null +++ b/ext/vpetrigocaches/fifo_cache_policy.hpp @@ -0,0 +1,53 @@ +#ifndef FIFO_CACHE_POLICY_HPP +#define FIFO_CACHE_POLICY_HPP + +#include +#include "cache_policy.hpp" + +namespace caches +{ + +template +class FIFOCachePolicy : public ICachePolicy +{ + public: + + FIFOCachePolicy() = default; + ~FIFOCachePolicy() = default; + + void Insert(const Key& key) override + { + fifo_queue.emplace_front(key); + } + + // handle request to the key-element in a cache + void Touch(const Key& key) override + { + // nothing to do here in the FIFO strategy + } + + // handle element deletion from a cache + void Erase(const Key& key) override + { + fifo_queue.pop_back(); + } + + // return a key of a replacement candidate + const Key& ReplCandidate() const override + { + return fifo_queue.back(); + } + + // return a key of a displacement candidate + void Clear() override + { + fifo_queue.clear(); + } + +private: + + std::list fifo_queue; +}; +} // namespace caches + +#endif // FIFO_CACHE_POLICY_HPP diff --git a/ext/vpetrigocaches/lfu_cache_policy.hpp b/ext/vpetrigocaches/lfu_cache_policy.hpp new file mode 100644 index 0000000..0f735d7 --- /dev/null +++ b/ext/vpetrigocaches/lfu_cache_policy.hpp @@ -0,0 +1,76 @@ +#ifndef LFU_CACHE_POLICY_HPP +#define LFU_CACHE_POLICY_HPP + +#include +#include +#include +#include +#include "cache_policy.hpp" + +namespace caches +{ +template +class LFUCachePolicy : public ICachePolicy +{ + public: + + using lfu_iterator = typename std::multimap::iterator; + + LFUCachePolicy() = default; + + ~LFUCachePolicy() override = default; + + void Insert(const Key& key) override + { + constexpr std::size_t INIT_VAL = 1; + + // all new value initialized with the frequency 1 + lfu_storage[key] = frequency_storage.emplace_hint( + frequency_storage.cbegin(), INIT_VAL, key); + } + + void Touch(const Key& key) override + { + // get the previous frequency value of a key + auto elem_for_update = lfu_storage[key]; + + auto updated_elem = std::make_pair( + elem_for_update->first + 1, elem_for_update->second); + + // update the previous value + frequency_storage.erase(elem_for_update); + + lfu_storage[key] = frequency_storage.emplace_hint( + frequency_storage.cend(), std::move(updated_elem)); + } + + void Erase(const Key& key) override + { + frequency_storage.erase(lfu_storage[key]); + lfu_storage.erase(key); + } + + const Key& ReplCandidate() const override + { + // at the beginning of the frequency_storage we have the + // least frequency used value + return frequency_storage.cbegin()->second; + } + + // return a key of a displacement candidate + void Clear() override + { + frequency_storage.clear(); + lfu_storage.clear(); + } + + + private: + + std::multimap frequency_storage; + + std::unordered_map lfu_storage; +}; +} // namespace caches + +#endif // LFU_CACHE_POLICY_HPP diff --git a/ext/vpetrigocaches/lru_cache_policy.hpp b/ext/vpetrigocaches/lru_cache_policy.hpp new file mode 100644 index 0000000..5dcdac0 --- /dev/null +++ b/ext/vpetrigocaches/lru_cache_policy.hpp @@ -0,0 +1,63 @@ +#ifndef LRU_CACHE_POLICY_HPP +#define LRU_CACHE_POLICY_HPP + +#include +#include +#include "cache_policy.hpp" + +namespace caches +{ +template +class LRUCachePolicy : public ICachePolicy +{ + + public: + + using lru_iterator = typename std::list::const_iterator; + + LRUCachePolicy() = default; + ~LRUCachePolicy() = default; + + void Insert(const Key& key) override + { + lru_queue.emplace_front(key); + key_finder[key] = lru_queue.cbegin(); + } + + void Touch(const Key& key) override + { + // move the touched element at the beginning of the lru_queue + lru_queue.splice(lru_queue.cbegin(), lru_queue, key_finder[key]); + } + + void Erase(const Key& key) override + { + // remove the least recently used element + key_finder.erase(lru_queue.back()); + + lru_queue.pop_back(); + } + + // return a key of a displacement candidate + const Key& ReplCandidate() const override + { + return lru_queue.back(); + } + + // return a key of a displacement candidate + void Clear() override + { + lru_queue.clear(); + key_finder.clear(); + } + + private: + + std::list lru_queue; + + std::unordered_map key_finder; +}; + +} // namespace caches + +#endif // LRU_CACHE_POLICY_HPP diff --git a/main.cpp b/main.cpp index e5d0b8c..4e1d2fc 100644 --- a/main.cpp +++ b/main.cpp @@ -6,10 +6,8 @@ #include "src/MicroCore.h" #include "src/page.h" - -#include "ext/member_checker.h" - #include +#include using boost::filesystem::path; @@ -21,11 +19,6 @@ int main(int ac, const char* av[]) { xmreg::CmdLineOptions opts {ac, av}; auto help_opt = opts.get_option("help"); - auto testnet_opt = opts.get_option("testnet"); - auto enable_key_image_checker_opt = opts.get_option("enable-key-image-checker"); - auto enable_output_key_checker_opt = opts.get_option("enable-output-key-checker"); - auto enable_autorefresh_option_opt = opts.get_option("enable-autorefresh-option"); - auto enable_pusher_opt = opts.get_option("enable-pusher"); // if help was chosen, display help text and finish if (*help_opt) @@ -33,19 +26,38 @@ int main(int ac, const char* av[]) { return EXIT_SUCCESS; } - bool testnet {*testnet_opt}; - bool enable_pusher {*enable_pusher_opt}; - bool enable_key_image_checker {*enable_key_image_checker_opt}; - bool enable_autorefresh_option {*enable_autorefresh_option_opt}; - bool enable_output_key_checker {*enable_output_key_checker_opt}; + auto port_opt = opts.get_option("port"); + auto bc_path_opt = opts.get_option("bc-path"); + auto deamon_url_opt = opts.get_option("deamon-url"); + auto ssl_crt_file_opt = opts.get_option("ssl-crt-file"); + auto ssl_key_file_opt = opts.get_option("ssl-key-file"); + auto no_blocks_on_index_opt = opts.get_option("no-blocks-on-index"); + auto testnet_url = opts.get_option("testnet-url"); + auto mainnet_url = opts.get_option("mainnet-url"); + auto testnet_opt = opts.get_option("testnet"); + auto enable_key_image_checker_opt = opts.get_option("enable-key-image-checker"); + auto enable_output_key_checker_opt = opts.get_option("enable-output-key-checker"); + auto enable_autorefresh_option_opt = opts.get_option("enable-autorefresh-option"); + auto enable_pusher_opt = opts.get_option("enable-pusher"); + auto enable_mixin_details_opt = opts.get_option("enable-mixin-details"); + auto enable_mempool_cache_opt = opts.get_option("enable-mempool-cache"); + auto enable_json_api_opt = opts.get_option("enable-json-api"); + auto enable_tx_cache_opt = opts.get_option("enable-tx-cache"); + auto enable_block_cache_opt = opts.get_option("enable-block-cache"); + auto show_cache_times_opt = opts.get_option("show-cache-times"); + + bool testnet {*testnet_opt}; + bool enable_pusher {*enable_pusher_opt}; + bool enable_key_image_checker {*enable_key_image_checker_opt}; + bool enable_autorefresh_option {*enable_autorefresh_option_opt}; + bool enable_output_key_checker {*enable_output_key_checker_opt}; + bool enable_mixin_details {*enable_mixin_details_opt}; + bool enable_mempool_cache {*enable_mempool_cache_opt}; + bool enable_json_api {*enable_json_api_opt}; + bool enable_tx_cache {*enable_tx_cache_opt}; + bool enable_block_cache {*enable_block_cache_opt}; + bool show_cache_times {*show_cache_times_opt}; - auto port_opt = opts.get_option("port"); - auto bc_path_opt = opts.get_option("bc-path"); - auto custom_db_path_opt = opts.get_option("custom-db-path"); - auto deamon_url_opt = opts.get_option("deamon-url"); - auto ssl_crt_file_opt = opts.get_option("ssl-crt-file"); - auto ssl_key_file_opt = opts.get_option("ssl-key-file"); - auto no_blocks_on_index_opt = opts.get_option("no-blocks-on-index"); // set monero log output level uint32_t log_level = 0; @@ -114,36 +126,6 @@ int main(int ac, const char* av[]) { return EXIT_FAILURE; } - // check if we have path to lmdb2 (i.e., custom db) - // and if it exists - - string custom_db_path_str; - - if (custom_db_path_opt) - { - if (boost::filesystem::exists(boost::filesystem::path(*custom_db_path_opt))) - { - custom_db_path_str = *custom_db_path_opt; - } - else - { - cerr << "Custom db path: " << *custom_db_path_opt - << "does not exist" << endl; - - return EXIT_FAILURE; - } - } - else - { - // if not given assume it is located in ~./bitmonero/lmdb2 folder - // or ~./bitmonero/testnet/lmdb2 for testnet network - custom_db_path_str = blockchain_path.parent_path().string() - + string("/lmdb2"); - } - - custom_db_path_str = xmreg::remove_trailing_path_separator(custom_db_path_str); - - string deamon_url {*deamon_url_opt}; if (testnet && deamon_url == "http:://127.0.0.1:18081") @@ -154,13 +136,19 @@ int main(int ac, const char* av[]) { xmreg::page xmrblocks(&mcore, core_storage, deamon_url, - custom_db_path_str, testnet, enable_pusher, enable_key_image_checker, enable_output_key_checker, enable_autorefresh_option, - no_blocks_on_index); + enable_mixin_details, + enable_mempool_cache, + enable_tx_cache, + enable_block_cache, + show_cache_times, + no_blocks_on_index, + *testnet_url, + *mainnet_url); // crow instance crow::SimpleApp app; @@ -350,6 +338,108 @@ int main(int ac, const char* av[]) { return text; }); + if (enable_json_api) + { + CROW_ROUTE(app, "/api/transaction/") + ([&](const crow::request &req, string tx_hash) { + + crow::response r{xmrblocks.json_transaction(tx_hash).dump()}; + + r.add_header("Access-Control-Allow-Origin", "*"); + r.add_header("Access-Control-Allow-Headers", "Content-Type"); + r.add_header("Content-Type", "application/json"); + + return r; + }); + + CROW_ROUTE(app, "/api/block/") + ([&](const crow::request &req, string block_no_or_hash) { + + crow::response r{xmrblocks.json_block(block_no_or_hash).dump()}; + + r.add_header("Access-Control-Allow-Origin", "*"); + r.add_header("Access-Control-Allow-Headers", "Content-Type"); + r.add_header("Content-Type", "application/json"); + + return r; + }); + + + CROW_ROUTE(app, "/api/transactions").methods("GET"_method) + ([&](const crow::request &req) { + + string page = regex_search(req.raw_url, regex {"page=\\d+"}) ? + req.url_params.get("page") : "0"; + + string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ? + req.url_params.get("limit") : "25"; + + crow::response r{xmrblocks.json_transactions(page, limit).dump()}; + + r.add_header("Access-Control-Allow-Origin", "*"); + r.add_header("Access-Control-Allow-Headers", "Content-Type"); + r.add_header("Content-Type", "application/json"); + + return r; + }); + + CROW_ROUTE(app, "/api/mempool") + ([&](const crow::request &req) { + + crow::response r{xmrblocks.json_mempool().dump()}; + + r.add_header("Access-Control-Allow-Origin", "*"); + r.add_header("Access-Control-Allow-Headers", "Content-Type"); + r.add_header("Content-Type", "application/json"); + + return r; + }); + + CROW_ROUTE(app, "/api/search/") + ([&](const crow::request &req, string search_value) { + + crow::response r{xmrblocks.json_search(search_value).dump()}; + + r.add_header("Access-Control-Allow-Origin", "*"); + r.add_header("Access-Control-Allow-Headers", "Content-Type"); + r.add_header("Content-Type", "application/json"); + + return r; + }); + + CROW_ROUTE(app, "/api/outputs").methods("GET"_method) + ([&](const crow::request &req) { + + string tx_hash = regex_search(req.raw_url, regex {"txhash=\\w+"}) ? + req.url_params.get("txhash") : ""; + + string address = regex_search(req.raw_url, regex {"address=\\w+"}) ? + req.url_params.get("address") : ""; + + string viewkey = regex_search(req.raw_url, regex {"viewkey=\\w+"}) ? + req.url_params.get("viewkey") : ""; + + bool tx_prove{false}; + + try { + tx_prove = regex_search(req.raw_url, regex {"txprove=[01]"}) ? + boost::lexical_cast(req.url_params.get("txprove")) : + false; + } + catch (const boost::bad_lexical_cast &e) { + cerr << "Cant parse tx_prove as bool. Using default value" << endl; + } + + crow::response r{xmrblocks.json_outputs( + tx_hash, address, viewkey, tx_prove).dump()}; + + r.add_header("Access-Control-Allow-Origin", "*"); + r.add_header("Access-Control-Allow-Headers", "Content-Type"); + r.add_header("Content-Type", "application/json"); + + return r; + }); + } if (enable_autorefresh_option) { diff --git a/screenshot/screenshot_01.jpg b/screenshot/screenshot_01.jpg deleted file mode 100644 index 5558856..0000000 Binary files a/screenshot/screenshot_01.jpg and /dev/null differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4437c5b..a15dc01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,8 @@ set(SOURCE_FILES tools.cpp CmdLineOptions.cpp page.h - rpccalls.cpp rpccalls.h version.h.in) + rpccalls.cpp rpccalls.h + version.h.in) # make static library called libmyxrm # that we are going to link to diff --git a/src/CmdLineOptions.cpp b/src/CmdLineOptions.cpp index aa2fe07..2a2830a 100644 --- a/src/CmdLineOptions.cpp +++ b/src/CmdLineOptions.cpp @@ -27,14 +27,30 @@ namespace xmreg "use testnet blockchain") ("enable-pusher", value()->default_value(false)->implicit_value(true), "enable pushing signed tx") + ("enable-mixin-details", value()->default_value(false)->implicit_value(true), + "enable mixin details for key images, e.g., timescale, mixin of mixins, in tx context") ("enable-key-image-checker", value()->default_value(false)->implicit_value(true), "enable key images file checker") ("enable-output-key-checker", value()->default_value(false)->implicit_value(true), "enable outputs key file checker") + ("enable-mempool-cache", value()->default_value(true), + "enable caching txs in the mempool") + ("enable-json-api", value()->default_value(true), + "enable JSON REST api") + ("enable-tx-cache", value()->default_value(false)->implicit_value(true), + "enable caching of tx details") + ("show-cache-times", value()->default_value(false)->implicit_value(true), + "show times of getting data from cache vs no cache") + ("enable-block-cache", value()->default_value(false)->implicit_value(true), + "enable caching of block details") ("enable-autorefresh-option", value()->default_value(false)->implicit_value(true), "enable users to have the index page on autorefresh") ("port,p", value()->default_value("8081"), "default port") + ("testnet-url", value()->default_value(""), + "you can specifiy testnet url, if you run it on mainet. link will show on front page to testnet explorer") + ("mainnet-url", value()->default_value(""), + "you can specifiy mainnet url, if you run it on testnet. link will show on front page to mainnet explorer") ("no-blocks-on-index", value()->default_value("10"), "number of last blocks to be shown on index page") ("bc-path,b", value(), @@ -43,8 +59,6 @@ namespace xmreg "A path to crt file for ssl (https) functionality") ("ssl-key-file", value(), "A path to key file for ssl (https) functionality") - ("custom-db-path,c", value(), - "path to the custom lmdb database used for searching things") ("deamon-url,d", value()->default_value("http:://127.0.0.1:18081"), "monero address string"); diff --git a/src/MicroCore.cpp b/src/MicroCore.cpp index 66e4124..a75f24a 100644 --- a/src/MicroCore.cpp +++ b/src/MicroCore.cpp @@ -17,344 +17,344 @@ namespace namespace xmreg { - /** - * The constructor is interesting, as - * m_mempool and m_blockchain_storage depend - * on each other. - * - * So basically m_mempool is initialized with - * reference to Blockchain (i.e., Blockchain&) - * and m_blockchain_storage is initialized with - * reference to m_mempool (i.e., tx_memory_pool&) - * - * The same is done in cryptonode::core. - */ - MicroCore::MicroCore(): - m_mempool(m_blockchain_storage), - m_blockchain_storage(m_mempool) - {} +/** + * The constructor is interesting, as + * m_mempool and m_blockchain_storage depend + * on each other. + * + * So basically m_mempool is initialized with + * reference to Blockchain (i.e., Blockchain&) + * and m_blockchain_storage is initialized with + * reference to m_mempool (i.e., tx_memory_pool&) + * + * The same is done in cryptonode::core. + */ +MicroCore::MicroCore(): + m_mempool(m_blockchain_storage), + m_blockchain_storage(m_mempool) +{} - /** - * Initialized the MicroCore object. - * - * Create BlockchainLMDB on the heap. - * Open database files located in blockchain_path. - * Initialize m_blockchain_storage with the BlockchainLMDB object. - */ - bool - MicroCore::init(const string& _blockchain_path) +/** + * Initialized the MicroCore object. + * + * Create BlockchainLMDB on the heap. + * Open database files located in blockchain_path. + * Initialize m_blockchain_storage with the BlockchainLMDB object. + */ +bool +MicroCore::init(const string& _blockchain_path) +{ + int db_flags = 0; + + blockchain_path = _blockchain_path; + + //db_flags |= MDB_RDONLY; + db_flags |= MDB_NOLOCK; + //db_flags |= MDB_SYNC; + + // uint64_t DEFAULT_FLAGS = MDB_NOMETASYNC | MDB_NORDAHEAD; + + //db_flags = DEFAULT_FLAGS; + + HardFork* m_hardfork = nullptr; + + BlockchainDB* db = nullptr; + db = new BlockchainLMDB(); + + bool use_testnet {false}; + + uint64_t hard_fork_version_1_till = use_testnet ? testnet_hard_fork_version_1_till : mainnet_hard_fork_version_1_till; + + m_hardfork = new HardFork(*db, 1, hard_fork_version_1_till); + + try { - int db_flags = 0; - - blockchain_path = _blockchain_path; - - //db_flags |= MDB_RDONLY; - db_flags |= MDB_NOLOCK; - //db_flags |= MDB_SYNC; - - // uint64_t DEFAULT_FLAGS = MDB_NOMETASYNC | MDB_NORDAHEAD; - - //db_flags = DEFAULT_FLAGS; - - HardFork* m_hardfork = nullptr; - - BlockchainDB* db = nullptr; - db = new BlockchainLMDB(); - - bool use_testnet {false}; - - uint64_t hard_fork_version_1_till = use_testnet ? testnet_hard_fork_version_1_till : mainnet_hard_fork_version_1_till; - - m_hardfork = new HardFork(*db, 1, hard_fork_version_1_till); - - try - { - // try opening lmdb database files - db->open(blockchain_path, db_flags); - } - catch (const std::exception& e) - { - cerr << "Error opening database: " << e.what(); - return false; - } - - // check if the blockchain database - // is successful opened - if(!db->is_open()) - { - return false; - } - - // initialize Blockchain object to manage - // the database. - return m_blockchain_storage.init(db, m_hardfork, false); + // try opening lmdb database files + db->open(blockchain_path, db_flags); + } + catch (const std::exception& e) + { + cerr << "Error opening database: " << e.what(); + return false; } - /** - * Get m_blockchain_storage. - * Initialize m_blockchain_storage with the BlockchainLMDB object. - */ - Blockchain& - MicroCore::get_core() + // check if the blockchain database + // is successful opened + if(!db->is_open()) { - return m_blockchain_storage; + return false; } - /** - * Get block by its height - * - * returns true if success - */ - bool - MicroCore::get_block_by_height(const uint64_t& height, block& blk) + // initialize Blockchain object to manage + // the database. + return m_blockchain_storage.init(db, m_hardfork, false); +} + +/** +* Get m_blockchain_storage. +* Initialize m_blockchain_storage with the BlockchainLMDB object. +*/ +Blockchain& +MicroCore::get_core() +{ + return m_blockchain_storage; +} + +/** + * Get block by its height + * + * returns true if success + */ +bool +MicroCore::get_block_by_height(const uint64_t& height, block& blk) +{ + try { - try - { - blk = m_blockchain_storage.get_db().get_block_from_height(height); - } - catch (const BLOCK_DNE& e) - { - cerr << "Block of height " << height - << " not found in the blockchain!" - << e.what() - << endl; + blk = m_blockchain_storage.get_db().get_block_from_height(height); + } + catch (const BLOCK_DNE& e) + { + cerr << "Block of height " << height + << " not found in the blockchain!" + << e.what() + << endl; - return false; - } - catch (const DB_ERROR& e) - { - cerr << "Blockchain access error when getting block " << height - << e.what() - << endl; + return false; + } + catch (const DB_ERROR& e) + { + cerr << "Blockchain access error when getting block " << height + << e.what() + << endl; - return false; - } - catch (...) - { - cerr << "Something went terribly wrong when getting block " << height - << endl; + return false; + } + catch (...) + { + cerr << "Something went terribly wrong when getting block " << height + << endl; - return false; - } + return false; + } + + return true; +} + + + +/** + * Get transaction tx from the blockchain using it hash + */ +bool +MicroCore::get_tx(const crypto::hash& tx_hash, transaction& tx) +{ + if (m_blockchain_storage.have_tx(tx_hash)) + { + // get transaction with given hash + tx = m_blockchain_storage.get_db().get_tx(tx_hash); + } + else + { + cerr << "MicroCore::get_tx tx does not exist in blockchain: " << tx_hash << endl; + return false; + } + + + return true; +} + +bool +MicroCore::get_tx(const string& tx_hash_str, transaction& tx) +{ + + // parse tx hash string to hash object + crypto::hash tx_hash; + + if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) + { + cerr << "Cant parse tx hash: " << tx_hash_str << endl; + return false; + } + + + if (!get_tx(tx_hash, tx)) + { + return false; + } + + + return true; +} + + + + +/** + * Find output with given public key in a given transaction + */ +bool +MicroCore::find_output_in_tx(const transaction& tx, + const public_key& output_pubkey, + tx_out& out, + size_t& output_index) +{ + + size_t idx {0}; + + + // search in the ouputs for an output which + // public key matches to what we want + auto it = std::find_if(tx.vout.begin(), tx.vout.end(), + [&](const tx_out& o) + { + const txout_to_key& tx_in_to_key + = boost::get(o.target); + + ++idx; + + return tx_in_to_key.key == output_pubkey; + }); + + if (it != tx.vout.end()) + { + // we found the desired public key + out = *it; + output_index = idx > 0 ? idx - 1 : idx; + + //cout << idx << " " << output_index << endl; return true; } + return false; +} - /** - * Get transaction tx from the blockchain using it hash - */ - bool - MicroCore::get_tx(const crypto::hash& tx_hash, transaction& tx) +/** + * Returns tx hash in a given block which + * contains given output's public key + */ +bool +MicroCore::get_tx_hash_from_output_pubkey(const public_key& output_pubkey, + const uint64_t& block_height, + crypto::hash& tx_hash, + cryptonote::transaction& tx_found) +{ + + tx_hash = null_hash; + + // get block of given height + block blk; + if (!get_block_by_height(block_height, blk)) { - if (m_blockchain_storage.have_tx(tx_hash)) - { - // get transaction with given hash - tx = m_blockchain_storage.get_db().get_tx(tx_hash); - } - else - { - cerr << "MicroCore::get_tx tx does not exist in blockchain: " << tx_hash << endl; - return false; - } - - - return true; - } - - bool - MicroCore::get_tx(const string& tx_hash_str, transaction& tx) - { - - // parse tx hash string to hash object - crypto::hash tx_hash; - - if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) - { - cerr << "Cant parse tx hash: " << tx_hash_str << endl; - return false; - } - - - if (!get_tx(tx_hash, tx)) - { - return false; - } - - - return true; + cerr << "Cant get block of height: " << block_height << endl; + return false; } + // get all transactions in the block found + // initialize the first list with transaction for solving + // the block i.e. coinbase. + list txs {blk.miner_tx}; + list missed_txs; + + if (!m_blockchain_storage.get_transactions(blk.tx_hashes, txs, missed_txs)) + { + cerr << "Cant find transcations in block: " << block_height << endl; + return false; + } + + if (!missed_txs.empty()) + { + cerr << "Transactions not found in blk: " << block_height << endl; + + for (const crypto::hash& h : missed_txs) + { + cerr << " - tx hash: " << h << endl; + } + + return false; + } - /** - * Find output with given public key in a given transaction - */ - bool - MicroCore::find_output_in_tx(const transaction& tx, - const public_key& output_pubkey, - tx_out& out, - size_t& output_index) + // search outputs in each transactions + // until output with pubkey of interest is found + for (const transaction& tx : txs) { - size_t idx {0}; + tx_out found_out; + // we dont need here output_index + size_t output_index; - // search in the ouputs for an output which - // public key matches to what we want - auto it = std::find_if(tx.vout.begin(), tx.vout.end(), - [&](const tx_out& o) - { - const txout_to_key& tx_in_to_key - = boost::get(o.target); - - ++idx; - - return tx_in_to_key.key == output_pubkey; - }); - - if (it != tx.vout.end()) + if (find_output_in_tx(tx, output_pubkey, found_out, output_index)) { // we found the desired public key - out = *it; - output_index = idx > 0 ? idx - 1 : idx; - - //cout << idx << " " << output_index << endl; + tx_hash = get_transaction_hash(tx); + tx_found = tx; return true; } + } + + return false; +} + + +uint64_t +MicroCore::get_blk_timestamp(uint64_t blk_height) +{ + cryptonote::block blk; + + if (!get_block_by_height(blk_height, blk)) + { + cerr << "Cant get block by height: " << blk_height << endl; + return 0; + } + + return blk.timestamp; +} + + +/** + * De-initialized Blockchain. + * + * since blockchain is opened as MDB_RDONLY + * need to manually free memory taken on heap + * by BlockchainLMDB + */ +MicroCore::~MicroCore() +{ + delete &m_blockchain_storage.get_db(); +} + + +bool +init_blockchain(const string& path, + MicroCore& mcore, + Blockchain*& core_storage) +{ + + // initialize the core using the blockchain path + if (!mcore.init(path)) + { + cerr << "Error accessing blockchain." << endl; return false; } + // get the high level Blockchain object to interact + // with the blockchain lmdb database + core_storage = &(mcore.get_core()); - /** - * Returns tx hash in a given block which - * contains given output's public key - */ - bool - MicroCore::get_tx_hash_from_output_pubkey(const public_key& output_pubkey, - const uint64_t& block_height, - crypto::hash& tx_hash, - cryptonote::transaction& tx_found) - { + return true; +} - tx_hash = null_hash; - - // get block of given height - block blk; - if (!get_block_by_height(block_height, blk)) - { - cerr << "Cant get block of height: " << block_height << endl; - return false; - } - - - // get all transactions in the block found - // initialize the first list with transaction for solving - // the block i.e. coinbase. - list txs {blk.miner_tx}; - list missed_txs; - - if (!m_blockchain_storage.get_transactions(blk.tx_hashes, txs, missed_txs)) - { - cerr << "Cant find transcations in block: " << block_height << endl; - return false; - } - - if (!missed_txs.empty()) - { - cerr << "Transactions not found in blk: " << block_height << endl; - - for (const crypto::hash& h : missed_txs) - { - cerr << " - tx hash: " << h << endl; - } - - return false; - } - - - // search outputs in each transactions - // until output with pubkey of interest is found - for (const transaction& tx : txs) - { - - tx_out found_out; - - // we dont need here output_index - size_t output_index; - - if (find_output_in_tx(tx, output_pubkey, found_out, output_index)) - { - // we found the desired public key - tx_hash = get_transaction_hash(tx); - tx_found = tx; - - return true; - } - - } - - return false; - } - - - uint64_t - MicroCore::get_blk_timestamp(uint64_t blk_height) - { - cryptonote::block blk; - - if (!get_block_by_height(blk_height, blk)) - { - cerr << "Cant get block by height: " << blk_height << endl; - return 0; - } - - return blk.timestamp; - } - - - /** - * De-initialized Blockchain. - * - * since blockchain is opened as MDB_RDONLY - * need to manually free memory taken on heap - * by BlockchainLMDB - */ - MicroCore::~MicroCore() - { - delete &m_blockchain_storage.get_db(); - } - - - bool - init_blockchain(const string& path, - MicroCore& mcore, - Blockchain*& core_storage) - { - - // initialize the core using the blockchain path - if (!mcore.init(path)) - { - cerr << "Error accessing blockchain." << endl; - return false; - } - - // get the high level Blockchain object to interact - // with the blockchain lmdb database - core_storage = &(mcore.get_core()); - - return true; - } - - string - MicroCore::get_blkchain_path() - { - return blockchain_path; - } +string +MicroCore::get_blkchain_path() +{ + return blockchain_path; +} } diff --git a/src/mylmdb.h b/src/mylmdb.h deleted file mode 100644 index ead7edf..0000000 --- a/src/mylmdb.h +++ /dev/null @@ -1,755 +0,0 @@ -// -// Created by mwo on 27/04/16. -// -#ifndef XMRLMDBCPP_MYLMDB_H -#define XMRLMDBCPP_MYLMDB_H - -#include "../ext/lmdb++.h" - -#include -#include - -namespace xmreg -{ - - using epee::string_tools::pod_to_hex; - using epee::string_tools::hex_to_pod; - - using namespace std; - - - - /** - * Stores info about outputs useful - * for checking which ouputs belong to a - * given address and viewkey - */ - struct output_info - { - crypto::public_key out_pub_key; - crypto::hash tx_hash; - crypto::public_key tx_pub_key; - uint64_t amount; - uint64_t index_in_tx; - }; - - std::ostream& operator<<(std::ostream& os, const output_info& out_info) - { - os << ", out_pub_key: " << out_info.out_pub_key - << ", tx_hash: " << out_info.tx_hash - << ", tx_pub_key: " << out_info.tx_pub_key - << ", amount: " << XMR_AMOUNT(out_info.amount) - << ", index_in_tx: " << out_info.index_in_tx; - - return os; - } - - class MyLMDB - { - - - static const uint64_t DEFAULT_MAPSIZE = 30UL * 1024UL * 1024UL * 1024UL; /* 30 GiB */ - static const uint64_t DEFAULT_NO_DBs = 10; - - - string m_db_path; - - uint64_t m_mapsize; - uint64_t m_no_dbs; - - lmdb::env m_env; - - - public: - MyLMDB(string _path, - uint64_t _mapsize = DEFAULT_MAPSIZE, - uint64_t _no_dbs = DEFAULT_NO_DBs) - : m_db_path {_path}, - m_mapsize {_mapsize}, - m_no_dbs {_no_dbs}, - m_env {nullptr} - { - create_and_open_env(); - } - - bool - create_and_open_env() - { - try - { m_env = lmdb::env::create(); - m_env.set_mapsize(m_mapsize); - m_env.set_max_dbs(m_no_dbs); - m_env.open(m_db_path.c_str(), MDB_CREATE, 0664); - } - catch (lmdb::error& e ) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - - bool - write_key_images(const transaction& tx) - { - crypto::hash tx_hash = get_transaction_hash(tx); - - string tx_hash_str = pod_to_hex(tx_hash); - - vector key_images - = xmreg::get_key_images(tx); - - lmdb::txn wtxn {nullptr}; - lmdb::dbi wdbi {0}; - - unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED; - - try - { - wtxn = lmdb::txn::begin(m_env); - wdbi = lmdb::dbi::open(wtxn, "key_images", flags); - } - catch (lmdb::error& e ) - { - cerr << e.what() << endl; - return false; - } - - for (const cryptonote::txin_to_key& key_image: key_images) - { - string key_img_str = pod_to_hex(key_image.k_image); - - lmdb::val key_img_val {key_img_str}; - lmdb::val tx_hash_val {tx_hash_str}; - - wdbi.put(wtxn, key_img_val, tx_hash_val); - } - - try - { - wtxn.commit(); - } - catch (lmdb::error& e ) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - bool - write_output_public_keys(const transaction& tx, const block& blk) - { - crypto::hash tx_hash = get_transaction_hash(tx); - - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - - string tx_hash_str = pod_to_hex(tx_hash); - - vector> outputs = - xmreg::get_ouputs_tuple(tx); - - lmdb::txn wtxn {nullptr}; - lmdb::dbi wdbi1 {0}; - lmdb::dbi wdbi2 {0}; - lmdb::dbi wdbi3 {0}; - - unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED; - - try - { - wtxn = lmdb::txn::begin(m_env); - wdbi1 = lmdb::dbi::open(wtxn, "output_public_keys", flags); - wdbi2 = lmdb::dbi::open(wtxn, "output_amounts", flags); - wdbi3 = lmdb::dbi::open(wtxn, "output_info", - flags | MDB_INTEGERKEY | MDB_INTEGERDUP); - } - catch (lmdb::error& e ) - { - cerr << e.what() << endl; - return false; - } - - for (auto& output: outputs) - { - - public_key out_pub_key = std::get<0>(output).key; - - string public_key_str = pod_to_hex(out_pub_key); - - lmdb::val public_key_val {public_key_str}; - lmdb::val tx_hash_val {tx_hash_str}; - - uint64_t amount = std::get<1>(output); - - lmdb::val amount_val {static_cast(&amount), sizeof(amount)}; - - uint64_t index_in_tx = std::get<2>(output); - - output_info out_info {out_pub_key, tx_hash, - tx_pub_key, amount, - index_in_tx}; - - uint64_t out_timestamp = blk.timestamp; - - lmdb::val out_timestamp_val {static_cast(&out_timestamp), - sizeof(out_timestamp)}; - lmdb::val out_info_val {static_cast(&out_info), - sizeof(out_info)}; - - wdbi1.put(wtxn, public_key_val, tx_hash_val); - wdbi2.put(wtxn, public_key_val, amount_val); - wdbi3.put(wtxn, out_timestamp_val, out_info_val); - } - - try - { - wtxn.commit(); - } - catch (lmdb::error& e ) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - bool - write_tx_public_key(const transaction& tx) - { - crypto::hash tx_hash = get_transaction_hash(tx); - - string tx_hash_str = pod_to_hex(tx_hash); - - unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED; - - public_key pk = get_tx_pub_key_from_extra(tx); - - string pk_str = pod_to_hex(pk); - - try - { - lmdb::txn wtxn = lmdb::txn::begin(m_env); - lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "tx_public_keys", flags); - - //cout << "Saving public_key: " << pk_str << endl; - - lmdb::val public_key_val {pk_str}; - lmdb::val tx_hash_val {tx_hash_str}; - - wdbi.put(wtxn, public_key_val, tx_hash_val); - - wtxn.commit(); - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - bool - write_payment_id(const transaction& tx) - { - crypto::hash tx_hash = get_transaction_hash(tx); - - string tx_hash_str = pod_to_hex(tx_hash); - - crypto::hash payment_id; - crypto::hash8 payment_id8; - - get_payment_id(tx, payment_id, payment_id8); - - if (payment_id == null_hash) - { - return true; - } - - unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED; - - string payment_id_str = pod_to_hex(payment_id); - - try - { - lmdb::txn wtxn = lmdb::txn::begin(m_env); - lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "payments_id", flags); - - //cout << "Saving payiment_id: " << payment_id_str << endl; - - lmdb::val payment_id_val {payment_id_str}; - lmdb::val tx_hash_val {tx_hash_str}; - - wdbi.put(wtxn, payment_id_val, tx_hash_val); - - wtxn.commit(); - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - bool - write_encrypted_payment_id(const transaction& tx) - { - crypto::hash tx_hash = get_transaction_hash(tx); - - string tx_hash_str = pod_to_hex(tx_hash); - - crypto::hash payment_id; - crypto::hash8 payment_id8; - - get_payment_id(tx, payment_id, payment_id8); - - if (payment_id8 == null_hash8) - { - return true; - } - - unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED; - - string payment_id_str = pod_to_hex(payment_id8); - - try - { - lmdb::txn wtxn = lmdb::txn::begin(m_env); - lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "encrypted_payments_id", flags); - - //cout << "Saving encrypted payiment_id: " << payment_id_str << endl; - //string wait_for_enter; - //cin >> wait_for_enter; - - lmdb::val payment_id_val {payment_id_str}; - lmdb::val tx_hash_val {tx_hash_str}; - - wdbi.put(wtxn, payment_id_val, tx_hash_val); - - wtxn.commit(); - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - -// // this seems to be not needed as outputs are written based on timestamps -// -// bool -// write_block_timestamp(uint64_t& blk_timestamp, uint64_t& blk_height) -// { -// -// unsigned int flags = MDB_CREATE | MDB_INTEGERKEY; -// -// try -// { -// lmdb::txn wtxn = lmdb::txn::begin(m_env); -// lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "block_timestamps", flags); -// -// lmdb::val blk_timestamp_val {static_cast(&blk_timestamp), -// sizeof(blk_timestamp)}; -// lmdb::val blk_height_val {static_cast(&blk_height), -// sizeof(blk_height)}; -// -// wdbi.put(wtxn, blk_timestamp_val, blk_height_val); -// -// wtxn.commit(); -// } -// catch (lmdb::error& e) -// { -// cerr << e.what() << endl; -// return false; -// } -// -// return true; -// } - - bool - search(const string& key, - vector& found_tx_hashes, - const string& db_name = "key_images") - { - unsigned int flags = MDB_DUPSORT | MDB_DUPFIXED; - - try - { - - lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY); - lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags); - lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi); - - lmdb::val key_to_find{key}; - lmdb::val tx_hash_val; - - // set cursor the the first item - if (cr.get(key_to_find, tx_hash_val, MDB_SET)) - { - //cout << key_val_to_str(key_to_find, tx_hash_val) << endl; - found_tx_hashes.push_back(string(tx_hash_val.data(), tx_hash_val.size())); - - // process other values for the same key - while (cr.get(key_to_find, tx_hash_val, MDB_NEXT_DUP)) - { - //cout << key_val_to_str(key_to_find, tx_hash_val) << endl; - found_tx_hashes.push_back(string(tx_hash_val.data(), tx_hash_val.size())); - } - } - else - { - return false; - } - - cr.close(); - rtxn.abort(); - - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - bool - get_output_amount(const string& key, - uint64_t& amount, - const string& db_name = "output_amounts") - { - - unsigned int flags = 0; - - try - { - - lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY); - lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags); - - lmdb::val key_to_find{key}; - lmdb::val amount_val; - - if(!rdbi.get(rtxn, key_to_find, amount_val)) - { - return false; - } - - amount = *(amount_val.data()); - - rtxn.abort(); - - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - - bool - get_output_info(uint64_t key_timestamp, - vector& out_infos, - const string& db_name = "output_info") - { - - unsigned int flags = 0; - - try - { - - lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY); - lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags); - - lmdb::val key_to_find{static_cast(&key_timestamp), - sizeof(key_timestamp)}; - lmdb::val info_val; - - lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi); - - // set cursor the the first item - if (cr.get(key_to_find, info_val, MDB_SET_RANGE)) - { - out_infos.push_back(*(info_val.data())); - - // process other values for the same key - while (cr.get(key_to_find, info_val, MDB_NEXT_DUP)) - { - //cout << key_val_to_str(key_to_find, tx_hash_val) << endl; - out_infos.push_back(*(info_val.data())); - } - } - else - { - return false; - } - - rtxn.abort(); - - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - bool - get_output_info_range(uint64_t key_timestamp_start, - uint64_t key_timestamp_end, - vector>& out_infos, - const string& db_name = "output_info") - { - - unsigned int flags = 0; - - try - { - lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY); - lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags); - - lmdb::val key_to_find{static_cast(&key_timestamp_start), - sizeof(key_timestamp_start)}; - lmdb::val info_val; - - lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi); - - uint64_t current_timestamp = key_timestamp_start; - - // set cursor the the first item - if (cr.get(key_to_find, info_val, MDB_SET_RANGE)) - { - - current_timestamp = *key_to_find.data(); - - if (current_timestamp > key_timestamp_end) - { - return false; - } - - out_infos.push_back(make_pair( - current_timestamp, - *(info_val.data()))); - - // process other values for the same key - while (cr.get(key_to_find, info_val, MDB_NEXT)) - { - current_timestamp = *key_to_find.data(); - - if (current_timestamp > key_timestamp_end) - { - break; - } - - out_infos.push_back(make_pair( - current_timestamp, - *(info_val.data()))); - } - } - else - { - return false; - } - - rtxn.abort(); - - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return false; - } - - return true; - } - - /** - * Returns sorted and unique tx hashes withing a - * given timestamp range - * - * @param key_timestamp_start - * @param key_timestamp_end - * @param out_txs - * @return bool - */ - bool - get_txs_from_timestamp_range(uint64_t key_timestamp_start, - uint64_t key_timestamp_end, - vector& out_txs) - { - using output_pair = pair; - - auto sort_by_timestamp = [](const output_pair& l, - const output_pair& r) - { - return l.first < r.first; - }; - - vector out_infos; - - if (get_output_info_range(key_timestamp_start, - key_timestamp_end, - out_infos)) - { - - set unique_txs(sort_by_timestamp); - - for (auto oi: out_infos) - unique_txs.insert(oi); - - for (auto ut: unique_txs) - out_txs.push_back(ut.second.tx_hash); - - return true; - } - - return false; - } - - - - void - for_all_outputs( - std::function f) - { - unsigned int flags = MDB_DUPSORT | MDB_DUPFIXED; - - try - { - - lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY); - lmdb::dbi rdbi = lmdb::dbi::open(rtxn, "output_info", flags); - lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi); - - lmdb::val key_to_find; - lmdb::val amount_val; - - - // process all values for the same key - while (cr.get(key_to_find, amount_val, MDB_NEXT)) - { - public_key pub_key; - - hex_to_pod(string(key_to_find.data(), key_to_find.size()), - pub_key); - - output_info out_info = *(amount_val.data()); - - if (f(pub_key, out_info) == false) - { - break; - } - } - - cr.close(); - rtxn.abort(); - - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - } - } - - - void - print_all(const string& db_name) - { - unsigned int flags = MDB_DUPSORT | MDB_DUPFIXED; - - try - { - - lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY); - lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags); - lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi); - - lmdb::val key_to_find; - lmdb::val tx_hash_val; - - - // process other values for the same key - while (cr.get(key_to_find, tx_hash_val, MDB_NEXT)) - { - cout << key_val_to_str(key_to_find, tx_hash_val) << endl; - } - - cr.close(); - rtxn.abort(); - - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - } - } - - static uint64_t - get_blockchain_height(string blk_path = "/home/mwo/.blockchain/lmdb") - { - uint64_t height {0}; - - try - { - auto env = lmdb::env::create(); - env.set_mapsize(DEFAULT_MAPSIZE * 3); - env.set_max_dbs(20); - env.open(blk_path.c_str(), MDB_CREATE, 0664); - - //auto rtxn = lmdb::txn::begin(env, nullptr, MDB_RDONLY); - auto rtxn = lmdb::txn::begin(env, nullptr); - auto rdbi = lmdb::dbi::open(rtxn, "blocks"); - - MDB_stat stats = rdbi.stat(rtxn); - - height = static_cast(stats.ms_entries); - - rtxn.abort(); - } - catch (lmdb::error& e) - { - cerr << e.what() << endl; - return height; - } - catch (exception& e) - { - cerr << e.what() << endl; - return height; - } - - //cout << height << endl; - - return height; - - } - - string - key_val_to_str(const lmdb::val& key, const lmdb::val& val) - { - return "key: " + string(key.data(), key.size()) - + ", val: " + string(val.data(), val.size()); - } - - - - }; - -} - -#endif //XMRLMDBCPP_MYLMDB_H diff --git a/src/page.h b/src/page.h index eb3bc45..8c794fb 100644 --- a/src/page.h +++ b/src/page.h @@ -16,9 +16,11 @@ #include "MicroCore.h" #include "tools.h" #include "rpccalls.h" -#include "mylmdb.h" #include "../ext/crow/http_request.h" +#include "../ext/vpetrigocaches/cache.hpp" +#include "../ext/vpetrigocaches/lru_cache_policy.hpp" +#include "../ext/vpetrigocaches/fifo_cache_policy.hpp" #include #include #include @@ -44,6 +46,51 @@ #define TMPL_MY_RAWOUTPUTKEYS TMPL_DIR "/rawoutputkeys.html" #define TMPL_MY_CHECKRAWOUTPUTKEYS TMPL_DIR "/checkrawoutputkeys.html" + + + +// basic info about tx to be stored in cashe. +// we need to store block_no and timestamp, +// as this time and number of confirmation needs +// to be updated between requests. Just cant +// get it from cash, as it will be old very soon +struct tx_info_cache +{ + uint64_t block_no; + uint64_t timestamp; + mstch::map tx_map; + + // custom key for use in cache. + // cache uses unordeded map for keys + struct key + { + crypto::hash tx_hash; + bool detailed; + + bool operator==(const key &other) const + { + return (tx_hash == other.tx_hash && detailed == other.detailed); + } + }; +}; + + +// indect overload of hash for tx_info_cache::key +namespace std +{ + template<> + struct hash + { + size_t operator()(const tx_info_cache::key& k) const + { + size_t const h1 ( std::hash{}(k.tx_hash) ); + size_t const h2 ( std::hash{}(k.detailed) ); + return h1 ^ (h2 << 1); + }; + }; +} + + namespace xmreg { @@ -52,48 +99,8 @@ using namespace cryptonote; using namespace crypto; using namespace std; -// define a checker to test if a structure has "tx_blob" -// member variable. I use modified daemon with few extra -// bits and pieces here and there. One of them is -// tx_blob in cryptonote::tx_info structure -// thus I check if I run my version, or just -// generic one -DEFINE_MEMBER_CHECKER(tx_blob) - -// define getter to get tx_blob, i.e., get_tx_blob function -// as string if exists. the getter return empty string if -// tx_blob does not exist -DEFINE_MEMBER_GETTER(tx_blob, string) - - - -/** - * Check if a given header filed contains value string - * - * @param req - * @param field - * @param value - * @return string - */ -string -does_header_has(const crow::request& req, - const string& field = "Accept", - const string& value = "q=.2, */*; q=.2") -{ - string accept = req.get_header_value(field); - - if (!accept.empty()) - { - if (accept.find(value) != std::string::npos) - { - return accept; - } - } - - return string {}; -} - - +using epee::string_tools::pod_to_hex; +using epee::string_tools::hex_to_pod; /** * @brief The tx_details struct @@ -121,9 +128,7 @@ struct tx_details crypto::hash payment_id = null_hash; // normal crypto::hash8 payment_id8 = null_hash8; // encrypted - string json_representation; - - std::vector > signatures; + std::vector> signatures; // key images of inputs vector input_key_imgs; @@ -132,38 +137,39 @@ struct tx_details vector> output_pub_keys; mstch::map - get_mstch_map() + get_mstch_map() const { // remove "<" and ">" from the hash string - string tx_hash_str = pod_to_hex(hash); + string tx_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", hash)); - string tx_prefix_hash_str = pod_to_hex(prefix_hash); + string tx_prefix_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", prefix_hash)); - string tx_pk_str = pod_to_hex(pk); + string tx_pk_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", pk)); //cout << "payment_id: " << payment_id << endl; - string pid_str = pod_to_hex(payment_id); - string pid8_str = pod_to_hex(payment_id8); + string pid_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", payment_id)); + string pid8_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", payment_id8)); string mixin_str {"N/A"}; string fee_str {"N/A"}; string fee_short_str {"N/A"}; + const double& xmr_amount = XMR_AMOUNT(fee); if (!input_key_imgs.empty()) { mixin_str = std::to_string(mixin_no - 1); - fee_str = fmt::format("{:0.6f}", XMR_AMOUNT(fee)); - fee_short_str = fmt::format("{:0.3f}", XMR_AMOUNT(fee)); + fee_str = fmt::format("{:0.6f}", xmr_amount); + fee_short_str = fmt::format("{:0.3f}", xmr_amount); } - //cout << "extra: " << extra_str << endl; + const double& tx_size = static_cast(size)/1024.0; mstch::map txd_map { - {"hash" , tx_hash_str}, - {"prefix_hash" , tx_prefix_hash_str}, - {"pub_key" , tx_pk_str}, + {"hash" , pod_to_hex(hash)}, + {"prefix_hash" , pod_to_hex(prefix_hash)}, + {"pub_key" , pod_to_hex(pk)}, {"tx_fee" , fee_str}, {"tx_fee_short" , fee_short_str}, {"sum_inputs" , xmr_amount_to_str(xmr_inputs , "{:0.6f}")}, @@ -175,18 +181,16 @@ struct tx_details {"no_nonrct_inputs" , num_nonrct_inputs}, {"mixin" , mixin_str}, {"blk_height" , blk_height}, - {"version" , std::to_string(version)}, + {"version" , version}, {"has_payment_id" , payment_id != null_hash}, {"has_payment_id8" , payment_id8 != null_hash8}, - {"payment_id" , pid_str}, + {"payment_id" , pod_to_hex(payment_id)}, {"confirmations" , no_confirmations}, {"extra" , get_extra_str()}, - {"payment_id8" , pid8_str}, - {"unlock_time" , std::to_string(unlock_time)}, - {"tx_size" , fmt::format("{:0.4f}", - static_cast(size)/1024.0)}, - {"tx_size_short" , fmt::format("{:0.2f}", - static_cast(size)/1024.0)} + {"payment_id8" , pod_to_hex(payment_id8)}, + {"unlock_time" , unlock_time}, + {"tx_size" , fmt::format("{:0.4f}", tx_size)}, + {"tx_size_short" , fmt::format("{:0.2f}", tx_size)} }; @@ -195,13 +199,11 @@ struct tx_details string - get_extra_str() + get_extra_str() const { - string extra_str = epee::string_tools::buff_to_hex_nodelimer( - string{reinterpret_cast(extra.data()), extra.size()}); - - return extra_str; + return epee::string_tools::buff_to_hex_nodelimer( + string{reinterpret_cast(extra.data()), extra.size()}); } @@ -237,21 +239,17 @@ struct tx_details } }; -class page { - // check if we have tx_blob member in tx_info structure - static const bool HAVE_TX_BLOB { - HAS_MEMBER(cryptonote::tx_info, tx_blob) - }; +class page +{ static const bool FULL_AGE_FORMAT {true}; MicroCore* mcore; Blockchain* core_storage; rpccalls rpc; - time_t server_timestamp; - string lmdb2_path; + atomic server_timestamp; bool testnet; @@ -259,15 +257,21 @@ class page { bool enable_key_image_checker; bool enable_output_key_checker; + bool enable_mixins_details; + bool enable_mempool_cache; + bool enable_tx_cache; + bool enable_block_cache; + bool show_cache_times; bool enable_autorefresh_option; - bool have_custom_lmdb; uint64_t no_of_mempool_tx_of_frontpage; uint64_t no_blocks_on_index; + string testnet_url; + string mainnet_url; // instead of constatnly reading template files // from hard drive for each request, we can read // them only once, when the explorer starts into this map @@ -276,58 +280,92 @@ class page { map template_file; + // alias for easy class typing + template + using lru_cache_t = caches::fixed_sized_cache>; + + + + // alias for easy class typing + template + using fifo_cache_t = caches::fixed_sized_cache>; + + + // this struct is used to keep info about mempool + // txs in FIFO cache. Should speed up processing + // mempool txs for each request + struct mempool_tx_info + { + uint64_t sum_inputs; + uint64_t sum_outputs; + uint64_t no_inputs; + uint64_t no_outputs; + + uint64_t num_nonrct_inputs; + + uint64_t mixin_no; + + string hash; + string fee; + string xmr_inputs_str; + string xmr_outputs_str; + string timestamp; + + string txsize; + }; + + // cache of txs in mempool, so that we dont + // parse their json for each request + fifo_cache_t mempool_tx_json_cache; + + // cache of txs_map of txs in blocks. this is useful for + // index2 page, so that we dont parse txs in each block + // for each request. + fifo_cache_t>> block_tx_cache; + + lru_cache_t tx_context_cache; public: - page(MicroCore* _mcore, Blockchain* _core_storage, - string _deamon_url, string _lmdb2_path, - bool _testnet, bool _enable_pusher, + page(MicroCore* _mcore, + Blockchain* _core_storage, + string _deamon_url, + bool _testnet, + bool _enable_pusher, bool _enable_key_image_checker, bool _enable_output_key_checker, bool _enable_autorefresh_option, - uint64_t _no_blocks_on_index) + bool _enable_mixins_details, + bool _enable_mempool_cache, + bool _enable_tx_cache, + bool _enable_block_cache, + bool _show_cache_times, + uint64_t _no_blocks_on_index, + string _testnet_url, + string _mainnet_url) : mcore {_mcore}, core_storage {_core_storage}, rpc {_deamon_url}, server_timestamp {std::time(nullptr)}, - lmdb2_path {_lmdb2_path}, testnet {_testnet}, enable_pusher {_enable_pusher}, - have_custom_lmdb {false}, enable_key_image_checker {_enable_key_image_checker}, enable_output_key_checker {_enable_output_key_checker}, enable_autorefresh_option {_enable_autorefresh_option}, - no_blocks_on_index {_no_blocks_on_index} + enable_mixins_details {_enable_mixins_details}, + enable_mempool_cache {_enable_mempool_cache}, + enable_tx_cache {_enable_tx_cache}, + enable_block_cache {_enable_block_cache}, + show_cache_times {_show_cache_times}, + no_blocks_on_index {_no_blocks_on_index}, + testnet_url {_testnet_url}, + mainnet_url {_mainnet_url}, + mempool_tx_json_cache(1000), + block_tx_cache(200), + tx_context_cache(1000) { no_of_mempool_tx_of_frontpage = 25; - // just moneky patching now, to check - // if custom lmdb database exist, so that - // we can search for, e.g., key images, - // payments ids. try to open this database. - // if it fails, we assume it does not exist. - // this is ugly check, but will do for now. - // it does not even check if this custom lmdb - // is up to date. - try - { - unique_ptr mylmdb; - - if (bf::is_directory(lmdb2_path)) - { - mylmdb = make_unique(lmdb2_path); - - // if we got to here, it seems that database exist - have_custom_lmdb = true; - } - - } - catch (const std::exception& e) - { - cerr << "Custom lmdb databse seem not to exist. Its not big deal. " - "Just some searches wont be possible" - << endl; - } // read template files for all the pages @@ -351,10 +389,10 @@ public: template_file["checkoutputkeys"] = get_full_page(xmreg::read(TMPL_MY_CHECKRAWOUTPUTKEYS)); template_file["address"] = get_full_page(xmreg::read(TMPL_ADDRESS)); template_file["search_results"] = get_full_page(xmreg::read(TMPL_SEARCH_RESULTS)); - template_file["tx_details"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_details.html"); - template_file["tx_table_head"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_table_head.html"); - template_file["tx_table_row"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_table_row.html"); + template_file["tx_details"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_details.html"); + template_file["tx_table_header"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_table_header.html"); + template_file["tx_table_row"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_table_row.html"); } /** @@ -370,7 +408,6 @@ public: server_timestamp = std::time(nullptr); uint64_t local_copy_server_timestamp = server_timestamp; - // number of last blocks to show uint64_t no_of_last_blocks {no_blocks_on_index + 1}; @@ -379,22 +416,24 @@ public: // initalise page tempate map with basic info about blockchain mstch::map context { - {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb}, - {"refresh" , refresh_page}, - {"height" , std::to_string(height)}, - {"server_timestamp" , xmreg::timestamp_to_str(local_copy_server_timestamp)}, - {"age_format" , string("[h:m:d]")}, - {"page_no" , std::to_string(page_no)}, - {"total_page_no" , std::to_string(height / (no_of_last_blocks))}, - {"is_page_zero" , !bool(page_no)}, - {"no_of_last_blocks", no_of_last_blocks}, - {"next_page" , std::to_string(page_no + 1)}, - {"prev_page" , std::to_string((page_no > 0 ? page_no - 1 : 0))}, + {"testnet" , testnet}, + {"testnet_url" , testnet_url}, + {"mainnet_url" , mainnet_url}, + {"refresh" , refresh_page}, + {"height" , height}, + {"server_timestamp" , xmreg::timestamp_to_str_gm(local_copy_server_timestamp)}, + {"age_format" , string("[h:m:d]")}, + {"page_no" , page_no}, + {"total_page_no" , (height / no_of_last_blocks)}, + {"is_page_zero" , !bool(page_no)}, + {"no_of_last_blocks" , no_of_last_blocks}, + {"next_page" , (page_no + 1)}, + {"prev_page" , (page_no > 0 ? page_no - 1 : 0)}, {"enable_pusher" , enable_pusher}, {"enable_key_image_checker" , enable_key_image_checker}, {"enable_output_key_checker", enable_output_key_checker}, - {"enable_autorefresh_option", enable_autorefresh_option} + {"enable_autorefresh_option", enable_autorefresh_option}, + {"show_cache_times" , show_cache_times} }; context.emplace("txs", mstch::array()); // will keep tx to show @@ -402,24 +441,25 @@ public: mstch::array& txs = boost::get(context["txs"]); // calculate starting and ending block numbers to show - uint64_t start_height = height - no_of_last_blocks * (page_no + 1); - uint64_t end_height = height - no_of_last_blocks * (page_no); + int64_t start_height = height - no_of_last_blocks * (page_no + 1); - // check few conditions to make sure we are whithin the avaliable range - //@TODO its too messed up. needs to find cleaner way. - start_height = start_height > 0 ? start_height : 0; - end_height = end_height < height ? end_height : height; - start_height = start_height > end_height ? 0 : start_height; - end_height = end_height - start_height > no_of_last_blocks - ? no_of_last_blocks : end_height; + // check if start height is not below range + start_height = start_height < 0 ? 0 : start_height; - // previous blk timestamp, initalised to lowest possible value - double prev_blk_timestamp {std::numeric_limits::lowest()}; + int64_t end_height = start_height + no_of_last_blocks - 1; vector blk_sizes; + // measure time of cache based execution, and non-cached execution + double duration_cached {0.0}; + double duration_non_cached {0.0}; + uint64_t cache_hits {0}; + uint64_t cache_misses {0}; + + // loop index + int64_t i = end_height; // iterate over last no_of_last_blocks of blocks - for (uint64_t i = start_height; i <= end_height; ++i) + while (i >= start_height) { // get block at the given height i block blk; @@ -427,6 +467,7 @@ public: if (!mcore->get_block_by_height(i, blk)) { cerr << "Cant get block: " << i << endl; + --i; continue; } @@ -441,88 +482,226 @@ public: blk_sizes.push_back(blk_size); // remove "<" and ">" from the hash string - string blk_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", blk_hash)); + string blk_hash_str = pod_to_hex(blk_hash); // get block age pair age = get_age(local_copy_server_timestamp, blk.timestamp); context["age_format"] = age.second; - // get time difference [m] between previous and current blocks - string time_delta_str {}; - if (prev_blk_timestamp > std::numeric_limits::lowest()) + if (enable_block_cache && block_tx_cache.Contains(i)) { - time_delta_str = fmt::format("({:06.2f})", - (double(blk.timestamp) - double(prev_blk_timestamp))/60.0); - } + // get txs info in the ith block from + // our cache - // get all transactions in the block found - // initialize the first list with transaction for solving - // the block i.e. coinbase. - list blk_txs {blk.miner_tx}; - list missed_txs; + // start measure time here + auto start = std::chrono::steady_clock::now(); - if (!core_storage->get_transactions(blk.tx_hashes, blk_txs, missed_txs)) - { - cerr << "Cant get transactions in block: " << i << endl; - continue; - } + const vector>& txd_pairs + = block_tx_cache.Get(i); - uint64_t tx_i {0}; - - for(list::reverse_iterator rit = blk_txs.rbegin(); - rit != blk_txs.rend(); ++rit) - { - const cryptonote::transaction& tx = *rit; - - tx_details txd = get_tx_details(tx); - - mstch::map txd_map = txd.get_mstch_map(); - - //add age to the txd mstch map - txd_map.insert({"height" , i}); - txd_map.insert({"blk_hash" , blk_hash_str}); - txd_map.insert({"time_delta", time_delta_str}); - txd_map.insert({"age" , age.first}); - txd_map.insert({"is_ringct" , (tx.version > 1)}); - txd_map.insert({"rct_type" , tx.rct_signatures.type}); - txd_map.insert({"blk_size" , blk_size_str}); - - - // do not show block info for other than - // last (i.e., first after reverse below) - // tx in the block - if (tx_i < blk_txs.size() - 1) + // copy tx maps from txs_maps_tmp into txs array, + // that will go to templates + for (const pair& txd_pair: txd_pairs) { - txd_map["height"] = string(""); - txd_map["age"] = string(""); - txd_map["time_delta"] = string(""); - txd_map["blk_size"] = string(""); + // we need to check if the given transaction is still + // in the same block as when it was cached. it is possible + // the block got orphaned, and this tx is in mempool + // or different block, and what we have in cache + // is thus wrong + + // but we do this only for first top blocks. no sense + // doing it for all blocks + + bool is_tx_still_in_block_as_expected {true}; + + if (i + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > height) + { + const crypto::hash& tx_hash = txd_pair.first; + + if (core_storage->have_tx(tx_hash)) + { + try + { + uint64_t tx_height_in_blockchain = + core_storage->get_db().get_tx_block_height(tx_hash); + + // check if height of the given tx that we have in cache, + // denoted by i, is same as what is acctually stored + // in blockchain + if (tx_height_in_blockchain == i) + { + is_tx_still_in_block_as_expected = true; + } + else + { + // if no tx in the given block, just stop + // any futher search. no need. we are going + // to ditch the cache, in a monent + is_tx_still_in_block_as_expected = false; + break; + } + } + catch (const TX_DNE& e) + { + cerr << "Tx from cache" << pod_to_hex(tx_hash) + << " is no longer in the blockchain " + << endl; + + is_tx_still_in_block_as_expected = false; + break; + } + } + else + { + is_tx_still_in_block_as_expected = false; + break; + } + + } // if (i + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > height) + + + if (!is_tx_still_in_block_as_expected) + { + // if some tx in cache is not in blockchain + // where it should be, its probably better to + // ditch entire cache, as redo it below. + + block_tx_cache.Clear(); + txs.clear(); + i = end_height; + continue; // reado the main loop + } + + // if we got to here, it means that everything went fine + // and no unexpeced things happended. + mstch::map txd_map = boost::get(txd_pair.second); + + // now we need to update age of txs from cashe + if (!boost::get(txd_map["age"]).empty()) + { + txd_map["age"] = age.first; + } + + txs.push_back(txd_map); + + } // for (const pair& txd_pair: txd_pairs) + + auto duration = std::chrono::duration_cast + (std::chrono::steady_clock::now() - start); + + // cout << "block_tx_json_cache from cache" << endl; + + duration_cached += duration.count(); + + ++cache_hits; + } + else + { + // this is new block. not in cashe. + // need to process its txs and add to cache + + // start measure time here + auto start = std::chrono::steady_clock::now(); + + // get all transactions in the block found + // initialize the first list with transaction for solving + // the block i.e. coinbase. + list blk_txs {blk.miner_tx}; + list missed_txs; + + if (!core_storage->get_transactions(blk.tx_hashes, blk_txs, missed_txs)) + { + cerr << "Cant get transactions in block: " << i << endl; + --i; + continue; } - txs.push_back(txd_map); + uint64_t tx_i {0}; - ++tx_i; - } + // this vector will go into block_tx cache + // tx_hash , txd_map + vector> txd_pairs; - // save current's block timestamp as reference for the next one - prev_blk_timestamp = static_cast(blk.timestamp); + for(auto it = blk_txs.begin(); it != blk_txs.end(); ++it) + { + const cryptonote::transaction& tx = *it; - } // for (uint64_t i = start_height; i <= end_height; ++i) + const tx_details& txd = get_tx_details(tx, false, i, height); + + mstch::map txd_map = txd.get_mstch_map(); + + //add age to the txd mstch map + txd_map.insert({"height" , i}); + txd_map.insert({"blk_hash" , blk_hash_str}); + txd_map.insert({"age" , age.first}); + txd_map.insert({"is_ringct" , (tx.version > 1)}); + txd_map.insert({"rct_type" , tx.rct_signatures.type}); + txd_map.insert({"blk_size" , blk_size_str}); + + + // do not show block info for other than first tx in a block + if (tx_i > 0) + { + txd_map["height"] = string(""); + txd_map["age"] = string(""); + txd_map["blk_size"] = string(""); + } + + txd_pairs.emplace_back(txd.hash, txd_map); + + ++tx_i; + + } // for(list::reverse_iterator rit = blk_txs.rbegin(); + + // copy tx maps from txs_maps_tmp into txs array, + // that will go to templates + for (const pair& txd_pair: txd_pairs) + { + txs.push_back(boost::get(txd_pair.second)); + } + + auto duration = std::chrono::duration_cast + (std::chrono::steady_clock::now() - start); + + duration_non_cached += duration.count(); + + ++cache_misses; + + if (enable_block_cache) + { + // save in block_tx cache + block_tx_cache.Put(i, txd_pairs); + } + + } // else if (block_tx_json_cache.Contains(i)) + + --i; // go to next block number + + } // while (i <= end_height) // calculate median size of the blocks shown double blk_size_median = xmreg::calc_median(blk_sizes.begin(), blk_sizes.end()); context["blk_size_median"] = fmt::format("{:0.2f}", blk_size_median); - // reverse txs and remove last (i.e., oldest) - // tx. This is done so that time delats - // are easier to calcualte in the above for loop - std::reverse(txs.begin(), txs.end()); + // save computational times for disply in the frontend + + context["construction_time_cached"] = fmt::format( + "{:0.4f}", duration_cached/1.0e6); + + context["construction_time_non_cached"] = fmt::format( + "{:0.4f}", duration_non_cached/1.0e6); + + context["construction_time_total"] = fmt::format( + "{:0.4f}", (duration_non_cached+duration_cached)/1.0e6); + + context["cache_hits"] = cache_hits; + context["cache_misses"] = cache_misses; // get memory pool rendered template - string mempool_html = mempool(); + string mempool_html = mempool(false, no_of_mempool_tx_of_frontpage); // append mempool_html to the index context map context["mempool_info"] = mempool_html; @@ -539,7 +718,7 @@ public: * Render mempool data */ string - mempool(bool add_header_and_footer = false) + mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25) { std::vector mempool_txs; @@ -550,7 +729,8 @@ public: // initalise page tempate map with basic info about mempool mstch::map context { - {"mempool_size", std::to_string(mempool_txs.size())}, + {"mempool_size" , mempool_txs.size()}, + {"show_cache_times" , show_cache_times} }; context.emplace("mempooltxs" , mstch::array()); @@ -558,12 +738,36 @@ public: // get reference to blocks template map to be field below mstch::array& txs = boost::get(context["mempooltxs"]); - uint64_t mempool_size_bytes {0}; + // process only up to no_of_mempool_tx txs of mempool. + // this is useful from the front page were we show by default + // only 25 mempool txs. this way, we just parse 25 txs, rather + // than potentially hundrets just to ditch most of them later. + + if (add_header_and_footer == false) + { + // this is to show limited number of txs in mempool + // for example, in the front page + no_of_mempool_tx = mempool_txs.size() > no_of_mempool_tx + ? no_of_mempool_tx + : mempool_txs.size(); + } + else + { + // if we are adding footers and headers, means we + // disply mempool on its own page, thus show all mempoool txs. + no_of_mempool_tx = mempool_txs.size(); + } + + + double duration_cached {0.0}; + double duration_non_cached {0.0}; + uint64_t cache_hits {0}; + uint64_t cache_misses {0}; uint64_t local_copy_server_timestamp = server_timestamp; // for each transaction in the memory pool - for (size_t i = 0; i < mempool_txs.size(); ++i) + for (size_t i = 0; i < no_of_mempool_tx; ++i) { // get transaction info of the tx in the mempool tx_info _tx_info = mempool_txs.at(i); @@ -591,84 +795,165 @@ public: } // sum xmr in inputs and ouputs in the given tx - pair sum_inputs; - pair sum_outputs; - uint64_t num_nonrct_inputs; - - // get mixin number in each transaction - vector mixin_numbers; - + uint64_t sum_inputs {0}; + uint64_t sum_outputs {0}; + uint64_t no_inputs {0}; + uint64_t no_outputs {0}; + uint64_t num_nonrct_inputs {0}; uint64_t mixin_no {0}; - string is_ringct_str {"N/A"}; - string rct_type_str {"N/A"}; + string hash_str; + string fee_str; + string xmr_inputs_str; + string xmr_outputs_str; + string timestamp_str; + + string txsize; try { + // get the above incormation from json of that tx json j_tx; - j_tx = json::parse(_tx_info.tx_json); - - // sum xmr in inputs and ouputs in the given tx - sum_inputs = xmreg::sum_money_in_inputs(j_tx); - sum_outputs = xmreg::sum_money_in_outputs(j_tx); - num_nonrct_inputs = xmreg::count_nonrct_inputs(j_tx); - mixin_numbers = xmreg::get_mixin_no(j_tx); - - if (!mixin_numbers.empty()) - mixin_no = mixin_numbers.at(0) - 1; - - - if (j_tx["version"].get() > 1) + if (enable_mempool_cache && mempool_tx_json_cache.Contains(_tx_info.id_hash)) { - is_ringct_str = "yes"; - rct_type_str = string("/") + to_string(j_tx["rct_signatures"]["type"].get()); + // maybe its already in cashe, so we can save some time + // by using this, rather then making parsing json + // and calculating it from json + + // start measure time here + auto start = std::chrono::steady_clock::now(); + + const mempool_tx_info& cached_tx_info = mempool_tx_json_cache.Get(_tx_info.id_hash); + + sum_inputs = cached_tx_info.sum_inputs; + sum_outputs = cached_tx_info.sum_outputs; + no_inputs = cached_tx_info.no_inputs; + no_outputs = cached_tx_info.no_outputs; + num_nonrct_inputs = cached_tx_info.num_nonrct_inputs; + mixin_no = cached_tx_info.mixin_no; + hash_str = cached_tx_info.hash; + fee_str = cached_tx_info.fee; + xmr_inputs_str = cached_tx_info.xmr_inputs_str; + xmr_outputs_str = cached_tx_info.xmr_outputs_str; + timestamp_str = cached_tx_info.timestamp; + txsize = cached_tx_info.txsize; + + auto duration = std::chrono::duration_cast + (std::chrono::steady_clock::now() - start); + + // cout << "block_tx_json_cache from cache" << endl; + + duration_cached += duration.count(); + + ++cache_hits; + + //cout << "getting json from cash for: " << _tx_info.id_hash << endl; } else { - is_ringct_str = "no"; - rct_type_str = ""; - } + // its not in cash. Its new tx in mempool, so + // construct this data and save into cash for later use + + // start measure time here + auto start = std::chrono::steady_clock::now(); + + j_tx = json::parse(_tx_info.tx_json); + + // sum xmr in inputs and ouputs in the given tx + const array& sum_data = summary_of_in_out_rct(j_tx); + + sum_outputs = sum_data[0]; + sum_inputs = sum_data[1]; + no_outputs = sum_data[2]; + no_inputs = sum_data[3]; + mixin_no = sum_data[4]; + num_nonrct_inputs = sum_data[5]; + + hash_str = _tx_info.id_hash; + fee_str = xmreg::xmr_amount_to_str(_tx_info.fee, "{:0.3f}"); + xmr_inputs_str = xmreg::xmr_amount_to_str(sum_inputs , "{:0.3f}"); + xmr_outputs_str = xmreg::xmr_amount_to_str(sum_outputs, "{:0.3f}"); + timestamp_str = xmreg::timestamp_to_str_gm(_tx_info.receive_time); + + txsize = fmt::format("{:0.2f}", + static_cast(_tx_info.blob_size)/1024.0); + + auto duration = std::chrono::duration_cast + (std::chrono::steady_clock::now() - start); + + // cout << "block_tx_json_cache from cache" << endl; + + duration_non_cached += duration.count(); + + ++cache_misses; + + if (enable_mempool_cache) + { + // save in mempool cache + mempool_tx_json_cache.Put( + _tx_info.id_hash, + mempool_tx_info { + sum_inputs, sum_outputs, + no_inputs, no_outputs, + num_nonrct_inputs, mixin_no, + hash_str, fee_str, + xmr_inputs_str, xmr_outputs_str, + timestamp_str, txsize + }); + } + } // else if (mempool_tx_json_cache.Contains(_tx_info.id_hash)) } catch (std::invalid_argument& e) { - cerr << " j_tx = json::parse(_tx_info.tx_json);: " << e.what() << endl; + cerr << " j_tx = json::parse(_tx_info.tx_json): " << e.what() << endl; } // set output page template map txs.push_back(mstch::map { - {"timestamp_no" , _tx_info.receive_time}, - {"timestamp" , xmreg::timestamp_to_str(_tx_info.receive_time)}, - {"age" , age_str}, - {"hash" , fmt::format("{:s}", _tx_info.id_hash)}, - {"fee" , xmreg::xmr_amount_to_str(_tx_info.fee , "{:0.3f}")}, - {"xmr_inputs" , xmreg::xmr_amount_to_str(sum_inputs.first , "{:0.3f}")}, - {"xmr_outputs" , xmreg::xmr_amount_to_str(sum_outputs.first, "{:0.3f}")}, - {"no_inputs" , sum_inputs.second}, - {"no_outputs" , sum_outputs.second}, + {"timestamp_no" , _tx_info.receive_time}, + {"timestamp" , timestamp_str}, + {"age" , age_str}, + {"hash" , hash_str}, + {"fee" , fee_str}, + {"xmr_inputs" , xmr_inputs_str}, + {"xmr_outputs" , xmr_outputs_str}, + {"no_inputs" , no_inputs}, + {"no_outputs" , no_outputs}, {"no_nonrct_inputs", num_nonrct_inputs}, {"is_ringct" , is_ringct_str}, {"rct_type" , rct_type_str}, - {"mixin" , mixin_no}, + {"mixin" , fmt::format("{:d}", mixin_no)}, {"txsize" , fmt::format("{:0.2f}", static_cast(_tx_info.blob_size)/1024.0)} }); + } + + // calculate mempool size using all txs in mempool. + // not only those shown on the front page + uint64_t mempool_size_bytes {0}; + + for (const tx_info& _tx_info: mempool_txs) + { mempool_size_bytes += _tx_info.blob_size; } context.insert({"mempool_size_kB", - fmt::format("{:0.2f}", static_cast(mempool_size_bytes)/1024.0)}); + fmt::format("{:0.2f}", + static_cast(mempool_size_bytes)/1024.0)}); - // sort txs in mempool based on their age - std::sort(txs.begin(), txs.end(), [](mstch::node& m1, mstch::node& m2) - { - uint64_t t1 = boost::get(boost::get(m1)["timestamp_no"]); - uint64_t t2 = boost::get(boost::get(m2)["timestamp_no"]); + context["construction_time_cached"] = fmt::format( + "{:0.4f}", duration_cached/1.0e6); - return t1 > t2; - }); + context["construction_time_non_cached"] = fmt::format( + "{:0.4f}", duration_non_cached/1.0e6); + context["construction_time_total"] = fmt::format( + "{:0.4f}", (duration_non_cached+duration_cached)/1.0e6); + + context["cache_hits"] = cache_hits; + context["cache_misses"] = cache_misses; if (add_header_and_footer) { @@ -683,15 +968,8 @@ public: // this is for partial disply on front page. - context["mempool_fits_on_front_page"] = (txs.size() <= no_of_mempool_tx_of_frontpage); - context["no_of_mempool_tx_of_frontpage"] = no_of_mempool_tx_of_frontpage; - - if (txs.size() > no_of_mempool_tx_of_frontpage) - { - // dont show more than the specific number mempool txs on - // the front page - txs.resize(no_of_mempool_tx_of_frontpage); - } + context["mempool_fits_on_front_page"] = (mempool_txs.size() <= no_of_mempool_tx); + context["no_of_mempool_tx_of_frontpage"] = no_of_mempool_tx; context["partial_mempool_shown"] = true; @@ -744,14 +1022,14 @@ public: bool have_prev_hash = (prev_hash == null_hash ? false : true); // remove "<" and ">" from the hash string - string prev_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", prev_hash)); - string next_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", next_hash)); + string prev_hash_str = pod_to_hex(prev_hash); + string next_hash_str = pod_to_hex(next_hash); // remove "<" and ">" from the hash string - string blk_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", blk_hash)); + string blk_hash_str = pod_to_hex(blk_hash); // get block timestamp in user friendly format - string blk_timestamp = xmreg::timestamp_to_str(blk.timestamp); + string blk_timestamp = xmreg::timestamp_to_str_gm(blk.timestamp); // get age of the block relative to the server time pair age = get_age(server_timestamp, blk.timestamp); @@ -783,12 +1061,12 @@ public: uint64_t sum_fees = 0; // get tx details for the coinbase tx, i.e., miners reward - tx_details txd_coinbase = get_tx_details(blk.miner_tx, true); + tx_details txd_coinbase = get_tx_details(blk.miner_tx, true, + _blk_height, current_blockchain_height); // initalise page tempate map with basic info about blockchain mstch::map context { {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb}, {"blk_hash" , blk_hash_str}, {"blk_height" , _blk_height}, {"blk_timestamp" , blk_timestamp}, @@ -830,7 +1108,7 @@ public: const crypto::hash& tx_hash = blk.tx_hashes.at(i); // remove "<" and ">" from the hash string - string tx_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", tx_hash)); + string tx_hash_str = pod_to_hex(tx_hash); // get transaction @@ -842,7 +1120,9 @@ public: continue; } - tx_details txd = get_tx_details(tx); + tx_details txd = get_tx_details(tx, false, + _blk_height, + current_blockchain_height); // add fee to the rest sum_fees += txd.fee; @@ -941,14 +1221,14 @@ public: uint64_t tx_recieve_timestamp = found_txs.at(0).first.receive_time; - blk_timestamp = xmreg::timestamp_to_str(tx_recieve_timestamp); + blk_timestamp = xmreg::timestamp_to_str_gm(tx_recieve_timestamp); age = get_age(server_timestamp, tx_recieve_timestamp, FULL_AGE_FORMAT); // for mempool tx, we dont show more details, e.g., json tx representation // so no need for the link - show_more_details_link = false; + // show_more_details_link = false; } else { @@ -957,7 +1237,151 @@ public: } } - mstch::map tx_context = construct_tx_context(tx, with_ring_signatures); + mstch::map tx_context; + + if (enable_tx_cache && tx_context_cache.Contains({tx_hash, with_ring_signatures})) + { + // with_ring_signatures == 0 means that cache is not used + // when obtaining detailed information about tx is requested. + + // we are going to measure time for the construction of the + // tx context from cashe. just for fun, to see if cache is any faster. + auto start = std::chrono::steady_clock::now(); + + const tx_info_cache& tx_info_cashed + = tx_context_cache.Get({tx_hash, with_ring_signatures}); + + tx_context = tx_info_cashed.tx_map; + + //cout << "get tx from cash: " << tx_hash_str <(tx_context["tx_blk_height"]) <(tx_context["blk_timestamp_uint"]) <(tx_context["tx_blk_height"]); + uint64_t blk_timestamp_uint = boost::get(tx_context["blk_timestamp_uint"]); + + if (tx_blk_height > 0) + { + // seems to be in blockchain. off course it could have been orphaned + // so double check if its for sure in blockchain + + if (core_storage->have_tx(tx_hash)) + { + // ok, it is still in blockchain + // update its age and number of confirmations + + pair age + = get_age(std::time(nullptr), + blk_timestamp_uint, + FULL_AGE_FORMAT); + + tx_context["delta_time"] = age.first; + + uint64_t bc_height = core_storage->get_current_blockchain_height(); + + tx_context["confirmations"] = bc_height - (tx_blk_height - 1); + + // marke it as from cashe. useful if we want to show + // info about cashed/not cashed in frontend. + tx_context["from_cache"] = true; + + auto duration = std::chrono::duration_cast + (std::chrono::steady_clock::now() - start); + + tx_context["construction_time"] = fmt::format( + "{:0.4f}", static_cast(duration.count())/1.0e6); + + // normally we should update this into in the cache. + // but since we make this check all the time, + // we can skip updating cashed version + + } // if (core_storage->have_tx(tx_hash)) + else + { + // its not in blockchain, but it was there when we cashed it. + // so we update it in cash, as it should be back in mempool + + tx_context = construct_tx_context(tx, with_ring_signatures); + + tx_context_cache.Put( + {tx_hash, with_ring_signatures}, + tx_info_cache { + boost::get(tx_context["tx_blk_height"]), + boost::get(tx_context["blk_timestamp_uint"]), + tx_context} + ); + } + } // if (tx_blk_height > 0) + else + { + // the tx was cashed when in mempool. + // since then, it might have been included in some block. + // so we check it. + + if (core_storage->have_tx(tx_hash)) + { + // checking if in blockchain already + // it was before in mempool, but now maybe already in blockchain + + tx_context = construct_tx_context(tx, with_ring_signatures); + + tx_context_cache.Put( + {tx_hash, with_ring_signatures}, + tx_info_cache { + boost::get(tx_context["tx_blk_height"]), + boost::get(tx_context["blk_timestamp_uint"]), + tx_context}); + + + } // if (core_storage->have_tx(tx_hash)) + else + { + // still seems to be in mempool only. + // so just get its time duration, as its read only + // from cache + + tx_context["from_cache"] = true; + + auto duration = std::chrono::duration_cast + (std::chrono::steady_clock::now() - start); + + tx_context["construction_time"] = fmt::format( + "{:0.4f}", static_cast(duration.count())/1.0e6); + + } + + } // else if (tx_blk_height > 0) + + } // if (tx_context_cache.Contains(tx_hash)) + else + { + + // we are going to measure time for the construction of the + // tx context. just for fun, to see if cache is any faster. + auto start = std::chrono::steady_clock::now(); + + tx_context = construct_tx_context(tx, with_ring_signatures); + + auto duration = std::chrono::duration_cast + (std::chrono::steady_clock::now() - start); + + if (enable_tx_cache) + { + tx_context_cache.Put( + {tx_hash, with_ring_signatures}, + tx_info_cache { + boost::get(tx_context["tx_blk_height"]), + boost::get(tx_context["blk_timestamp_uint"]), + tx_context}); + } + + tx_context["construction_time"] = fmt::format( + "{:0.4f}", static_cast(duration.count())/1.0e6); + + } // else if (tx_context_cache.Contains(tx_hash)) + tx_context["show_more_details_link"] = show_more_details_link; @@ -968,9 +1392,9 @@ public: mstch::map context { {"testnet" , this->testnet}, - {"have_custom_lmdb" , have_custom_lmdb} + {"show_cache_times" , show_cache_times}, + {"txs" , mstch::array{}} }; - context.emplace("txs" , mstch::array{}); boost::get(context["txs"]).push_back(tx_context); @@ -978,8 +1402,10 @@ public: {"tx_details", template_file["tx_details"]}, }; + add_css_style(context); + // render the page return mstch::render(template_file["tx"], context, partials); } @@ -1117,7 +1543,7 @@ public: uint64_t tx_recieve_timestamp = found_txs.at(0).first.receive_time; - blk_timestamp = xmreg::timestamp_to_str(tx_recieve_timestamp); + blk_timestamp = xmreg::timestamp_to_str_gm(tx_recieve_timestamp); age = get_age(server_timestamp, tx_recieve_timestamp, @@ -1163,25 +1589,24 @@ public: // calculate difference between tx and server timestamps age = get_age(server_timestamp, blk.timestamp, FULL_AGE_FORMAT); - blk_timestamp = xmreg::timestamp_to_str(blk.timestamp); + blk_timestamp = xmreg::timestamp_to_str_gm(blk.timestamp); tx_blk_height_str = std::to_string(tx_blk_height); } // payments id. both normal and encrypted (payment_id8) - string pid_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", txd.payment_id)); - string pid8_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", txd.payment_id8)); + string pid_str = pod_to_hex(txd.payment_id); + string pid8_str = pod_to_hex(txd.payment_id8); // initalise page tempate map with basic info about blockchain mstch::map context { {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb}, {"tx_hash" , tx_hash_str}, {"tx_prefix_hash" , pod_to_hex(txd.prefix_hash)}, {"xmr_address" , xmr_address_str}, {"viewkey" , viewkey_str}, - {"tx_pub_key" , REMOVE_HASH_BRAKETS(fmt::format("{:s}", txd.pk))}, + {"tx_pub_key" , pod_to_hex(txd.pk)}, {"blk_height" , tx_blk_height_str}, {"tx_size" , fmt::format("{:0.4f}", static_cast(txd.size) / 1024.0)}, @@ -1196,7 +1621,7 @@ public: {"tx_prove" , tx_prove} }; - string server_time_str = xmreg::timestamp_to_str(server_timestamp, "%F"); + string server_time_str = xmreg::timestamp_to_str_gm(server_timestamp, "%F"); @@ -1283,9 +1708,7 @@ public: } outputs.push_back(mstch::map { - {"out_pub_key" , REMOVE_HASH_BRAKETS( - fmt::format("{:s}", - outp.first.key))}, + {"out_pub_key" , pod_to_hex(outp.first.key)}, {"amount" , xmreg::xmr_amount_to_str(outp.second)}, {"mine_output" , mine_output}, {"output_idx" , fmt::format("{:02d}", output_idx)} @@ -1542,7 +1965,7 @@ public: {"my_public_key" , pod_to_hex(txout_k.key)}, {"tx_hash" , tx_hash_str}, {"mine_output" , mine_output}, - {"out_idx" , to_string(output_idx_in_tx)}, + {"out_idx" , output_idx_in_tx}, {"formed_output_pk", out_pub_key_str}, {"out_in_match" , output_match}, {"amount" , xmreg::xmr_amount_to_str(amount)} @@ -1678,8 +2101,7 @@ public: // initalise page tempate map with basic info about blockchain mstch::map context { - {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb} + {"testnet" , testnet} }; add_css_style(context); @@ -1711,13 +2133,17 @@ public: // initalize page template context map mstch::map context { {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb}, {"unsigned_tx_given" , unsigned_tx_given}, {"have_raw_tx" , true}, + {"has_error" , false}, + {"error_msg" , string {}}, {"data_prefix" , data_prefix}, }; context.emplace("txs", mstch::array{}); + string full_page = template_file["checkrawtx"]; + + add_css_style(context); if (unsigned_tx_given) { @@ -1952,8 +2378,8 @@ public: ); tx_cd_data.emplace("timescales", mixins_timescales.first); - tx_cd_data["min_mix_time"] = xmreg::timestamp_to_str(min_mix_timestamp); - tx_cd_data["max_mix_time"] = xmreg::timestamp_to_str(max_mix_timestamp); + tx_cd_data["min_mix_time"] = xmreg::timestamp_to_str_gm(min_mix_timestamp); + tx_cd_data["max_mix_time"] = xmreg::timestamp_to_str_gm(max_mix_timestamp); tx_cd_data["timescales_scale"] = fmt::format("{:0.2f}", mixins_timescales.second / 3600.0 / 24.0); // in days @@ -2017,11 +2443,12 @@ public: tx_hash_from_blob, tx_prefix_hash_from_blob)) { - string msg = fmt::format("failed to validate transaction"); + string error_msg = fmt::format("failed to validate transaction"); - cout << msg << endl; + context["has_error"] = true; + context["error_msg"] = error_msg; - return string(msg); + return mstch::render(full_page, context); } //cout << "tx_from_blob.vout.size(): " << tx_from_blob.vout.size() << endl; @@ -2054,6 +2481,7 @@ public: {"tx_details", template_file["tx_details"]}, }; + add_css_style(context); // render the page @@ -2095,7 +2523,7 @@ public: for (tools::wallet2::pending_tx& ptx: ptxs) { - mstch::map tx_context = construct_tx_context(ptx.tx); + mstch::map tx_context = construct_tx_context(ptx.tx, 1); if (boost::get(tx_context["has_error"])) { @@ -2303,10 +2731,9 @@ public: {"tx_details", template_file["tx_details"]}, }; - add_css_style(context); // render the page - return mstch::render(template_file["checkrawtx"], context, partials); + return mstch::render(full_page, context, partials); } string @@ -2323,7 +2750,6 @@ public: // initalize page template context map mstch::map context { {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb}, {"have_raw_tx" , true}, {"has_error" , false}, {"error_msg" , string {}}, @@ -2503,7 +2929,6 @@ public: // initalize page template context map mstch::map context { {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb} }; add_css_style(context); @@ -2517,8 +2942,7 @@ public: { // initalize page template context map mstch::map context { - {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb} + {"testnet" , testnet} }; add_css_style(context); @@ -2542,7 +2966,6 @@ public: // initalize page template context map mstch::map context{ {"testnet" , testnet}, - {"have_custom_lmdb", have_custom_lmdb}, {"has_error" , false}, {"error_msg" , string{}}, }; @@ -2633,16 +3056,6 @@ public: context.insert({"total_xmr" , string{}}); context.insert({"key_imgs" , mstch::array{}}); - unique_ptr mylmdb; - - if (bf::is_directory(lmdb2_path)) - { - mylmdb = make_unique(lmdb2_path); - } - else - { - cout << "Custom lmdb database seem does not exist at: " << lmdb2_path << endl; - } size_t no_key_images = (decoded_raw_data.size() - header_lenght) / record_lenght; @@ -2665,13 +3078,10 @@ public: = *reinterpret_cast(record_ptr + key_img_size); + // found_tx_hashes was filed using custom lmdb which was droped. + // so this will be empty always for now. vector found_tx_hashes; - if (mylmdb) - { - mylmdb->search(epee::string_tools::pod_to_hex(key_image), - found_tx_hashes, "key_images"); - } mstch::map key_img_info { {"key_no" , fmt::format("{:03d}", n)}, @@ -2868,7 +3278,7 @@ public: } // for (it = tx_key_imgs.begin(); it != tx_key_imgs.end(); ++it) - key_img_info["timestamp"] = xmreg::timestamp_to_str(blk_timestamp); + key_img_info["timestamp"] = xmreg::timestamp_to_str_gm(blk_timestamp); } // if (mcore->get_tx(tx_hash_str, tx)) @@ -2902,7 +3312,6 @@ public: // initalize page template context map mstch::map context{ {"testnet" , testnet}, - {"have_custom_lmdb", have_custom_lmdb}, {"has_error" , false}, {"error_msg" , string{}} }; @@ -2984,16 +3393,6 @@ public: mstch::array& output_keys_ctx = boost::get(context["output_keys"]); - unique_ptr mylmdb; - - if (bf::is_directory(lmdb2_path)) - { - mylmdb = make_unique(lmdb2_path); - } - else - { - cout << "Custom lmdb database seem does not exist at: " << lmdb2_path << endl; - } std::vector outputs; @@ -3095,7 +3494,7 @@ public: {"output_pub_key" , REMOVE_HASH_BRAKETS(fmt::format("{:s}", txout_key.key))}, {"amount" , xmreg::xmr_amount_to_str(xmr_amount)}, {"tx_hash" , REMOVE_HASH_BRAKETS(fmt::format("{:s}", td.m_txid))}, - {"timestamp" , xmreg::timestamp_to_str(blk_timestamp)}, + {"timestamp" , xmreg::timestamp_to_str_gm(blk_timestamp)}, {"is_spent" , is_output_spent}, {"is_ringct" , td.m_rct} }; @@ -3130,18 +3529,20 @@ public: string result_html {default_txt}; - // check first if we look for output with given global index - // such search start with "goi_", e.g., "goi_543" - bool search_for_global_output_idx = (search_text.substr(0, 4) == "goi_"); + uint64_t search_str_length = search_text.length(); - // check if we look for output with amout index and amount - // such search start with "aoi_", e.g., "aoi_444-23.00" - bool search_for_amount_output_idx = (search_text.substr(0, 4) == "aoi_"); + // first let try searching for tx + result_html = show_tx(search_text); + + // nasty check if output is "Cant get" as a sign of + // a not found tx. Later need to think of something better. + if (result_html.find("Cant get") == string::npos) + { + return result_html; + } // first check if searching for block of given height - if (search_text.size() < 12 && - (search_for_global_output_idx == false - ||search_for_amount_output_idx == false)) + if (search_text.size() < 12) { uint64_t blk_height; @@ -3155,7 +3556,7 @@ public: // a not found tx. Later need to think of something better. if (result_html.find("Cant get") == string::npos) { - return result_html; + return result_html; } } catch(boost::bad_lexical_cast &e) @@ -3165,10 +3566,20 @@ public: } } + // if tx search not successful, check if we are looking + // for a block with given hash + result_html = show_block(search_text); + + if (result_html.find("Cant get") == string::npos) + { + return result_html; + } + + result_html = default_txt; // check if monero address is given based on its length // if yes, then we can only show its public components - if (search_text.length() == 95) + if (search_str_length == 95) { // parse string representing given monero address cryptonote::account_public_address address; @@ -3190,7 +3601,7 @@ public: // check if integrated monero address is given based on its length // if yes, then show its public components search tx based on encrypted id - if (search_text.length() == 106) + if (search_str_length == 106) { cryptonote::account_public_address address; @@ -3215,434 +3626,12 @@ public: return show_integrated_address_details(address, encrypted_payment_id, testnet); } - // second let try searching for tx - result_html = show_tx(search_text); - - // nasty check if output is "Cant get" as a sign of - // a not found tx. Later need to think of something better. - if (result_html.find("Cant get") == string::npos) - { - return result_html; - } - - // if tx search not successful, check if we are looking - // for a block with given hash - result_html = show_block(search_text); - - if (result_html.find("Cant get") == string::npos) - { - return result_html; - } - - result_html = default_txt; - - // get mempool transaction so that what we search, - // might be there. Note: show_tx above already searches it - // but only looks for tx hash. Now want to check - // for key_images, public_keys, payments_id, etc. - vector mempool_txs = get_mempool_txs(); - - // key is string indicating where search_text was found. - map> tx_search_results - = search_txs(mempool_txs, search_text); - - // now search my own custom lmdb database - // with key_images, public_keys, payments_id etc. + // all_possible_tx_hashes was field using custom lmdb database + // it was dropped, so all_possible_tx_hashes will be alwasy empty + // for now vector>> all_possible_tx_hashes; - try - { - unique_ptr mylmdb; - - if (!bf::is_directory(lmdb2_path)) - { - cout << "Custom lmdb database seem does not exist at: " << lmdb2_path << endl; - - result_html = show_search_results(search_text, all_possible_tx_hashes); - - return result_html; - } - - cout << "Custom lmdb database seem to exist at: " << lmdb2_path << endl; - cout << "So lets try to search there for what we are after." << endl; - - mylmdb = make_unique(lmdb2_path); - - // check if date given in format: 2015-04-15 12:02:33 - // this is 19 characters - if (search_text.length() == 19) - { - uint64_t estimated_blk_height {0}; - - // first parse the string to date::sys_seconds and then to timestamp - // since epoch - uint64_t blk_timestamp_utc = parse(search_text).time_since_epoch().count(); - - if (blk_timestamp_utc) - { - // seems we have a correct date! - // so try to estimate block height from it. - // - // to find block we can use our lmdb outputs_info table - // its indexes are timestamps. - - vector out_infos; - - if (mylmdb->get_output_info(blk_timestamp_utc, out_infos)) - { - // since many outputs can be in a single block - // just get the first one to obtained its block - - uint64_t found_blk_height = core_storage->get_db() - .get_tx_block_height(out_infos.at(0).tx_hash); - - return show_block(found_blk_height); - } - } - } - else if (search_text.length() == 16) - { - // check if date given in format: 2015-04-15 12:02 - // this is 16 characters, i.e., only minut given - // so search all blocks made within that minute - - // first parse the string to date::sys_seconds and then to timestamp - // since epoch - uint64_t blk_timestamp_utc_start - = parse(search_text, "%Y-%m-%d %H:%M") - .time_since_epoch().count(); - - if (blk_timestamp_utc_start) - { - // seems we have a correct date! - - // add 60 seconds, i.e. 1 min - uint64_t blk_timestamp_utc_end - = blk_timestamp_utc_start + 59; - - all_possible_tx_hashes.push_back( - make_pair("tx_in_the_minute", vector{})); - - vector& txs_found_ref - = all_possible_tx_hashes.back().second; - - get_txs_from_timestamp_range( - blk_timestamp_utc_start, - blk_timestamp_utc_end, - mylmdb, - txs_found_ref); - } - } - else if (search_text.length() == 13) - { - // check if date given in format: 2015-04-15 12 - // this is 13 characters, i.e., only hour given - // so search all blocks made within that hour - - // first parse the string to date::sys_seconds and then to timestamp - // since epoch - uint64_t blk_timestamp_utc_start - = parse(search_text, "%Y-%m-%d %H") - .time_since_epoch().count(); - - if (blk_timestamp_utc_start) - { - // seems we have a correct date! - - // add 60 seconds, i.e. 1 hour - uint64_t blk_timestamp_utc_end - = blk_timestamp_utc_start + 3599; - - all_possible_tx_hashes.push_back( - make_pair("tx_in_the_hour", vector{})); - - vector& txs_found_ref - = all_possible_tx_hashes.back().second; - - get_txs_from_timestamp_range( - blk_timestamp_utc_start, - blk_timestamp_utc_end, - mylmdb, - txs_found_ref); - } - } - else if (search_text.length() == 10) - { - // check if date given in format: 2015-04-15 - // this is 10 characters, i.e., only day given - // so search all blocks made within that day - - // first parse the string to date::sys_seconds and then to timestamp - // since epoch - uint64_t blk_timestamp_utc_start - = parse(search_text, "%Y-%m-%d") - .time_since_epoch().count(); - - if (blk_timestamp_utc_start) - { - // seems we have a correct date! - - // add 60 seconds, i.e. 1 day - uint64_t blk_timestamp_utc_end - = blk_timestamp_utc_start + 86399; - - all_possible_tx_hashes.push_back( - make_pair("tx_in_the_day", vector{})); - - vector& txs_found_ref - = all_possible_tx_hashes.back().second; - - get_txs_from_timestamp_range( - blk_timestamp_utc_start, - blk_timestamp_utc_end, - mylmdb, - txs_found_ref); - } - } - - mylmdb->search(search_text, - tx_search_results["key_images"], - "key_images"); - - //cout << "size: " << tx_search_results["key_images"].size() << endl; - - all_possible_tx_hashes.push_back( - make_pair("key_images", - tx_search_results["key_images"])); - - - // search the custum lmdb for tx_public_keys and append the result - // to those from the mempool search if found - - mylmdb->search(search_text, - tx_search_results["tx_public_keys"], - "tx_public_keys"); - - if (!tx_search_results["tx_public_keys"].empty()) - { - all_possible_tx_hashes.push_back( - make_pair("tx_public_keys", - tx_search_results["tx_public_keys"])); - } - else - { - // if private tx key is added, use it to obtained tx_public_key - // and than search for corresponding tx - - public_key tx_pub_key = null_pkey; - secret_key tx_prv_key; - - if (hex_to_pod(search_text, tx_prv_key)) - { - secret_key recovery_key = tx_prv_key; - - const unsigned char * tx_prv_key_ptr = reinterpret_cast(&tx_prv_key); - unsigned char * tx_pub_key_ptr = reinterpret_cast(&tx_pub_key); - - //memcpy(&tx_pub_key.data, reinterpret_cast(tx_pub_key_ptr), sizeof(tx_pub_key.data)); - - ge_p3 point; - ge_scalarmult_base(&point, tx_prv_key_ptr); - ge_p3_tobytes(tx_pub_key_ptr, &point); - - string tx_pub_key_str = pod_to_hex(tx_pub_key); - - mylmdb->search(tx_pub_key_str, - tx_search_results["tx_public_keys"], - "tx_public_keys"); - - all_possible_tx_hashes.push_back( - make_pair("tx_public_keys", - tx_search_results["tx_public_keys"])); - } - } - - - // search the custum lmdb for payments_id and append the result - // to those from the mempool search if found - - mylmdb->search(search_text, - tx_search_results["payments_id"], - "payments_id"); - - all_possible_tx_hashes.push_back( - make_pair("payments_id", - tx_search_results["payments_id"])); - - // search the custum lmdb for encrypted_payments_id and append the result - // to those from the mempool search if found - - mylmdb->search(search_text, - tx_search_results["encrypted_payments_id"], - "encrypted_payments_id"); - - all_possible_tx_hashes.push_back( - make_pair("encrypted_payments_id", - tx_search_results["encrypted_payments_id"])); - - // search the custum lmdb for output_public_keys and append the result - // to those from the mempool search if found - - mylmdb->search(search_text, - tx_search_results["output_public_keys"], - "output_public_keys"); - - all_possible_tx_hashes.push_back( - make_pair("output_public_keys", - tx_search_results["output_public_keys"])); - - - // seach for output using output global index - - if (search_for_global_output_idx) - { - try - { - uint64_t global_idx = boost::lexical_cast( - search_text.substr(4)); - - - output_data_t output_data; - - try - { - // get info about output of a given global index - output_data = core_storage->get_db() - .get_output_key(global_idx); - } - catch (const OUTPUT_DNE& e) - { - string out_msg = fmt::format( - "Output with index {:d} does not exist!", - global_idx - ); - - cerr << out_msg << endl; - - return out_msg; - } - - //cout << "tx_out.first: " << tx_out.first << endl; - //cout << "tx_out.second: " << tx_out.second << endl; - - string output_pub_key = pod_to_hex(output_data.pubkey); - - //cout << "output_pub_key: " << output_pub_key << endl; - - vector found_outputs; - - mylmdb->search(output_pub_key, - found_outputs, - "output_public_keys"); - - //cout << "found_outputs.size(): " << found_outputs.size() << endl; - - all_possible_tx_hashes.push_back( - make_pair("output_public_keys_based_on_global_idx", - found_outputs)); - - } - catch(boost::bad_lexical_cast &e) - { - cerr << "Cant cast global_idx string: " - << search_text.substr(4) << endl; - } - } // if (search_for_global_output_idx) - - // seach for output using output amount index and amount - - if (search_for_amount_output_idx) - { - try - { - - string str_to_split = search_text.substr(4); - - vector string_parts; - - boost::split(string_parts, str_to_split, - boost::is_any_of("-")); - - if (string_parts.size() != 2) - { - throw; - } - - uint64_t amount_idx = boost::lexical_cast( - string_parts[0]); - - uint64_t amount = static_cast - (boost::lexical_cast( - string_parts[1]) * 1e12); - - - //cout << "amount_idx: " << amount_idx << endl; - //cout << "amount: " << amount << endl; - - output_data_t output_data; - - try - { - // get info about output of a given global index - output_data = core_storage->get_db() - .get_output_key( - amount, amount_idx); - } - catch (const OUTPUT_DNE& e) - { - string out_msg = fmt::format( - "Output with amount {:d} and index {:d} does not exist!", - amount, amount_idx - ); - - cerr << out_msg << endl; - - return out_msg; - } - - string output_pub_key = pod_to_hex(output_data.pubkey); - - //cout << "output_pub_key: " << output_pub_key << endl; - - vector found_outputs; - - mylmdb->search(output_pub_key, - found_outputs, - "output_public_keys"); - - //cout << "found_outputs.size(): " << found_outputs.size() << endl; - - all_possible_tx_hashes.push_back( - make_pair("output_public_keys_based_on_amount_idx", - found_outputs)); - - } - catch(boost::bad_lexical_cast& e) - { - cerr << "Cant parse amout index and amout string: " - << search_text.substr(4) << endl; - } - catch(const OUTPUT_DNE& e) - { - cerr << "Output not found in the blockchain: " - << search_text.substr(4) << endl; - - return(string("Output not found in the blockchain: ") - + search_text.substr(4)); - } - } // if (search_for_amount_output_idx) - } - catch (const lmdb::runtime_error& e) - { - cerr << "Error opening/accessing custom lmdb database: " - << e.what() << endl; - } - catch (std::exception& e) - { - cerr << "Error opening/accessing custom lmdb database: " - << e.what() << endl; - } result_html = show_search_results(search_text, all_possible_tx_hashes); @@ -3663,8 +3652,7 @@ public: {"public_viewkey" , REMOVE_HASH_BRAKETS(pub_viewkey_str)}, {"public_spendkey" , REMOVE_HASH_BRAKETS(pub_spendkey_str)}, {"is_integrated_addr" , false}, - {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb} + {"testnet" , testnet} }; add_css_style(context); @@ -3691,8 +3679,7 @@ public: {"public_spendkey" , REMOVE_HASH_BRAKETS(pub_spendkey_str)}, {"encrypted_payment_id" , REMOVE_HASH_BRAKETS(enc_payment_id_str)}, {"is_integrated_addr" , true}, - {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb} + {"testnet" , testnet} }; add_css_style(context); @@ -3775,24 +3762,6 @@ public: } - vector - get_mempool_txs() - { - // get mempool data using rpc call - vector> mempool_data = search_mempool(); - - // output only transactions - vector mempool_txs; - - mempool_txs.reserve(mempool_data.size()); - - for (const auto& a_pair: mempool_data) - { - mempool_txs.push_back(a_pair.second); - } - - return mempool_txs; - } string show_search_results(const string& search_text, @@ -3804,8 +3773,7 @@ public: {"testnet" , testnet}, {"search_text" , search_text}, {"no_results" , true}, - {"to_many_results" , false}, - {"have_custom_lmdb", have_custom_lmdb} + {"to_many_results" , false} }; for (const pair>& found_txs: all_possible_tx_hashes) @@ -3881,7 +3849,7 @@ public: // add the timestamp to tx mstch map - txd_map.insert({"timestamp", xmreg::timestamp_to_str(blk_timestamp)}); + txd_map.insert({"timestamp", xmreg::timestamp_to_str_gm(blk_timestamp)}); boost::get((res.first)->second).push_back(txd_map); @@ -3905,7 +3873,7 @@ public: // read partial for showing details of tx(s) found map partials { - {"tx_table_head", template_file["tx_table_head"]}, + {"tx_table_head", template_file["tx_table_header"]}, {"tx_table_row" , template_file["tx_table_row"]} }; @@ -3916,8 +3884,762 @@ public: } + /* + * Lets use this json api convention for success and error + * https://labs.omniti.com/labs/jsend + */ + json + json_transaction(string tx_hash_str) + { + json j_response { + {"status", "fail"}, + {"data" , json {}} + }; + + json& j_data = j_response["data"]; + + // parse tx hash string to hash object + crypto::hash tx_hash; + + if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) + { + j_data["title"] = fmt::format("Cant parse tx hash: {:s}", tx_hash_str); + return j_response; + } + + // get transaction + transaction tx; + + // flag to indicate if tx is in mempool + bool found_in_mempool {false}; + + // for tx in blocks we get block timestamp + // for tx in mempool we get recievive time + uint64_t tx_timestamp {0}; + + if (!find_tx(tx_hash, tx, found_in_mempool, tx_timestamp)) + { + j_data["title"] = fmt::format("Cant find tx hash: {:s}", tx_hash_str); + return j_response; + } + + uint64_t block_height {0}; + uint64_t is_coinbase_tx = is_coinbase(tx); + uint64_t no_confirmations {0}; + + if (found_in_mempool == false) + { + + block blk; + + try + { + // get block cointaining this tx + block_height = core_storage->get_db().get_tx_block_height(tx_hash); + + if (!mcore->get_block_by_height(block_height, blk)) + { + j_data["title"] = fmt::format("Cant get block: {:d}", block_height); + return j_response; + } + + tx_timestamp = blk.timestamp; + } + catch (const exception& e) + { + j_response["status"] = "error"; + j_response["message"] = fmt::format("Tx does not exist in blockchain, " + "but was there before: {:s}", tx_hash_str); + return j_response; + } + } + + string blk_timestamp_utc = xmreg::timestamp_to_str_gm(tx_timestamp); + + // get the current blockchain height. Just to check + uint64_t bc_height = core_storage->get_current_blockchain_height(); + + tx_details txd = get_tx_details(tx, is_coinbase_tx, block_height, bc_height); + + json outputs; + + for (const auto& output: txd.output_pub_keys) + { + outputs.push_back(json { + {"public_key", pod_to_hex(output.first.key)}, + {"amount" , output.second} + }); + } + + json inputs; + + for (const auto& input: txd.input_key_imgs) + { + inputs.push_back(json { + {"key_image" , pod_to_hex(input.k_image)}, + {"amount" , input.amount} + }); + } + + if (found_in_mempool == false) + { + no_confirmations = txd.no_confirmations; + } + + // get tx from tx fetched. can be use to double check + // if what we return in the json response agrees with + // what tx_hash was requested + string tx_hash_str_again = pod_to_hex(get_transaction_hash(tx)); + + // get basic tx info + j_data = get_tx_json(tx, txd); + + // append additional info from block, as we don't + // return block data in this function + j_data["timestamp"] = tx_timestamp; + j_data["timestamp_utc"] = blk_timestamp_utc; + j_data["block_height"] = block_height; + j_data["confirmations"] = no_confirmations; + j_data["outputs"] = outputs; + j_data["inputs"] = inputs; + j_data["current_height"] = bc_height; + + j_response["status"] = "success"; + + return j_response; + } + + /* + * Lets use this json api convention for success and error + * https://labs.omniti.com/labs/jsend + */ + json + json_block(string block_no_or_hash) + { + json j_response { + {"status", "fail"}, + {"data" , json {}} + }; + + json& j_data = j_response["data"]; + + uint64_t current_blockchain_height + = core_storage->get_current_blockchain_height(); + + uint64_t block_height {0}; + + crypto::hash blk_hash; + + block blk; + + if (block_no_or_hash.length() <= 8) + { + // we have something that seems to be a block number + try + { + block_height = boost::lexical_cast(block_no_or_hash); + } + catch (const boost::bad_lexical_cast& e) + { + j_data["title"] = fmt::format( + "Cant parse block number: {:s}", block_no_or_hash); + return j_response; + } + + if (block_height > current_blockchain_height) + { + j_data["title"] = fmt::format( + "Requested block is higher than blockchain:" + " {:d}, {:d}", block_height,current_blockchain_height); + return j_response; + } + + if (!mcore->get_block_by_height(block_height, blk)) + { + j_data["title"] = fmt::format("Cant get block: {:d}", block_height); + return j_response; + } + + blk_hash = core_storage->get_block_id_by_height(block_height); + + } + else if (block_no_or_hash.length() == 64) + { + // this seems to be block hash + if (!xmreg::parse_str_secret_key(block_no_or_hash, blk_hash)) + { + j_data["title"] = fmt::format("Cant parse blk hash: {:s}", block_no_or_hash); + return j_response; + } + + if (!core_storage->get_block_by_hash(blk_hash, blk)) + { + j_data["title"] = fmt::format("Cant get block: {:s}", blk_hash); + return j_response; + } + + block_height = core_storage->get_db().get_block_height(blk_hash); + } + else + { + j_data["title"] = fmt::format("Cant find blk using search string: {:s}", block_no_or_hash); + return j_response; + } + + + // get block size in bytes + uint64_t blk_size = core_storage->get_db().get_block_size(block_height); + + // miner reward tx + transaction coinbase_tx = blk.miner_tx; + + // transcation in the block + vector tx_hashes = blk.tx_hashes; + + // sum of all transactions in the block + uint64_t sum_fees = 0; + + // get tx details for the coinbase tx, i.e., miners reward + tx_details txd_coinbase = get_tx_details(blk.miner_tx, true, + block_height, + current_blockchain_height); + + json j_txs; + + j_txs.push_back(get_tx_json(coinbase_tx, txd_coinbase)); + + // for each transaction in the block + for (size_t i = 0; i < blk.tx_hashes.size(); ++i) + { + const crypto::hash &tx_hash = blk.tx_hashes.at(i); + + // get transaction + transaction tx; + + if (!mcore->get_tx(tx_hash, tx)) + { + j_response["status"] = "error"; + j_response["message"] + = fmt::format("Cant get transactions in block: {:d}", block_height); + return j_response; + } + + tx_details txd = get_tx_details(tx, false, + block_height, + current_blockchain_height); + + j_txs.push_back(get_tx_json(tx, txd)); + + // add fee to the rest + sum_fees += txd.fee; + } + + j_data = json { + {"block_height" , block_height}, + {"hash" , pod_to_hex(blk_hash)}, + {"timestamp" , blk.timestamp}, + {"timestamp_utc" , xmreg::timestamp_to_str_gm(blk.timestamp)}, + {"block_height" , block_height}, + {"size" , blk_size}, + {"txs" , j_txs}, + {"current_height", current_blockchain_height} + }; + + j_response["status"] = "success"; + + return j_response; + } + + + + /* + * Lets use this json api convention for success and error + * https://labs.omniti.com/labs/jsend + */ + json + json_transactions(string _page, string _limit) + { + json j_response { + {"status", "fail"}, + {"data", json {}} + }; + + json& j_data = j_response["data"]; + + // parse page and limit into numbers + + uint64_t page {0}; + uint64_t limit {0}; + + try + { + page = boost::lexical_cast(_page); + limit = boost::lexical_cast(_limit); + } + catch (const boost::bad_lexical_cast& e) + { + j_data["title"] = fmt::format( + "Cant parse page and/or limit numbers: {:s}, {:s}", _page, _limit); + return j_response; + } + + // enforce maximum number of blocks per page to 100 + limit = limit > 100 ? 100 : limit; + + //get current server timestamp + server_timestamp = std::time(nullptr); + + uint64_t local_copy_server_timestamp = server_timestamp; + + uint64_t height = core_storage->get_current_blockchain_height(); + + // calculate starting and ending block numbers to show + int64_t start_height = height - limit * (page + 1); + + // check if start height is not below range + start_height = start_height < 0 ? 0 : start_height; + + int64_t end_height = start_height + limit - 1; + + // loop index + int64_t i = end_height; + + j_data["blocks"] = json::array(); + json& j_blocks = j_data["blocks"]; + + // iterate over last no_of_last_blocks of blocks + while (i >= start_height) + { + // get block at the given height i + block blk; + + if (!mcore->get_block_by_height(i, blk)) + { + j_response["status"] = "error"; + j_response["message"] = fmt::format("Cant get block: {:d}", i); + return j_response; + } + + // get block size in bytes + double blk_size = core_storage->get_db().get_block_size(i); + + crypto::hash blk_hash = core_storage->get_block_id_by_height(i); + + // get block age + pair age = get_age(local_copy_server_timestamp, blk.timestamp); + + j_blocks.push_back(json { + {"height" , i}, + {"hash" , pod_to_hex(blk_hash)}, + {"age" , age.first}, + {"size" , blk_size}, + {"timestamp" , blk.timestamp}, + {"timestamp_utc", xmreg::timestamp_to_str_gm(blk.timestamp)}, + {"txs" , json::array()} + }); + + json& j_txs = j_blocks.back()["txs"]; + + list blk_txs {blk.miner_tx}; + list missed_txs; + + if (!core_storage->get_transactions(blk.tx_hashes, blk_txs, missed_txs)) + { + j_response["status"] = "error"; + j_response["message"] = fmt::format("Cant get transactions in block: {:d}", i); + return j_response; + } + + (void) missed_txs; + + for(auto it = blk_txs.begin(); it != blk_txs.end(); ++it) + { + const cryptonote::transaction &tx = *it; + + const tx_details& txd = get_tx_details(tx, false, i, height); + + j_txs.push_back(get_tx_json(tx, txd)); + } + + --i; + } + + j_data["page"] = page; + j_data["limit"] = limit; + j_data["current_height"] = height; + + j_response["status"] = "success"; + + return j_response; + } + + + /* + * Lets use this json api convention for success and error + * https://labs.omniti.com/labs/jsend + */ + json + json_mempool() + { + json j_response { + {"status", "fail"}, + {"data", json {}} + }; + + json& j_data = j_response["data"]; + + //get current server timestamp + server_timestamp = std::time(nullptr); + + uint64_t local_copy_server_timestamp = server_timestamp; + + uint64_t height = core_storage->get_current_blockchain_height(); + + vector> mempool_data = search_mempool(); + + // for each transaction in the memory pool + for (const auto& a_pair: mempool_data) + { + const tx_details& txd = get_tx_details(a_pair.second, false, 1, height); // 1 is dummy here + + // get basic tx info + json j_tx = get_tx_json(a_pair.second, txd); + + // we add some extra data, for mempool txs, such as recieve timestamp + j_tx["timestamp"] = a_pair.first.receive_time; + j_tx["timestamp_utc"] = xmreg::timestamp_to_str_gm(a_pair.first.receive_time); + + j_data.push_back(j_tx); + } + + j_response["status"] = "success"; + + return j_response; + } + + + /* + * Lets use this json api convention for success and error + * https://labs.omniti.com/labs/jsend + */ + json + json_search(const string& search_text) + { + json j_response { + {"status", "fail"}, + {"data", json {}} + }; + + json& j_data = j_response["data"]; + + //get current server timestamp + server_timestamp = std::time(nullptr); + + uint64_t local_copy_server_timestamp = server_timestamp; + + uint64_t height = core_storage->get_current_blockchain_height(); + + uint64_t search_str_length = search_text.length(); + + // first let check if the search_text matches any tx or block hash + if (search_str_length == 64) + { + // first check for tx + json j_tx = json_transaction(search_text); + + if (j_tx["status"] == "success") + { + j_response["data"] = j_tx["data"]; + j_response["data"]["title"] = "transaction"; + j_response["status"] = "success"; + return j_response; + } + + // now check for block + + json j_block = json_block(search_text); + + if (j_block["status"] == "success") + { + j_response["data"] = j_block["data"]; + j_response["data"]["title"] = "block"; + j_response["status"] = "success"; + return j_response; + } + } + + // now lets see if this is a block number + if (search_str_length <= 8) + { + json j_block = json_block(search_text); + + if (j_block["status"] == "success") + { + j_response["data"] = j_block["data"]; + j_response["data"]["title"] = "block"; + j_response["status"] = "success"; + return j_response; + } + } + + j_data["title"] = "Nothing was found that matches search string: " + search_text; + + return j_response; + } + + json + json_outputs(string tx_hash_str, + string address_str, + string viewkey_str, + bool tx_prove = false) + { + boost::trim(tx_hash_str); + boost::trim(address_str); + boost::trim(viewkey_str); + + json j_response { + {"status", "fail"}, + {"data", json {}} + }; + + json& j_data = j_response["data"]; + + + if (tx_hash_str.empty()) + { + j_response["status"] = "error"; + j_response["message"] = "Tx hash not provided"; + return j_response; + } + + if (address_str.empty()) + { + j_response["status"] = "error"; + j_response["message"] = "Monero address not provided"; + return j_response; + } + + if (viewkey_str.empty()) + { + if (!tx_prove) + { + j_response["status"] = "error"; + j_response["message"] = "Viewkey not provided"; + return j_response; + } + else + { + j_response["status"] = "error"; + j_response["message"] = "Tx private key not provided"; + return j_response; + } + } + + + // parse tx hash string to hash object + crypto::hash tx_hash; + + if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) + { + j_response["status"] = "error"; + j_response["message"] = "Cant parse tx hash: " + tx_hash_str; + return j_response; + } + + // parse string representing given monero address + cryptonote::account_public_address address; + + if (!xmreg::parse_str_address(address_str, address, testnet)) + { + j_response["status"] = "error"; + j_response["message"] = "Cant parse monero address: " + address_str; + return j_response; + + } + + // parse string representing given private key + crypto::secret_key prv_view_key; + + if (!xmreg::parse_str_secret_key(viewkey_str, prv_view_key)) + { + j_response["status"] = "error"; + j_response["message"] = "Cant parse view key or tx private key: " + + viewkey_str; + return j_response; + } + + // get transaction + transaction tx; + + // flag to indicate if tx is in mempool + bool found_in_mempool {false}; + + // for tx in blocks we get block timestamp + // for tx in mempool we get recievive time + uint64_t tx_timestamp {0}; + + if (!find_tx(tx_hash, tx, found_in_mempool, tx_timestamp)) + { + j_data["title"] = fmt::format("Cant find tx hash: {:s}", tx_hash_str); + return j_response; + } + + (void) tx_timestamp; + (void) found_in_mempool; + + tx_details txd = get_tx_details(tx); + + // public transaction key is combined with our viewkey + // to create, so called, derived key. + key_derivation derivation; + + public_key pub_key = tx_prove ? address.m_view_public_key : txd.pk; + + //cout << "txd.pk: " << pod_to_hex(txd.pk) << endl; + + if (!generate_key_derivation(pub_key, prv_view_key, derivation)) + { + j_response["status"] = "error"; + j_response["message"] = "Cant calculate key_derivation"; + return j_response; + } + + uint64_t output_idx {0}; + + std::vector money_transfered(tx.vout.size(), 0); + + j_data["outputs"] = json::array(); + json& j_outptus = j_data["outputs"]; + + for (pair& outp: txd.output_pub_keys) + { + + // get the tx output public key + // that normally would be generated for us, + // if someone had sent us some xmr. + public_key tx_pubkey; + + derive_public_key(derivation, + output_idx, + address.m_spend_public_key, + tx_pubkey); + + // check if generated public key matches the current output's key + bool mine_output = (outp.first.key == tx_pubkey); + + // if mine output has RingCT, i.e., tx version is 2 + if (mine_output && tx.version == 2) + { + // cointbase txs have amounts in plain sight. + // so use amount from ringct, only for non-coinbase txs + if (!is_coinbase(tx)) + { + + // initialize with regular amount + uint64_t rct_amount = money_transfered[output_idx]; + + bool r; + + r = decode_ringct(tx.rct_signatures, + pub_key, + prv_view_key, + output_idx, + tx.rct_signatures.ecdhInfo[output_idx].mask, + rct_amount); + + if (!r) + { + cerr << "\nshow_my_outputs: Cant decode ringCT! " << endl; + } + + outp.second = rct_amount; + money_transfered[output_idx] = rct_amount; + + } // if (!is_coinbase(tx)) + + } // if (mine_output && tx.version == 2) + + j_outptus.push_back(json { + {"output_pubkey", pod_to_hex(outp.first.key)}, + {"amount" , outp.second}, + {"match" , mine_output}, + {"output_idx" , output_idx}, + }); + + ++output_idx; + + } // for (pair& outp: txd.output_pub_keys) + + // return parsed values. can be use to double + // check if submited data in the request + // matches to what was used to produce response. + j_data["tx_hash"] = pod_to_hex(txd.hash); + j_data["address"] = pod_to_hex(address); + j_data["viewkey"] = pod_to_hex(prv_view_key); + j_data["tx_prove"] = tx_prove; + + j_response["status"] = "success"; + + return j_response; + } private: + json + get_tx_json(const transaction& tx, const tx_details& txd) + { + + json j_tx { + {"tx_hash" , pod_to_hex(txd.hash)}, + {"tx_fee" , txd.fee}, + {"mixin" , txd.mixin_no}, + {"tx_size" , txd.size}, + {"xmr_outputs" , txd.xmr_outputs}, + {"xmr_inputs" , txd.xmr_inputs}, + {"tx_version" , txd.version}, + {"rct_type" , tx.rct_signatures.type}, + {"coinbase" , is_coinbase(tx)}, + {"mixin" , txd.mixin_no}, + {"extra" , txd.get_extra_str()}, + {"payment_id" , (txd.payment_id != null_hash ? pod_to_hex(txd.payment_id) : "")}, + {"payment_id8" , (txd.payment_id8 != null_hash8 ? pod_to_hex(txd.payment_id8) : "")}, + }; + + return j_tx; + } + + bool + find_tx(const crypto::hash& tx_hash, + transaction& tx, + bool& found_in_mempool, + uint64_t& tx_timestamp) + { + + found_in_mempool = false; + + if (!mcore->get_tx(tx_hash, tx)) + { + cerr << "Cant get tx in blockchain: " << tx_hash + << ". \n Check mempool now" << endl; + + vector> found_txs + = search_mempool(tx_hash); + + if (!found_txs.empty()) + { + // there should be only one tx found + tx = found_txs.at(0).second; + found_in_mempool = true; + tx_timestamp = found_txs.at(0).first.receive_time; + } + else + { + // tx is nowhere to be found :-( + return false; + } + } + + return true; + } void mark_real_mixins_on_timescales( @@ -3962,7 +4684,7 @@ private: tx_details txd = get_tx_details(tx); - crypto::hash tx_hash = txd.hash; + const crypto::hash& tx_hash = txd.hash; string tx_hash_str = pod_to_hex(tx_hash); @@ -3970,11 +4692,12 @@ private: bool tx_blk_found {false}; + bool detailed_view {enable_mixins_details || static_cast(with_ring_signatures)}; if (core_storage->have_tx(tx_hash)) { // currently get_tx_block_height seems to return a block hight // +1. Before it was not like this. - tx_blk_height = core_storage->get_db().get_tx_block_height(tx_hash) - 1; + tx_blk_height = core_storage->get_db().get_tx_block_height(tx_hash); tx_blk_found = true; } @@ -3998,7 +4721,7 @@ private: // calculate difference between tx and server timestamps age = get_age(server_timestamp, blk.timestamp, FULL_AGE_FORMAT); - blk_timestamp = xmreg::timestamp_to_str(blk.timestamp); + blk_timestamp = xmreg::timestamp_to_str_gm(blk.timestamp); tx_blk_height_str = std::to_string(tx_blk_height); } @@ -4013,15 +4736,15 @@ private: // initalise page tempate map with basic info about blockchain mstch::map context { {"testnet" , testnet}, - {"have_custom_lmdb" , have_custom_lmdb}, {"tx_hash" , tx_hash_str}, - {"tx_prefix_hash" , pod_to_hex(txd.prefix_hash)}, + {"tx_prefix_hash" , string{}}, {"tx_pub_key" , pod_to_hex(txd.pk)}, {"blk_height" , tx_blk_height_str}, + {"tx_blk_height" , tx_blk_height}, {"tx_size" , fmt::format("{:0.4f}", static_cast(txd.size) / 1024.0)}, {"tx_fee" , xmreg::xmr_amount_to_str(txd.fee)}, - {"tx_version" , fmt::format("{:d}", txd.version)}, + {"tx_version" , txd.version}, {"blk_timestamp" , blk_timestamp}, {"blk_timestamp_uint" , blk.timestamp}, {"delta_time" , age.first}, @@ -4034,17 +4757,20 @@ private: {"payment_id" , pid_str}, {"payment_id8" , pid8_str}, {"extra" , txd.get_extra_str()}, - {"with_ring_signatures" , static_cast(with_ring_signatures)}, + {"with_ring_signatures" , static_cast( + with_ring_signatures)}, {"tx_json" , tx_json}, {"is_ringct" , (tx.version > 1)}, {"rct_type" , tx.rct_signatures.type}, {"has_error" , false}, {"error_msg" , string("")}, {"have_raw_tx" , false}, - {"show_more_details_link", true} + {"show_more_details_link", true}, + {"from_cache" , false}, + {"construction_time" , string {}}, }; - string server_time_str = xmreg::timestamp_to_str(server_timestamp, "%F"); + string server_time_str = xmreg::timestamp_to_str_gm(server_timestamp, "%F"); mstch::array inputs = mstch::array{}; @@ -4060,11 +4786,27 @@ private: // umounts. bool have_any_unknown_amount {false}; + uint64_t max_no_of_inputs_to_show {10}; + + // if a tx has more inputs than max_no_of_inputs_to_show, + // we only show 10 first. + bool show_part_of_inputs = (txd.input_key_imgs.size() > max_no_of_inputs_to_show); + + // but if we show all details, i.e., + // the show all inputs, regardless of their number + if (detailed_view) + { + show_part_of_inputs = false; + } vector> mixin_timestamp_groups; // make timescale maps for mixins in input - for (const txin_to_key& in_key: txd.input_key_imgs) + for (const txin_to_key &in_key: txd.input_key_imgs) { + if (show_part_of_inputs && (input_idx > max_no_of_inputs_to_show)) + { + break; + } // get absolute offsets of mixins std::vector absolute_offsets = cryptonote::relative_output_offsets_to_absolute( @@ -4079,7 +4821,7 @@ private: absolute_offsets, outputs); } - catch (const OUTPUT_DNE& e) + catch (const OUTPUT_DNE &e) { string out_msg = fmt::format( "Outputs with amount {:d} do not exist and indexes ", @@ -4104,10 +4846,15 @@ private: {"amount" , xmreg::xmr_amount_to_str(in_key.amount)}, {"input_idx" , fmt::format("{:02d}", input_idx)}, {"mixins" , mstch::array{}}, - {"ring_sigs" , txd.get_ring_sig_for_input(input_idx)}, + {"ring_sigs" , mstch::array{}}, {"already_spent", false} // placeholder for later }); + if (detailed_view) + { + boost::get(inputs.back())["ring_sigs"] + = txd.get_ring_sig_for_input(input_idx); + } inputs_xmr_sum += in_key.amount; if (in_key.amount == 0) @@ -4127,7 +4874,7 @@ private: size_t count = 0; // for each found output public key find its block to get timestamp - for (const uint64_t &i: absolute_offsets) + for (const uint64_t& i: absolute_offsets) { // get basic information about mixn's output cryptonote::output_data_t output_data = outputs.at(count); @@ -4136,12 +4883,12 @@ private: try { - // get pair pair where first is tx hash - // and second is local index of the output i in that tx - tx_out_idx = core_storage->get_db() - .get_output_tx_and_index(in_key.amount, i); + // get pair pair where first is tx hash + // and second is local index of the output i in that tx + tx_out_idx = core_storage->get_db() + .get_output_tx_and_index(in_key.amount, i); } - catch (const OUTPUT_DNE& e) + catch (const OUTPUT_DNE &e) { string out_msg = fmt::format( @@ -4158,53 +4905,66 @@ private: } - // get block of given height, as we want to get its timestamp - cryptonote::block blk; - - if (!mcore->get_block_by_height(output_data.height, blk)) + if (detailed_view) { - cerr << "- cant get block of height: " << output_data.height << endl; + // get block of given height, as we want to get its timestamp + cryptonote::block blk; - context["has_error"] = true; - context["error_msg"] = fmt::format("- cant get block of height: {}", - output_data.height); + if (!mcore->get_block_by_height(output_data.height, blk)) + { + cerr << "- cant get block of height: " << output_data.height << endl; + + context["has_error"] = true; + context["error_msg"] = fmt::format("- cant get block of height: {}", + output_data.height); + } + + // get age of mixin relative to server time + pair mixin_age = get_age(server_timestamp, + blk.timestamp, + FULL_AGE_FORMAT); + // get mixin transaction + transaction mixin_tx; + + if (!mcore->get_tx(tx_out_idx.first, mixin_tx)) + { + cerr << "Cant get tx: " << tx_out_idx.first << endl; + + context["has_error"] = true; + context["error_msg"] = fmt::format("Cant get tx: {:s}", tx_out_idx.first); + } + + // mixin tx details + tx_details mixin_txd = get_tx_details(mixin_tx, true); + + mixins.push_back(mstch::map { + {"mix_blk", fmt::format("{:08d}", output_data.height)}, + {"mix_pub_key", pod_to_hex(output_data.pubkey)}, + {"mix_tx_hash", pod_to_hex(tx_out_idx.first)}, + {"mix_out_indx", tx_out_idx.second}, + {"mix_timestamp", xmreg::timestamp_to_str_gm(blk.timestamp)}, + {"mix_age", mixin_age.first}, + {"mix_mixin_no", mixin_txd.mixin_no}, + {"mix_inputs_no", static_cast(mixin_txd.input_key_imgs.size())}, + {"mix_outputs_no", static_cast(mixin_txd.output_pub_keys.size())}, + {"mix_age_format", mixin_age.second}, + {"mix_idx", fmt::format("{:02d}", count)}, + {"mix_is_it_real", false}, // a placeholder for future + }); + + // get mixin timestamp from its orginal block + mixin_timestamps.push_back(blk.timestamp); } - - // get age of mixin relative to server time - pair mixin_age = get_age(server_timestamp, - blk.timestamp, - FULL_AGE_FORMAT); - // get mixin transaction - transaction mixin_tx; - - if (!mcore->get_tx(tx_out_idx.first, mixin_tx)) + else // if (detailed_view) { - cerr << "Cant get tx: " << tx_out_idx.first << endl; + mixins.push_back(mstch::map { + {"mix_blk", fmt::format("{:08d}", output_data.height)}, + {"mix_pub_key", pod_to_hex(output_data.pubkey)}, + {"mix_idx", fmt::format("{:02d}", count)}, + {"mix_is_it_real", false}, // a placeholder for future + }); - context["has_error"] = true; - context["error_msg"] = fmt::format("Cant get tx: {:s}", tx_out_idx.first); - } - - // mixin tx details - tx_details mixin_txd = get_tx_details(mixin_tx, true); - - mixins.push_back(mstch::map { - {"mix_blk" , fmt::format("{:08d}", output_data.height)}, - {"mix_pub_key" , pod_to_hex(output_data.pubkey)}, - {"mix_tx_hash" , pod_to_hex(tx_out_idx.first)}, - {"mix_out_indx" , tx_out_idx.second}, - {"mix_timestamp" , xmreg::timestamp_to_str(blk.timestamp)}, - {"mix_age" , mixin_age.first}, - {"mix_mixin_no" , mixin_txd.mixin_no}, - {"mix_inputs_no" , static_cast(mixin_txd.input_key_imgs.size())}, - {"mix_outputs_no" , static_cast(mixin_txd.output_pub_keys.size())}, - {"mix_age_format" , mixin_age.second}, - {"mix_idx" , fmt::format("{:02d}", count)}, - {"mix_is_it_real" , false}, // a placeholder for future - }); - - // get mixin timestamp from its orginal block - mixin_timestamps.push_back(blk.timestamp); + } // else if (enable_mixins_details) ++count; @@ -4215,31 +4975,46 @@ private: input_idx++; } // for (const txin_to_key& in_key: txd.input_key_imgs) - uint64_t min_mix_timestamp; - uint64_t max_mix_timestamp; - pair mixins_timescales - = construct_mstch_mixin_timescales( - mixin_timestamp_groups, - min_mix_timestamp, - max_mix_timestamp - ); - context["have_any_unknown_amount"] = have_any_unknown_amount; - context["inputs_xmr_sum_not_zero"] = (inputs_xmr_sum > 0); - context["inputs_xmr_sum"] = xmreg::xmr_amount_to_str(inputs_xmr_sum); - context["server_time"] = server_time_str; + if (detailed_view) + { + uint64_t min_mix_timestamp {0}; + uint64_t max_mix_timestamp {0}; + + pair mixins_timescales + = construct_mstch_mixin_timescales( + mixin_timestamp_groups, + min_mix_timestamp, + max_mix_timestamp + ); + + + context["min_mix_time"] = xmreg::timestamp_to_str_gm(min_mix_timestamp); + context["max_mix_time"] = xmreg::timestamp_to_str_gm(max_mix_timestamp); + + context.emplace("timescales", mixins_timescales.first); + + + context["timescales_scale"] = fmt::format("{:0.2f}", + mixins_timescales.second / 3600.0 / 24.0); // in days + + context["tx_prefix_hash"] = pod_to_hex(get_transaction_prefix_hash(tx)); + + } + + + context["have_any_unknown_amount"] = have_any_unknown_amount; + context["inputs_xmr_sum_not_zero"] = (inputs_xmr_sum > 0); + context["inputs_xmr_sum"] = xmreg::xmr_amount_to_str(inputs_xmr_sum); + context["server_time"] = server_time_str; + context["enable_mixins_details"] = detailed_view; + context["show_part_of_inputs"] = show_part_of_inputs; + context["max_no_of_inputs_to_show"] = max_no_of_inputs_to_show; + context.emplace("inputs", inputs); - context["min_mix_time"] = xmreg::timestamp_to_str(min_mix_timestamp); - context["max_mix_time"] = xmreg::timestamp_to_str(max_mix_timestamp); - - context.emplace("timescales", mixins_timescales.first); - - context["timescales_scale"] = fmt::format("{:0.2f}", - mixins_timescales.second / 3600.0 / 24.0); // in days - // get indices of outputs in amounts tables vector out_amount_indices; @@ -4363,15 +5138,16 @@ private: tx_details - get_tx_details(const transaction& tx, bool coinbase = false) + get_tx_details(const transaction& tx, + bool coinbase = false, + uint64_t blk_height = 0, + uint64_t bc_height = 0) { tx_details txd; // get tx hash txd.hash = get_transaction_hash(tx); - // get tx prefix hash - txd.prefix_hash = get_transaction_prefix_hash(tx); // get tx public key from extra // this check if there are two public keys @@ -4380,18 +5156,19 @@ private: txd.pk = xmreg::get_tx_pub_key_from_received_outs(tx); // sum xmr in inputs and ouputs in the given tx - txd.xmr_inputs = sum_money_in_inputs(tx); - txd.xmr_outputs = sum_money_in_outputs(tx); - txd.num_nonrct_inputs = count_nonrct_inputs(tx); + const array& sum_data = summary_of_in_out_rct( + tx, txd.output_pub_keys, txd.input_key_imgs); - // get mixin number - txd.mixin_no = get_mixin_no(tx); + txd.xmr_outputs = sum_data[0]; + txd.xmr_inputs = sum_data[1]; + txd.mixin_no = sum_data[2]; + txd.num_nonrct_inputs = sum_data[3]; txd.fee = 0; transaction tx_copy = tx; - // txd.json_representation = obj_to_json_str(tx_copy); + txd.json_representation = obj_to_json_str(tx_copy); if (!coinbase && tx.vin.size() > 0) @@ -4408,15 +5185,9 @@ private: get_payment_id(tx, txd.payment_id, txd.payment_id8); - //blobdata tx_blob = t_serializable_object_to_blob(tx); // get tx size in bytes txd.size = get_object_blobsize(tx); - //txd.size = tx_blob.size(); - //txd.size = core_storage->get_db().get_block_size(); - - txd.input_key_imgs = get_key_images(tx); - txd.output_pub_keys = get_ouputs(tx); txd.extra = tx.extra; @@ -4431,14 +5202,24 @@ private: txd.no_confirmations = 0; - if (core_storage->have_tx(txd.hash)) + if (blk_height == 0 && core_storage->have_tx(txd.hash)) { + // if blk_height is zero then search for tx block in + // the blockchain. but since often block height is know a priory + // this is not needed txd.blk_height = core_storage->get_db().get_tx_block_height(txd.hash); // get the current blockchain height. Just to check uint64_t bc_height = core_storage->get_current_blockchain_height(); - txd.no_confirmations = bc_height - (txd.blk_height - 1); + txd.no_confirmations = bc_height - (txd.blk_height); + } + else + { + // if we know blk_height, and current blockchan height + // just use it to get no_confirmations. + + txd.no_confirmations = bc_height - (blk_height); } return txd; @@ -4458,36 +5239,13 @@ private: boost::erase_all(raw_tx_data, "-----END CERTIFICATE-----"); } - bool - get_txs_from_timestamp_range( - uint64_t timestamp_start, - uint64_t timestamp_end, - const unique_ptr& mylmdb, - vector& out_txs) - { - - vector txs_found; - - if (mylmdb->get_txs_from_timestamp_range( - timestamp_start, - timestamp_end, - txs_found)) - { - - for (auto tf: txs_found) - { - out_txs.push_back(pod_to_hex(tf)); - } - - return true; - } - - return false; - } vector> search_mempool(crypto::hash tx_hash = null_hash) { + // if tx_hash == null_hash then this method + // will just return the vector containing all + // txs in mempool vector> found_txs; @@ -4500,97 +5258,46 @@ private: return found_txs; } - // if we have tx blob disply more. - // this info can also be obtained from json that is - // normally returned by the RCP call (see below in detailed view) - if (HAVE_TX_BLOB) + // if dont have tx_blob member, construct tx + // from json obtained from the rpc call + + for (size_t i = 0; i < mempool_txs.size(); ++i) { - // get tx_blob if exists - //string tx_blob = get_tx_blob(_tx_info); + // get transaction info of the tx in the mempool + tx_info _tx_info = mempool_txs.at(i); - for (size_t i = 0; i < mempool_txs.size(); ++i) + crypto::hash mem_tx_hash = null_hash; + + if (hex_to_pod(_tx_info.id_hash, mem_tx_hash)) { - // get transaction info of the tx in the mempool - tx_info _tx_info = mempool_txs.at(i); - - // get tx_blob if exists - string tx_blob = get_tx_blob(_tx_info); - - if (tx_blob.empty()) - { - cerr << "tx_blob is empty. Probably its not a custom deamon." << endl; - continue; - } - - // pare tx_blob into tx class transaction tx; - if (!parse_and_validate_tx_from_blob( - tx_blob, tx)) + if (!xmreg::make_tx_from_json(_tx_info.tx_json, tx)) { - cerr << "Cant get tx from blob" << endl; + cerr << "Cant make tx from _tx_info.tx_json" << endl; continue; } - - // if we dont provide tx_hash, just get all txs in - // the mempool - if (tx_hash != null_hash) + if (mem_tx_hash != get_transaction_hash(tx)) { - // check if tx hash matches, and if yes, save it for return - if (tx_hash == get_transaction_hash(tx)) - { - found_txs.push_back(make_pair(_tx_info, tx)); + cerr << "Hash of reconstructed tx from json does not match " + "what we should get!" + << endl; + continue; + } + + if (tx_hash == mem_tx_hash || tx_hash == null_hash) + { + found_txs.push_back(make_pair(_tx_info, tx)); + + if (tx_hash != null_hash) break; - } - } - else - { - found_txs.push_back(make_pair(_tx_info, tx)); } - } - } - else - { - // if dont have tx_blob member, construct tx - // from json obtained from the rpc call + } // if (hex_to_pod(_tx_info.id_hash, mem_tx_hash)) - for (size_t i = 0; i < mempool_txs.size(); ++i) - { - // get transaction info of the tx in the mempool - tx_info _tx_info = mempool_txs.at(i); + } // for (size_t i = 0; i < mempool_txs.size(); ++i) - crypto::hash mem_tx_hash = null_hash; - - if (hex_to_pod(_tx_info.id_hash, mem_tx_hash)) - { - transaction tx; - - //cout << "\n\n\n_tx_info.id_hash:" << _tx_info.id_hash << endl; - - if (!xmreg::make_tx_from_json(_tx_info.tx_json, tx)) - { - cerr << "Cant make tx from _tx_info.tx_json" << endl; - continue; - } - - if (_tx_info.id_hash != pod_to_hex(get_transaction_hash(tx))) - { - cerr << "Hash of reconstructed tx from json does not match " - "what we should get!" - << endl; - continue; - } - - if (tx_hash == mem_tx_hash) - { - found_txs.push_back(make_pair(_tx_info, tx)); - break; - } - } - } - } return found_txs; } diff --git a/src/rpccalls.cpp b/src/rpccalls.cpp index a69350f..61bcf93 100644 --- a/src/rpccalls.cpp +++ b/src/rpccalls.cpp @@ -3,3 +3,150 @@ // #include "rpccalls.h" + +namespace xmreg +{ + + +rpccalls::rpccalls(string _deamon_url, + uint64_t _timeout) + : deamon_url {_deamon_url}, + timeout_time {_timeout} +{ + epee::net_utils::parse_url(deamon_url, url); + + port = std::to_string(url.port); + + timeout_time_ms = std::chrono::milliseconds {timeout_time}; + + m_http_client.set_server( + deamon_url, + boost::optional{}); +} + +bool +rpccalls::connect_to_monero_deamon() +{ + //std::lock_guard guard(m_daemon_rpc_mutex); + + if(m_http_client.is_connected()) + { + return true; + } + + return m_http_client.connect(timeout_time_ms); +} + +uint64_t +rpccalls::get_current_height() +{ + COMMAND_RPC_GET_HEIGHT::request req; + COMMAND_RPC_GET_HEIGHT::response res; + + std::lock_guard guard(m_daemon_rpc_mutex); + + if (!connect_to_monero_deamon()) + { + cerr << "get_current_height: not connected to deamon" << endl; + return false; + } + + bool r = epee::net_utils::invoke_http_json( + "/getheight", + req, res, m_http_client, timeout_time_ms); + + if (!r) + { + cerr << "Error connecting to Monero deamon at " + << deamon_url << endl; + return 0; + } + else + { + cout << "rpc call /getheight OK: " << endl; + } + + return res.height; +} + +bool +rpccalls::get_mempool(vector& mempool_txs) +{ + + COMMAND_RPC_GET_TRANSACTION_POOL::request req; + COMMAND_RPC_GET_TRANSACTION_POOL::response res; + + std::lock_guard guard(m_daemon_rpc_mutex); + + if (!connect_to_monero_deamon()) + { + cerr << "get_mempool: not connected to deamon" << endl; + return false; + } + + bool r = epee::net_utils::invoke_http_json( + "/get_transaction_pool", + req, res, m_http_client, timeout_time_ms); + + if (!r) + { + cerr << "Error connecting to Monero deamon at " + << deamon_url << endl; + return false; + } + + + mempool_txs = res.transactions; + + // mempool txs are not sorted base on their arival time, + // so we sort it here. + + std::sort(mempool_txs.begin(), mempool_txs.end(), + [](tx_info& t1, tx_info& t2) + { + return t1.receive_time > t2.receive_time; + }); + + + return true; +} + + +bool +rpccalls::commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg) +{ + COMMAND_RPC_SEND_RAW_TX::request req; + COMMAND_RPC_SEND_RAW_TX::response res; + + req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer( + tx_to_blob(ptx.tx) + ); + + req.do_not_relay = false; + + std::lock_guard guard(m_daemon_rpc_mutex); + + if (!connect_to_monero_deamon()) + { + cerr << "commit_tx: not connected to deamon" << endl; + return false; + } + + bool r = epee::net_utils::invoke_http_json( + "/sendrawtransaction", + req, res, m_http_client, timeout_time_ms); + + if (!r || res.status == "Failed") + { + error_msg = res.reason; + + cerr << "Error sending tx: " << res.reason << endl; + return false; + } + + return true; +} + + + +} diff --git a/src/rpccalls.h b/src/rpccalls.h index 9959ded..95ac59f 100644 --- a/src/rpccalls.h +++ b/src/rpccalls.h @@ -13,157 +13,44 @@ namespace xmreg { - using namespace cryptonote; - using namespace crypto; - using namespace std; +using namespace cryptonote; +using namespace crypto; +using namespace std; - class rpccalls - { - string deamon_url ; - uint64_t timeout_time; +class rpccalls +{ + string deamon_url ; + uint64_t timeout_time; - std::chrono::milliseconds timeout_time_ms; + std::chrono::milliseconds timeout_time_ms; - epee::net_utils::http::url_content url; + epee::net_utils::http::url_content url; - epee::net_utils::http::http_simple_client m_http_client; - std::mutex m_daemon_rpc_mutex; + epee::net_utils::http::http_simple_client m_http_client; + std::mutex m_daemon_rpc_mutex; - string port; + string port; - public: +public: - rpccalls(string _deamon_url = "http:://127.0.0.1:18081", - uint64_t _timeout = 200000) - : deamon_url {_deamon_url}, - timeout_time {_timeout} - { - epee::net_utils::parse_url(deamon_url, url); + rpccalls(string _deamon_url = "http:://127.0.0.1:18081", + uint64_t _timeout = 200000); - port = std::to_string(url.port); + bool + connect_to_monero_deamon(); - timeout_time_ms = std::chrono::milliseconds {timeout_time}; + uint64_t + get_current_height(); - m_http_client.set_server( - deamon_url, - boost::optional{}); - } - - bool - connect_to_monero_deamon() - { - //std::lock_guard guard(m_daemon_rpc_mutex); - - if(m_http_client.is_connected()) - { - return true; - } - - return m_http_client.connect(timeout_time_ms); - } - - uint64_t - get_current_height() - { - COMMAND_RPC_GET_HEIGHT::request req; - COMMAND_RPC_GET_HEIGHT::response res; - - std::lock_guard guard(m_daemon_rpc_mutex); - - if (!connect_to_monero_deamon()) - { - cerr << "get_current_height: not connected to deamon" << endl; - return false; - } - - bool r = epee::net_utils::invoke_http_json( - "/getheight", - req, res, m_http_client, timeout_time_ms); - - if (!r) - { - cerr << "Error connecting to Monero deamon at " - << deamon_url << endl; - return 0; - } - else - { - cout << "rpc call /getheight OK: " << endl; - } - - return res.height; - } - - bool - get_mempool(vector& mempool_txs) - { - - COMMAND_RPC_GET_TRANSACTION_POOL::request req; - COMMAND_RPC_GET_TRANSACTION_POOL::response res; - - std::lock_guard guard(m_daemon_rpc_mutex); - - if (!connect_to_monero_deamon()) - { - cerr << "get_mempool: not connected to deamon" << endl; - return false; - } - - bool r = epee::net_utils::invoke_http_json( - "/get_transaction_pool", - req, res, m_http_client, timeout_time_ms); - - if (!r) - { - cerr << "Error connecting to Monero deamon at " - << deamon_url << endl; - return false; - } + bool + get_mempool(vector& mempool_txs); - mempool_txs = res.transactions; + bool + commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg); - return true; - } - - - bool - commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg) - { - COMMAND_RPC_SEND_RAW_TX::request req; - COMMAND_RPC_SEND_RAW_TX::response res; - - req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer( - tx_to_blob(ptx.tx) - ); - - req.do_not_relay = false; - - std::lock_guard guard(m_daemon_rpc_mutex); - - if (!connect_to_monero_deamon()) - { - cerr << "commit_tx: not connected to deamon" << endl; - return false; - } - - bool r = epee::net_utils::invoke_http_json( - "/sendrawtransaction", - req, res, m_http_client, timeout_time_ms); - - if (!r || res.status == "Failed") - { - error_msg = res.reason; - - cerr << "Error sending tx: " << res.reason << endl; - return false; - } - - return true; - } - - }; +}; } diff --git a/src/templates/checkrawtx.html b/src/templates/checkrawtx.html index 364d66e..37772b6 100644 --- a/src/templates/checkrawtx.html +++ b/src/templates/checkrawtx.html @@ -7,7 +7,13 @@
-

Data file prefix: {{data_prefix}}

+ {{#has_error}} +

Checking tx failed

+

{{error_msg}}

+ {{/has_error}} + {{^has_error}} +

Data file prefix: {{data_prefix}}

+ {{/has_error}} {{#unsigned_tx_given}}

Details of unsigned raw tx data given

@@ -16,7 +22,7 @@

Basic information

- {{#dest_infos}} + {{#dest_infos}} Send {{dest_amount}} to {{dest_address}}
{{/dest_infos}} diff --git a/src/templates/css/style.css b/src/templates/css/style.css index 8f90f7a..f11dee2 100644 --- a/src/templates/css/style.css +++ b/src/templates/css/style.css @@ -115,7 +115,7 @@ form { input#toggle-1[type=checkbox] { position: absolute; - top: -9999px; + /*top: -9999px;*/ left: -9999px; } diff --git a/src/templates/footer.html b/src/templates/footer.html index 3d286a1..d4ab012 100644 --- a/src/templates/footer.html +++ b/src/templates/footer.html @@ -4,7 +4,6 @@ | explorer version: {{last_git_commit_date}}-{{last_git_commit_hash}} | monero version: {{monero_version_full}}
-
diff --git a/src/templates/header.html b/src/templates/header.html index 3cb7817..140f09d 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -22,23 +22,12 @@
+
+
+ + +
+
- {{#have_custom_lmdb}} -
-
- - -
-
- {{/have_custom_lmdb}} - {{^have_custom_lmdb}} -
-
- - -
-
- {{/have_custom_lmdb}} - + diff --git a/src/templates/index2.html b/src/templates/index2.html index 62b251f..8e70134 100644 --- a/src/templates/index2.html +++ b/src/templates/index2.html @@ -17,12 +17,18 @@ {{#refresh}} Autorefresh is ON (10 s) {{/refresh}} - {{^refresh}} - Autorefresh is OFF - {{/refresh}} + {{^refresh}} + Autorefresh is OFF + {{/refresh}} {{/enable_autorefresh_option}} + {{#testnet_url}} + | Go to testnet explorer + {{/testnet_url}} + {{#mainnet_url}} + | Go to mainnet explorer + {{/mainnet_url}} {{#testnet}} - | This is testnet blockchian + | This is testnet blockchian {{/testnet}} @@ -52,7 +58,6 @@ fees outputs in(nonrct)/out - rct/type mixin tx size [kB] @@ -65,10 +70,6 @@ {{tx_fee_short}} {{sum_outputs_short}} {{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}} - - {{#is_ringct}}yes/{{rct_type}}{{/is_ringct}} - {{^is_ringct}}no{{/is_ringct}} - {{mixin}} {{tx_size_short}} @@ -85,3 +86,14 @@ + +{{#show_cache_times}} +
+
+ Tx details construction time: {{construction_time_total}} s +
+ includes {{construction_time_cached}} s from block cache ({{cache_hits}} hits) + and {{construction_time_non_cached}} s from non cache ({{cache_misses}} misses) +
+
+{{/show_cache_times}} \ No newline at end of file diff --git a/src/templates/mempool.html b/src/templates/mempool.html index ecf22d0..1f00920 100644 --- a/src/templates/mempool.html +++ b/src/templates/mempool.html @@ -11,7 +11,6 @@ fee outputs in(nonrct)/out - rct/type mixin tx size [kB] @@ -22,7 +21,6 @@ {{fee}} {{xmr_outputs}} {{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}} - {{is_ringct}}{{rct_type}} {{mixin}} {{txsize}} @@ -31,7 +29,7 @@ {{^mempool_fits_on_front_page}} {{#partial_mempool_shown}} -
+ {{/partial_mempool_shown}} @@ -39,4 +37,17 @@ {{/mempool_fits_on_front_page}} + + {{#show_cache_times}} +
+
+ Mempoool tx details construction time: {{construction_time_total}} s +
+ includes {{construction_time_cached}} s from mempool cache ({{cache_hits}} hits) + and {{construction_time_non_cached}} s from non cache ({{cache_misses}} misses) +
+
+ {{/show_cache_times}} + +
diff --git a/src/templates/partials/tx_details.html b/src/templates/partials/tx_details.html index 40362a7..91deb2d 100644 --- a/src/templates/partials/tx_details.html +++ b/src/templates/partials/tx_details.html @@ -2,12 +2,10 @@

Tx hash: {{tx_hash}}

-
Tx prefix hash: {{tx_prefix_hash}}
+ {{#enable_mixins_details}} +
Tx prefix hash: {{tx_prefix_hash}}
+ {{/enable_mixins_details}}
Tx public key: {{tx_pub_key}}
- {{#have_raw_tx}} - - {{/have_raw_tx}} - {{#has_payment_id}}
Payment id: {{payment_id}}
@@ -19,11 +17,11 @@ {{#have_prev_hash}} -
Previous tx: {{prev_hash}}
+
Previous tx: {{prev_hash}}
{{/have_prev_hash}} {{#have_next_hash}} -
Next tx: {{next_hash}}
+
Next tx: {{next_hash}}
{{/have_next_hash}} @@ -126,19 +124,21 @@ {{#has_inputs}} -

Inputs' mixins time scale (from {{min_mix_time}} till {{max_mix_time}}; - resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}})

-
-
    - {{#timescales}} -
  • |{{timescale}}|
  • - {{/timescales}} -
-
- + {{#enable_mixins_details}} +

Inputs' mixins time scale (from {{min_mix_time}} till {{max_mix_time}}; + resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}}) +

+
+
    + {{#timescales}} +
  • |{{timescale}}|
  • + {{/timescales}} +
+
+ {{/enable_mixins_details}} {{^inputs_xmr_sum_not_zero}} -

{{inputs_no}} inputs(s) for total of {{inputs_xmr_sum}} xmr

+

{{inputs_no}} input(s) for total of {{inputs_xmr_sum}} xmr

{{/inputs_xmr_sum_not_zero}} {{#inputs_xmr_sum_not_zero}} {{^have_any_unknown_amount}} @@ -149,6 +149,12 @@ {{/have_any_unknown_amount}} {{/inputs_xmr_sum_not_zero}} + {{#show_part_of_inputs}} +
+ Only {{max_no_of_inputs_to_show}} are inputs shown. To see all, + click "more details" +
+ {{/show_part_of_inputs}}
@@ -171,39 +177,65 @@ + + {{#have_raw_tx}} + + {{/have_raw_tx}} + + + {{#mixins}} + + + {{#have_raw_tx}} + {{#mix_is_it_real}} + + {{/mix_is_it_real}} + {{^mix_is_it_real}} + + {{/mix_is_it_real}} + {{/have_raw_tx}} + + + {{/mixins}} +
+ {{#enable_mixins_details}} + + + + {{#have_raw_tx}} + + {{/have_raw_tx}} + + + + + + + + {{#mixins}} + + + {{#have_raw_tx}} + {{#mix_is_it_real}} + + {{/mix_is_it_real}} + {{^mix_is_it_real}} + + {{/mix_is_it_real}} + {{/have_raw_tx}} + + + + + + + {{/mixins}} +
Mixin stealth addressIs it real?blkmixinin/outtimestampage [y:d:h:m:s]
- {{mix_idx}}: {{mix_pub_key}}{{mix_is_it_real}}{{mix_is_it_real}}{{mix_blk}}{{mix_mixin_no}}{{mix_inputs_no}}/{{mix_outputs_no}}{{mix_timestamp}}{{mix_age}}
+ {{/enable_mixins_details}} + {{^enable_mixins_details}} - - - {{#have_raw_tx}} - - {{/have_raw_tx}} - - - - - - - - {{#mixins}} - - - {{#have_raw_tx}} - {{#mix_is_it_real}} - - {{/mix_is_it_real}} - {{^mix_is_it_real}} - - {{/mix_is_it_real}} - {{/have_raw_tx}} - - - - - - - - {{/mixins}} -
Mixin stealth addressIs it real?blkmixinin/outtimestampage [y:d:h:m:s]
- {{mix_idx}}: {{mix_pub_key}}{{mix_is_it_real}}{{mix_is_it_real}}{{mix_blk}}{{mix_mixin_no}}{{mix_inputs_no}}/{{mix_outputs_no}}{{mix_timestamp}}{{mix_age}}
+
Mixin stealth addressIs it real?blk
- {{mix_idx}}: {{mix_pub_key}}{{mix_is_it_real}}{{mix_is_it_real}}{{mix_blk}}
+ {{/enable_mixins_details}} @@ -234,6 +266,7 @@
+ {{/has_inputs}} {{^have_raw_tx}} @@ -243,14 +276,32 @@ {{/show_more_details_link}} {{/with_ring_signatures}} {{#with_ring_signatures}} -

JSON representation of tx

-
- - {{tx_json}} - + + +
+
+ + {{tx_json}} + +
+

Less details
{{/with_ring_signatures}} {{/have_raw_tx}} + +{{#show_cache_times}} +
+ {{#construction_time}} +
+ Tx details construction time: {{construction_time}} s + {{#from_cache}} +
Tx read from the tx cache + {{/from_cache}} +
+ {{/construction_time}} +
+{{/show_cache_times}} +
diff --git a/src/tools.cpp b/src/tools.cpp index f641dd5..626248a 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -148,53 +148,32 @@ remove_trailing_path_separator(const bf::path& in_path) return bf::path(remove_trailing_path_separator(path_str)); } -string -timestamp_to_str(time_t timestamp, const char* format) -{ - auto a_time_point = chrono::system_clock::from_time_t(timestamp); - - try - { - auto utc = date::to_utc_time(chrono::system_clock::from_time_t(timestamp)); - auto sys_time = date::to_sys_time(utc); - - return date::format(format, date::floor(sys_time)); - } - catch (std::runtime_error& e) - { - cerr << "xmreg::timestamp_to_str: " << e.what() << endl; - cerr << "Seems cant convert to UTC timezone using date library. " - "So just use local timezone." < +summary_of_in_out_rct( + const transaction& tx, + vector>& output_pub_keys, + vector& input_key_imgs) +{ + + uint64_t xmr_outputs {0}; + uint64_t xmr_inputs {0}; + uint64_t mixin_no {0}; + uint64_t num_nonrct_inputs {0}; + + + for (const tx_out& txout: tx.vout) + { + if (txout.target.type() != typeid(txout_to_key)) + { + // push empty pair. + output_pub_keys.push_back(pair{}); + continue; + } + + // get tx input key + const txout_to_key& txout_key + = boost::get(txout.target); + + output_pub_keys.push_back(make_pair(txout_key, txout.amount)); + + xmr_outputs += txout.amount; + } + + size_t input_no = tx.vin.size(); + + for (size_t i = 0; i < input_no; ++i) + { + + if(tx.vin[i].type() != typeid(cryptonote::txin_to_key)) + { + continue; + } + + // get tx input key + const cryptonote::txin_to_key& tx_in_to_key + = boost::get(tx.vin[i]); + + xmr_inputs += tx_in_to_key.amount; + + if (tx_in_to_key.amount != 0) + { + ++num_nonrct_inputs; + } + + if (mixin_no == 0) + { + mixin_no = tx_in_to_key.key_offsets.size(); + } + + input_key_imgs.push_back(tx_in_to_key); + + } // for (size_t i = 0; i < input_no; ++i) + + + return {xmr_outputs, xmr_inputs, mixin_no, num_nonrct_inputs}; +}; + + +// this version for mempool txs from json +array +summary_of_in_out_rct(const json& _json) +{ + uint64_t xmr_outputs {0}; + uint64_t xmr_inputs {0}; + uint64_t no_outputs {0}; + uint64_t no_inputs {0}; + uint64_t mixin_no {0}; + uint64_t num_nonrct_inputs {0}; + + for (const json& vout: _json["vout"]) + { + xmr_outputs += vout["amount"].get(); + } + + no_outputs = _json["vout"].size(); + + for (const json& vin: _json["vin"]) + { + uint64_t amount = vin["key"]["amount"].get(); + + xmr_inputs += amount; + + if (amount != 0) + ++num_nonrct_inputs; + } + + no_inputs = _json["vin"].size(); + + mixin_no = _json["vin"].at(0)["key"]["key_offsets"].size() - 1; + + return {xmr_outputs, xmr_inputs, no_outputs, no_inputs, mixin_no, num_nonrct_inputs}; +}; + + uint64_t sum_money_in_inputs(const transaction& tx) { @@ -1035,21 +1117,6 @@ get_tx_pub_key_from_received_outs(const transaction &tx) return null_pkey; } -date::sys_seconds -parse(const std::string& str, string format) -{ - std::istringstream in(str); - date::sys_seconds tp; - in >> date::parse(format, tp); - if (in.fail()) - { - in.clear(); - in.str(str); - in >> date::parse(format, tp); - } - return tp; -} - /** * Check if given output (specified by output_index) * belongs is ours based diff --git a/src/tools.h b/src/tools.h index 5fa3715..66ff1de 100644 --- a/src/tools.h +++ b/src/tools.h @@ -16,7 +16,6 @@ #include "monero_headers.h" #include "../ext/infix_iterator.h" -#include "../ext/date/tz.h" #include "../ext/fmt/ostream.h" #include "../ext/fmt/format.h" #include "../ext/json.hpp" @@ -104,10 +103,7 @@ bf::path remove_trailing_path_separator(const bf::path& in_path); string -timestamp_to_str(time_t timestamp, const char* format = "%F %T"); - -string -timestamp_to_str_local(time_t timestamp, const char* format = "%F %T"); +timestamp_to_str_gm(time_t timestamp, const char* format = "%F %T"); ostream& operator<< (ostream& os, const account_public_address& addr); @@ -136,6 +132,15 @@ sum_money_in_outputs(const string& json_str); pair sum_money_in_outputs(const json& _json); +array +summary_of_in_out_rct( + const transaction& tx, + vector>& output_pub_keys, + vector& input_key_imgs); + +// this version for mempool txs from json +array +summary_of_in_out_rct(const json& _json); uint64_t sum_money_in_inputs(const transaction& tx);