1// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "xfa/fgas/crt/cfgas_formatstring.h"
8
9#include <algorithm>
10#include <vector>
11
12#include "core/fxcrt/cfx_decimal.h"
13#include "core/fxcrt/fx_extension.h"
14#include "core/fxcrt/xml/cxml_element.h"
15
16#define FX_LOCALECATEGORY_DateHash 0xbde9abde
17#define FX_LOCALECATEGORY_TimeHash 0x2d71b00f
18#define FX_LOCALECATEGORY_DateTimeHash 0x158c72ed
19#define FX_LOCALECATEGORY_NumHash 0x0b4ff870
20#define FX_LOCALECATEGORY_TextHash 0x2d08af85
21#define FX_LOCALECATEGORY_ZeroHash 0x568cb500
22#define FX_LOCALECATEGORY_NullHash 0x052931bb
23
24#define FX_NUMSTYLE_Percent 0x01
25#define FX_NUMSTYLE_Exponent 0x02
26#define FX_NUMSTYLE_DotVorv 0x04
27
28namespace {
29
30struct FX_LOCALESUBCATEGORYINFO {
31  uint32_t uHash;
32  const wchar_t* pName;
33  int32_t eSubCategory;
34};
35
36const FX_LOCALESUBCATEGORYINFO g_FXLocaleDateTimeSubCatData[] = {
37    {0x14da2125, L"default", FX_LOCALEDATETIMESUBCATEGORY_Default},
38    {0x9041d4b0, L"short", FX_LOCALEDATETIMESUBCATEGORY_Short},
39    {0xa084a381, L"medium", FX_LOCALEDATETIMESUBCATEGORY_Medium},
40    {0xcdce56b3, L"full", FX_LOCALEDATETIMESUBCATEGORY_Full},
41    {0xf6b4afb0, L"long", FX_LOCALEDATETIMESUBCATEGORY_Long},
42};
43const int32_t g_iFXLocaleDateTimeSubCatCount =
44    sizeof(g_FXLocaleDateTimeSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
45
46const FX_LOCALESUBCATEGORYINFO g_FXLocaleNumSubCatData[] = {
47    {0x46f95531, L"percent", FX_LOCALENUMPATTERN_Percent},
48    {0x4c4e8acb, L"currency", FX_LOCALENUMPATTERN_Currency},
49    {0x54034c2f, L"decimal", FX_LOCALENUMPATTERN_Decimal},
50    {0x7568e6ae, L"integer", FX_LOCALENUMPATTERN_Integer},
51};
52const int32_t g_iFXLocaleNumSubCatCount =
53    sizeof(g_FXLocaleNumSubCatData) / sizeof(FX_LOCALESUBCATEGORYINFO);
54
55struct FX_LOCALETIMEZONEINFO {
56  const wchar_t* name;
57  int16_t iHour;
58  int16_t iMinute;
59};
60
61const FX_LOCALETIMEZONEINFO g_FXLocaleTimeZoneData[] = {
62    {L"CDT", -5, 0}, {L"CST", -6, 0}, {L"EDT", -4, 0}, {L"EST", -5, 0},
63    {L"MDT", -6, 0}, {L"MST", -7, 0}, {L"PDT", -7, 0}, {L"PST", -8, 0},
64};
65
66const wchar_t gs_wsTimeSymbols[] = L"hHkKMSFAzZ";
67const wchar_t gs_wsDateSymbols[] = L"DJMEeGgYwW";
68const wchar_t gs_wsConstChars[] = L",-:/. ";
69
70int32_t ParseTimeZone(const wchar_t* pStr, int32_t iLen, FX_TIMEZONE* tz) {
71  tz->tzHour = 0;
72  tz->tzMinute = 0;
73  if (iLen < 0)
74    return 0;
75
76  int32_t iStart = 1;
77  int32_t iEnd = iStart + 2;
78  while (iStart < iLen && iStart < iEnd)
79    tz->tzHour = tz->tzHour * 10 + FXSYS_DecimalCharToInt(pStr[iStart++]);
80
81  if (iStart < iLen && pStr[iStart] == ':')
82    iStart++;
83
84  iEnd = iStart + 2;
85  while (iStart < iLen && iStart < iEnd)
86    tz->tzMinute = tz->tzMinute * 10 + FXSYS_DecimalCharToInt(pStr[iStart++]);
87
88  if (pStr[0] == '-')
89    tz->tzHour = -tz->tzHour;
90
91  return iStart;
92}
93
94int32_t ConvertHex(int32_t iKeyValue, wchar_t ch) {
95  if (FXSYS_isHexDigit(ch))
96    return iKeyValue * 16 + FXSYS_HexCharToInt(ch);
97  return iKeyValue;
98}
99
100WideString GetLiteralText(const wchar_t* pStrPattern,
101                          int32_t* iPattern,
102                          int32_t iLenPattern) {
103  WideString wsOutput;
104  if (pStrPattern[*iPattern] != '\'')
105    return wsOutput;
106
107  (*iPattern)++;
108  int32_t iQuote = 1;
109  while (*iPattern < iLenPattern) {
110    if (pStrPattern[*iPattern] == '\'') {
111      iQuote++;
112      if ((*iPattern + 1 >= iLenPattern) ||
113          ((pStrPattern[*iPattern + 1] != '\'') && (iQuote % 2 == 0))) {
114        break;
115      }
116      iQuote++;
117      (*iPattern)++;
118    } else if (pStrPattern[*iPattern] == '\\' &&
119               (*iPattern + 1 < iLenPattern) &&
120               pStrPattern[*iPattern + 1] == 'u') {
121      int32_t iKeyValue = 0;
122      *iPattern += 2;
123      int32_t i = 0;
124      while (*iPattern < iLenPattern && i++ < 4) {
125        wchar_t ch = pStrPattern[(*iPattern)++];
126        iKeyValue = ConvertHex(iKeyValue, ch);
127      }
128      if (iKeyValue != 0)
129        wsOutput += static_cast<wchar_t>(iKeyValue & 0x0000FFFF);
130
131      continue;
132    }
133    wsOutput += pStrPattern[(*iPattern)++];
134  }
135  return wsOutput;
136}
137
138WideString GetLiteralTextReverse(const wchar_t* pStrPattern,
139                                 int32_t* iPattern) {
140  WideString wsOutput;
141  if (pStrPattern[*iPattern] != '\'')
142    return wsOutput;
143
144  (*iPattern)--;
145  int32_t iQuote = 1;
146  while (*iPattern >= 0) {
147    if (pStrPattern[*iPattern] == '\'') {
148      iQuote++;
149      if (*iPattern - 1 >= 0 ||
150          ((pStrPattern[*iPattern - 1] != '\'') && (iQuote % 2 == 0))) {
151        break;
152      }
153      iQuote++;
154      (*iPattern)--;
155    } else if (pStrPattern[*iPattern] == '\\' &&
156               pStrPattern[*iPattern + 1] == 'u') {
157      (*iPattern)--;
158      int32_t iKeyValue = 0;
159      int32_t iLen = wsOutput.GetLength();
160      int32_t i = 1;
161      for (; i < iLen && i < 5; i++) {
162        wchar_t ch = wsOutput[i];
163        iKeyValue = ConvertHex(iKeyValue, ch);
164      }
165      if (iKeyValue != 0) {
166        wsOutput.Delete(0, i);
167        wsOutput = (wchar_t)(iKeyValue & 0x0000FFFF) + wsOutput;
168      }
169      continue;
170    }
171    wsOutput = pStrPattern[(*iPattern)--] + wsOutput;
172  }
173  return wsOutput;
174}
175
176bool GetNumericDotIndex(const WideString& wsNum,
177                        const WideString& wsDotSymbol,
178                        int32_t* iDotIndex) {
179  int32_t ccf = 0;
180  int32_t iLenf = wsNum.GetLength();
181  const wchar_t* pStr = wsNum.c_str();
182  int32_t iLenDot = wsDotSymbol.GetLength();
183  while (ccf < iLenf) {
184    if (pStr[ccf] == '\'') {
185      GetLiteralText(pStr, &ccf, iLenf);
186    } else if (ccf + iLenDot <= iLenf &&
187               !wcsncmp(pStr + ccf, wsDotSymbol.c_str(), iLenDot)) {
188      *iDotIndex = ccf;
189      return true;
190    }
191    ccf++;
192  }
193  auto result = wsNum.Find('.');
194  *iDotIndex = result.value_or(iLenf);
195  return result.has_value();
196}
197
198bool ExtractCountDigits(const wchar_t* str,
199                        int len,
200                        int count,
201                        int* cc,
202                        uint32_t* value) {
203  for (int i = count; i > 0; --i) {
204    if (*cc >= len)
205      return false;
206    if (!FXSYS_isDecimalDigit(str[*cc]))
207      return false;
208    *value = *value * 10 + FXSYS_DecimalCharToInt(str[(*cc)++]);
209  }
210  return true;
211}
212
213bool ExtractCountDigitsWithOptional(const wchar_t* str,
214                                    int len,
215                                    int count,
216                                    int* cc,
217                                    uint32_t* value) {
218  if (!ExtractCountDigits(str, len, count, cc, value))
219    return false;
220  ExtractCountDigits(str, len, 1, cc, value);
221  return true;
222}
223
224bool ParseLocaleDate(const WideString& wsDate,
225                     const WideString& wsDatePattern,
226                     IFX_Locale* pLocale,
227                     CFX_DateTime* datetime,
228                     int32_t* cc) {
229  uint32_t year = 1900;
230  uint32_t month = 1;
231  uint32_t day = 1;
232  int32_t ccf = 0;
233  const wchar_t* str = wsDate.c_str();
234  int32_t len = wsDate.GetLength();
235  const wchar_t* strf = wsDatePattern.c_str();
236  int32_t lenf = wsDatePattern.GetLength();
237  WideStringView wsDateSymbols(gs_wsDateSymbols);
238  while (*cc < len && ccf < lenf) {
239    if (strf[ccf] == '\'') {
240      WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
241      int32_t iLiteralLen = wsLiteral.GetLength();
242      if (*cc + iLiteralLen > len ||
243          wcsncmp(str + *cc, wsLiteral.c_str(), iLiteralLen)) {
244        return false;
245      }
246      *cc += iLiteralLen;
247      ccf++;
248      continue;
249    }
250    if (!wsDateSymbols.Contains(strf[ccf])) {
251      if (strf[ccf] != str[*cc])
252        return false;
253      (*cc)++;
254      ccf++;
255      continue;
256    }
257
258    WideString symbol;
259    symbol.Reserve(4);
260    symbol += strf[ccf++];
261    while (ccf < lenf && strf[ccf] == symbol[0])
262      symbol += strf[ccf++];
263
264    if (symbol == L"D" || symbol == L"DD") {
265      day = 0;
266      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &day))
267        return false;
268    } else if (symbol == L"J") {
269      uint32_t val = 0;
270      ExtractCountDigits(str, len, 3, cc, &val);
271    } else if (symbol == L"M" || symbol == L"MM") {
272      month = 0;
273      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &month))
274        return false;
275    } else if (symbol == L"MMM" || symbol == L"MMMM") {
276      for (uint16_t i = 0; i < 12; i++) {
277        WideString wsMonthName = pLocale->GetMonthName(i, symbol == L"MMM");
278        if (wsMonthName.IsEmpty())
279          continue;
280        if (!wcsncmp(wsMonthName.c_str(), str + *cc, wsMonthName.GetLength())) {
281          *cc += wsMonthName.GetLength();
282          month = i + 1;
283          break;
284        }
285      }
286    } else if (symbol == L"EEE" || symbol == L"EEEE") {
287      for (uint16_t i = 0; i < 7; i++) {
288        WideString wsDayName = pLocale->GetDayName(i, symbol == L"EEE");
289        if (wsDayName.IsEmpty())
290          continue;
291        if (!wcsncmp(wsDayName.c_str(), str + *cc, wsDayName.GetLength())) {
292          *cc += wsDayName.GetLength();
293          break;
294        }
295      }
296    } else if (symbol == L"YY" || symbol == L"YYYY") {
297      if (*cc + pdfium::base::checked_cast<int32_t>(symbol.GetLength()) > len)
298        return false;
299
300      year = 0;
301      if (!ExtractCountDigits(str, len, symbol.GetLength(), cc, &year))
302        return false;
303      if (symbol == L"YY") {
304        if (year <= 29)
305          year += 2000;
306        else
307          year += 1900;
308      }
309    } else if (symbol == L"G") {
310      *cc += 2;
311    } else if (symbol == L"JJJ" || symbol == L"E" || symbol == L"e" ||
312               symbol == L"w" || symbol == L"WW") {
313      *cc += symbol.GetLength();
314    }
315  }
316  if (*cc < len)
317    return false;
318
319  datetime->SetDate(year, month, day);
320  return !!(*cc);
321}
322
323void ResolveZone(FX_TIMEZONE tzDiff,
324                 IFX_Locale* pLocale,
325                 uint32_t* wHour,
326                 uint32_t* wMinute) {
327  int32_t iMinuteDiff = *wHour * 60 + *wMinute;
328  FX_TIMEZONE tzLocale = pLocale->GetTimeZone();
329  iMinuteDiff += tzLocale.tzHour * 60 +
330                 (tzLocale.tzHour < 0 ? -tzLocale.tzMinute : tzLocale.tzMinute);
331  iMinuteDiff -= tzDiff.tzHour * 60 +
332                 (tzDiff.tzHour < 0 ? -tzDiff.tzMinute : tzDiff.tzMinute);
333
334  iMinuteDiff %= 1440;
335  if (iMinuteDiff < 0)
336    iMinuteDiff += 1440;
337
338  *wHour = iMinuteDiff / 60;
339  *wMinute = iMinuteDiff % 60;
340}
341
342bool ParseLocaleTime(const WideString& wsTime,
343                     const WideString& wsTimePattern,
344                     IFX_Locale* pLocale,
345                     CFX_DateTime* datetime,
346                     int32_t* cc) {
347  uint32_t hour = 0;
348  uint32_t minute = 0;
349  uint32_t second = 0;
350  uint32_t millisecond = 0;
351  int32_t ccf = 0;
352  const wchar_t* str = wsTime.c_str();
353  int len = wsTime.GetLength();
354  const wchar_t* strf = wsTimePattern.c_str();
355  int lenf = wsTimePattern.GetLength();
356  bool bHasA = false;
357  bool bPM = false;
358  WideStringView wsTimeSymbols(gs_wsTimeSymbols);
359  while (*cc < len && ccf < lenf) {
360    if (strf[ccf] == '\'') {
361      WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
362      int32_t iLiteralLen = wsLiteral.GetLength();
363      if (*cc + iLiteralLen > len ||
364          wcsncmp(str + *cc, wsLiteral.c_str(), iLiteralLen)) {
365        return false;
366      }
367      *cc += iLiteralLen;
368      ccf++;
369      continue;
370    }
371    if (!wsTimeSymbols.Contains(strf[ccf])) {
372      if (strf[ccf] != str[*cc])
373        return false;
374      (*cc)++;
375      ccf++;
376      continue;
377    }
378
379    WideString symbol;
380    symbol.Reserve(4);
381    symbol += strf[ccf++];
382    while (ccf < lenf && strf[ccf] == symbol[0])
383      symbol += strf[ccf++];
384
385    if (symbol == L"k" || symbol == L"K" || symbol == L"h" || symbol == L"H") {
386      hour = 0;
387      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &hour))
388        return false;
389      if (symbol == L"K" && hour == 24)
390        hour = 0;
391    } else if (symbol == L"kk" || symbol == L"KK" || symbol == L"hh" ||
392               symbol == L"HH") {
393      hour = 0;
394      if (!ExtractCountDigits(str, len, 2, cc, &hour))
395        return false;
396      if (symbol == L"KK" && hour == 24)
397        hour = 0;
398    } else if (symbol == L"M") {
399      minute = 0;
400      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &minute))
401        return false;
402    } else if (symbol == L"MM") {
403      minute = 0;
404      if (!ExtractCountDigits(str, len, 2, cc, &minute))
405        return false;
406    } else if (symbol == L"S") {
407      second = 0;
408      if (!ExtractCountDigitsWithOptional(str, len, 1, cc, &second))
409        return false;
410    } else if (symbol == L"SS") {
411      second = 0;
412      if (!ExtractCountDigits(str, len, 2, cc, &second))
413        return false;
414    } else if (symbol == L"FFF") {
415      millisecond = 0;
416      if (!ExtractCountDigits(str, len, 3, cc, &millisecond))
417        return false;
418    } else if (symbol == L"A") {
419      WideString wsAM = pLocale->GetMeridiemName(true);
420      WideString wsPM = pLocale->GetMeridiemName(false);
421      if ((*cc + pdfium::base::checked_cast<int32_t>(wsAM.GetLength()) <=
422           len) &&
423          (WideStringView(str + *cc, wsAM.GetLength()) == wsAM)) {
424        *cc += wsAM.GetLength();
425        bHasA = true;
426      } else if ((*cc + pdfium::base::checked_cast<int32_t>(wsPM.GetLength()) <=
427                  len) &&
428                 (WideStringView(str + *cc, wsPM.GetLength()) == wsPM)) {
429        *cc += wsPM.GetLength();
430        bHasA = true;
431        bPM = true;
432      }
433    } else if (symbol == L"Z") {
434      if (*cc + 3 > len)
435        continue;
436
437      WideString tz(str[(*cc)++]);
438      tz += str[(*cc)++];
439      tz += str[(*cc)++];
440      if (tz == L"GMT") {
441        FX_TIMEZONE tzDiff;
442        tzDiff.tzHour = 0;
443        tzDiff.tzMinute = 0;
444        if (*cc < len && (str[*cc] == '-' || str[*cc] == '+'))
445          *cc += ParseTimeZone(str + *cc, len - *cc, &tzDiff);
446
447        ResolveZone(tzDiff, pLocale, &hour, &minute);
448      } else {
449        // Search the timezone list. There are only 8 of them, so linear scan.
450        for (size_t i = 0; i < FX_ArraySize(g_FXLocaleTimeZoneData); ++i) {
451          const FX_LOCALETIMEZONEINFO& info = g_FXLocaleTimeZoneData[i];
452          if (tz != info.name)
453            continue;
454
455          hour += info.iHour;
456          minute += info.iHour > 0 ? info.iMinute : -info.iMinute;
457          break;
458        }
459      }
460    } else if (symbol == L"z") {
461      if (str[*cc] != 'Z') {
462        FX_TIMEZONE tzDiff;
463        *cc += ParseTimeZone(str + *cc, len - *cc, &tzDiff);
464        ResolveZone(tzDiff, pLocale, &hour, &minute);
465      } else {
466        (*cc)++;
467      }
468    }
469  }
470  if (bHasA) {
471    if (bPM) {
472      hour += 12;
473      if (hour == 24)
474        hour = 12;
475    } else {
476      if (hour == 12)
477        hour = 0;
478    }
479  }
480  datetime->SetTime(hour, minute, second, millisecond);
481  return !!(*cc);
482}
483
484int32_t GetNumTrailingLimit(const WideString& wsFormat,
485                            int iDotPos,
486                            bool* bTrimTailZeros) {
487  if (iDotPos < 0)
488    return 0;
489
490  int32_t iCount = wsFormat.GetLength();
491  int32_t iTreading = 0;
492  for (iDotPos++; iDotPos < iCount; iDotPos++) {
493    wchar_t wc = wsFormat[iDotPos];
494    if (wc == L'z' || wc == L'9' || wc == 'Z') {
495      iTreading++;
496      *bTrimTailZeros = wc != L'9';
497    }
498  }
499  return iTreading;
500}
501
502bool IsLeapYear(uint32_t year) {
503  return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
504}
505
506bool MonthHas30Days(uint32_t month) {
507  return month == 4 || month == 6 || month == 9 || month == 11;
508}
509
510bool MonthHas31Days(uint32_t month) {
511  return month != 2 && !MonthHas30Days(month);
512}
513
514// |month| is 1-based. e.g. 1 means January.
515uint16_t GetSolarMonthDays(uint16_t year, uint16_t month) {
516  if (month == 2)
517    return FX_IsLeapYear(year) ? 29 : 28;
518
519  return MonthHas30Days(month) ? 30 : 31;
520}
521
522uint16_t GetWeekDay(uint16_t year, uint16_t month, uint16_t day) {
523  static const uint16_t month_day[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
524  uint16_t nDays =
525      (year - 1) % 7 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
526  nDays += month_day[month - 1] + day;
527  if (FX_IsLeapYear(year) && month > 2)
528    nDays++;
529  return nDays % 7;
530}
531
532uint16_t GetWeekOfMonth(uint16_t year, uint16_t month, uint16_t day) {
533  uint16_t week_day = GetWeekDay(year, month, 1);
534  uint16_t week_index = 0;
535  week_index += day / 7;
536  day = day % 7;
537  if (week_day + day > 7)
538    week_index++;
539  return week_index;
540}
541
542uint16_t GetWeekOfYear(uint16_t year, uint16_t month, uint16_t day) {
543  uint16_t nDays = 0;
544  for (uint16_t i = 1; i < month; i++)
545    nDays += GetSolarMonthDays(year, i);
546
547  nDays += day;
548  uint16_t week_day = GetWeekDay(year, 1, 1);
549  uint16_t week_index = 1;
550  week_index += nDays / 7;
551  nDays = nDays % 7;
552  if (week_day + nDays > 7)
553    week_index++;
554  return week_index;
555}
556
557WideString NumToString(size_t fmt_size, int32_t value) {
558  return WideString::Format(
559      fmt_size == 1 ? L"%d" : fmt_size == 2 ? L"%02d" : L"%03d", value);
560}
561
562WideString DateFormat(const WideString& wsDatePattern,
563                      IFX_Locale* pLocale,
564                      const CFX_DateTime& datetime) {
565  WideString wsResult;
566  int32_t year = datetime.GetYear();
567  uint8_t month = datetime.GetMonth();
568  uint8_t day = datetime.GetDay();
569  int32_t ccf = 0;
570  const wchar_t* strf = wsDatePattern.c_str();
571  int32_t lenf = wsDatePattern.GetLength();
572  WideStringView wsDateSymbols(gs_wsDateSymbols);
573  while (ccf < lenf) {
574    if (strf[ccf] == '\'') {
575      wsResult += GetLiteralText(strf, &ccf, lenf);
576      ccf++;
577      continue;
578    }
579    if (!wsDateSymbols.Contains(strf[ccf])) {
580      wsResult += strf[ccf++];
581      continue;
582    }
583
584    WideString symbol;
585    symbol.Reserve(4);
586    symbol += strf[ccf++];
587    while (ccf < lenf && strf[ccf] == symbol[0])
588      symbol += strf[ccf++];
589
590    if (symbol == L"D" || symbol == L"DD") {
591      wsResult += NumToString(symbol.GetLength(), day);
592    } else if (symbol == L"J" || symbol == L"JJJ") {
593      uint16_t nDays = 0;
594      for (int i = 1; i < month; i++)
595        nDays += GetSolarMonthDays(year, i);
596      nDays += day;
597      wsResult += NumToString(symbol.GetLength(), nDays);
598    } else if (symbol == L"M" || symbol == L"MM") {
599      wsResult += NumToString(symbol.GetLength(), month);
600    } else if (symbol == L"MMM" || symbol == L"MMMM") {
601      wsResult += pLocale->GetMonthName(month - 1, symbol == L"MMM");
602    } else if (symbol == L"E" || symbol == L"e") {
603      uint16_t wWeekDay = GetWeekDay(year, month, day);
604      wsResult += NumToString(
605          1, symbol == L"E" ? wWeekDay + 1 : (wWeekDay ? wWeekDay : 7));
606    } else if (symbol == L"EEE" || symbol == L"EEEE") {
607      wsResult +=
608          pLocale->GetDayName(GetWeekDay(year, month, day), symbol == L"EEE");
609    } else if (symbol == L"G") {
610      wsResult += pLocale->GetEraName(year > 0);
611    } else if (symbol == L"YY") {
612      wsResult += NumToString(2, year % 100);
613    } else if (symbol == L"YYYY") {
614      wsResult += NumToString(1, year);
615    } else if (symbol == L"w") {
616      wsResult += NumToString(1, GetWeekOfMonth(year, month, day));
617    } else if (symbol == L"WW") {
618      wsResult += NumToString(2, GetWeekOfYear(year, month, day));
619    }
620  }
621  return wsResult;
622}
623
624WideString TimeFormat(const WideString& wsTimePattern,
625                      IFX_Locale* pLocale,
626                      const CFX_DateTime& datetime) {
627  WideString wsResult;
628  uint8_t hour = datetime.GetHour();
629  uint8_t minute = datetime.GetMinute();
630  uint8_t second = datetime.GetSecond();
631  uint16_t millisecond = datetime.GetMillisecond();
632  int32_t ccf = 0;
633  const wchar_t* strf = wsTimePattern.c_str();
634  int32_t lenf = wsTimePattern.GetLength();
635  uint16_t wHour = hour;
636  bool bPM = false;
637  if (wsTimePattern.Contains('A')) {
638    if (wHour >= 12)
639      bPM = true;
640  }
641
642  WideStringView wsTimeSymbols(gs_wsTimeSymbols);
643  while (ccf < lenf) {
644    if (strf[ccf] == '\'') {
645      wsResult += GetLiteralText(strf, &ccf, lenf);
646      ccf++;
647      continue;
648    }
649    if (!wsTimeSymbols.Contains(strf[ccf])) {
650      wsResult += strf[ccf++];
651      continue;
652    }
653
654    WideString symbol;
655    symbol.Reserve(4);
656    symbol += strf[ccf++];
657    while (ccf < lenf && strf[ccf] == symbol[0])
658      symbol += strf[ccf++];
659
660    if (symbol == L"h" || symbol == L"hh") {
661      if (wHour > 12)
662        wHour -= 12;
663      wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 12 : wHour);
664    } else if (symbol == L"K" || symbol == L"KK") {
665      wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 24 : wHour);
666    } else if (symbol == L"k" || symbol == L"kk") {
667      if (wHour > 12)
668        wHour -= 12;
669      wsResult += NumToString(symbol.GetLength(), wHour);
670    } else if (symbol == L"H" || symbol == L"HH") {
671      wsResult += NumToString(symbol.GetLength(), wHour);
672    } else if (symbol == L"M" || symbol == L"MM") {
673      wsResult += NumToString(symbol.GetLength(), minute);
674    } else if (symbol == L"S" || symbol == L"SS") {
675      wsResult += NumToString(symbol.GetLength(), second);
676    } else if (symbol == L"FFF") {
677      wsResult += NumToString(3, millisecond);
678    } else if (symbol == L"A") {
679      wsResult += pLocale->GetMeridiemName(!bPM);
680    } else if (symbol == L"Z" || symbol == L"z") {
681      if (symbol == L"Z")
682        wsResult += L"GMT";
683
684      FX_TIMEZONE tz = pLocale->GetTimeZone();
685      if (tz.tzHour != 0 || tz.tzMinute != 0) {
686        wsResult += tz.tzHour < 0 ? L"-" : L"+";
687        wsResult +=
688            WideString::Format(L"%02d:%02d", abs(tz.tzHour), tz.tzMinute);
689      }
690    }
691  }
692  return wsResult;
693}
694
695WideString FormatDateTimeInternal(const CFX_DateTime& dt,
696                                  const WideString& wsDatePattern,
697                                  const WideString& wsTimePattern,
698                                  bool bDateFirst,
699                                  IFX_Locale* pLocale) {
700  WideString wsDateOut;
701  if (!wsDatePattern.IsEmpty())
702    wsDateOut = DateFormat(wsDatePattern, pLocale, dt);
703
704  WideString wsTimeOut;
705  if (!wsTimePattern.IsEmpty())
706    wsTimeOut = TimeFormat(wsTimePattern, pLocale, dt);
707
708  return bDateFirst ? wsDateOut + wsTimeOut : wsTimeOut + wsDateOut;
709}
710
711}  // namespace
712
713bool FX_DateFromCanonical(const WideString& wsDate, CFX_DateTime* datetime) {
714  const wchar_t* str = wsDate.c_str();
715  int len = wsDate.GetLength();
716  if (len > 10)
717    return false;
718
719  int cc = 0;
720  uint32_t year = 0;
721  if (!ExtractCountDigits(str, len, 4, &cc, &year))
722    return false;
723  if (year < 1900)
724    return false;
725  if (cc >= len) {
726    datetime->SetDate(year, 1, 1);
727    return true;
728  }
729
730  if (str[cc] == '-')
731    cc++;
732
733  uint32_t month = 0;
734  if (!ExtractCountDigits(str, len, 2, &cc, &month))
735    return false;
736  if (month > 12 || month < 1)
737    return false;
738  if (cc >= len) {
739    datetime->SetDate(year, month, 1);
740    return true;
741  }
742
743  if (str[cc] == '-')
744    cc++;
745
746  uint32_t day = 0;
747  if (!ExtractCountDigits(str, len, 2, &cc, &day))
748    return false;
749  if (day < 1)
750    return false;
751  if ((MonthHas31Days(month) && day > 31) ||
752      (MonthHas30Days(month) && day > 30)) {
753    return false;
754  }
755  if (month == 2 && day > (IsLeapYear(year) ? 29U : 28U))
756    return false;
757
758  datetime->SetDate(year, month, day);
759  return true;
760}
761
762bool FX_TimeFromCanonical(const WideStringView& wsTime,
763                          CFX_DateTime* datetime,
764                          IFX_Locale* pLocale) {
765  if (wsTime.GetLength() == 0)
766    return false;
767
768  const wchar_t* str = wsTime.unterminated_c_str();
769  int len = wsTime.GetLength();
770
771  int cc = 0;
772  uint32_t hour = 0;
773  if (!ExtractCountDigits(str, len, 2, &cc, &hour))
774    return false;
775  if (hour >= 24)
776    return false;
777  if (cc >= len) {
778    datetime->SetTime(hour, 0, 0, 0);
779    return true;
780  }
781
782  if (str[cc] == ':')
783    cc++;
784
785  uint32_t minute = 0;
786  if (!ExtractCountDigits(str, len, 2, &cc, &minute))
787    return false;
788  if (minute >= 60)
789    return false;
790
791  if (cc >= len) {
792    datetime->SetTime(hour, minute, 0, 0);
793    return true;
794  }
795
796  if (str[cc] == ':')
797    cc++;
798
799  uint32_t second = 0;
800  uint32_t millisecond = 0;
801  if (str[cc] != 'Z') {
802    if (!ExtractCountDigits(str, len, 2, &cc, &second))
803      return false;
804    if (second >= 60)
805      return false;
806    if (cc < len && str[cc] == '.') {
807      cc++;
808      if (!ExtractCountDigits(str, len, 3, &cc, &millisecond))
809        return false;
810    }
811  }
812
813  // Skip until we find a + or - for the time zone.
814  while (cc < len) {
815    if (str[cc] == '+' || str[cc] == '-')
816      break;
817    ++cc;
818  }
819
820  if (cc < len) {
821    FX_TIMEZONE tzDiff;
822    tzDiff.tzHour = 0;
823    tzDiff.tzMinute = 0;
824    if (str[cc] != 'Z')
825      cc += ParseTimeZone(str + cc, len - cc, &tzDiff);
826
827    ResolveZone(tzDiff, pLocale, &hour, &minute);
828  }
829
830  datetime->SetTime(hour, minute, second, millisecond);
831  return true;
832}
833
834CFGAS_FormatString::CFGAS_FormatString(CXFA_LocaleMgr* pLocaleMgr)
835    : m_pLocaleMgr(pLocaleMgr) {}
836
837CFGAS_FormatString::~CFGAS_FormatString() {}
838
839void CFGAS_FormatString::SplitFormatString(
840    const WideString& wsFormatString,
841    std::vector<WideString>* wsPatterns) {
842  int32_t iStrLen = wsFormatString.GetLength();
843  const wchar_t* pStr = wsFormatString.c_str();
844  const wchar_t* pToken = pStr;
845  const wchar_t* pEnd = pStr + iStrLen;
846  bool iQuote = false;
847  while (true) {
848    if (pStr >= pEnd) {
849      wsPatterns->push_back(WideString(pToken, pStr - pToken));
850      return;
851    }
852    if (*pStr == '\'') {
853      iQuote = !iQuote;
854    } else if (*pStr == L'|' && !iQuote) {
855      wsPatterns->push_back(WideString(pToken, pStr - pToken));
856      pToken = pStr + 1;
857    }
858    pStr++;
859  }
860}
861
862FX_LOCALECATEGORY CFGAS_FormatString::GetCategory(const WideString& wsPattern) {
863  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
864  int32_t ccf = 0;
865  int32_t iLenf = wsPattern.GetLength();
866  const wchar_t* pStr = wsPattern.c_str();
867  bool bBraceOpen = false;
868  WideStringView wsConstChars(gs_wsConstChars);
869  while (ccf < iLenf) {
870    if (pStr[ccf] == '\'') {
871      GetLiteralText(pStr, &ccf, iLenf);
872    } else if (!bBraceOpen && !wsConstChars.Contains(pStr[ccf])) {
873      WideString wsCategory(pStr[ccf]);
874      ccf++;
875      while (true) {
876        if (ccf == iLenf)
877          return eCategory;
878        if (pStr[ccf] == '.' || pStr[ccf] == '(')
879          break;
880        if (pStr[ccf] == '{') {
881          bBraceOpen = true;
882          break;
883        }
884        wsCategory += pStr[ccf];
885        ccf++;
886      }
887
888      uint32_t dwHash = FX_HashCode_GetW(wsCategory.AsStringView(), false);
889      if (dwHash == FX_LOCALECATEGORY_DateTimeHash)
890        return FX_LOCALECATEGORY_DateTime;
891      if (dwHash == FX_LOCALECATEGORY_TextHash)
892        return FX_LOCALECATEGORY_Text;
893      if (dwHash == FX_LOCALECATEGORY_NumHash)
894        return FX_LOCALECATEGORY_Num;
895      if (dwHash == FX_LOCALECATEGORY_ZeroHash)
896        return FX_LOCALECATEGORY_Zero;
897      if (dwHash == FX_LOCALECATEGORY_NullHash)
898        return FX_LOCALECATEGORY_Null;
899      if (dwHash == FX_LOCALECATEGORY_DateHash) {
900        if (eCategory == FX_LOCALECATEGORY_Time)
901          return FX_LOCALECATEGORY_DateTime;
902        eCategory = FX_LOCALECATEGORY_Date;
903      } else if (dwHash == FX_LOCALECATEGORY_TimeHash) {
904        if (eCategory == FX_LOCALECATEGORY_Date)
905          return FX_LOCALECATEGORY_DateTime;
906        eCategory = FX_LOCALECATEGORY_Time;
907      }
908    } else if (pStr[ccf] == '}') {
909      bBraceOpen = false;
910    }
911    ccf++;
912  }
913  return eCategory;
914}
915
916WideString CFGAS_FormatString::GetTextFormat(const WideString& wsPattern,
917                                             const WideStringView& wsCategory) {
918  int32_t ccf = 0;
919  int32_t iLenf = wsPattern.GetLength();
920  const wchar_t* pStr = wsPattern.c_str();
921  bool bBrackOpen = false;
922  WideStringView wsConstChars(gs_wsConstChars);
923  WideString wsPurgePattern;
924  while (ccf < iLenf) {
925    if (pStr[ccf] == '\'') {
926      int32_t iCurChar = ccf;
927      GetLiteralText(pStr, &ccf, iLenf);
928      wsPurgePattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
929    } else if (!bBrackOpen && !wsConstChars.Contains(pStr[ccf])) {
930      WideString wsSearchCategory(pStr[ccf]);
931      ccf++;
932      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
933             pStr[ccf] != '(') {
934        wsSearchCategory += pStr[ccf];
935        ccf++;
936      }
937      if (wsSearchCategory != wsCategory)
938        continue;
939
940      while (ccf < iLenf) {
941        if (pStr[ccf] == '(') {
942          ccf++;
943          // Skip over the encoding name.
944          while (ccf < iLenf && pStr[ccf] != ')')
945            ccf++;
946        } else if (pStr[ccf] == '{') {
947          bBrackOpen = true;
948          break;
949        }
950        ccf++;
951      }
952    } else if (pStr[ccf] != '}') {
953      wsPurgePattern += pStr[ccf];
954    }
955    ccf++;
956  }
957  if (!bBrackOpen)
958    wsPurgePattern = wsPattern;
959
960  return wsPurgePattern;
961}
962
963IFX_Locale* CFGAS_FormatString::GetNumericFormat(const WideString& wsPattern,
964                                                 int32_t* iDotIndex,
965                                                 uint32_t* dwStyle,
966                                                 WideString* wsPurgePattern) {
967  *dwStyle = 0;
968  IFX_Locale* pLocale = nullptr;
969  int32_t ccf = 0;
970  int32_t iLenf = wsPattern.GetLength();
971  const wchar_t* pStr = wsPattern.c_str();
972  bool bFindDot = false;
973  bool bBrackOpen = false;
974  WideStringView wsConstChars(gs_wsConstChars);
975  while (ccf < iLenf) {
976    if (pStr[ccf] == '\'') {
977      int32_t iCurChar = ccf;
978      GetLiteralText(pStr, &ccf, iLenf);
979      *wsPurgePattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
980    } else if (!bBrackOpen && !wsConstChars.Contains(pStr[ccf])) {
981      WideString wsCategory(pStr[ccf]);
982      ccf++;
983      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
984             pStr[ccf] != '(') {
985        wsCategory += pStr[ccf];
986        ccf++;
987      }
988      if (wsCategory != L"num") {
989        bBrackOpen = true;
990        ccf = 0;
991        continue;
992      }
993      while (ccf < iLenf) {
994        if (pStr[ccf] == '{') {
995          bBrackOpen = true;
996          break;
997        }
998        if (pStr[ccf] == '(') {
999          ccf++;
1000          WideString wsLCID;
1001          while (ccf < iLenf && pStr[ccf] != ')')
1002            wsLCID += pStr[ccf++];
1003
1004          pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
1005        } else if (pStr[ccf] == '.') {
1006          WideString wsSubCategory;
1007          ccf++;
1008          while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{')
1009            wsSubCategory += pStr[ccf++];
1010
1011          uint32_t dwSubHash =
1012              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
1013          FX_LOCALENUMSUBCATEGORY eSubCategory = FX_LOCALENUMPATTERN_Decimal;
1014          for (int32_t i = 0; i < g_iFXLocaleNumSubCatCount; i++) {
1015            if (g_FXLocaleNumSubCatData[i].uHash == dwSubHash) {
1016              eSubCategory = (FX_LOCALENUMSUBCATEGORY)g_FXLocaleNumSubCatData[i]
1017                                 .eSubCategory;
1018              break;
1019            }
1020          }
1021          if (!pLocale)
1022            pLocale = m_pLocaleMgr->GetDefLocale();
1023
1024          ASSERT(pLocale);
1025
1026          wsSubCategory = pLocale->GetNumPattern(eSubCategory);
1027          auto result = wsSubCategory.Find('.');
1028          if (result.has_value() && result.value() != 0) {
1029            *iDotIndex += wsPurgePattern->GetLength();
1030            bFindDot = true;
1031            *dwStyle |= FX_NUMSTYLE_DotVorv;
1032          }
1033          *wsPurgePattern += wsSubCategory;
1034          if (eSubCategory == FX_LOCALENUMPATTERN_Percent)
1035            *dwStyle |= FX_NUMSTYLE_Percent;
1036
1037          continue;
1038        }
1039        ccf++;
1040      }
1041    } else if (pStr[ccf] == 'E') {
1042      *dwStyle |= FX_NUMSTYLE_Exponent;
1043      *wsPurgePattern += pStr[ccf];
1044    } else if (pStr[ccf] == '%') {
1045      *dwStyle |= FX_NUMSTYLE_Percent;
1046      *wsPurgePattern += pStr[ccf];
1047    } else if (pStr[ccf] != '}') {
1048      *wsPurgePattern += pStr[ccf];
1049    }
1050    if (!bFindDot) {
1051      if (pStr[ccf] == '.' || pStr[ccf] == 'V' || pStr[ccf] == 'v') {
1052        bFindDot = true;
1053        *iDotIndex = wsPurgePattern->GetLength() - 1;
1054        *dwStyle |= FX_NUMSTYLE_DotVorv;
1055      }
1056    }
1057    ccf++;
1058  }
1059  if (!bFindDot)
1060    *iDotIndex = wsPurgePattern->GetLength();
1061  if (!pLocale)
1062    pLocale = m_pLocaleMgr->GetDefLocale();
1063  return pLocale;
1064}
1065
1066bool CFGAS_FormatString::ParseText(const WideString& wsSrcText,
1067                                   const WideString& wsPattern,
1068                                   WideString* wsValue) {
1069  wsValue->clear();
1070  if (wsSrcText.IsEmpty() || wsPattern.IsEmpty())
1071    return false;
1072
1073  WideString wsTextFormat = GetTextFormat(wsPattern, L"text");
1074  if (wsTextFormat.IsEmpty())
1075    return false;
1076
1077  int32_t iText = 0;
1078  int32_t iPattern = 0;
1079  const wchar_t* pStrText = wsSrcText.c_str();
1080  int32_t iLenText = wsSrcText.GetLength();
1081  const wchar_t* pStrPattern = wsTextFormat.c_str();
1082  int32_t iLenPattern = wsTextFormat.GetLength();
1083  while (iPattern < iLenPattern && iText < iLenText) {
1084    switch (pStrPattern[iPattern]) {
1085      case '\'': {
1086        WideString wsLiteral =
1087            GetLiteralText(pStrPattern, &iPattern, iLenPattern);
1088        int32_t iLiteralLen = wsLiteral.GetLength();
1089        if (iText + iLiteralLen > iLenText ||
1090            wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
1091          *wsValue = wsSrcText;
1092          return false;
1093        }
1094        iText += iLiteralLen;
1095        iPattern++;
1096        break;
1097      }
1098      case 'A':
1099        if (FXSYS_iswalpha(pStrText[iText])) {
1100          *wsValue += pStrText[iText];
1101          iText++;
1102        }
1103        iPattern++;
1104        break;
1105      case 'X':
1106        *wsValue += pStrText[iText];
1107        iText++;
1108        iPattern++;
1109        break;
1110      case 'O':
1111      case '0':
1112        if (FXSYS_isDecimalDigit(pStrText[iText]) ||
1113            FXSYS_iswalpha(pStrText[iText])) {
1114          *wsValue += pStrText[iText];
1115          iText++;
1116        }
1117        iPattern++;
1118        break;
1119      case '9':
1120        if (FXSYS_isDecimalDigit(pStrText[iText])) {
1121          *wsValue += pStrText[iText];
1122          iText++;
1123        }
1124        iPattern++;
1125        break;
1126      default:
1127        if (pStrPattern[iPattern] != pStrText[iText]) {
1128          *wsValue = wsSrcText;
1129          return false;
1130        }
1131        iPattern++;
1132        iText++;
1133        break;
1134    }
1135  }
1136  return iPattern == iLenPattern && iText == iLenText;
1137}
1138
1139bool CFGAS_FormatString::ParseNum(const WideString& wsSrcNum,
1140                                  const WideString& wsPattern,
1141                                  WideString* wsValue) {
1142  wsValue->clear();
1143  if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty())
1144    return false;
1145
1146  int32_t dot_index_f = -1;
1147  uint32_t dwFormatStyle = 0;
1148  WideString wsNumFormat;
1149  IFX_Locale* pLocale =
1150      GetNumericFormat(wsPattern, &dot_index_f, &dwFormatStyle, &wsNumFormat);
1151  if (!pLocale || wsNumFormat.IsEmpty())
1152    return false;
1153
1154  int32_t iExponent = 0;
1155  WideString wsDotSymbol =
1156      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
1157  WideString wsGroupSymbol =
1158      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping);
1159  int32_t iGroupLen = wsGroupSymbol.GetLength();
1160  WideString wsMinus = pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus);
1161  int32_t iMinusLen = wsMinus.GetLength();
1162  const wchar_t* str = wsSrcNum.c_str();
1163  int len = wsSrcNum.GetLength();
1164  const wchar_t* strf = wsNumFormat.c_str();
1165  int lenf = wsNumFormat.GetLength();
1166  bool bHavePercentSymbol = false;
1167  bool bNeg = false;
1168  bool bReverseParse = false;
1169  int32_t dot_index = 0;
1170
1171  // If we're looking for a '.', 'V' or 'v' and the input string does not
1172  // have a dot index for one of those, then we disable parsing the decimal.
1173  if (!GetNumericDotIndex(wsSrcNum, wsDotSymbol, &dot_index) &&
1174      (dwFormatStyle & FX_NUMSTYLE_DotVorv))
1175    bReverseParse = true;
1176
1177  // This parse is broken into two parts based on the '.' in the number
1178  // (or 'V' or 'v'). |dot_index_f| is the location of the dot in the format and
1179  // |dot_index| is the location of the dot in the number.
1180  //
1181  // This first while() starts at the '.' and walks backwards to the start of
1182  // the number. The second while() walks from the dot forwards to the end of
1183  // the decimal.
1184
1185  int ccf = dot_index_f - 1;
1186  int cc = dot_index - 1;
1187  while (ccf >= 0 && cc >= 0) {
1188    switch (strf[ccf]) {
1189      case '\'': {
1190        WideString wsLiteral = GetLiteralTextReverse(strf, &ccf);
1191        int32_t iLiteralLen = wsLiteral.GetLength();
1192        cc -= iLiteralLen - 1;
1193        if (cc < 0 || wcsncmp(str + cc, wsLiteral.c_str(), iLiteralLen))
1194          return false;
1195
1196        cc--;
1197        ccf--;
1198        break;
1199      }
1200      case '9':
1201        if (!FXSYS_isDecimalDigit(str[cc]))
1202          return false;
1203
1204        wsValue->InsertAtFront(str[cc]);
1205        cc--;
1206        ccf--;
1207        break;
1208      case 'z':
1209      case 'Z':
1210        if (strf[ccf] == 'z' || str[cc] != ' ') {
1211          if (FXSYS_isDecimalDigit(str[cc])) {
1212            wsValue->InsertAtFront(str[cc]);
1213            cc--;
1214          }
1215        } else {
1216          cc--;
1217        }
1218        ccf--;
1219        break;
1220      case 'S':
1221      case 's':
1222        if (str[cc] == '+' || (strf[ccf] == 'S' && str[cc] == ' ')) {
1223          cc--;
1224        } else {
1225          cc -= iMinusLen - 1;
1226          if (cc < 0 || wcsncmp(str + cc, wsMinus.c_str(), iMinusLen))
1227            return false;
1228
1229          cc--;
1230          bNeg = true;
1231        }
1232        ccf--;
1233        break;
1234      case 'E': {
1235        bool bExpSign = false;
1236        while (cc >= 0) {
1237          if (str[cc] == 'E' || str[cc] == 'e')
1238            break;
1239          if (FXSYS_isDecimalDigit(str[cc])) {
1240            iExponent = iExponent + FXSYS_DecimalCharToInt(str[cc]) * 10;
1241            cc--;
1242            continue;
1243          }
1244          if (str[cc] == '+') {
1245            cc--;
1246            continue;
1247          }
1248          if (cc - iMinusLen + 1 > 0 && !wcsncmp(str + (cc - iMinusLen + 1),
1249                                                 wsMinus.c_str(), iMinusLen)) {
1250            bExpSign = true;
1251            cc -= iMinusLen;
1252            continue;
1253          }
1254
1255          return false;
1256        }
1257        cc--;
1258        iExponent = bExpSign ? -iExponent : iExponent;
1259        ccf--;
1260        break;
1261      }
1262      case '$': {
1263        WideString wsSymbol =
1264            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
1265        int32_t iSymbolLen = wsSymbol.GetLength();
1266        cc -= iSymbolLen - 1;
1267        if (cc < 0 || wcsncmp(str + cc, wsSymbol.c_str(), iSymbolLen))
1268          return false;
1269
1270        cc--;
1271        ccf--;
1272        break;
1273      }
1274      case 'r':
1275      case 'R':
1276        if (ccf - 1 >= 0 && ((strf[ccf] == 'R' && strf[ccf - 1] == 'C') ||
1277                             (strf[ccf] == 'r' && strf[ccf - 1] == 'c'))) {
1278          if (strf[ccf] == 'R' && str[cc] == ' ') {
1279            cc -= 2;
1280          } else if (str[cc] == 'R' && cc - 1 >= 0 && str[cc - 1] == 'C') {
1281            bNeg = true;
1282            cc -= 2;
1283          }
1284          ccf -= 2;
1285        } else {
1286          ccf--;
1287        }
1288        break;
1289      case 'b':
1290      case 'B':
1291        if (ccf - 1 >= 0 && ((strf[ccf] == 'B' && strf[ccf - 1] == 'D') ||
1292                             (strf[ccf] == 'b' && strf[ccf - 1] == 'd'))) {
1293          if (strf[ccf] == 'B' && str[cc] == ' ') {
1294            cc -= 2;
1295          } else if (str[cc] == 'B' && cc - 1 >= 0 && str[cc - 1] == 'D') {
1296            bNeg = true;
1297            cc -= 2;
1298          }
1299          ccf -= 2;
1300        } else {
1301          ccf--;
1302        }
1303        break;
1304      case '%': {
1305        WideString wsSymbol =
1306            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
1307        int32_t iSysmbolLen = wsSymbol.GetLength();
1308        cc -= iSysmbolLen - 1;
1309        if (cc < 0 || wcsncmp(str + cc, wsSymbol.c_str(), iSysmbolLen))
1310          return false;
1311
1312        cc--;
1313        ccf--;
1314        bHavePercentSymbol = true;
1315        break;
1316      }
1317      case '.':
1318      case 'V':
1319      case 'v':
1320      case '8':
1321        return false;
1322      case ',': {
1323        if (cc >= 0) {
1324          cc -= iGroupLen - 1;
1325          if (cc >= 0 &&
1326              wcsncmp(str + cc, wsGroupSymbol.c_str(), iGroupLen) == 0) {
1327            cc--;
1328          } else {
1329            cc += iGroupLen - 1;
1330          }
1331        }
1332        ccf--;
1333        break;
1334      }
1335      case '(':
1336      case ')':
1337        if (str[cc] == strf[ccf])
1338          bNeg = true;
1339        else if (str[cc] != L' ')
1340          return false;
1341
1342        cc--;
1343        ccf--;
1344        break;
1345      default:
1346        if (strf[ccf] != str[cc])
1347          return false;
1348
1349        cc--;
1350        ccf--;
1351    }
1352  }
1353  if (cc >= 0) {
1354    if (str[cc] == '-') {
1355      bNeg = true;
1356      cc--;
1357    }
1358    if (cc >= 0)
1359      return false;
1360  }
1361  if (dot_index < len && (dwFormatStyle & FX_NUMSTYLE_DotVorv))
1362    *wsValue += '.';
1363  if (!bReverseParse) {
1364    ccf = dot_index_f + 1;
1365    cc = (dot_index == len) ? len : dot_index + 1;
1366    while (cc < len && ccf < lenf) {
1367      switch (strf[ccf]) {
1368        case '\'': {
1369          WideString wsLiteral = GetLiteralText(strf, &ccf, lenf);
1370          int32_t iLiteralLen = wsLiteral.GetLength();
1371          if (cc + iLiteralLen > len ||
1372              wcsncmp(str + cc, wsLiteral.c_str(), iLiteralLen)) {
1373            return false;
1374          }
1375          cc += iLiteralLen;
1376          ccf++;
1377          break;
1378        }
1379        case '9':
1380          if (!FXSYS_isDecimalDigit(str[cc]))
1381            return false;
1382
1383          *wsValue += str[cc];
1384          cc++;
1385          ccf++;
1386          break;
1387        case 'z':
1388        case 'Z':
1389          if (strf[ccf] == 'z' || str[cc] != ' ') {
1390            if (FXSYS_isDecimalDigit(str[cc])) {
1391              *wsValue += str[cc];
1392              cc++;
1393            }
1394          } else {
1395            cc++;
1396          }
1397          ccf++;
1398          break;
1399        case 'S':
1400        case 's':
1401          if (str[cc] == '+' || (strf[ccf] == 'S' && str[cc] == ' ')) {
1402            cc++;
1403          } else {
1404            if (cc + iMinusLen > len ||
1405                wcsncmp(str + cc, wsMinus.c_str(), iMinusLen)) {
1406              return false;
1407            }
1408            bNeg = true;
1409            cc += iMinusLen;
1410          }
1411          ccf++;
1412          break;
1413        case 'E': {
1414          if (cc >= len || (str[cc] != 'E' && str[cc] != 'e'))
1415            return false;
1416
1417          bool bExpSign = false;
1418          cc++;
1419          if (cc < len) {
1420            if (str[cc] == '+') {
1421              cc++;
1422            } else if (str[cc] == '-') {
1423              bExpSign = true;
1424              cc++;
1425            }
1426          }
1427          while (cc < len) {
1428            if (!FXSYS_isDecimalDigit(str[cc]))
1429              break;
1430
1431            iExponent = iExponent * 10 + FXSYS_DecimalCharToInt(str[cc]);
1432            cc++;
1433          }
1434          iExponent = bExpSign ? -iExponent : iExponent;
1435          ccf++;
1436          break;
1437        }
1438        case '$': {
1439          WideString wsSymbol =
1440              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
1441          int32_t iSymbolLen = wsSymbol.GetLength();
1442          if (cc + iSymbolLen > len ||
1443              wcsncmp(str + cc, wsSymbol.c_str(), iSymbolLen)) {
1444            return false;
1445          }
1446          cc += iSymbolLen;
1447          ccf++;
1448          break;
1449        }
1450        case 'c':
1451        case 'C':
1452          if (ccf + 1 < lenf && ((strf[ccf] == 'C' && strf[ccf + 1] == 'R') ||
1453                                 (strf[ccf] == 'c' && strf[ccf + 1] == 'r'))) {
1454            if (strf[ccf] == 'C' && str[cc] == ' ') {
1455              cc++;
1456            } else if (str[cc] == 'C' && cc + 1 < len && str[cc + 1] == 'R') {
1457              bNeg = true;
1458              cc += 2;
1459            }
1460            ccf += 2;
1461          }
1462          break;
1463        case 'd':
1464        case 'D':
1465          if (ccf + 1 < lenf && ((strf[ccf] == 'D' && strf[ccf + 1] == 'B') ||
1466                                 (strf[ccf] == 'd' && strf[ccf + 1] == 'b'))) {
1467            if (strf[ccf] == 'D' && str[cc] == ' ') {
1468              cc++;
1469            } else if (str[cc] == 'D' && cc + 1 < len && str[cc + 1] == 'B') {
1470              bNeg = true;
1471              cc += 2;
1472            }
1473            ccf += 2;
1474          }
1475          break;
1476        case '.':
1477        case 'V':
1478        case 'v':
1479          return false;
1480        case '%': {
1481          WideString wsSymbol =
1482              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
1483          int32_t iSysmbolLen = wsSymbol.GetLength();
1484          if (cc + iSysmbolLen <= len &&
1485              !wcsncmp(str + cc, wsSymbol.c_str(), iSysmbolLen)) {
1486            cc += iSysmbolLen;
1487          }
1488          ccf++;
1489          bHavePercentSymbol = true;
1490        } break;
1491        case '8': {
1492          while (ccf < lenf && strf[ccf] == '8')
1493            ccf++;
1494
1495          while (cc < len && FXSYS_isDecimalDigit(str[cc])) {
1496            *wsValue += str[cc];
1497            cc++;
1498          }
1499        } break;
1500        case ',': {
1501          if (cc + iGroupLen <= len &&
1502              wcsncmp(str + cc, wsGroupSymbol.c_str(), iGroupLen) == 0) {
1503            cc += iGroupLen;
1504          }
1505          ccf++;
1506          break;
1507        }
1508        case '(':
1509        case ')':
1510          if (str[cc] == strf[ccf])
1511            bNeg = true;
1512          else if (str[cc] != L' ')
1513            return false;
1514
1515          cc++;
1516          ccf++;
1517          break;
1518        default:
1519          if (strf[ccf] != str[cc])
1520            return false;
1521
1522          cc++;
1523          ccf++;
1524      }
1525    }
1526    if (cc != len)
1527      return false;
1528  }
1529  if (iExponent || bHavePercentSymbol) {
1530    CFX_Decimal decimal = CFX_Decimal(wsValue->AsStringView());
1531    if (iExponent) {
1532      decimal = decimal *
1533                CFX_Decimal(FXSYS_pow(10, static_cast<float>(iExponent)), 3);
1534    }
1535    if (bHavePercentSymbol)
1536      decimal = decimal / CFX_Decimal(100);
1537
1538    *wsValue = decimal;
1539  }
1540  if (bNeg)
1541    wsValue->InsertAtFront(L'-');
1542
1543  return true;
1544}
1545
1546FX_DATETIMETYPE CFGAS_FormatString::GetDateTimeFormat(
1547    const WideString& wsPattern,
1548    IFX_Locale** pLocale,
1549    WideString* wsDatePattern,
1550    WideString* wsTimePattern) {
1551  *pLocale = nullptr;
1552  WideString wsTempPattern;
1553  FX_LOCALECATEGORY eCategory = FX_LOCALECATEGORY_Unknown;
1554  int32_t ccf = 0;
1555  int32_t iLenf = wsPattern.GetLength();
1556  const wchar_t* pStr = wsPattern.c_str();
1557  int32_t iFindCategory = 0;
1558  bool bBraceOpen = false;
1559  WideStringView wsConstChars(gs_wsConstChars);
1560  while (ccf < iLenf) {
1561    if (pStr[ccf] == '\'') {
1562      int32_t iCurChar = ccf;
1563      GetLiteralText(pStr, &ccf, iLenf);
1564      wsTempPattern += WideStringView(pStr + iCurChar, ccf - iCurChar + 1);
1565    } else if (!bBraceOpen && iFindCategory != 3 &&
1566               !wsConstChars.Contains(pStr[ccf])) {
1567      WideString wsCategory(pStr[ccf]);
1568      ccf++;
1569      while (ccf < iLenf && pStr[ccf] != '{' && pStr[ccf] != '.' &&
1570             pStr[ccf] != '(') {
1571        if (pStr[ccf] == 'T') {
1572          *wsDatePattern = wsPattern.Left(ccf);
1573          *wsTimePattern = wsPattern.Right(wsPattern.GetLength() - ccf);
1574          wsTimePattern->SetAt(0, ' ');
1575          if (!*pLocale)
1576            *pLocale = m_pLocaleMgr->GetDefLocale();
1577
1578          return FX_DATETIMETYPE_DateTime;
1579        }
1580        wsCategory += pStr[ccf];
1581        ccf++;
1582      }
1583      if (!(iFindCategory & 1) && wsCategory == L"date") {
1584        iFindCategory |= 1;
1585        eCategory = FX_LOCALECATEGORY_Date;
1586        if (iFindCategory & 2)
1587          iFindCategory = 4;
1588      } else if (!(iFindCategory & 2) && wsCategory == L"time") {
1589        iFindCategory |= 2;
1590        eCategory = FX_LOCALECATEGORY_Time;
1591      } else if (wsCategory == L"datetime") {
1592        iFindCategory = 3;
1593        eCategory = FX_LOCALECATEGORY_DateTime;
1594      } else {
1595        continue;
1596      }
1597      while (ccf < iLenf) {
1598        if (pStr[ccf] == '{') {
1599          bBraceOpen = true;
1600          break;
1601        }
1602        if (pStr[ccf] == '(') {
1603          ccf++;
1604          WideString wsLCID;
1605          while (ccf < iLenf && pStr[ccf] != ')')
1606            wsLCID += pStr[ccf++];
1607
1608          *pLocale = m_pLocaleMgr->GetLocaleByName(wsLCID);
1609        } else if (pStr[ccf] == '.') {
1610          WideString wsSubCategory;
1611          ccf++;
1612          while (ccf < iLenf && pStr[ccf] != '(' && pStr[ccf] != '{')
1613            wsSubCategory += pStr[ccf++];
1614
1615          uint32_t dwSubHash =
1616              FX_HashCode_GetW(wsSubCategory.AsStringView(), false);
1617          FX_LOCALEDATETIMESUBCATEGORY eSubCategory =
1618              FX_LOCALEDATETIMESUBCATEGORY_Medium;
1619          for (int32_t i = 0; i < g_iFXLocaleDateTimeSubCatCount; i++) {
1620            if (g_FXLocaleDateTimeSubCatData[i].uHash == dwSubHash) {
1621              eSubCategory =
1622                  (FX_LOCALEDATETIMESUBCATEGORY)g_FXLocaleDateTimeSubCatData[i]
1623                      .eSubCategory;
1624              break;
1625            }
1626          }
1627          if (!*pLocale)
1628            *pLocale = m_pLocaleMgr->GetDefLocale();
1629          ASSERT(*pLocale);
1630
1631          switch (eCategory) {
1632            case FX_LOCALECATEGORY_Date:
1633              *wsDatePattern =
1634                  wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1635              break;
1636            case FX_LOCALECATEGORY_Time:
1637              *wsTimePattern =
1638                  wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
1639              break;
1640            case FX_LOCALECATEGORY_DateTime:
1641              *wsDatePattern =
1642                  wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1643              *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
1644              break;
1645            default:
1646              break;
1647          }
1648          wsTempPattern.clear();
1649          continue;
1650        }
1651        ccf++;
1652      }
1653    } else if (pStr[ccf] == '}') {
1654      bBraceOpen = false;
1655      if (!wsTempPattern.IsEmpty()) {
1656        if (eCategory == FX_LOCALECATEGORY_Time)
1657          *wsTimePattern = wsTempPattern;
1658        else if (eCategory == FX_LOCALECATEGORY_Date)
1659          *wsDatePattern = wsTempPattern;
1660
1661        wsTempPattern.clear();
1662      }
1663    } else {
1664      wsTempPattern += pStr[ccf];
1665    }
1666    ccf++;
1667  }
1668
1669  if (!wsTempPattern.IsEmpty()) {
1670    if (eCategory == FX_LOCALECATEGORY_Date)
1671      *wsDatePattern += wsTempPattern;
1672    else
1673      *wsTimePattern += wsTempPattern;
1674  }
1675  if (!*pLocale)
1676    *pLocale = m_pLocaleMgr->GetDefLocale();
1677  if (!iFindCategory) {
1678    wsTimePattern->clear();
1679    *wsDatePattern = wsPattern;
1680  }
1681  return (FX_DATETIMETYPE)iFindCategory;
1682}
1683
1684bool CFGAS_FormatString::ParseDateTime(const WideString& wsSrcDateTime,
1685                                       const WideString& wsPattern,
1686                                       FX_DATETIMETYPE eDateTimeType,
1687                                       CFX_DateTime* dtValue) {
1688  dtValue->Reset();
1689  if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty())
1690    return false;
1691
1692  WideString wsDatePattern;
1693  WideString wsTimePattern;
1694  IFX_Locale* pLocale = nullptr;
1695  FX_DATETIMETYPE eCategory =
1696      GetDateTimeFormat(wsPattern, &pLocale, &wsDatePattern, &wsTimePattern);
1697  if (!pLocale)
1698    return false;
1699  if (eCategory == FX_DATETIMETYPE_Unknown)
1700    eCategory = eDateTimeType;
1701  if (eCategory == FX_DATETIMETYPE_Unknown)
1702    return false;
1703  if (eCategory == FX_DATETIMETYPE_TimeDate) {
1704    int32_t iStart = 0;
1705    if (!ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1706                         &iStart)) {
1707      return false;
1708    }
1709    if (!ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1710                         &iStart)) {
1711      return false;
1712    }
1713  } else {
1714    int32_t iStart = 0;
1715    if ((eCategory & FX_DATETIMETYPE_Date) &&
1716        !ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1717                         &iStart)) {
1718      return false;
1719    }
1720    if ((eCategory & FX_DATETIMETYPE_Time) &&
1721        !ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1722                         &iStart)) {
1723      return false;
1724    }
1725  }
1726  return true;
1727}
1728
1729bool CFGAS_FormatString::ParseZero(const WideString& wsSrcText,
1730                                   const WideString& wsPattern) {
1731  WideString wsTextFormat = GetTextFormat(wsPattern, L"zero");
1732
1733  int32_t iText = 0;
1734  int32_t iPattern = 0;
1735  const wchar_t* pStrText = wsSrcText.c_str();
1736  int32_t iLenText = wsSrcText.GetLength();
1737  const wchar_t* pStrPattern = wsTextFormat.c_str();
1738  int32_t iLenPattern = wsTextFormat.GetLength();
1739  while (iPattern < iLenPattern && iText < iLenText) {
1740    if (pStrPattern[iPattern] == '\'') {
1741      WideString wsLiteral =
1742          GetLiteralText(pStrPattern, &iPattern, iLenPattern);
1743      int32_t iLiteralLen = wsLiteral.GetLength();
1744      if (iText + iLiteralLen > iLenText ||
1745          wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
1746        return false;
1747      }
1748      iText += iLiteralLen;
1749      iPattern++;
1750      continue;
1751    }
1752    if (pStrPattern[iPattern] != pStrText[iText])
1753      return false;
1754
1755    iText++;
1756    iPattern++;
1757  }
1758  return iPattern == iLenPattern && iText == iLenText;
1759}
1760
1761bool CFGAS_FormatString::ParseNull(const WideString& wsSrcText,
1762                                   const WideString& wsPattern) {
1763  WideString wsTextFormat = GetTextFormat(wsPattern, L"null");
1764
1765  int32_t iText = 0;
1766  int32_t iPattern = 0;
1767  const wchar_t* pStrText = wsSrcText.c_str();
1768  int32_t iLenText = wsSrcText.GetLength();
1769  const wchar_t* pStrPattern = wsTextFormat.c_str();
1770  int32_t iLenPattern = wsTextFormat.GetLength();
1771  while (iPattern < iLenPattern && iText < iLenText) {
1772    if (pStrPattern[iPattern] == '\'') {
1773      WideString wsLiteral =
1774          GetLiteralText(pStrPattern, &iPattern, iLenPattern);
1775      int32_t iLiteralLen = wsLiteral.GetLength();
1776      if (iText + iLiteralLen > iLenText ||
1777          wcsncmp(pStrText + iText, wsLiteral.c_str(), iLiteralLen)) {
1778        return false;
1779      }
1780      iText += iLiteralLen;
1781      iPattern++;
1782      continue;
1783    }
1784    if (pStrPattern[iPattern] != pStrText[iText])
1785      return false;
1786
1787    iText++;
1788    iPattern++;
1789  }
1790  return iPattern == iLenPattern && iText == iLenText;
1791}
1792
1793bool CFGAS_FormatString::FormatText(const WideString& wsSrcText,
1794                                    const WideString& wsPattern,
1795                                    WideString* wsOutput) {
1796  if (wsPattern.IsEmpty())
1797    return false;
1798
1799  int32_t iLenText = wsSrcText.GetLength();
1800  if (iLenText == 0)
1801    return false;
1802
1803  WideString wsTextFormat = GetTextFormat(wsPattern, L"text");
1804
1805  int32_t iText = 0;
1806  int32_t iPattern = 0;
1807  const wchar_t* pStrText = wsSrcText.c_str();
1808  const wchar_t* pStrPattern = wsTextFormat.c_str();
1809  int32_t iLenPattern = wsTextFormat.GetLength();
1810  while (iPattern < iLenPattern) {
1811    switch (pStrPattern[iPattern]) {
1812      case '\'': {
1813        *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
1814        iPattern++;
1815        break;
1816      }
1817      case 'A':
1818        if (iText >= iLenText || !FXSYS_iswalpha(pStrText[iText]))
1819          return false;
1820
1821        *wsOutput += pStrText[iText++];
1822        iPattern++;
1823        break;
1824      case 'X':
1825        if (iText >= iLenText)
1826          return false;
1827
1828        *wsOutput += pStrText[iText++];
1829        iPattern++;
1830        break;
1831      case 'O':
1832      case '0':
1833        if (iText >= iLenText || (!FXSYS_isDecimalDigit(pStrText[iText]) &&
1834                                  !FXSYS_iswalpha(pStrText[iText]))) {
1835          return false;
1836        }
1837        *wsOutput += pStrText[iText++];
1838        iPattern++;
1839        break;
1840      case '9':
1841        if (iText >= iLenText || !FXSYS_isDecimalDigit(pStrText[iText]))
1842          return false;
1843
1844        *wsOutput += pStrText[iText++];
1845        iPattern++;
1846        break;
1847      default:
1848        *wsOutput += pStrPattern[iPattern++];
1849        break;
1850    }
1851  }
1852  return iText == iLenText;
1853}
1854
1855bool CFGAS_FormatString::FormatStrNum(const WideStringView& wsInputNum,
1856                                      const WideString& wsPattern,
1857                                      WideString* wsOutput) {
1858  if (wsInputNum.IsEmpty() || wsPattern.IsEmpty())
1859    return false;
1860
1861  int32_t dot_index_f = -1;
1862  uint32_t dwNumStyle = 0;
1863  WideString wsNumFormat;
1864  IFX_Locale* pLocale =
1865      GetNumericFormat(wsPattern, &dot_index_f, &dwNumStyle, &wsNumFormat);
1866  if (!pLocale || wsNumFormat.IsEmpty())
1867    return false;
1868
1869  int32_t cc = 0, ccf = 0;
1870  const wchar_t* strf = wsNumFormat.c_str();
1871  int lenf = wsNumFormat.GetLength();
1872  WideString wsSrcNum(wsInputNum);
1873  wsSrcNum.TrimLeft('0');
1874  if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.')
1875    wsSrcNum.InsertAtFront('0');
1876
1877  CFX_Decimal decimal = CFX_Decimal(wsSrcNum.AsStringView());
1878  if (dwNumStyle & FX_NUMSTYLE_Percent) {
1879    decimal = decimal * CFX_Decimal(100);
1880    wsSrcNum = decimal;
1881  }
1882
1883  int32_t exponent = 0;
1884  if (dwNumStyle & FX_NUMSTYLE_Exponent) {
1885    int fixed_count = 0;
1886    while (ccf < dot_index_f) {
1887      switch (strf[ccf]) {
1888        case '\'':
1889          GetLiteralText(strf, &ccf, dot_index_f);
1890          break;
1891        case '9':
1892        case 'z':
1893        case 'Z':
1894          fixed_count++;
1895          break;
1896      }
1897      ccf++;
1898    }
1899
1900    int threshold = 1;
1901    while (fixed_count > 1) {
1902      threshold *= 10;
1903      fixed_count--;
1904    }
1905    if (decimal != CFX_Decimal(0)) {
1906      if (decimal < CFX_Decimal(threshold)) {
1907        decimal = decimal * CFX_Decimal(10);
1908        exponent = -1;
1909        while (decimal < CFX_Decimal(threshold)) {
1910          decimal = decimal * CFX_Decimal(10);
1911          exponent -= 1;
1912        }
1913      } else if (decimal > CFX_Decimal(threshold)) {
1914        threshold *= 10;
1915        while (decimal > CFX_Decimal(threshold)) {
1916          decimal = decimal / CFX_Decimal(10);
1917          exponent += 1;
1918        }
1919      }
1920    }
1921  }
1922
1923  bool bTrimTailZeros = false;
1924  int32_t iTreading =
1925      GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
1926  int32_t scale = decimal.GetScale();
1927  if (iTreading < scale) {
1928    decimal.SetScale(iTreading);
1929    wsSrcNum = decimal;
1930  }
1931  if (bTrimTailZeros && scale > 0 && iTreading > 0) {
1932    wsSrcNum.TrimRight(L"0");
1933    wsSrcNum.TrimRight(L".");
1934  }
1935
1936  WideString wsGroupSymbol =
1937      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Grouping);
1938  bool bNeg = false;
1939  if (wsSrcNum[0] == '-') {
1940    bNeg = true;
1941    wsSrcNum.Delete(0, 1);
1942  }
1943
1944  bool bAddNeg = false;
1945  const wchar_t* str = wsSrcNum.c_str();
1946  int len = wsSrcNum.GetLength();
1947  auto dot_index = wsSrcNum.Find('.');
1948  if (!dot_index.has_value())
1949    dot_index = len;
1950
1951  ccf = dot_index_f - 1;
1952  cc = dot_index.value() - 1;
1953  while (ccf >= 0) {
1954    switch (strf[ccf]) {
1955      case '9':
1956        if (cc >= 0) {
1957          if (!FXSYS_isDecimalDigit(str[cc]))
1958            return false;
1959
1960          wsOutput->InsertAtFront(str[cc]);
1961          cc--;
1962        } else {
1963          wsOutput->InsertAtFront(L'0');
1964        }
1965        ccf--;
1966        break;
1967      case 'z':
1968        if (cc >= 0) {
1969          if (!FXSYS_isDecimalDigit(str[cc]))
1970            return false;
1971          if (str[0] != '0')
1972            wsOutput->InsertAtFront(str[cc]);
1973
1974          cc--;
1975        }
1976        ccf--;
1977        break;
1978      case 'Z':
1979        if (cc >= 0) {
1980          if (!FXSYS_isDecimalDigit(str[cc]))
1981            return false;
1982
1983          wsOutput->InsertAtFront(str[0] == '0' ? L' ' : str[cc]);
1984          cc--;
1985        } else {
1986          wsOutput->InsertAtFront(L' ');
1987        }
1988        ccf--;
1989        break;
1990      case 'S':
1991        if (bNeg) {
1992          *wsOutput =
1993              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
1994          bAddNeg = true;
1995        } else {
1996          wsOutput->InsertAtFront(L' ');
1997        }
1998        ccf--;
1999        break;
2000      case 's':
2001        if (bNeg) {
2002          *wsOutput =
2003              pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
2004          bAddNeg = true;
2005        }
2006        ccf--;
2007        break;
2008      case 'E': {
2009        *wsOutput = WideString::Format(L"E%+d", exponent) + *wsOutput;
2010        ccf--;
2011        break;
2012      }
2013      case '$': {
2014        *wsOutput =
2015            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol) +
2016            *wsOutput;
2017        ccf--;
2018        break;
2019      }
2020      case 'r':
2021        if (ccf - 1 >= 0 && strf[ccf - 1] == 'c') {
2022          if (bNeg)
2023            *wsOutput = L"CR" + *wsOutput;
2024
2025          ccf -= 2;
2026          bAddNeg = true;
2027        }
2028        break;
2029      case 'R':
2030        if (ccf - 1 >= 0 && strf[ccf - 1] == 'C') {
2031          *wsOutput = bNeg ? L"CR" : L"  " + *wsOutput;
2032          ccf -= 2;
2033          bAddNeg = true;
2034        }
2035        break;
2036      case 'b':
2037        if (ccf - 1 >= 0 && strf[ccf - 1] == 'd') {
2038          if (bNeg)
2039            *wsOutput = L"db" + *wsOutput;
2040
2041          ccf -= 2;
2042          bAddNeg = true;
2043        }
2044        break;
2045      case 'B':
2046        if (ccf - 1 >= 0 && strf[ccf - 1] == 'D') {
2047          *wsOutput = bNeg ? L"DB" : L"  " + *wsOutput;
2048          ccf -= 2;
2049          bAddNeg = true;
2050        }
2051        break;
2052      case '%': {
2053        *wsOutput =
2054            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent) + *wsOutput;
2055        ccf--;
2056        break;
2057      }
2058      case ',':
2059        if (cc >= 0)
2060          *wsOutput = wsGroupSymbol + *wsOutput;
2061
2062        ccf--;
2063        break;
2064      case '(':
2065        wsOutput->InsertAtFront(bNeg ? L'(' : L' ');
2066        bAddNeg = true;
2067        ccf--;
2068        break;
2069      case ')':
2070        wsOutput->InsertAtFront(bNeg ? L')' : L' ');
2071        ccf--;
2072        break;
2073      case '\'':
2074        *wsOutput = GetLiteralTextReverse(strf, &ccf) + *wsOutput;
2075        ccf--;
2076        break;
2077      default:
2078        wsOutput->InsertAtFront(strf[ccf]);
2079        ccf--;
2080    }
2081  }
2082
2083  if (cc >= 0) {
2084    int nPos = dot_index.value() % 3;
2085    wsOutput->clear();
2086    for (int32_t i = 0;
2087         i < pdfium::base::checked_cast<int32_t>(dot_index.value()); i++) {
2088      if (i % 3 == nPos && i != 0)
2089        *wsOutput += wsGroupSymbol;
2090      *wsOutput += wsSrcNum[i];
2091    }
2092    if (pdfium::base::checked_cast<int32_t>(dot_index.value()) < len) {
2093      *wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
2094      *wsOutput += wsSrcNum.Right(len - dot_index.value() - 1);
2095    }
2096    if (bNeg) {
2097      *wsOutput =
2098          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
2099    }
2100    return false;
2101  }
2102  if (dot_index_f ==
2103      pdfium::base::checked_cast<int32_t>(wsNumFormat.GetLength())) {
2104    if (!bAddNeg && bNeg) {
2105      *wsOutput =
2106          pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) + *wsOutput;
2107    }
2108    return true;
2109  }
2110
2111  WideString wsDotSymbol =
2112      pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Decimal);
2113  if (strf[dot_index_f] == 'V') {
2114    *wsOutput += wsDotSymbol;
2115  } else if (strf[dot_index_f] == '.') {
2116    if (pdfium::base::checked_cast<int32_t>(dot_index.value()) < len)
2117      *wsOutput += wsDotSymbol;
2118    else if (strf[dot_index_f + 1] == '9' || strf[dot_index_f + 1] == 'Z')
2119      *wsOutput += wsDotSymbol;
2120  }
2121
2122  ccf = dot_index_f + 1;
2123  cc = dot_index.value() + 1;
2124  while (ccf < lenf) {
2125    switch (strf[ccf]) {
2126      case '\'':
2127        *wsOutput += GetLiteralText(strf, &ccf, lenf);
2128        ccf++;
2129        break;
2130      case '9':
2131        if (cc < len) {
2132          if (!FXSYS_isDecimalDigit(str[cc]))
2133            return false;
2134
2135          *wsOutput += str[cc];
2136          cc++;
2137        } else {
2138          *wsOutput += L'0';
2139        }
2140        ccf++;
2141        break;
2142      case 'z':
2143        if (cc < len) {
2144          if (!FXSYS_isDecimalDigit(str[cc]))
2145            return false;
2146
2147          *wsOutput += str[cc];
2148          cc++;
2149        }
2150        ccf++;
2151        break;
2152      case 'Z':
2153        if (cc < len) {
2154          if (!FXSYS_isDecimalDigit(str[cc]))
2155            return false;
2156
2157          *wsOutput += str[cc];
2158          cc++;
2159        } else {
2160          *wsOutput += L'0';
2161        }
2162        ccf++;
2163        break;
2164      case 'E': {
2165        *wsOutput += WideString::Format(L"E%+d", exponent);
2166        ccf++;
2167        break;
2168      }
2169      case '$':
2170        *wsOutput +=
2171            pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_CurrencySymbol);
2172        ccf++;
2173        break;
2174      case 'c':
2175        if (ccf + 1 < lenf && strf[ccf + 1] == 'r') {
2176          if (bNeg)
2177            *wsOutput += L"CR";
2178
2179          ccf += 2;
2180          bAddNeg = true;
2181        }
2182        break;
2183      case 'C':
2184        if (ccf + 1 < lenf && strf[ccf + 1] == 'R') {
2185          *wsOutput += bNeg ? L"CR" : L"  ";
2186          ccf += 2;
2187          bAddNeg = true;
2188        }
2189        break;
2190      case 'd':
2191        if (ccf + 1 < lenf && strf[ccf + 1] == 'b') {
2192          if (bNeg)
2193            *wsOutput += L"db";
2194
2195          ccf += 2;
2196          bAddNeg = true;
2197        }
2198        break;
2199      case 'D':
2200        if (ccf + 1 < lenf && strf[ccf + 1] == 'B') {
2201          *wsOutput += bNeg ? L"DB" : L"  ";
2202          ccf += 2;
2203          bAddNeg = true;
2204        }
2205        break;
2206      case '%':
2207        *wsOutput += pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Percent);
2208        ccf++;
2209        break;
2210      case '8':
2211        while (ccf < lenf && strf[ccf] == '8')
2212          ccf++;
2213        while (cc < len && FXSYS_isDecimalDigit(str[cc])) {
2214          *wsOutput += str[cc];
2215          cc++;
2216        }
2217        break;
2218      case ',':
2219        *wsOutput += wsGroupSymbol;
2220        ccf++;
2221        break;
2222      case '(':
2223        *wsOutput += bNeg ? '(' : ' ';
2224        bAddNeg = true;
2225        ccf++;
2226        break;
2227      case ')':
2228        *wsOutput += bNeg ? ')' : ' ';
2229        ccf++;
2230        break;
2231      default:
2232        ccf++;
2233    }
2234  }
2235  if (!bAddNeg && bNeg) {
2236    *wsOutput = pLocale->GetNumbericSymbol(FX_LOCALENUMSYMBOL_Minus) +
2237                (*wsOutput)[0] + wsOutput->Right(wsOutput->GetLength() - 1);
2238  }
2239  return true;
2240}
2241
2242bool CFGAS_FormatString::FormatNum(const WideString& wsSrcNum,
2243                                   const WideString& wsPattern,
2244                                   WideString* wsOutput) {
2245  if (wsSrcNum.IsEmpty() || wsPattern.IsEmpty())
2246    return false;
2247  return FormatStrNum(wsSrcNum.AsStringView(), wsPattern, wsOutput);
2248}
2249
2250bool CFGAS_FormatString::FormatDateTime(const WideString& wsSrcDateTime,
2251                                        const WideString& wsPattern,
2252                                        FX_DATETIMETYPE eDateTimeType,
2253                                        WideString* wsOutput) {
2254  if (wsSrcDateTime.IsEmpty() || wsPattern.IsEmpty())
2255    return false;
2256
2257  WideString wsDatePattern;
2258  WideString wsTimePattern;
2259  IFX_Locale* pLocale = nullptr;
2260  FX_DATETIMETYPE eCategory =
2261      GetDateTimeFormat(wsPattern, &pLocale, &wsDatePattern, &wsTimePattern);
2262  if (!pLocale)
2263    return false;
2264
2265  if (eCategory == FX_DATETIMETYPE_Unknown) {
2266    if (eDateTimeType == FX_DATETIMETYPE_Time) {
2267      wsTimePattern = wsDatePattern;
2268      wsDatePattern.clear();
2269    }
2270    eCategory = eDateTimeType;
2271  }
2272  if (eCategory == FX_DATETIMETYPE_Unknown)
2273    return false;
2274
2275  CFX_DateTime dt;
2276  auto iT = wsSrcDateTime.Find(L"T");
2277  if (!iT.has_value()) {
2278    if (eCategory == FX_DATETIMETYPE_Date &&
2279        FX_DateFromCanonical(wsSrcDateTime, &dt)) {
2280      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2281                                         pLocale);
2282      return true;
2283    }
2284    if (eCategory == FX_DATETIMETYPE_Time &&
2285        FX_TimeFromCanonical(wsSrcDateTime.AsStringView(), &dt, pLocale)) {
2286      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2287                                         pLocale);
2288      return true;
2289    }
2290  } else {
2291    WideString wsSrcDate(wsSrcDateTime.c_str(), iT.value());
2292    WideStringView wsSrcTime(wsSrcDateTime.c_str() + iT.value() + 1,
2293                             wsSrcDateTime.GetLength() - iT.value() - 1);
2294    if (wsSrcDate.IsEmpty() || wsSrcTime.IsEmpty())
2295      return false;
2296    if (FX_DateFromCanonical(wsSrcDate, &dt) &&
2297        FX_TimeFromCanonical(wsSrcTime, &dt, pLocale)) {
2298      *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
2299                                         eCategory != FX_DATETIMETYPE_TimeDate,
2300                                         pLocale);
2301      return true;
2302    }
2303  }
2304  return false;
2305}
2306
2307bool CFGAS_FormatString::FormatZero(const WideString& wsPattern,
2308                                    WideString* wsOutput) {
2309  if (wsPattern.IsEmpty())
2310    return false;
2311
2312  WideString wsTextFormat = GetTextFormat(wsPattern, L"zero");
2313  int32_t iPattern = 0;
2314  const wchar_t* pStrPattern = wsTextFormat.c_str();
2315  int32_t iLenPattern = wsTextFormat.GetLength();
2316  while (iPattern < iLenPattern) {
2317    if (pStrPattern[iPattern] == '\'') {
2318      *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
2319      iPattern++;
2320    } else {
2321      *wsOutput += pStrPattern[iPattern++];
2322    }
2323  }
2324  return true;
2325}
2326
2327bool CFGAS_FormatString::FormatNull(const WideString& wsPattern,
2328                                    WideString* wsOutput) {
2329  if (wsPattern.IsEmpty())
2330    return false;
2331
2332  WideString wsTextFormat = GetTextFormat(wsPattern, L"null");
2333  int32_t iPattern = 0;
2334  const wchar_t* pStrPattern = wsTextFormat.c_str();
2335  int32_t iLenPattern = wsTextFormat.GetLength();
2336  while (iPattern < iLenPattern) {
2337    if (pStrPattern[iPattern] == '\'') {
2338      *wsOutput += GetLiteralText(pStrPattern, &iPattern, iLenPattern);
2339      iPattern++;
2340      continue;
2341    }
2342    *wsOutput += pStrPattern[iPattern++];
2343  }
2344  return true;
2345}
2346