1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2009 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef WTIME_H_
8 #define WTIME_H_
9 
10 #include <Wt/WDateTime.h>
11 #include <Wt/WString.h>
12 #include <Wt/WStringStream.h>
13 #include <exception>
14 
15 namespace Wt {
16 
17 /*! \class WTime Wt/WTime.h Wt/WTime.h
18  *  \brief A value class that defines a clock time.
19  *
20  * A clock time represents the time of day (usually 0 to 24 hour), up
21  * to millisecond precision.
22  *
23  * As of version %3.3.1, the time class itself will no longer limit times
24  * to the 24-hour range, but will allow any time (duration), including negative
25  * values.
26  *
27  * \sa WDate, WDateTime
28  */
29 class WT_API WTime
30 {
31 public:
32   /*! \brief Construct a <i>Null</i> time.
33    *
34    * A time for which isNull() returns true. A <i>Null</i> time is also
35    * invalid.
36    *
37    * \sa isValid(), isNull()
38    */
39   WTime();
40 
41   /*! \brief Construct a time given hour, minutes, seconds, and milliseconds.
42    *
43    * \p m and \p s have range 0-59, and \p ms has range 0-999. A duration can
44    * be positive or negative depending on the sign of \p h.
45    *
46    * When the time is invalid, isValid() is set to \c false.
47    */
48   WTime(int h, int m, int s = 0, int ms = 0);
49 
50   /*! \brief Sets the time.
51    *
52    * \p m and \p s have range 0-59, and \p ms has range 0-999.
53    *
54    * When the time is invalid, isValid() is set to \c false.
55    */
56   bool setHMS(int h, int m, int s, int ms = 0);
57 
58   /*! \brief Adds seconds.
59    *
60    * Returns a time that is \p s seconds later than this time. Negative
61    * values for \p s will result in a time that is as many seconds
62    * earlier.
63    */
64   WTime addSecs(int s) const;
65 
66   /*! \brief Adds milliseconds.
67    *
68    * Returns a time that is \p ms milliseconds later than this
69    * time. Negative values for \p ms will result in a time that
70    * is as many milliseconds earlier.
71    */
72   WTime addMSecs(int ms) const;
73 
74   /*! \brief Returns if this time is <i>Null</i>.
75    *
76    * A null time is also invalid.
77    *
78    * \sa isValid(), WTime()
79    */
isNull()80   bool isNull() const { return null_; }
81 
82   /*! \brief Returns if this time is valid.
83    */
isValid()84   bool isValid() const { return valid_; }
85 
86   /*! \brief Returns the hour.
87    */
88   int hour() const;
89 
90   /*! \brief Returns the minutes (0-59).
91    */
92   int minute() const;
93 
94   /*! \brief Returns the seconds (0-59).
95    */
96   int second() const;
97 
98   /*! \brief Returns the milliseconds (0-999)
99    */
100   int msec() const;
101 
102   /*! \brief Returns the difference between two time values (in seconds).
103    *
104    * The result is negative if t is earlier than this.
105    */
106   long secsTo(const WTime& t) const;
107 
108   /*! \brief Returns the difference between two time values (in milliseconds).
109    *
110    * The result is negative if t is earlier than this.
111    */
112   long msecsTo(const WTime& t) const;
113 
114   /*! \brief Compares two time values.
115    */
116   bool operator< (const WTime& other) const;
117 
118   /*! \brief Compares two time values.
119    */
120   bool operator<= (const WTime& other) const;
121 
122   /*! \brief Compares two time values.
123    */
124   bool operator> (const WTime& other) const;
125 
126   /*! \brief Compares two time values.
127    */
128   bool operator>= (const WTime& other) const;
129 
130   /*! \brief Compares two time values.
131    */
132   bool operator== (const WTime& other) const;
133 
134   /*! \brief Compares two time values.
135    */
136   bool operator!= (const WTime& other) const;
137 
138   static WT_USTRING defaultFormat();
139 
140   /*! \brief Formats this time to a string using a default format.
141    *
142    * The default format is "hh:mm:ss".
143    */
144   WT_USTRING toString() const;
145 
146   /*! \brief Formats this time to a string using a specified format.
147    *
148    * The \p format is a string in which the following contents has
149    * a special meaning.
150    *
151    * <table>
152    *  <tr><td><b>Code</b></td><td><b>Meaning</b></td>
153    *      <td><b>Example (for 14:06:23.045)</b></td></tr>
154    *  <tr><td>h</td><td>The hour without leading zero (0-23 or 1-12 for AM/PM display)</td>
155           <td>14 or 2</td></tr>
156    *  <tr><td>hh</td><td>The hour with leading zero (00-23 or 01-12 for AM/PM display)</td>
157           <td>14 or 02</td></tr>
158    *  <tr><td>H</td><td>The hour without leading zero (0-23)</td>
159           <td>14</td></tr>
160    *  <tr><td>HH</td><td>The hour with leading zero (00-23)</td>
161           <td>14</td></tr>
162    *  <tr><td>+ followed by (h/hh/H/HH)</td><td>The sign of the hour (+/-)</td>
163           <td>+</td></tr>
164    *  <tr><td>m</td><td>The minutes without leading zero (0-59)</td>
165           <td>6</td></tr>
166    *  <tr><td>mm</td><td>The minutes with leading zero (00-59)</td>
167           <td>06</td></tr>
168    *  <tr><td>s</td><td>The seconds without leading zero (0-59)</td>
169           <td>23</td></tr>
170    *  <tr><td>ss</td><td>The seconds with leading zero (00-59)</td>
171           <td>23</td></tr>
172    *  <tr><td>z</td><td>The milliseconds without leading zero (0-999)</td>
173           <td>45</td></tr>
174    *  <tr><td>zzz</td><td>The millisecons with leading zero (000-999)</td>
175           <td>045</td></tr>
176    *  <tr><td>AP or A</td><td>use AM/PM display: affects h or hh display and is replaced itself by AM/PM</td>
177           <td>PM</td></tr>
178    *  <tr><td>ap or a</td><td>use am/pm display: affects h or hh display and is replaced itself by am/pm</td>
179           <td>pm</td></tr>
180    *  <tr><td>Z</td><td>the timezone in RFC 822 format (e.g. -0800)</td>
181           <td>+0000</td></tr>
182    * </table>
183    *
184    * Any other text is kept literally. String content between single
185    * quotes (') are not interpreted as special codes. LabelOption::Inside a string, a literal
186    * quote may be specifed using a double quote ('').
187    *
188    * Examples of format and result:
189    * <table>
190    *  <tr><td><b>Format</b></td><td><b>Result (for 22:53:13.078)</b></td></tr>
191    *  <tr><td>hh:mm:ss.zzz</td><td>22:53:13.078</td></tr>
192    *  <tr><td>hh:mm:ss AP</td><td>10:53:13 PM</td></tr>
193    * </table>
194    *
195    * \sa fromString(const WString& value, const WString& format)
196    */
197   WT_USTRING toString(const WT_USTRING& format) const;
198 
199   /*! \brief Parses a string to a time using a default format.
200    *
201    * The default format is "hh:mm:ss".
202    * For example, a time specified as:
203    * \code
204    *   "22:55:15"
205    * \endcode
206    * will be parsed as a time that equals a time constructed as:
207    * \code
208    *   WTime d(22,55,15);
209    * \endcode
210    *
211    * When the time could not be parsed or is not valid, an invalid
212    * time is returned (for which isValid() returns false).
213    *
214    * \sa fromString(const WString& s, const WString& format), isValid()
215    */
216   static WTime fromString(const WT_USTRING& s);
217 
218   /*! \brief Parses a string to a time using a specified format.
219    *
220    * The \p format follows the same syntax as used by
221    * \link toString(const WString& format) const toString(const WString& format)\endlink.
222    *
223    * When the time could not be parsed or is not valid, an invalid
224    * time is returned (for which isValid() returns false).
225    *
226    * \sa toString(const WString&) const
227    */
228   static WTime fromString(const WT_USTRING& s, const WT_USTRING& format);
229 
230   /*! \brief Reports the current client date.
231    *
232    * This method uses browser information to retrieve the time that is
233    * configured in the client.
234    *
235    * \sa WLocalDateTime::currentDate()
236    */
237   static WTime currentTime();
238 
239   /*! \brief Reports the current server time.
240    *
241    * This method returns the local time on the server.
242    *
243    * \sa WDateTime::currentDateTime(), WLocalDateTime::currentServerDateTime()
244    */
245   static WTime currentServerTime();
246 
247   struct RegExpInfo {
248     std::string regexp;
249     std::string hourGetJS;
250     std::string minuteGetJS;
251     std::string secGetJS;
252     std::string msecGetJS;
253   };
254 
255   static RegExpInfo formatToRegExp(const WT_USTRING& format);
256 
257 #ifndef WT_TARGET_JAVA
258   std::chrono::duration<int, std::milli> toTimeDuration() const;
259   static WTime fromTimeDuration(const std::chrono::duration<int, std::milli>& duration);
260 #else
261   std::chrono::milliseconds toTimeDuration() const;
262   static WTime fromTimeDuration(const std::chrono::milliseconds& duration);
263 #endif
264 
265 private:
266   bool valid_, null_;
267   long time_;
268 
269   WTime (long time);
270 
271   struct ParseState {
272     int h, m, s, z, a;
273     int hour, minute, sec, msec;
274     bool pm, parseAMPM, haveAMPM;
275 
276     ParseState();
277   };
278 
279   static bool parseLast(const std::string& v, unsigned& vi,
280 			ParseState& parse, const WString& format);
281 
282   static WDateTime::CharState handleSpecial(char c, const std::string& v,
283 					    unsigned& vi, ParseState& parse,
284 					    const WString& format);
285 
286   bool writeSpecial(const std::string& f, unsigned& i, WStringStream& result,
287 		    bool useAMPM, int zoneOffset) const;
288 
289   int pmhour() const;
290 
291   static RegExpInfo formatHourToRegExp(RegExpInfo &result, const std::string& format, unsigned& i, int& currentGroup);
292   static RegExpInfo formatMinuteToRegExp(RegExpInfo &result, const std::string& format, unsigned& i, int& currentGroup);
293   static RegExpInfo formatSecondToRegExp(RegExpInfo &result, const std::string& format, unsigned& i, int& currentGroup);
294   static RegExpInfo formatMSecondToRegExp(RegExpInfo &result, const std::string& format, unsigned& i, int& currentGroup);
295   static RegExpInfo formatAPToRegExp(RegExpInfo &result, const std::string& format, unsigned& i);
296   static RegExpInfo processChar(RegExpInfo &result, const std::string& format, unsigned& i);
297   static bool usesAmPm(const WString& format);
298 
299   friend class WDateTime;
300   friend class WTimePicker;
301 };
302 
303 }
304 
305 #endif // WTIME_H_
306