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 : :
25 : : #include <ctime>
26 : :
27 : : #include <xlnt/utils/date.hpp>
28 : : #include <detail/time_helpers.hpp>
29 : :
30 : : /// Invalid weekday for checking whether std::mktime was successful - see below.
31 : : /// Must be outside of the range [0, 6].
32 : : constexpr int INVALID_WDAY = -1;
33 : :
34 : : namespace xlnt {
35 : :
36 :CBC 87 : date::date(int year_, int month_, int day_)
37 : 87 : : year(year_), month(month_), day(day_), _is_null(false)
38 : : {
39 : 87 : }
40 : :
41 : 50 : date date::from_number(int days_since_base_year, calendar base_date)
42 : : {
43 : 50 : date result(0, 0, 0);
44 : :
45 [ + + ]: 50 : if (base_date == calendar::mac_1904)
46 : : {
47 : 1 : days_since_base_year += 1462;
48 : : }
49 : :
50 [ + + ]: 50 : if (days_since_base_year == 60)
51 : : {
52 : 1 : result.day = 29;
53 : 1 : result.month = 2;
54 : 1 : result.year = 1900;
55 : :
56 : 1 : return result;
57 : : }
58 [ + + ]: 49 : else if (days_since_base_year < 60)
59 : : {
60 : 17 : days_since_base_year++;
61 : : }
62 : :
63 : 49 : int l = days_since_base_year + 68569 + 2415019;
64 : 49 : int n = int((4 * l) / 146097);
65 : 49 : l = l - int((146097 * n + 3) / 4);
66 : 49 : int i = int((4000 * (l + 1)) / 1461001);
67 : 49 : l = l - int((1461 * i) / 4) + 31;
68 : 49 : int j = int((80 * l) / 2447);
69 : 49 : result.day = l - int((2447 * j) / 80);
70 : 49 : l = int(j / 11);
71 : 49 : result.month = j + 2 - (12 * l);
72 : 49 : result.year = 100 * (n - 49) + i + l;
73 : :
74 : 49 : return result;
75 : : }
76 : :
77 : 5 : bool date::operator==(const date &comparand) const
78 : : {
79 [ + - + - : 5 : return year == comparand.year && month == comparand.month && day == comparand.day && _is_null == comparand._is_null;
+ + + - ]
80 : : }
81 : :
82 : 1 : bool date::operator!=(const date &comparand) const
83 : : {
84 : 1 : return !(*this == comparand);
85 : : }
86 : :
87 : 27 : int date::to_number(calendar base_date) const
88 : : {
89 [ + + ]: 27 : if (_is_null)
90 : : {
91 [ + ]: 1 : throw xlnt::invalid_attribute("cannot convert invalid/empty date to a number");
92 : : }
93 : :
94 [ + + + + : 26 : if (day == 29 && month == 2 && year == 1900)
+ - ]
95 : : {
96 : 1 : return 60;
97 : : }
98 : :
99 : 25 : int days_since_1900 = int((1461 * (year + 4800 + int((month - 14) / 12))) / 4)
100 : 25 : + int((367 * (month - 2 - 12 * ((month - 14) / 12))) / 12)
101 : 25 : - int((3 * (int((year + 4900 + int((month - 14) / 12)) / 100))) / 4) + day - 2415019 - 32075;
102 : :
103 [ + + ]: 25 : if (days_since_1900 <= 60)
104 : : {
105 : 1 : days_since_1900--;
106 : : }
107 : :
108 [ + + ]: 25 : if (base_date == calendar::mac_1904)
109 : : {
110 : 1 : return days_since_1900 - 1462;
111 : : }
112 : :
113 : 24 : return days_since_1900;
114 : : }
115 : :
116 : 3 : date date::today()
117 : : {
118 : 3 : optional<std::tm> now = detail::localtime_safe(std::time(nullptr));
119 : :
120 [ + - ]: 3 : if (now.is_set())
121 : : {
122 [ + + + ]: 3 : return date(1900 + now.get().tm_year, now.get().tm_mon + 1, now.get().tm_mday);
123 : : }
124 : : else
125 : : {
126 :UBC 0 : return date();
127 : : }
128 :CBC 3 : }
129 : :
130 : 7 : int date::weekday() const
131 : : {
132 [ + + ]: 7 : if (!_is_null)
133 : : {
134 : 6 : std::tm tm = std::tm();
135 : 6 : tm.tm_wday = INVALID_WDAY;
136 : 6 : tm.tm_mday = day;
137 : 6 : tm.tm_mon = month - 1;
138 : 6 : tm.tm_year = year - 1900;
139 : :
140 : : // Important: if the conversion made by std::mktime is successful, the time object is modified. All fields of time are updated
141 : : // to fit their proper ranges. time->tm_wday and time->tm_yday are recalculated using information available in other fields.
142 : : // IMPORTANT: the return value -1 could either be an error or mean 1 second before 1970-1-1. However, an application wishing to check
143 : : // for error situations should set tm_wday to a value less than 0 or greater than 6 before calling mktime(). On return, if tm_wday has not changed an error has occurred.
144 : 6 : /*std::time_t time =*/std::mktime(&tm);
145 : :
146 [ + - ]: 6 : if (tm.tm_wday != INVALID_WDAY)
147 : : {
148 : 6 : return tm.tm_wday;
149 : : }
150 : : else
151 : : {
152 :UBC 0 : return -1;
153 : : }
154 : : }
155 : : else
156 : : {
157 :CBC 1 : return -1;
158 : : }
159 : : }
160 : :
161 : 51 : int date::get_year() const
162 : : {
163 [ + + ]: 51 : if (_is_null)
164 : : {
165 [ + ]: 1 : throw xlnt::invalid_attribute("access to invalid/empty year of xlnt::date");
166 : : }
167 : :
168 : :
169 : 50 : return year;
170 : : }
171 : :
172 : 51 : int date::get_month() const
173 : : {
174 [ + + ]: 51 : if (_is_null)
175 : : {
176 [ + ]: 1 : throw xlnt::invalid_attribute("access to invalid/empty month of xlnt::date");
177 : : }
178 : :
179 : 50 : return month;
180 : : }
181 : :
182 : 51 : int date::get_day() const
183 : : {
184 [ + + ]: 51 : if (_is_null)
185 : : {
186 [ + ]: 1 : throw xlnt::invalid_attribute("access to invalid/empty day of xlnt::date");
187 : : }
188 : :
189 : 50 : return day;
190 : : }
191 : :
192 : : } // namespace xlnt
|