Line data Source code
1 : // SPDX-License-Identifier: BSD-3-Clause 2 : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 3 : /// @file 4 : /// @author Simon Heybrock 5 : #include <chrono> 6 : #include <iomanip> 7 : #include <mutex> 8 : #include <sstream> 9 : 10 : #include "scipp/units/unit.h" 11 : 12 : #include "scipp/core/dimensions.h" 13 : #include "scipp/core/except.h" 14 : #include "scipp/core/slice.h" 15 : #include "scipp/core/string.h" 16 : #include "scipp/core/time_point.h" 17 : 18 : namespace scipp::core { 19 : 20 12 : std::ostream &operator<<(std::ostream &os, const Dimensions &dims) { 21 12 : return os << to_string(dims); 22 : } 23 : 24 158 : std::ostream &operator<<(std::ostream &os, const scipp::index_pair &index) { 25 158 : return os << to_string(index); 26 : } 27 : 28 1006 : std::string to_string(const Dimensions &dims) { 29 1006 : if (dims.empty()) 30 218 : return "()"; 31 788 : std::string s = "("; 32 1768 : for (int32_t i = 0; i < scipp::size(dims.shape()); ++i) 33 1960 : s += to_string(dims.labels()[i]) + ": " + std::to_string(dims.shape()[i]) + 34 980 : ", "; 35 788 : s.resize(s.size() - 2); 36 788 : s += ")"; 37 788 : return s; 38 788 : } 39 : 40 0 : std::string labels_to_string(const Dimensions &dims) { 41 0 : if (dims.empty()) 42 0 : return "()"; 43 0 : std::string s = "("; 44 0 : for (const auto &dim : dims.labels()) 45 0 : s += to_string(dim) + ", "; 46 0 : s.resize(s.size() - 2); 47 0 : s += ")"; 48 0 : return s; 49 0 : } 50 : 51 336 : std::string to_string(const Sizes &sizes) { 52 336 : std::string repr("Sizes["); 53 879 : for (const auto &dim : sizes) 54 543 : repr += to_string(dim) + ":" + std::to_string(sizes[dim]) + ", "; 55 336 : repr += "]"; 56 336 : return repr; 57 0 : } 58 : 59 27 : const std::string &to_string(const std::string &s) { return s; } 60 0 : std::string_view to_string(const std::string_view &s) { return s; } 61 0 : std::string to_string(const char *s) { return std::string(s); } 62 : 63 56 : std::string to_string(const bool b) { return b ? "True" : "False"; } 64 : 65 3815 : std::string to_string(const DType dtype) { 66 3815 : return dtypeNameRegistry().at(dtype); 67 : } 68 : 69 104 : std::string to_string(const Slice &slice) { 70 144 : std::string end = slice.end() >= 0 ? ", " + std::to_string(slice.end()) : ""; 71 208 : return "Slice(" + to_string(slice.dim()) + ", " + 72 520 : std::to_string(slice.begin()) + end + ')'; 73 104 : } 74 : 75 158 : std::string to_string(const scipp::index_pair &index) { 76 316 : return '(' + std::to_string(index.first) + ", " + 77 474 : std::to_string(index.second) + ')'; 78 : } 79 : 80 4128 : std::map<DType, std::string> &dtypeNameRegistry() { 81 4128 : static std::map<DType, std::string> registry; 82 4128 : return registry; 83 : } 84 : 85 : namespace { 86 5 : template <class Ratio> constexpr int64_t num_digits() { 87 : static_assert(Ratio::num == 1 || Ratio::num % 10 == 0); 88 : static_assert(Ratio::den == 1 || Ratio::den % 10 == 0); 89 : static_assert(Ratio::den > Ratio::num); 90 5 : int64_t result = 0; 91 32 : for (std::size_t i = Ratio::num; i < Ratio::den; i *= 10) { 92 27 : ++result; 93 : } 94 5 : return result; 95 : } 96 : 97 : // For synchronizing access to gmtime because its return value is shared. 98 : std::mutex gmtime_mutex; 99 : 100 17 : void put_time(std::ostream &os, const std::time_t time_point, 101 : const bool include_time) { 102 17 : std::lock_guard guard_{gmtime_mutex}; 103 17 : const std::tm *tm = std::gmtime(&time_point); 104 17 : if (include_time) 105 17 : os << std::put_time(tm, "%FT%T"); 106 : else 107 0 : os << std::put_time(tm, "%F"); 108 17 : } 109 : 110 : template <class Rep, class Period> 111 17 : std::string to_string(const std::chrono::duration<Rep, Period> &duration) { 112 : using Clock = std::chrono::system_clock; 113 : 114 17 : std::ostringstream oss; 115 : 116 : #ifdef _WIN32 117 : // Windows' time functions (e.g. gmtime) don't support datetimes before 1970. 118 : if (duration < std::chrono::duration<Rep, Period>::zero()) { 119 : return "(datetime before 1970, cannot format)"; 120 : } 121 : #endif 122 : 123 : // Cast to seconds to be independent of clock precision. 124 : // Sub-second digits are formatted manually. 125 17 : put_time(oss, 126 17 : Clock::to_time_t(Clock::time_point{ 127 17 : std::chrono::duration_cast<std::chrono::seconds>(duration)}), 128 : true); 129 : if constexpr (std::ratio_less_v<Period, std::ratio<1, 1>>) { 130 5 : oss << '.' << std::setw(num_digits<Period>()) << std::setfill('0') 131 5 : << (duration.count() % (Period::den / Period::num)); 132 : } 133 34 : return oss.str(); 134 17 : } 135 : } // namespace 136 : 137 20 : std::string to_iso_date(const scipp::core::time_point &item, 138 : const units::Unit &unit) { 139 20 : if (unit == units::ns) { 140 2 : return to_string(std::chrono::nanoseconds{item.time_since_epoch()}); 141 19 : } else if (unit == units::s) { 142 16 : return to_string(std::chrono::seconds{item.time_since_epoch()}); 143 11 : } else if (unit == units::us) { 144 4 : return to_string(std::chrono::microseconds{item.time_since_epoch()}); 145 9 : } else if (unit == units::Unit(llnl::units::precise::ms)) { 146 4 : return to_string(std::chrono::milliseconds{item.time_since_epoch()}); 147 7 : } else if (unit == units::Unit(llnl::units::precise::min)) { 148 4 : return to_string(std::chrono::minutes{item.time_since_epoch()}); 149 5 : } else if (unit == units::Unit(llnl::units::precise::hr)) { 150 4 : return to_string(std::chrono::hours{item.time_since_epoch()}); 151 3 : } else if (unit == units::Unit(llnl::units::precise::day) || 152 6 : unit == units::Unit("month") || unit == units::Unit("year")) { 153 0 : throw except::UnitError("Printing of time points with units greater than " 154 0 : "hours is not yet implemented."); 155 : } 156 3 : throw except::UnitError("Cannot display time point, unsupported unit: " + 157 6 : to_string(unit)); 158 : } 159 : } // namespace scipp::core