Line data Source code
1 : // Copyright 2023 The terCAD team. All rights reserved.
2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
3 :
4 : import 'package:app_finance/_classes/herald/app_locale.dart';
5 : import 'package:intl/intl.dart';
6 :
7 : class _Assertion {
8 : int min = 0;
9 : int max = 0;
10 : int length = 0;
11 : }
12 :
13 : class DateFormatHelper {
14 1 : String detectFormat(List<String> data, String locale, [List<String>? ampm]) {
15 0 : ampm ??= [AppLocale.labels.dtAm, AppLocale.labels.dtPm];
16 1 : List<String?> format = [];
17 1 : Map<int, _Assertion> check = {};
18 3 : for (int i = 0; i < data.length; i++) {
19 2 : final value = splitDateTime(data[i]);
20 3 : if (format.length < value.length) {
21 5 : format.addAll(List<String?>.filled(value.length - format.length, null));
22 : }
23 3 : for (int j = 0; j < value.length; j++) {
24 2 : final nm = int.tryParse(value[j]);
25 4 : if (nm != null && nm > 0 && value[j].length == 8) {
26 1 : format[j] = 'yyyyMMdd';
27 2 : } else if (value[j].startsWith('T')) {
28 11 : format[j] = ["'T'", 'HH', if (value[j].length > 3) 'mm', if (value[j].length > 5) 'ss'].join('');
29 2 : } else if (value[j] == "'") {
30 1 : format[j] = "''";
31 3 : } else if (value[j].contains(RegExp(r'[,.\\\/\:\|; -]'))) {
32 2 : format[j] = value[j];
33 : } else if (nm != null) {
34 2 : check[j] ??= _Assertion()
35 1 : ..min = nm
36 1 : ..max = nm
37 3 : ..length = value[j].length;
38 7 : check[j]!.min = check[j]!.min > nm ? nm : check[j]!.min;
39 7 : check[j]!.max = check[j]!.max < nm ? nm : check[j]!.max;
40 11 : check[j]!.length = check[j]!.length < value[j].length ? value[j].length : check[j]!.length;
41 6 : } else if (value.length > j + 1 && value[j + 1] == ',') {
42 1 : format[j] = 'E';
43 3 : } else if (ampm.contains(value[j].toLowerCase())) {
44 1 : format[j] = 'a';
45 : } else {
46 0 : format[j] = "'${value[j].replaceAll("'", "''")}'";
47 : }
48 : }
49 : }
50 2 : return _fillDates(format, check, locale).join('');
51 : }
52 :
53 1 : List<String> _fillDates(List<String?> format, Map<int, _Assertion> check, String locale) {
54 1 : if (format.isEmpty) return [''];
55 1 : bool isMonth = _isMonthFirst(locale);
56 4 : final timeDiv = format.where((v) => v == ':').length;
57 3 : final time = ['HH', 'mm', if (timeDiv > 1) 'ss'];
58 1 : final dmy = ['y', 'M', 'd'];
59 1 : final idxFist = format.indexOf(null);
60 2 : final firstDay = check[idxFist]?.max ?? 0;
61 3 : List<String> dates = isMonth && firstDay <= 12 ? ['M', 'd'] : ['d', 'M'];
62 4 : if (firstDay > 31 || check[idxFist]?.min == 0) {
63 2 : final idxSecond = format.indexOf(null, idxFist + 1);
64 3 : if (isMonth && (check[idxSecond]?.max ?? 0) > 12) {
65 0 : dates = dates.reversed.toList();
66 : }
67 1 : dates.insert(0, 'y');
68 : } else {
69 1 : dates.add('y');
70 : }
71 2 : final max = format.length - 1;
72 2 : for (int i = max; i >= 0; i--) {
73 1 : if (format[i] != null) {
74 : continue;
75 : }
76 10 : if (time.isNotEmpty && (i > 1 && format[i - 1] == ':' || i + 1 < max && format[i + 1] == ':')) {
77 2 : format[i] = time.removeLast();
78 10 : } else if (dmy.isNotEmpty && (i > 1 && format[i - 1] == '-' || i + 1 < max && format[i + 1] == '-')) {
79 2 : format[i] = dmy.removeLast();
80 2 : } else if (dates.isNotEmpty && check[i] != null) {
81 2 : format[i] = dates.removeLast();
82 : } else {
83 0 : format[i] = '?';
84 : }
85 2 : if (format[i] == 'y') {
86 5 : format[i] = List<String>.filled(check[i]?.length ?? 1, 'y').join('');
87 : }
88 : }
89 1 : return format.cast();
90 : }
91 :
92 1 : bool _isMonthFirst(String locale) {
93 1 : DateFormat dateFormat = DateFormat.yMd(locale);
94 3 : List<String> formattedDate = dateFormat.format(DateTime(2023, 10, 20)).split('/');
95 3 : return formattedDate.indexOf('10') < formattedDate.indexOf('20');
96 : }
97 :
98 1 : List<String> splitDateTime(String value) {
99 1 : RegExp regex = RegExp(r"\d+|[,.\\\/\:\|;-]|'| |\w+");
100 5 : return regex.allMatches(value).map((match) => match.group(0)!).toList();
101 : }
102 : }
|