Engauge Digitizer  2
 All Classes Files Functions Variables Enumerations Enumerator Friends Pages
FormatDateTime.cpp
1 #include "EngaugeAssert.h"
2 #include "FormatDateTime.h"
3 #include "Logger.h"
4 
6 {
7  loadFormatsFormat();
8  loadFormatsParseAcceptable();
9  loadFormatsParseIncomplete();
10 }
11 
12 bool FormatDateTime::ambiguityBetweenDateAndTime (CoordUnitsDate coordUnitsDate,
13  CoordUnitsTime coordUnitsTime,
14  const QString &string) const
15 {
16  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::ambiguityBetweenDateAndTime";
17 
18  bool ambiguous = false;
19 
20  // There is no ambiguity if user specified either date or time as empty
21  if (coordUnitsDate != COORD_UNITS_DATE_SKIP &&
22  coordUnitsTime != COORD_UNITS_TIME_SKIP) {
23 
24  // See if there is just a single number
25  QStringList fields = string.trimmed().split(QRegExp ("[/- :]"));
26 
27  if (fields.count() == 1) {
28 
29  // There is a single number. Since there are no attached delimiters to differentiate a date versus
30  // a time, this means the number is ambiguous
31  ambiguous = true;
32  }
33  }
34 
35  return ambiguous;
36 }
37 
38 void FormatDateTime::dateTimeLookup (const FormatsDate &formatsDateAll,
39  const FormatsTime &formatsTimeAll,
40  CoordUnitsDate coordUnitsDate,
41  CoordUnitsTime coordUnitsTime,
42  const QString &string,
43  bool useQDateTimeElseQRegExp,
44  double &value,
45  bool &success) const
46 {
47  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::dateTimeLookup";
48 
49  success = false;
50 
51  ENGAUGE_ASSERT (formatsDateAll.contains (coordUnitsDate));
52  ENGAUGE_ASSERT (formatsTimeAll.contains (coordUnitsTime));
53 
54  QStringList formatsDate = formatsDateAll [coordUnitsDate];
55  QStringList formatsTime = formatsTimeAll [coordUnitsTime];
56 
57  // Loop through the legal date/time combinations
58  QStringList::const_iterator itrDate, itrTime;
59  bool iterating = true;
60  for (itrDate = formatsDate.begin(); itrDate != formatsDate.end() && iterating; itrDate++) {
61 
62  QString formatDate = *itrDate;
63 
64  for (itrTime = formatsTime.begin(); itrTime != formatsTime.end() && iterating; itrTime++) {
65 
66  QString formatTime = *itrTime;
67 
68  // Insert space as separator only if needed. Do not use trim around here since formatDate may or may not end in a space
69  QString separator = (!formatDate.isEmpty() && !formatTime.isEmpty() ? " " : "");
70 
71  QString formatDateTime = formatDate + separator + formatTime;
72 
73  if (!formatDateTime.isEmpty()) {
74 
75  // Try parsing according to the current format
76  if (useQDateTimeElseQRegExp) {
77 
78  QDateTime dt = QDateTime::fromString (string,
79  formatDateTime);
80 
81  if (dt.isValid() && !ambiguityBetweenDateAndTime (coordUnitsDate,
82  coordUnitsTime,
83  string)) {
84 
85  success = true;
86  value = dt.toTime_t();
87  iterating = false; // Stop iterating
88 
89  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::dateTimeLookup"
90  << " string=" << string.toLatin1().data()
91  << " qDateTimeFormatMatched=" << formatDateTime.toLatin1().data()
92  << " value=" << value
93  << " stringQDateTime=" << dt.toString().toLatin1().data();
94 
95  }
96  } else {
97 
98  QRegExp reg (formatDateTime);
99  if (reg.exactMatch(string)) {
100 
101  success = true; // Note that value does not get set in QRegExp case
102  iterating = false; // Stop iterating
103 
104  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::dateTimeLookup"
105  << " string=" << string.toLatin1().data()
106  << " regExpMatched=" << formatDateTime.toLatin1().data();
107 
108  }
109  }
110  }
111  }
112  }
113 }
114 
115 QString FormatDateTime::formatOutput (CoordUnitsDate coordUnitsDate,
116  CoordUnitsTime coordUnitsTime,
117  double value) const
118 {
119  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::formatOutput"
120  << " value=" << value;
121 
122  ENGAUGE_ASSERT (m_formatsDateFormat.contains (coordUnitsDate));
123  ENGAUGE_ASSERT (m_formatsTimeFormat.contains (coordUnitsTime));
124 
125  QString format = m_formatsDateFormat [coordUnitsDate] + " " + m_formatsTimeFormat [coordUnitsTime];
126  format = format.trimmed();
127 
128  QDateTime dt = QDateTime::fromTime_t (value);
129 
130  return dt.toString (format);
131 }
132 
133 void FormatDateTime::loadFormatsFormat()
134 {
135  m_formatsDateFormat [COORD_UNITS_DATE_SKIP] = "";
136  m_formatsDateFormat [COORD_UNITS_DATE_MONTH_DAY_YEAR] = "MM/dd/yyyy";
137  m_formatsDateFormat [COORD_UNITS_DATE_DAY_MONTH_YEAR] = "dd/MM/yyyy";
138  m_formatsDateFormat [COORD_UNITS_DATE_YEAR_MONTH_DAY] = "yyyy/MM/dd";
139 
140  ENGAUGE_ASSERT (m_formatsDateFormat.count () == NUM_COORD_UNITS_DATE);
141 
142  m_formatsTimeFormat [COORD_UNITS_TIME_SKIP] = "";
143  m_formatsTimeFormat [COORD_UNITS_TIME_HOUR_MINUTE] = "hh/mm";
144  m_formatsTimeFormat [COORD_UNITS_TIME_HOUR_MINUTE_SECOND] = "hh:mm:ss";
145 
146  ENGAUGE_ASSERT (m_formatsTimeFormat.count () == NUM_COORD_UNITS_TIME);
147 }
148 
149 void FormatDateTime::loadFormatsParseAcceptable()
150 {
151  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::loadFormatsParseAcceptable";
152 
153  QStringList skip, dayMonth, dayMonthYear, monthDay, monthDayYear, yearMonth, yearMonthDay;
154 
155  // COORD_UNITS_DATE_SKIP and COORD_UNITS_TIME_SKIP allow date/time respectively even when skipped,
156  // although there can be ambiguity with between COORD_UNITS_DATE_MONTH_DAY_YEAR and COORD_UNITS_DATE_DAY_MONTH_YEAR
157  skip << "";
158 
159  dayMonth << "d/M"
160  << "d-M"
161  << "d/MM"
162  << "d-MM"
163  << "d/MMM"
164  << "d-MMM"
165  << "d/MMMM"
166  << "d-MMMM"
167  << "dd/M"
168  << "dd-M"
169  << "dd/M"
170  << "dd-M"
171  << "dd/MM"
172  << "dd-MM"
173  << "dd/MMM"
174  << "dd-MMM"
175  << "dd/MMMM"
176  << "dd-MMMM";
177  dayMonthYear << "d/M/yyyy"
178  << "d-M-yyyy"
179 
180  << "d/MM/yyyy"
181  << "d-MM-yyyy"
182  << "d/MMM/yyyy"
183  << "d-MMM-yyyy"
184  << "d MMM yyyy"
185  << "d/MMMM/yyyy"
186  << "d-MMMM-yyyy"
187  << "d MMMM yyyy"
188 
189  << "dd/MM/yyyy"
190  << "dd-MM-yyyy"
191  << "dd/MMM/yyyy"
192  << "dd-MMM-yyyy"
193  << "dd MMM yyyy"
194  << "dd/MMMM/yyyy"
195  << "dd-MMMM-yyyy"
196  << "dd MMMM yyyy";
197  monthDay << "M/d"
198  << "M-d"
199  << "M d"
200  << "M/dd"
201  << "M-dd"
202  << "M dd"
203  << "MM/d"
204  << "MM-d"
205  << "MM d"
206  << "MM/dd"
207  << "MM-dd"
208  << "MM dd"
209  << "MMM/d"
210  << "MMM-d"
211  << "MMM d"
212  << "MMM/dd"
213  << "MMM-dd"
214  << "MMM dd"
215  << "MMMM/d"
216  << "MMMM-d"
217  << "MMMM d"
218  << "MMMM/dd"
219  << "MMMM-dd"
220  << "MMMM dd";
221  monthDayYear << "M/d/yyyy"
222  << "M-d-yyyy"
223  << "M d yyyy"
224  << "M/dd/yyyy"
225  << "M-dd-yyyy"
226  << "M dd yyyy"
227  << "MM/d/yyyy"
228  << "MM-d-yyyy"
229  << "MM d yyyy"
230  << "MM/dd/yyyy"
231  << "MM-dd-yyyy"
232  << "MM dd yyyy"
233  << "MMM/d/yyyy"
234  << "MMM-d-yyyy"
235  << "MMM d yyyy"
236  << "MMM/dd/yyyy"
237  << "MMM-dd-yyyy"
238  << "MMM dd yyyy"
239  << "MMMM/d/yyyy"
240  << "MMMM-d-yyyy"
241  << "MMMM d"
242  << "MMMM/dd"
243  << "MMMM-dd"
244  << "MMMM dd";
245  yearMonth << "yyyy/M"
246  << "yyyy-M"
247  << "yyyy M"
248  << "yyyy/MM"
249  << "yyyy-MM"
250  << "yyyy MM"
251  << "yyyy/MMM"
252  << "yyyy-MMM"
253  << "yyyy MMM"
254  << "yyyy/MMMM"
255  << "yyyy-MMMM"
256  << "yyyy MMMM";
257  yearMonthDay << "yyyy/M/d"
258  << "yyyy-M-d"
259  << "yyyy M d"
260  << "yyyy/M/dd"
261  << "yyyy-M-dd"
262  << "yyyy M dd"
263  << "yyyy/MM/dd"
264  << "yyyy-MM-dd"
265  << "yyyy MM dd"
266  << "yyyy/MMM/d"
267  << "yyyy-MMM-d"
268  << "yyyy MMM d"
269  << "yyyy/MMM/dd"
270  << "yyyy-MMM-dd"
271  << "yyyy MMM dd"
272  << "yyyy/MMMM/dd"
273  << "yyyy-MMMM-dd"
274  << "yyyy MMMM dd";
275 
276  // Potential day-month ambiguity for COORD_UNITS_DATE_SKIP gets treated as month/day
277  m_formatsDateParseAcceptable [COORD_UNITS_DATE_SKIP] = skip + monthDay + monthDayYear + yearMonthDay;
278  m_formatsDateParseAcceptable [COORD_UNITS_DATE_MONTH_DAY_YEAR] = skip + monthDay + monthDayYear + yearMonthDay;
279  m_formatsDateParseAcceptable [COORD_UNITS_DATE_DAY_MONTH_YEAR] = skip + dayMonth + dayMonthYear + yearMonthDay;
280  m_formatsDateParseAcceptable [COORD_UNITS_DATE_YEAR_MONTH_DAY] = skip + yearMonth + yearMonthDay;
281 
282  ENGAUGE_ASSERT (m_formatsDateParseAcceptable.count () == NUM_COORD_UNITS_DATE);
283 
284  QStringList hour, hourMinute, hourMinuteSecond, hourMinutePm, hourMinuteSecondPm;
285 
286  hour << "hh";
287  hourMinute << "hh:mm";
288  hourMinuteSecond << "hh:mm:ss";
289  hourMinutePm << "hh:mmA"
290  << "hh:mm A"
291  << "hh:mma"
292  << "hh:mm a";
293  hourMinuteSecondPm << "hh:mm:ssA"
294  << "hh:mm:ss A"
295  << "hh:mm:ssa"
296  << "hh:mm:ss a";
297 
298  m_formatsTimeParseAcceptable [COORD_UNITS_TIME_SKIP] = skip + hour + hourMinute + hourMinuteSecond + hourMinutePm + hourMinuteSecondPm;
299  m_formatsTimeParseAcceptable [COORD_UNITS_TIME_HOUR_MINUTE] = skip + hour + hourMinute + hourMinutePm + hourMinuteSecond + hourMinuteSecondPm;
300  m_formatsTimeParseAcceptable [COORD_UNITS_TIME_HOUR_MINUTE_SECOND] = skip + hour + hourMinute + hourMinutePm + hourMinuteSecond + hourMinuteSecondPm;
301 
302  ENGAUGE_ASSERT (m_formatsTimeParseAcceptable.count () == NUM_COORD_UNITS_TIME);
303 }
304 
305 void FormatDateTime::loadFormatsParseIncomplete()
306 {
307  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::loadFormatsParseIncomplete";
308 
309  QStringList skip, day, dayMonth, month, monthDay, monthDayYear, year, yearMonth, yearMonthDay;
310 
311  // COORD_UNITS_DATE_SKIP and COORD_UNITS_TIME_SKIP allow date/time respectively even when skipped,
312  // although there can be ambiguity with between COORD_UNITS_DATE_MONTH_DAY_YEAR and COORD_UNITS_DATE_DAY_MONTH_YEAR
313  skip << "";
314 
315  // IMPORTANT! Be sure to include complete date values since the date, which goes before the time, will be
316  // complete when the time is getting
317  day << "\\d{1,2}"
318  << "\\d{1,2}/"
319  << "\\d{1,2}-";
320  dayMonth << "\\d{1,2}/\\d{1,2}"
321  << "\\d{1,2}/\\d{1,2} "
322  << "\\d{1,2}/\\d{1,2}/"
323  << "\\d{1,2}-\\d{1,2}-"
324  << "\\d{1,2}/[a-zA-Z]{1,12}/"
325  << "\\d{1,2}-[a-zA-Z]{1,12}-"
326  << "\\d{1,2} [a-zA-Z]{1,12} ";
327  month << "\\d{1,2}"
328  << "\\d{1,2}/"
329  << "[a-zA-Z]{1,12}"
330  << "[a-zA-Z]{1,12} ";
331  monthDay << "\\d{1,2}/\\d{1,2}"
332  << "\\d{1,2}/\\d{1,2} "
333  << "\\d{1,2}/\\d{1,2}/"
334  << "\\d{1,2} \\d{1,2}"
335  << "\\d{1,2} \\d{1,2} "
336  << "\\d{1,2}-\\d{1,2}-"
337  << "[a-zA-Z]{1,12}"
338  << "[a-zA-Z]{1,12} "
339  << "[a-zA-Z]{1,12} \\d{1,2}"
340  << "[a-zA-Z]{1,12} \\d{1,2} ";
341  monthDayYear << "\\d{1,2}/\\d{1,2}/\\d{1,4}"
342  << "\\d{1,2}/\\d{1,2}/\\d{1,4} "
343  << "\\d{1,2}-\\d{1,2}-\\d{1,4}"
344  << "\\d{1,2}-\\d{1,2}-\\d{1,4} "
345  << "\\d{1,2} \\d{1,2} \\d{1,4}"
346  << "\\d{1,2} \\d{1,2} \\d{1,4} ";
347  year << "\\d{1,4}"
348  << "\\d{1,4} "
349  << "\\d{1,4}/"
350  << "\\d{1,4}-";
351  yearMonth << "\\d{4}/\\d{1,2}"
352  << "\\d{4}/\\d{1,2} "
353  << "\\d{4}/\\d{1,2}/"
354  << "\\d{4}-\\d{1,2}"
355  << "\\d{4}-\\d{1,2} "
356  << "\\d{4}-\\d{1,2}-"
357  << "\\d{4} \\d{1,2}"
358  << "\\d{4} \\d{1,2} "
359  << "\\d{4}/[a-zA-Z]{1,12}"
360  << "\\d{4}/[a-zA-Z]{1,12} "
361  << "\\d{4}/[a-zA-Z]{1,12}/"
362  << "\\d{4}-[a-zA-Z]{1,12}"
363  << "\\d{4}-[a-zA-Z]{1,12} "
364  << "\\d{4}-[a-zA-Z]{1,12}-"
365  << "\\d{4} [a-zA-Z]{1,12}"
366  << "\\d{4} [a-zA-Z]{1,12} ";
367  yearMonthDay << "\\d{4}/\\d{1,2}/\\d{1,2}"
368  << "\\d{4}/\\d{1,2}-\\d{1,2}"
369  << "\\d{4} \\d{1,2} \\d{1,2}"
370  << "\\d{4}/[a-zA-Z]{1,12}/\\d{1,2}"
371  << "\\d{4}-[a-zA-Z]{1,12}-\\d{1,2}";
372 
373  // For every entry, the possible states leading up to the Acceptable states in m_formatsDateParseIncomplete are all included.
374  // Potential day-month ambiguity for COORD_UNITS_DATE_SKIP gets treated as month/day.
375  m_formatsDateParseIncomplete [COORD_UNITS_DATE_SKIP] = skip + month + monthDay + monthDayYear + year + yearMonth + yearMonthDay;
376  m_formatsDateParseIncomplete [COORD_UNITS_DATE_MONTH_DAY_YEAR] = skip + month + monthDay + monthDayYear + year + yearMonth + yearMonthDay;
377  m_formatsDateParseIncomplete [COORD_UNITS_DATE_DAY_MONTH_YEAR] = skip + day + dayMonth + year + yearMonth + yearMonthDay;
378  m_formatsDateParseIncomplete [COORD_UNITS_DATE_YEAR_MONTH_DAY] = skip + year + yearMonth + yearMonthDay;
379 
380  ENGAUGE_ASSERT (m_formatsDateParseIncomplete.count () == NUM_COORD_UNITS_DATE);
381 
382  QStringList hour, hourMinute, hourMinuteAmPm, hourMinuteSecond, hourMinuteSecondAmPm;
383 
384  hour << "\\d{1,2}"
385  << "\\d{1,2}:";
386  hourMinute << "\\d{1,2}:\\d{1,2}"
387  << "\\d{1,2}:\\d{1,2}:"
388  << "\\d{1,2}:\\d{1,2} ";
389  hourMinuteAmPm << "\\d{1,2}:\\d{1,2} [aApP]";
390  hourMinuteSecond << "\\d{1,2}:\\d{1,2}:\\d{1,2}"
391  << "\\d{1,2}:\\d{1,2}:\\d{1,2} ";
392  hourMinuteSecondAmPm << "\\d{1,2}:\\d{1,2}:\\d{1,2} [aApP]";
393 
394  // For every entry, the possible states leading up to the Acceptable states in m_formatsTimeParseIncomplete are all included.
395  m_formatsTimeParseIncomplete [COORD_UNITS_TIME_SKIP] = skip +
396  hour +
397  hourMinute + hourMinuteAmPm +
398  hourMinuteSecond + hourMinuteSecondAmPm;
399  m_formatsTimeParseIncomplete [COORD_UNITS_TIME_HOUR_MINUTE] = skip +
400  hour +
401  hourMinute + hourMinuteAmPm +
402  hourMinuteSecond + hourMinuteSecondAmPm;
403  m_formatsTimeParseIncomplete [COORD_UNITS_TIME_HOUR_MINUTE_SECOND] = skip +
404  hour +
405  hourMinute + hourMinuteAmPm +
406  hourMinuteSecond + hourMinuteSecondAmPm;
407 
408  ENGAUGE_ASSERT (m_formatsTimeParseIncomplete.count () == NUM_COORD_UNITS_TIME);
409 }
410 
411 QValidator::State FormatDateTime::parseInput (CoordUnitsDate coordUnitsDate,
412  CoordUnitsTime coordUnitsTime,
413  const QString &stringUntrimmed,
414  double &value) const
415 {
416  LOG4CPP_INFO_S ((*mainCat)) << "FormatDateTime::parseInput"
417  << " date=" << coordUnitsDateToString (coordUnitsDate).toLatin1().data()
418  << " time=" << coordUnitsTimeToString (coordUnitsTime).toLatin1().data()
419  << " string=" << stringUntrimmed.toLatin1().data();
420 
421  const bool USE_QREGEXP = true, DO_NOT_USE_QREGEXP = false;
422 
423  const QString string = stringUntrimmed.trimmed();
424 
425  QValidator::State state;
426  if (string.isEmpty()) {
427 
428  state = QValidator::Intermediate;
429 
430  } else {
431 
432  state = QValidator::Invalid;
433 
434  // First see if value is acceptable
435  bool success = false;
436  dateTimeLookup (m_formatsDateParseAcceptable,
437  m_formatsTimeParseAcceptable,
438  coordUnitsDate,
439  coordUnitsTime,
440  string,
441  USE_QREGEXP,
442  value,
443  success);
444  if (success) {
445 
446  state = QValidator::Acceptable;
447 
448  } else {
449 
450  // Not acceptable, but perhaps it is just incomplete
451  dateTimeLookup (m_formatsDateParseIncomplete,
452  m_formatsTimeParseIncomplete,
453  coordUnitsDate,
454  coordUnitsTime,
455  string,
456  DO_NOT_USE_QREGEXP,
457  value,
458  success);
459  if (success) {
460 
461  state = QValidator::Intermediate;
462 
463  }
464  }
465  }
466 
467  return state;
468 }
FormatDateTime()
Single constructor.
QValidator::State parseInput(CoordUnitsDate coordUnitsDate, CoordUnitsTime coordUnitsTime, const QString &stringUntrimmed, double &value) const
Parse the input string into a time value.
QString formatOutput(CoordUnitsDate coordUnitsDate, CoordUnitsTime coordUnitsTime, double value) const
Format the date/time value according to date/time format settings.