Branch data TLA Line data Source code
1 : : // Copyright (c) 2014-2022 Thomas Fussell
2 : : // Copyright (c) 2024-2025 xlnt-community
3 : : //
4 : : // Permission is hereby granted, free of charge, to any person obtaining a copy
5 : : // of this software and associated documentation files (the "Software"), to deal
6 : : // in the Software without restriction, including without limitation the rights
7 : : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : : // copies of the Software, and to permit persons to whom the Software is
9 : : // furnished to do so, subject to the following conditions:
10 : : //
11 : : // The above copyright notice and this permission notice shall be included in
12 : : // all copies or substantial portions of the Software.
13 : : //
14 : : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : : // THE SOFTWARE
21 : : //
22 : : // @license: http://www.opensource.org/licenses/mit-license.php
23 : : // @author: see AUTHORS file
24 : : #include <cmath>
25 : : #include <cstdint>
26 : : #include <ctime>
27 : :
28 : : #include <xlnt/utils/time.hpp>
29 : : #include <detail/time_helpers.hpp>
30 : : #include <detail/serialization/parsers.hpp>
31 : :
32 : : namespace xlnt {
33 : :
34 :CBC 50 : time time::from_number(double raw_time)
35 : : {
36 : 50 : time result;
37 : :
38 : : double integer_part;
39 : 50 : double fractional_part = std::modf(static_cast<double>(raw_time), &integer_part);
40 : :
41 : 50 : fractional_part *= 24;
42 : 50 : result.hour = static_cast<int>(fractional_part);
43 : 50 : fractional_part = 60 * (fractional_part - result.hour);
44 : 50 : result.minute = static_cast<int>(fractional_part);
45 : 50 : fractional_part = 60 * (fractional_part - result.minute);
46 : 50 : result.second = static_cast<int>(fractional_part);
47 : 50 : fractional_part = 1000000 * (fractional_part - result.second);
48 : 50 : result.microsecond = static_cast<int>(fractional_part);
49 : :
50 [ + + + - ]: 50 : if (result.microsecond == 999999 && fractional_part - result.microsecond > 0.5)
51 : : {
52 : 12 : result.microsecond = 0;
53 : 12 : result.second += 1;
54 : :
55 [ + + ]: 12 : if (result.second == 60)
56 : : {
57 : 1 : result.second = 0;
58 : 1 : result.minute += 1;
59 : :
60 : : // TODO: too much nesting
61 [ + - ]: 1 : if (result.minute == 60)
62 : : {
63 : 1 : result.minute = 0;
64 : 1 : result.hour += 1;
65 : : }
66 : : }
67 : : }
68 : :
69 : 50 : return result;
70 : : }
71 : :
72 : 92 : time::time(int hour_, int minute_, int second_, int microsecond_)
73 : 92 : : hour(hour_), minute(minute_), second(second_), microsecond(microsecond_)
74 : : {
75 : 92 : }
76 : :
77 : 3 : bool time::operator==(const time &comparand) const
78 : : {
79 [ + - + - ]: 3 : return hour == comparand.hour && minute == comparand.minute && second == comparand.second
80 [ + - + - ]: 6 : && microsecond == comparand.microsecond;
81 : : }
82 : :
83 :UBC 0 : bool time::operator!=(const time &comparand) const
84 : : {
85 : 0 : return !(*this == comparand);
86 : : }
87 : :
88 :CBC 8 : time::time(const std::string &time_string)
89 : : {
90 : 8 : bool ok = true;
91 : 8 : auto next_separator_index = time_string.find(':');
92 : 8 : next_separator_index = time_string.find(':');
93 [ + - + + : 8 : ok = ok && detail::parse(time_string.substr(0, next_separator_index), hour) == std::errc();
+ + + - -
- ]
94 : 8 : auto previous_separator_index = next_separator_index;
95 [ + + ]: 8 : next_separator_index = ok ? time_string.find(':', previous_separator_index + 1) : next_separator_index;
96 [ + + + + : 8 : ok = ok && detail::parse(time_string.substr(previous_separator_index + 1, next_separator_index), minute) == std::errc();
+ - + + -
- ]
97 : 8 : previous_separator_index = next_separator_index;
98 [ + + ]: 8 : next_separator_index = ok ? time_string.find('.', previous_separator_index + 1) : next_separator_index;
99 : 8 : bool subseconds_available = next_separator_index != std::string::npos;
100 [ + + ]: 8 : if (subseconds_available)
101 : : {
102 : : // First parse the seconds.
103 [ + - + + : 6 : ok = ok && detail::parse(time_string.substr(previous_separator_index + 1, next_separator_index), second) == std::errc();
+ - + - -
- ]
104 : 6 : previous_separator_index = next_separator_index;
105 : : }
106 [ + + ]: 8 : next_separator_index = ok ? std::string::npos : next_separator_index;
107 : 8 : size_t num_characters_parsed = 0;
108 [ + + + + : 8 : ok = ok && detail::parse(time_string.substr(previous_separator_index + 1, next_separator_index), subseconds_available ? microsecond : second, &num_characters_parsed) == std::errc();
+ + + - +
+ - - ]
109 : :
110 [ + + ]: 8 : if (subseconds_available)
111 : : {
112 : 6 : constexpr size_t expected_digits = 6; // microseconds have 6 digits
113 : 6 : size_t actual_digits = num_characters_parsed;
114 : :
115 [ + + ]: 12 : while (actual_digits > expected_digits)
116 : : {
117 : 6 : microsecond /= 10;
118 : 6 : --actual_digits;
119 : : }
120 : :
121 [ + + ]: 12 : while (actual_digits < expected_digits)
122 : : {
123 : 6 : microsecond *= 10;
124 : 6 : ++actual_digits;
125 : : }
126 : : }
127 : :
128 [ + + ]: 8 : if (!ok)
129 : : {
130 [ + ]: 1 : throw xlnt::invalid_parameter("invalid ISO time");
131 : : }
132 : 7 : }
133 : :
134 : 26 : double time::to_number() const
135 : : {
136 : 26 : std::uint64_t microseconds = static_cast<std::uint64_t>(microsecond);
137 : 26 : microseconds += static_cast<std::uint64_t>(second * 1e6);
138 : 26 : microseconds += static_cast<std::uint64_t>(minute * 1e6 * 60);
139 : 26 : auto microseconds_per_hour = static_cast<std::uint64_t>(1e6) * 60 * 60;
140 : 26 : microseconds += static_cast<std::uint64_t>(hour) * microseconds_per_hour;
141 : 26 : auto number = static_cast<double>(microseconds) / (24.0 * static_cast<double>(microseconds_per_hour));
142 : 26 : number = std::floor(number * 100e9 + 0.5) / 100e9;
143 : :
144 : 26 : return number;
145 : : }
146 : :
147 :UBC 0 : time time::now()
148 : : {
149 : 0 : optional<std::tm> now = detail::localtime_safe(std::time(nullptr));
150 : :
151 [ # # ]: 0 : if (now.is_set())
152 : : {
153 [ # # # ]: 0 : return time(now.get().tm_hour, now.get().tm_min, now.get().tm_sec);
154 : : }
155 : : else
156 : : {
157 : 0 : return time();
158 : : }
159 : 0 : }
160 : :
161 : : } // namespace xlnt
|