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