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 0 : String detectFormat(List<String> data, String locale, [List<String>? ampm]) { 15 0 : ampm ??= [AppLocale.labels.dtAm, AppLocale.labels.dtPm]; 16 0 : List<String?> format = []; 17 0 : Map<int, _Assertion> check = {}; 18 0 : for (int i = 0; i < data.length; i++) { 19 0 : final value = splitDateTime(data[i]); 20 0 : if (format.length < value.length) { 21 0 : format.addAll(List<String?>.filled(value.length - format.length, null)); 22 : } 23 0 : for (int j = 0; j < value.length; j++) { 24 0 : final nm = int.tryParse(value[j]); 25 0 : if (nm != null && nm > 0 && value[j].length == 8) { 26 0 : format[j] = 'yyyyMMdd'; 27 0 : } else if (value[j].startsWith('T')) { 28 0 : format[j] = ["'T'", 'HH', if (value[j].length > 3) 'mm', if (value[j].length > 5) 'ss'].join(''); 29 0 : } else if (value[j] == "'") { 30 0 : format[j] = "''"; 31 0 : } else if (value[j].contains(RegExp(r'[,.\\\/\:\|; -]'))) { 32 0 : format[j] = value[j]; 33 : } else if (nm != null) { 34 0 : check[j] ??= _Assertion() 35 0 : ..min = nm 36 0 : ..max = nm 37 0 : ..length = value[j].length; 38 0 : check[j]!.min = check[j]!.min > nm ? nm : check[j]!.min; 39 0 : check[j]!.max = check[j]!.max < nm ? nm : check[j]!.max; 40 0 : check[j]!.length = check[j]!.length < value[j].length ? value[j].length : check[j]!.length; 41 0 : } else if (value.length > j + 1 && value[j + 1] == ',') { 42 0 : format[j] = 'E'; 43 0 : } else if (ampm.contains(value[j].toLowerCase())) { 44 0 : format[j] = 'a'; 45 : } else { 46 0 : format[j] = "'${value[j].replaceAll("'", "''")}'"; 47 : } 48 : } 49 : } 50 0 : return _fillDates(format, check, locale).join(''); 51 : } 52 : 53 0 : List<String> _fillDates(List<String?> format, Map<int, _Assertion> check, String locale) { 54 0 : bool isMonth = _isMonthFirst(locale); 55 0 : final timeDiv = format.where((v) => v == ':').length; 56 0 : final time = ['HH', 'mm', if (timeDiv > 1) 'ss']; 57 0 : final dmy = ['y', 'M', 'd']; 58 0 : final idxFist = format.indexOf(null); 59 0 : final firstDay = check[idxFist]?.max ?? 0; 60 0 : List<String> dates = isMonth && firstDay <= 12 ? ['M', 'd'] : ['d', 'M']; 61 0 : if (firstDay > 31 || check[idxFist]?.min == 0) { 62 0 : final idxSecond = format.indexOf(null, idxFist + 1); 63 0 : if (isMonth && (check[idxSecond]?.max ?? 0) > 12) { 64 0 : dates = dates.reversed.toList(); 65 : } 66 0 : dates.insert(0, 'y'); 67 : } else { 68 0 : dates.add('y'); 69 : } 70 0 : final max = format.length - 1; 71 0 : for (int i = max; i >= 0; i--) { 72 0 : if (format[i] != null) { 73 : continue; 74 : } 75 0 : if (time.isNotEmpty && (i > 1 && format[i - 1] == ':' || i + 1 < max && format[i + 1] == ':')) { 76 0 : format[i] = time.removeLast(); 77 0 : } else if (dmy.isNotEmpty && (i > 1 && format[i - 1] == '-' || i + 1 < max && format[i + 1] == '-')) { 78 0 : format[i] = dmy.removeLast(); 79 0 : } else if (dates.isNotEmpty && check[i] != null) { 80 0 : format[i] = dates.removeLast(); 81 : } else { 82 0 : format[i] = '?'; 83 : } 84 0 : if (format[i] == 'y') { 85 0 : format[i] = List<String>.filled(check[i]?.length ?? 1, 'y').join(''); 86 : } 87 : } 88 0 : return format.cast(); 89 : } 90 : 91 0 : bool _isMonthFirst(String locale) { 92 0 : DateFormat dateFormat = DateFormat.yMd(locale); 93 0 : List<String> formattedDate = dateFormat.format(DateTime(2023, 10, 20)).split('/'); 94 0 : return formattedDate.indexOf('10') < formattedDate.indexOf('20'); 95 : } 96 : 97 0 : List<String> splitDateTime(String value) { 98 0 : RegExp regex = RegExp(r"\d+|[,.\\\/\:\|;-]|'| |\w+"); 99 0 : return regex.allMatches(value).map((match) => match.group(0)!).toList(); 100 : } 101 : }