Branch data Line data Source code
1 : : /********************************************************************\
2 : : * gnc-datetime.cpp -- Date and Time classes for GnuCash *
3 : : * *
4 : : * Copyright 2015 John Ralls <jralls@ceridwen.us> *
5 : : * *
6 : : * This program is free software; you can redistribute it and/or *
7 : : * modify it under the terms of the GNU General Public License as *
8 : : * published by the Free Software Foundation; either version 2 of *
9 : : * the License, or (at your option) any later version. *
10 : : * *
11 : : * This program is distributed in the hope that it will be useful, *
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 : : * GNU General Public License for more details. *
15 : : * *
16 : : * You should have received a copy of the GNU General Public License*
17 : : * along with this program; if not, contact: *
18 : : * *
19 : : * Free Software Foundation Voice: +1-617-542-5942 *
20 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
22 : : * *
23 : : \********************************************************************/
24 : :
25 : : #ifndef __GNC_DATETIME_HPP__
26 : : #define __GNC_DATETIME_HPP__
27 : :
28 : : #include <cstdint>
29 : : #include <memory>
30 : : #include <string>
31 : : #include <vector>
32 : : #include <functional>
33 : : #include <optional>
34 : :
35 : : #include <boost/date_time/gregorian/gregorian.hpp>
36 : :
37 : : typedef struct
38 : : {
39 : : int year; //1400-9999
40 : : int month; //1-12
41 : : int day; //1-31
42 : : } gnc_ymd;
43 : :
44 : : enum class DayPart {
45 : : start, // 00:00 local
46 : : neutral, // 10:59 UTC
47 : : end, // 23:59 local
48 : : };
49 : :
50 : : class GncDateTimeImpl;
51 : : class GncDateImpl;
52 : : class GncDate;
53 : : using time64 = int64_t;
54 : : constexpr const time64 MINTIME = -17987443200;
55 : : constexpr const time64 MAXTIME = 253402214400;
56 : :
57 : : /** GnuCash DateTime class
58 : : *
59 : : * Represents local time in the current timezone.
60 : : * As with GncDate, the represented time is limited to the period
61 : : * between 1400 and 9999 CE.
62 : : *
63 : : * Be careful when using times: A particular time is represented
64 : : * differently depending on the timezone, which can shift the displayed
65 : : * date. Accounting is generally not sensitive to the time of day, but
66 : : * is sensitive to the recorded day. Since GncDates are not timezone
67 : : * dependent they should be preferred for accounting entries.
68 : : */
69 : :
70 : : class GncDateTime
71 : : {
72 : : public:
73 : : /** Construct a GncDateTime representing the current time in the
74 : : * current timezone.
75 : : */
76 : : GncDateTime();
77 : : /** Construct a GncDateTime in the current timezone representing the
78 : : * timestamp as seconds from the POSIX epoch (1970-01-01T00:00:00UTC).
79 : : * @param time Seconds from the POSIX epoch.
80 : : * @exception std::invalid_argument if the year is outside the constraints.
81 : : */
82 : : GncDateTime(const time64 time);
83 : : /** Construct a GncDateTime in the current timezone representing the
84 : : * standard struct tm provided.
85 : : * @param tm A C-standard struct tm representing the date and
86 : : * time. Note that the timezone and offset are ignored on those
87 : : * systems which include them in struct tm.
88 : : * @exception std::invalid_argument if the year is outside the constraints.
89 : : */
90 : : GncDateTime(const struct tm tm);
91 : : /** Construct a GncDateTime from a GncDate. As a GncDate doesn't contain time
92 : : * information, the time will be set depending on the second parameter
93 : : * to start of day, neutral or end of day.
94 : : * @param date A GncDate representing a date.
95 : : * @param part An optinoal DayPart indicating which time to use in the conversion.
96 : : * This can be "DayPart::start" for start of day (00:00 local time),
97 : : * "DayPart::neutral" for a neutral time (10:59 UTC, chosen to have the
98 : : * least chance of date changes when crossing timezone borders),
99 : : * "DayPart::end" for end of day (23:59 local time).
100 : : * If omitted part defaults to DayPart::neutral.
101 : : * Note the different timezone used for DayPart::neutral compared to the other
102 : : * two options!
103 : : * @exception std::invalid_argument if the year is outside the constraints.
104 : : */
105 : : GncDateTime(const GncDate& date, DayPart part = DayPart::neutral);
106 : : /** Construct a GncDateTime
107 : : * @param str A string representing the date and time in some
108 : : * recognizable format. Note that if a timezone is not specified the
109 : : * default is UTC, not the local one.
110 : : * @exception std::invalid_argument if the year is outside the constraints.
111 : : */
112 : : GncDateTime(const std::string& str);
113 : : GncDateTime(const char* str);
114 : : ~GncDateTime();
115 : : /** Set the GncDateTime to the date and time indicated in the computer's clock.
116 : : */
117 : : void now();
118 : : /** Cast the GncDateTime to a time64, seconds from the POSIX epoch. */
119 : : explicit operator time64() const;
120 : : /** Cast the GncDateTime to a struct tm. Timezone field isn't filled.
121 : : */
122 : : explicit operator struct tm() const;
123 : : /** Obtain the UTC offset in seconds
124 : : * @return seconds difference between this local time and UTC. West
125 : : * is negative.
126 : : */
127 : : long offset()const;
128 : : /** Obtain a struct tm representing the time in UTC.
129 : : * @return struct tm
130 : : */
131 : : struct tm utc_tm() const;
132 : : /** Obtain the date from the time, as a GncDate, in the current timezone.
133 : : * @return GncDate represented by the GncDateTime.
134 : : */
135 : : GncDate date() const;
136 : : /** Test if the GncDateTime has a member pointer. Testing only. */
137 : : bool isnull (void) { return m_impl == nullptr; }
138 : : /** Format the GncDateTime into a std::string
139 : : * @param format A cstr describing the way the date and time are
140 : : * presented. Code letters preceded with % stand in for arguments;
141 : : * most are the same as described in strftime(3), but there are a few
142 : : * differences. Consult the boost::date_time documentation.
143 : : * @return a std::string containing a representation of the date and time in
144 : : * the locale's time zone according to the format.
145 : : */
146 : : std::string format(const char* format) const;
147 : : /** Format the GncDateTime into a std::string in GMT
148 : : * @param format A cstr describing the way the date and time are
149 : : * presented. Code letters preceded with % stand in for arguments;
150 : : * most are the same as described in strftime(3), but there are a few
151 : : * differences. Consult the boost::date_time documentation.
152 : : * @return a std::string containing a representation of the date and time in
153 : : * GMT (timezone Z) according to the format.
154 : : */
155 : : std::string format_zulu(const char* format) const;
156 : : /** Format the GncDateTime into a gnucash-style iso8601 string in UTC.
157 : : * @return a std::string in the format YYYY-MM-DD HH:MM:SS.
158 : : */
159 : : std::string format_iso8601() const;
160 : : /** Get an undelimited string representing the current date and time.
161 : : * @return a std::string in the format YYYYMMDDHHMMSS.
162 : : */
163 : : static std::string timestamp();
164 : :
165 : : private:
166 : : std::unique_ptr<GncDateTimeImpl> m_impl;
167 : : };
168 : :
169 : : /** GnuCash DateFormat class
170 : : *
171 : : * A helper class to represent a date format understood
172 : : * by the GncDate string/format constructor. Consumers
173 : : * of this header file are not supposed to create
174 : : * objects of this class themselves. Instead they
175 : : * can get a list of the understood formats from the
176 : : * GncDate::c_formats class variable and work with those.
177 : : */
178 : :
179 : : using StringToDate = std::function<boost::gregorian::date(const std::string&)>;
180 : :
181 : : class GncDateFormat
182 : : {
183 : : public:
184 : : /** Construct a GncDateFormat with a given format and corresponding
185 : : * regular expression. This should only be used internally by the
186 : : * GncDate implementation. Consumers should never construct a GncDateFormat
187 : : * themselves!
188 : : */
189 : 230 : GncDateFormat (const char* fmt, const char* re) :
190 : 1150 : m_fmt(fmt), m_re(re) {}
191 : 345 : GncDateFormat (const char* fmt, StringToDate str_to_date, const char* re) :
192 : 1725 : m_fmt(fmt), m_re(re), m_str_to_date(str_to_date) {}
193 : 115 : GncDateFormat (const char* fmt, StringToDate str_to_date) :
194 : 345 : m_fmt(fmt), m_str_to_date(str_to_date) {}
195 : : /** A string representing the format. */
196 : : const std::string m_fmt;
197 : : private:
198 : : /** Regular expression associated with the format string. This is to and
199 : : * only be used internally by the gnc-datetime code.
200 : : */
201 : : const std::string m_re;
202 : : std::optional<StringToDate> m_str_to_date;
203 : :
204 : : friend class GncDateImpl;
205 : : };
206 : :
207 : : /** GnuCash Date class
208 : : *
209 : : * The represented date is limited to the period
210 : : * between 1400 and 9999 CE.
211 : : */
212 : :
213 : : class GncDate
214 : : {
215 : : public:
216 : : /** A vector with all the date formats supported by the string constructor.
217 : : * The currently supported formats are:
218 : : * "y-m-d" (including yyyymmdd)
219 : : * "d-m-y" (including ddmmyyyy)
220 : : * "m-d-y" (including mmddyyyy)
221 : : * "d-m" (including ddmm)
222 : : * "m-d" (including mmdd)
223 : : *
224 : : * Notes:
225 : : * - while the format names are using a "-" as separator, the
226 : : * regexes will accept any of "-/.' " and will also work for dates
227 : : * without separators.
228 : : * - the format strings are marked for translation so it is possible
229 : : * to use a localized version of a format string using gettext. Example:
230 : : * gettext(GncDate::c_formats[0])
231 : : */
232 : : static const std::vector<GncDateFormat> c_formats;
233 : : /** Construct a GncDate representing the current day.
234 : : */
235 : : GncDate();
236 : : /** Construct a GncDate representing the given year, month, and day in
237 : : * the proleptic Gregorian calendar.
238 : : *
239 : : * Years are constrained to be from 1400 - 9999 CE inclusive. Dates
240 : : * will be normalized if the day or month values are outside of the
241 : : * normal ranges. e.g. 1994, -3, 47 will be normalized to 1993-10-17.
242 : : *
243 : : * @param year The year in the Common Era.
244 : : * @param month The month, where 1 is January and 12 is December.
245 : : * @param day The day of the month, beginning with 1.
246 : : * @exception std::invalid_argument if the calculated year is outside
247 : : * of the constrained range.
248 : : */
249 : : GncDate(int year, int month, int day);
250 : : /** Construct a GncDate by parsing a string assumed to be in the format
251 : : * passed in.
252 : : *
253 : : * The currently recognized formats are d-m-y, m-d-y, y-m-d, m-d, d-m.
254 : : * Note while the format descriptions use "-" as separator any of
255 : : * "-" (hyphen), "/" (slash), "'" (single quote), " " (space) or
256 : : * "." will be accepted.
257 : : *
258 : : * @param str The string to be interpreted.
259 : : * @param fmt The expected date format of the string passed in.
260 : : * @exception std::invalid_argument if
261 : : * - the string couldn't be parsed using the provided format
262 : : * - any of the date components is outside of its limit
263 : : * (like month being 13, or day being 31 in February)
264 : : * - fmt doesn't specify a year, yet a year was found in the string
265 : : */
266 : : GncDate(const std::string str, const std::string fmt);
267 : : /** Construct a GncDate from a GncDateImpl.
268 : : */
269 : : GncDate(std::unique_ptr<GncDateImpl> impl);
270 : : /** Copy constructor.
271 : : */
272 : : GncDate(const GncDate&);
273 : : /** Move constructor.
274 : : */
275 : : GncDate(GncDate&&);
276 : : /** Default destructor.
277 : : */
278 : : ~GncDate();
279 : : /** Copy assignment operator.
280 : : */
281 : : GncDate& operator=(const GncDate&);
282 : : /** Move assignment operator.
283 : : */
284 : : GncDate& operator=(GncDate&&);
285 : : /** Set the date object to the computer clock's current day. */
286 : : void today();
287 : : /** Get the year, month, and day from the date as a gnc_ymd.
288 : : * @return gnc_ymd struct
289 : : */
290 : : gnc_ymd year_month_day() const;
291 : : /** Format the GncDate into a std::string
292 : : * @param format A cstr describing the way the date and time are
293 : : * presented. Code letters preceded with % stand in for arguments;
294 : : * most are the same as described in strftime(3), but there are a few
295 : : * differences. Consult the boost::date_time documentation.
296 : : * @return a std::string containing a representation of the date
297 : : * according to the format.
298 : : */
299 : : std::string format(const char* format);
300 : : /** Test that the Date has an implementation. */
301 : : bool isnull (void) { return m_impl == nullptr; }
302 : :
303 : : private:
304 : : std::unique_ptr<GncDateImpl> m_impl;
305 : :
306 : : friend GncDateTime::GncDateTime(const GncDate&, DayPart);
307 : : friend bool operator<(const GncDate&, const GncDate&);
308 : : friend bool operator>(const GncDate&, const GncDate&);
309 : : friend bool operator==(const GncDate&, const GncDate&);
310 : : friend bool operator<=(const GncDate&, const GncDate&);
311 : : friend bool operator>=(const GncDate&, const GncDate&);
312 : : friend bool operator!=(const GncDate&, const GncDate&);
313 : : };
314 : :
315 : : /**@{
316 : : * Standard comparison operators working on GncDate objects.
317 : : */
318 : : bool operator<(const GncDate& a, const GncDate& b);
319 : : bool operator>(const GncDate& a, const GncDate& b);
320 : : bool operator==(const GncDate& a, const GncDate& b);
321 : : bool operator<=(const GncDate& a, const GncDate& b);
322 : : bool operator>=(const GncDate& a, const GncDate& b);
323 : : bool operator!=(const GncDate& a, const GncDate& b);
324 : : /**@}*/
325 : :
326 : : #endif // __GNC_DATETIME_HPP__
|