Coverage Report

Created: 2026-05-30 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wt/src/Wt/WDate.C
Line
Count
Source
1
/*
2
 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3
 *
4
 * See the LICENSE file for terms of use.
5
 */
6
7
#include "Wt/WApplication.h"
8
#include "Wt/WDate.h"
9
#include "Wt/WException.h"
10
#include "Wt/WLocalDateTime.h"
11
#include "Wt/WLogger.h"
12
13
#include "Wt/Date/date.h"
14
15
#include "WebUtils.h"
16
17
namespace {
18
  std::string WT_WDATE = "Wt.WDate.";
19
20
  // Nov 24, 4714 BCE (year(-4713) is 4714 BCE)
21
  // Normally Julian days start counting from midday, but since toJulianDay() and fromJulianDay()
22
  // use integers, we'll count from midnight instead. This is consistent with
23
  // boost::gregorian::date::julian_day(), which is what Wt 3 used.
24
  constexpr auto JULIAN_DAY_EPOCH = static_cast<date::sys_days>(date::year(-4713) / 11 / 24);
25
}
26
27
namespace Wt {
28
29
LOGGER("WDate");
30
31
WDate::WDate()
32
0
  : ymd_(0)
33
0
{ }
34
35
WDate::WDate(const std::chrono::system_clock::time_point& tp)
36
0
{
37
0
  setTimePoint(tp);
38
0
}
39
40
#ifdef WT_DATE_TZ_USE_STD
41
WDate::WDate(const std::chrono::year_month_day& date)
42
  : WDate(static_cast<int>(date.year()),
43
          static_cast<unsigned>(date.month()),
44
          static_cast<unsigned>(date.day()))
45
{ }
46
#endif
47
48
WDate::WDate(int year, int month, int day)
49
0
{
50
0
  setDate(year, month, day);
51
0
}
52
53
void WDate::setTimePoint(const std::chrono::system_clock::time_point& tp)
54
0
{
55
0
  date::sys_days dp = date::floor<date::days>(tp);
56
0
  auto ymd = date::year_month_day(dp);
57
0
  if(ymd.ok())
58
0
      setYmd((int)ymd.year(), (unsigned int)ymd.month(), (unsigned int)ymd.day());
59
0
  else
60
0
      ymd_ = 1; //not null and not valid
61
0
}
62
63
WDate WDate::addDays(int ndays) const
64
0
{
65
0
  if(isValid()){
66
0
      date::sys_days dp(date::day(day())/month()/year());
67
0
      date::year_month_day ymd(dp + date::days(ndays));
68
0
      return WDate((int)ymd.year(), (unsigned int)ymd.month(), (unsigned int)ymd.day());
69
0
  } else
70
0
      return WDate();
71
0
}
72
73
WDate WDate::addMonths(int nmonths) const
74
0
{
75
0
  if(isValid()){
76
0
      date::year_month_day ymd = date::day(day())/month()/year();
77
0
      ymd += date::months(nmonths);
78
0
      if(!ymd.ok() &&
79
0
         ymd.year().ok() &&
80
0
         ymd.month().ok() &&
81
0
         ymd.day() > (ymd.year() / ymd.month() / date::last).day())
82
0
        ymd = ymd.year() / ymd.month() / date::last;
83
0
      if(!ymd.ok())
84
0
          return WDate();
85
0
      return WDate((int)ymd.year(), (unsigned int)ymd.month(), (unsigned int)ymd.day());
86
0
  } else
87
0
      return WDate();
88
0
}
89
90
WDate WDate::addYears(int nyears) const
91
0
{
92
0
  if(isValid()){
93
0
      date::year_month_day ymd = date::day(day())/month()/year();
94
0
      ymd += date::years(nyears);
95
0
      if (!ymd.ok() &&
96
0
          ymd.year().ok() &&
97
0
          ymd.month().ok() &&
98
0
          ymd.day() > (ymd.year() / ymd.month() / date::last).day())
99
0
        ymd = ymd.year() / ymd.month() / date::last;
100
0
      if(!ymd.ok())
101
0
          return WDate();
102
0
      return WDate((int)ymd.year(), (unsigned int)ymd.month(), (unsigned int)ymd.day());
103
0
  } else
104
0
      return WDate();
105
0
}
106
107
void WDate::setDate(int year, int month, int day)
108
0
{
109
0
    date::year_month_day ymd = date::day(day)/month/year;
110
0
    if(!ymd.ok()){
111
0
        if(!ymd.year().ok())
112
0
            LOG_WARN("Invalid date: year not in range "
113
0
                     << (int)ymd.year().min()<< " .. "
114
0
                     << (int)ymd.year().max());
115
0
        if(!ymd.month().ok())
116
0
            LOG_WARN("Invalid date: month not in range 1 .. 12");
117
0
        if(!ymd.day().ok())
118
0
            LOG_WARN("Invalid date: day not in range 1 .. 31");
119
0
        ymd_ = 1;
120
0
        return;
121
0
    }
122
0
    setYmd(year, month, day);
123
0
}
124
125
void WDate::setYmd(int y, int m, int d)
126
0
{
127
0
  ymd_ = (y << 16) | ((m & 0xFF) << 8) | (d & 0xFF);
128
0
}
129
130
bool WDate::isLeapYear(int year)
131
0
{
132
0
    return date::year(year).is_leap();
133
0
}
134
135
int WDate::dayOfWeek() const
136
0
{
137
0
  if (!isValid())
138
0
    return 0;
139
140
0
  return date::weekday(date::day(day())/month()/year()).iso_encoding();
141
0
}
142
143
int WDate::daysTo(const WDate& other) const
144
0
{
145
0
  if (!isValid() || !other.isValid())
146
0
    return 0;
147
148
0
  date::year_month_day ymdthis = date::day(day())/month()/year();
149
0
  date::year_month_day ymdother = date::day(other.day())/other.month()/other.year();
150
0
  date::sys_days dpthis = ymdthis;
151
0
  date::sys_days dpother = ymdother;
152
0
  auto dd = dpother - dpthis;
153
0
  return dd.count();
154
0
}
155
156
int WDate::toJulianDay() const
157
0
{
158
0
  if (!isValid())
159
0
    return 0;
160
0
  else {
161
0
    return (static_cast<date::sys_days>(date::year(year()) / month() / day()) - JULIAN_DAY_EPOCH).count();
162
0
  }
163
0
}
164
165
std::chrono::system_clock::time_point WDate::toTimePoint() const
166
0
{
167
0
  if (isValid()){
168
0
    date::year_month_day ymd = date::day(day())/month()/year();
169
0
    date::sys_days days = ymd;
170
0
    return days;
171
0
  }
172
0
  else
173
0
    return std::chrono::system_clock::time_point();
174
0
}
175
176
WDate WDate::previousWeekday(WDate &d, int weekday)
177
0
{
178
0
    if(!d.isValid())
179
0
        return WDate();
180
0
    WDate dt = d.addDays(-1);
181
0
    while(dt.dayOfWeek() != weekday)
182
0
        dt = dt.addDays(-1);
183
0
    return dt;
184
0
}
185
186
bool WDate::isValid(int year, int month, int day)
187
0
{
188
0
  WDate d(year, month, day);
189
0
  return d.isValid();
190
0
}
191
192
bool WDate::operator> (const WDate& other) const
193
0
{
194
0
  return other < *this;
195
0
}
196
197
bool WDate::operator< (const WDate& other) const
198
0
{
199
0
  return ymd_ < other.ymd_;
200
0
}
201
202
bool WDate::operator!= (const WDate& other) const
203
0
{
204
0
  return !(*this == other);
205
0
}
206
207
bool WDate::operator== (const WDate& other) const
208
0
{
209
0
  return ymd_ == other.ymd_;
210
0
}
211
212
bool WDate::operator<= (const WDate& other) const
213
0
{
214
0
  return (*this == other) || (*this < other);
215
0
}
216
217
bool WDate::operator>= (const WDate& other) const
218
0
{
219
0
  return other <= *this;
220
0
}
221
222
WDate WDate::currentServerDate()
223
0
{
224
0
  return WLocalDateTime::currentServerDateTime().date();
225
0
}
226
227
WDate WDate::currentDate()
228
0
{
229
0
  return WLocalDateTime::currentDateTime().date();
230
0
}
231
232
WString WDate::shortDayName(int weekday, bool localizedString)
233
0
{
234
0
  static const char *v[]
235
0
    = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
236
237
0
  if (localizedString && WApplication::instance())
238
0
    return WString::tr(WT_WDATE + "3." + v[weekday - 1]);
239
0
  else
240
0
    return WString::fromUTF8(v[weekday - 1]);
241
0
}
242
243
int WDate::parseShortDayName(const std::string& v, unsigned& pos)
244
0
{
245
0
  if (pos + 2 >= v.length())
246
0
    return -1;
247
248
0
  std::string d = v.substr(pos, 3);
249
250
0
  for (int i = 1; i <= 7; ++i) {
251
0
    if (d == shortDayName(i).toUTF8()) {
252
0
      pos += 3;
253
0
      return i;
254
0
    }
255
0
  }
256
257
0
  return -1;
258
0
}
259
260
WString WDate::longDayName(int weekday, bool localizedString)
261
0
{
262
0
  static const char *v[]
263
0
    = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
264
0
       "Sunday" };
265
266
0
  if (localizedString && WApplication::instance())
267
0
    return WString::tr(WT_WDATE + v[weekday - 1]);
268
0
  else
269
0
    return WString::fromUTF8(v[weekday - 1]);
270
0
}
271
272
int WDate::parseLongDayName(const std::string& v, unsigned& pos)
273
0
{
274
0
  std::string remainder = v.substr(pos);
275
276
0
  for (int i = 1; i <= 7; ++i) {
277
0
    std::string m = longDayName(i).toUTF8();
278
279
0
    if (remainder.length() >= m.length())
280
0
      if (remainder.substr(0, m.length()) == m) {
281
0
        pos += m.length();
282
0
        return i;
283
0
      }
284
0
  }
285
286
0
  return -1;
287
0
}
288
289
WString WDate::shortMonthName(int month, bool localizedString)
290
0
{
291
0
  static const char *v[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
292
0
                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
293
294
0
  if (localizedString && WApplication::instance())
295
0
    return WString::tr(WT_WDATE + "3." + v[month - 1]);
296
0
  else
297
0
    return WString::fromUTF8(v[month - 1]);
298
0
}
299
300
int WDate::parseShortMonthName(const std::string& v, unsigned& pos)
301
0
{
302
0
  if (pos + 2 >= v.length())
303
0
    return -1;
304
305
0
  std::string m = v.substr(pos, 3);
306
307
0
  for (int i = 1; i <= 12; ++i) {
308
0
    if (m == shortMonthName(i).toUTF8()) {
309
0
      pos += 3;
310
0
      return i;
311
0
    }
312
0
  }
313
314
0
  return -1;
315
0
}
316
317
WString WDate::longMonthName(int month, bool localizedString)
318
0
{
319
0
  static const char *v[] = {"January", "February", "March", "April", "May",
320
0
                            "June", "July", "August", "September",
321
0
                            "October", "November", "December" };
322
323
0
  if (localizedString && WApplication::instance())
324
0
    return WString::tr(WT_WDATE + v[month - 1]);
325
0
  else
326
0
    return WString::fromUTF8(v[month - 1]);
327
0
}
328
329
int WDate::parseLongMonthName(const std::string& v, unsigned& pos)
330
0
{
331
0
  std::string remainder = v.substr(pos);
332
333
0
  for (int i = 1; i <= 12; ++i) {
334
0
    std::string m = longMonthName(i).toUTF8();
335
336
0
    if (remainder.length() >= m.length())
337
0
      if (remainder.substr(0, m.length()) == m) {
338
0
        pos += m.length();
339
0
        return i;
340
0
      }
341
0
  }
342
343
0
  return -1;
344
0
}
345
346
WString WDate::defaultFormat()
347
0
{
348
0
  return WString::fromUTF8("ddd MMM d yyyy");
349
0
}
350
351
WDate WDate::fromString(const WString& s)
352
0
{
353
0
  return fromString(s, defaultFormat());
354
0
}
355
356
WDate::ParseState::ParseState()
357
0
{
358
0
  d = M = y = 0;
359
0
  day = month = year = -1;
360
0
}
361
362
WDate WDate::fromString(const WString& s, const WString& format)
363
0
{
364
0
  WDate result;
365
366
0
  WDateTime::fromString(&result, nullptr, s, format);
367
368
0
  return result;
369
0
}
370
371
WDateTime::CharState WDate::handleSpecial(char c, const std::string& v,
372
                                          unsigned& vi, ParseState& parse,
373
                                          const WString& format)
374
0
{
375
0
  switch (c) {
376
0
  case 'd':
377
0
    if (parse.d == 0)
378
0
      if (!parseLast(v, vi, parse, format))
379
0
        return WDateTime::CharState::CharInvalid;
380
381
0
    ++parse.d;
382
383
0
    return WDateTime::CharState::CharHandled;
384
385
0
  case 'M':
386
0
    if (parse.M == 0)
387
0
      if (!parseLast(v, vi, parse, format))
388
0
        return WDateTime::CharState::CharInvalid;
389
390
0
    ++parse.M;
391
392
0
    return WDateTime::CharState::CharHandled;
393
394
0
  case 'y':
395
0
    if (parse.y == 0)
396
0
      if (!parseLast(v, vi, parse, format))
397
0
        return WDateTime::CharState::CharInvalid;
398
399
0
    ++parse.y;
400
401
0
    return WDateTime::CharState::CharHandled;
402
403
0
  default:
404
0
    if (!parseLast(v, vi, parse, format))
405
0
      return WDateTime::CharState::CharInvalid;
406
407
0
    return WDateTime::CharState::CharUnhandled;
408
0
  }
409
0
}
410
411
WDate WDate::fromJulianDay(int jd)
412
0
{
413
0
  const auto ymd = date::year_month_day(JULIAN_DAY_EPOCH + date::days(jd));
414
0
  return WDate(static_cast<int>(ymd.year()), static_cast<unsigned>(ymd.month()), static_cast<unsigned>(ymd.day()));
415
0
}
416
417
static void fatalFormatError(const WString& format, int c, const char* cs)
418
0
{
419
0
  std::stringstream s;
420
0
  s << "WDate format syntax error (for \"" << format.toUTF8()
421
0
    << "\"): Cannot handle " << c << " consecutive " << cs;
422
423
0
  throw WException(s.str());
424
0
}
425
426
bool WDate::parseLast(const std::string& v, unsigned& vi,
427
                      ParseState& parse,
428
                      const WString& format)
429
0
{
430
0
  if (parse.d != 0) {
431
0
    switch (parse.d) {
432
0
    case 1: {
433
0
      std::string dstr;
434
435
0
      if (vi >= v.length())
436
0
        return false;
437
0
      dstr += v[vi++];
438
439
0
      if (vi < v.length())
440
0
        if ('0' <= v[vi] && v[vi] <= '9')
441
0
          dstr += v[vi++];
442
443
0
      try {
444
0
        parse.day = Utils::stoi(dstr);
445
0
      } catch (std::exception&) {
446
0
        return false;
447
0
      }
448
449
0
      break;
450
0
    }
451
0
    case 2: {
452
0
      if (vi + 1 >= v.length())
453
0
        return false;
454
455
0
      std::string dstr = v.substr(vi, 2);
456
0
      vi += 2;
457
458
0
      try {
459
0
        parse.day = Utils::stoi(dstr);
460
0
      } catch (std::exception&) {
461
0
        return false;
462
0
      }
463
464
0
      break;
465
0
    }
466
0
    case 3:
467
0
      if (parseShortDayName(v, vi) == -1)
468
0
        return false;
469
0
      break;
470
0
    case 4:
471
0
      if (parseLongDayName(v, vi) == -1)
472
0
        return false;
473
0
      break;
474
0
    default:
475
0
      fatalFormatError(format, parse.d, "d's");
476
0
    }
477
478
0
    parse.d = 0;
479
0
  }
480
481
0
  if (parse.M != 0) {
482
0
    switch (parse.M) {
483
0
    case 1: {
484
0
      std::string Mstr;
485
486
0
      if (vi >= v.length())
487
0
        return false;
488
0
      Mstr += v[vi++];
489
490
0
      if (vi < v.length())
491
0
        if ('0' <= v[vi] && v[vi] <= '9')
492
0
          Mstr += v[vi++];
493
494
0
      try {
495
0
        parse.month = Utils::stoi(Mstr);
496
0
      } catch (std::exception&) {
497
0
        return false;
498
0
      }
499
500
0
      break;
501
0
    }
502
0
    case 2: {
503
0
      if (vi + 1 >= v.length())
504
0
        return false;
505
506
0
      std::string Mstr = v.substr(vi, 2);
507
0
      vi += 2;
508
509
0
      try {
510
0
        parse.month = Utils::stoi(Mstr);
511
0
      } catch (std::exception&) {
512
0
        return false;
513
0
      }
514
515
0
      break;
516
0
    }
517
0
    case 3:
518
0
      parse.month = parseShortMonthName(v, vi);
519
0
      if (parse.month == -1)
520
0
        return false;
521
0
      break;
522
0
    case 4:
523
0
      parse.month = parseLongMonthName(v, vi);
524
0
      if (parse.month == -1)
525
0
        return false;
526
0
      break;
527
0
    default:
528
0
      fatalFormatError(format, parse.M, "M's");
529
0
    }
530
531
0
    parse.M = 0;
532
0
  }
533
534
0
  if (parse.y != 0) {
535
0
    switch (parse.y) {
536
0
    case 2: {
537
0
      if (vi + 1 >= v.length())
538
0
        return false;
539
540
0
      std::string ystr = v.substr(vi, 2);
541
0
      vi += 2;
542
543
0
      try {
544
0
        parse.year = Utils::stoi(ystr);
545
0
        if (parse.year < 38)
546
0
          parse.year += 2000;
547
0
        else
548
0
          parse.year += 1900;
549
0
      } catch (std::exception&) {
550
0
        return false;
551
0
      }
552
553
0
      break;
554
0
    }
555
0
    case 4: {
556
0
      if (vi + 3 >= v.length())
557
0
        return false;
558
559
0
      std::string ystr = v.substr(vi, 4);
560
0
      vi += 4;
561
562
0
      try {
563
0
        parse.year = Utils::stoi(ystr);
564
0
      } catch (std::exception&) {
565
0
        return false;
566
0
      }
567
568
0
      break;
569
0
    }
570
0
    default:
571
0
      fatalFormatError(format, parse.y, "y's");
572
0
    }
573
574
0
    parse.y = 0;
575
0
  }
576
577
0
  return true;
578
0
}
579
580
WString WDate::toString() const
581
0
{
582
0
  return WDate::toString(defaultFormat());
583
0
}
584
585
WString WDate::toString(const WString& format, bool localizedString) const
586
0
{
587
0
  return WDateTime::toString(this, nullptr, format, localizedString, 0);
588
0
}
589
590
bool WDate::writeSpecial(const std::string& f, unsigned& i,
591
                         WStringStream& result, bool localizedString) const
592
0
{
593
0
  char buf[30];
594
595
0
  switch (f[i]) {
596
0
  case 'd':
597
0
    if (f[i + 1] == 'd') {
598
0
      if (f[i + 2] == 'd') {
599
0
        if (f[i + 3] == 'd') {
600
          // 4 d's
601
0
          i += 3;
602
0
          result << longDayName(dayOfWeek(), localizedString).toUTF8();
603
0
        } else {
604
          // 3 d's
605
0
          i += 2;
606
0
          result << shortDayName(dayOfWeek(), localizedString).toUTF8();
607
0
        }
608
0
      } else {
609
        // 2 d's
610
0
        i += 1;
611
0
        result << Utils::pad_itoa(day(), 2, buf);
612
0
      }
613
0
    } else {
614
      // 1 d
615
0
      result << Utils::itoa(day(), buf);
616
0
    }
617
618
0
    return true;
619
0
  case 'M':
620
0
    if (f[i + 1] == 'M') {
621
0
      if (f[i + 2] == 'M') {
622
0
        if (f[i + 3] == 'M') {
623
          // 4 M's
624
0
          i += 3;
625
0
          result << longMonthName(month(), localizedString).toUTF8();
626
0
        } else {
627
          // 3 M's
628
0
          i += 2;
629
0
          result << shortMonthName(month(), localizedString).toUTF8();
630
0
        }
631
0
      } else {
632
        // 2 M's
633
0
        i += 1;
634
0
        result << Utils::pad_itoa(month(), 2, buf);
635
0
      }
636
0
    } else {
637
      // 1 M
638
0
      result << Utils::itoa(month(), buf);
639
0
    }
640
641
0
    return true;
642
0
  case 'y':
643
0
    if (f[i + 1] == 'y') {
644
0
      if (f[i + 2] == 'y' && f[i + 3] == 'y') {
645
        // 4 y's
646
0
        i += 3;
647
0
        result << Utils::itoa(year(), buf);
648
649
0
        return true;
650
0
      } else {
651
        // 2 y's
652
0
        i += 1;
653
0
        result << Utils::pad_itoa(year() % 100, 2, buf);
654
655
0
        return true;
656
0
      }
657
0
    }
658
0
    WT_FALLTHROUGH
659
0
  default:
660
0
    return false;
661
0
  }
662
0
}
663
664
namespace {
665
666
0
  std::string extLiteral(char c) {
667
0
    std::string result("");
668
0
    switch (c) {
669
0
      case ',': case 'D': case 'j': case 'l':
670
0
      case 'S': case 'w': case 'z': case 'W':
671
0
      case 'F': case 'm': case 'M': case 'n':
672
0
      case 't': case 'L': case 'Y': case 'y':
673
0
      case 'a': case 'A': case 'g': case 'G':
674
0
      case 'h': case 'H': case 'i': case 's':
675
0
      case 'O': case 'T': case 'Z': case 0:
676
0
      result.push_back('\\');
677
0
    }
678
0
    result.push_back(c);
679
0
    return result;
680
0
  }
681
682
  void writeExtLast(std::string& result, int& d, int& M, int& y,
683
0
                    const WString& format) {
684
0
    if (d != 0) {
685
0
      switch (d) {
686
0
      case 1:
687
0
        result += 'j'; break;
688
0
      case 2:
689
0
        result += 'd'; break;
690
0
      case 3:
691
0
        result += 'D'; break;
692
0
      case 4:
693
0
        result += 'l'; break;
694
0
      default:
695
0
        fatalFormatError(format, d, "d's");
696
0
      }
697
698
0
      d = 0;
699
0
    }
700
701
0
    if (M != 0) {
702
0
      switch (M) {
703
0
      case 1:
704
0
        result += 'n'; break;
705
0
      case 2:
706
0
        result += 'm'; break;
707
0
      case 3:
708
0
        result += 'M'; break;
709
0
      case 4:
710
0
        result += 'F'; break;
711
0
      default:
712
0
        fatalFormatError(format, M, "M's");
713
0
      }
714
715
0
      M = 0;
716
0
    }
717
718
0
    if (y != 0) {
719
0
      switch (y) {
720
0
      case 2:
721
0
        result += 'y'; break;
722
0
      case 4:
723
0
        result += 'Y'; break;
724
0
      default:
725
0
        fatalFormatError(format, y, "y's");
726
0
      }
727
728
0
      y = 0;
729
0
    }
730
0
  }
731
}
732
733
std::string WDate::extFormat(const WString& format)
734
0
{
735
0
  std::string result;
736
0
  std::string f = format.toUTF8();
737
738
0
  bool inQuote = false;
739
0
  bool gotQuoteInQuote = false;
740
741
0
  int d = 0, M = 0, y = 0;
742
743
0
  for (unsigned i = 0; i < f.length(); ++i) {
744
0
    if (inQuote) {
745
0
      if (f[i] != '\'') {
746
0
        if (gotQuoteInQuote) {
747
0
          gotQuoteInQuote = false;
748
0
          inQuote = false;
749
0
        } else
750
0
          result += extLiteral(f[i]);
751
0
      } else {
752
0
        if (gotQuoteInQuote) {
753
0
          gotQuoteInQuote = false;
754
0
          result += extLiteral(f[i]);
755
0
        } else
756
0
          gotQuoteInQuote = true;
757
0
      }
758
0
    }
759
760
0
    if (!inQuote) {
761
0
      switch (f[i]) {
762
0
      case 'd':
763
0
        if (d == 0)
764
0
          writeExtLast(result, d, M, y, format);
765
0
        ++d;
766
0
        break;
767
0
      case 'M':
768
0
        if (M == 0)
769
0
          writeExtLast(result, d, M, y, format);
770
0
        ++M;
771
0
        break;
772
0
      case 'y':
773
0
        if (y == 0)
774
0
          writeExtLast(result, d, M, y, format);
775
0
        ++y;
776
0
        break;
777
0
      default:
778
0
        writeExtLast(result, d, M, y, format);
779
0
        if (f[i] == '\'') {
780
0
          inQuote = true;
781
0
          gotQuoteInQuote = false;
782
0
        } else
783
0
          result += extLiteral(f[i]);
784
0
      }
785
0
    }
786
0
  }
787
788
0
  writeExtLast(result, d, M, y, format);
789
790
0
  return result;
791
0
}
792
793
namespace {
794
795
  void fatalFormatRegExpError(const WString& format, int c, const char* cs)
796
0
  {
797
0
    std::stringstream s;
798
0
    s << "WDate to regexp: (for \"" << format.toUTF8()
799
0
      << "\"): cannot handle " << c << " consecutive " << cs;
800
0
    throw WException(s.str());
801
0
  }
802
803
  void writeRegExpLast(WDate::RegExpInfo& result, int& d, int& M, int& y,
804
0
                       const WString& format, int& currentGroup) {
805
0
    if (d != 0) {
806
0
      switch (d) {
807
0
      case 1:
808
0
      case 2:
809
0
        if (d == 1)
810
0
          result.regexp += "(\\d{1,2})";
811
0
        else
812
0
          result.regexp += "(\\d{2})";
813
814
0
        result.dayGetJS = "return parseInt(results["
815
0
          + std::to_string(currentGroup++) + "], 10);";
816
0
        break;
817
0
      default:
818
0
        fatalFormatRegExpError(format, d, "d's");
819
0
      }
820
821
0
      d = 0;
822
0
    }
823
824
0
    if (M != 0) {
825
0
      switch (M) {
826
0
      case 1:
827
0
      case 2:
828
0
        if (M == 1)
829
0
          result.regexp += "(\\d{1,2})";
830
0
        else
831
0
          result.regexp += "(\\d{2})";
832
833
0
        result.monthGetJS = "return parseInt(results["
834
0
          + std::to_string(currentGroup++) + "], 10);";
835
0
        break;
836
0
      default:
837
0
        fatalFormatRegExpError(format, M, "M's");
838
0
      }
839
840
0
      M = 0;
841
0
    }
842
843
0
    if (y != 0) {
844
0
      switch (y) {
845
0
      case 2:
846
0
        result.regexp += "(\\d{2})";
847
0
        result.yearGetJS = "var y=parseInt(results["
848
0
          + std::to_string(currentGroup++) + "], 10);"
849
0
          "return y > 38 ? 1900 + y : 2000 + y;";
850
0
        break;
851
0
      case 4:
852
0
        result.regexp += "(\\d{4})";
853
0
        result.yearGetJS = "return parseInt(results["
854
0
          + std::to_string(currentGroup++) + "], 10)";
855
0
        break;
856
0
      default:
857
0
        fatalFormatRegExpError(format, y, "y's");
858
0
      }
859
860
0
      y = 0;
861
0
    }
862
0
  }
863
}
864
865
WDate::RegExpInfo WDate::formatToRegExp(const WT_USTRING& format)
866
0
{
867
0
  RegExpInfo result;
868
0
  std::string f = format.toUTF8();
869
0
  int currentGroup = 1;
870
871
0
  result.dayGetJS = "return 1";
872
0
  result.monthGetJS = "return 1";
873
0
  result.yearGetJS = "return 2000";
874
875
0
  bool inQuote = false;
876
0
  bool gotQuoteInQuote = false;
877
878
0
  static const std::string regexSpecial = "/[\\^$.|?*+()";
879
880
0
  int d = 0, M = 0, y = 0;
881
882
0
  for (unsigned i = 0; i < f.length(); ++i) {
883
0
    if (inQuote) {
884
0
      if (f[i] != '\'') {
885
0
        if (gotQuoteInQuote) {
886
0
          gotQuoteInQuote = false;
887
0
          inQuote = false;
888
0
        } else
889
0
          result.regexp += f[i];
890
0
      } else {
891
0
        if (gotQuoteInQuote) {
892
0
          gotQuoteInQuote = false;
893
0
          result.regexp += f[i];
894
0
        } else
895
0
          gotQuoteInQuote = true;
896
0
      }
897
0
    }
898
899
0
    if (!inQuote) {
900
0
      switch (f[i]) {
901
0
      case 'd':
902
0
        if (d == 0)
903
0
          writeRegExpLast(result, d, M, y, format, currentGroup);
904
0
        ++d;
905
0
        break;
906
0
      case 'M':
907
0
        if (M == 0)
908
0
          writeRegExpLast(result, d, M, y, format, currentGroup);
909
0
        ++M;
910
0
        break;
911
0
      case 'y':
912
0
        if (y == 0)
913
0
          writeRegExpLast(result, d, M, y, format, currentGroup);
914
0
        ++y;
915
0
        break;
916
0
      default:
917
0
        writeRegExpLast(result, d, M, y, format, currentGroup);
918
0
        if (f[i] == '\'') {
919
0
          inQuote = true;
920
0
          gotQuoteInQuote = false;
921
0
        } else if (regexSpecial.find(f[i]) != std::string::npos) {
922
0
          result.regexp += "\\";
923
0
          result.regexp += f[i];
924
0
        } else
925
0
          result.regexp += f[i];
926
0
      }
927
0
    }
928
0
  }
929
930
0
  writeRegExpLast(result, d, M, y, format, currentGroup);
931
932
0
  return result;
933
0
}
934
935
}