Skip to content

Commit

Permalink
Merge pull request #7 from gbassisp/release/1.x
Browse files Browse the repository at this point in the history
Release 1.0.4
  • Loading branch information
gbassisp authored Aug 31, 2024
2 parents c531292 + 9ff66dc commit 277b235
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 33 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

## 1.0.4

- Added support for git non-sense format, e.g., `Thu May 16 10:18:07 2024 +0930` and `Thu May 16 10:18:07pm 2024 +0930`

## 1.0.3

- Merged in `0.1.14`:
Expand Down
27 changes: 18 additions & 9 deletions lib/src/any_date_rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,45 @@ const _yearPattern = r'(?<year>\d+)';
const _ambiguousYearPattern = r'(?<year>\d{2})';
const _dayPattern = r'(?<day>[0-3]?\d)';
const _textMonthPattern = r'(?<month>\w+)';
const _monthPattern = r'(?<month>[0-1]?\d)';
const _monthPattern = r'(?<month>(0?\d)|(1[0-2]))';
// const _anyMonthPattern = r'(?<month>\d{1,2}|\w+)';
const _hourPattern = r'(?<hour>[0-2]?\d)';
const _minutePattern = r'(?<minute>[0-5]?\d)';
const _secondPattern = r'(?<second>[0-5]?\d)';
const _microsecondPattern = r'(?<microsecond>\d{1,6})';
const _microsecondPattern = r'(?<microsecond>\d+)';
final _separatorPattern = '[${usedSeparators.reduce((v1, v2) => '$v1,$v2')}]+';
final _s = _separatorPattern;
// final _separatorPattern =
// '(${usedSeparators.reduce((v1, v2) => '$v1|$v2')})+';
// avoid const because this will be updated soon-ish
String get _timeSep => ':';
final _hmPattern = '$_hourPattern$_timeSep$_minutePattern';
final _hmsPattern = '$_hmPattern$_timeSep$_secondPattern';
final _hmsMsPattern = '$_hmsPattern.$_microsecondPattern';
final _hmIdeal = '$_hourPattern$_timeSep$_minutePattern';
final _hmsIdeal = '$_hmIdeal$_timeSep$_secondPattern';
final _hmsMsIdeal = '$_hmsIdeal\\.$_microsecondPattern';

/// ideal time expressions that uses only ':' as separators
@internal
final idealTimePatterns = [
_hmsMsPattern,
_hmsPattern, // + r'(\D|$)',
_hmPattern,
_hmsMsIdeal,
_hmsIdeal, // + r'(\D|$)',
_hmIdeal,
_hourPattern,
];

/// original patterns used in implementation that allows non-sense separators,
/// such as "-"
/// this is not entirely supported because was never tested for "h" or "min"
/// separators
final _timePatterns = idealTimePatterns.map((e) => e.replaceAll(_timeSep, _s));
final _hmPattern = '$_hourPattern$_s$_minutePattern';
final _hmsPattern = '$_hmPattern$_s$_secondPattern';
final _hmsMsPattern = '$_hmsPattern.$_microsecondPattern';

final _timePatterns = [
_hmsMsPattern,
_hmsPattern, // + r'(\D|$)',
_hmPattern,
_hourPattern,
];

/// default parsing rule from dart core
DateTime? dateTimeTryParse(String formattedString) =>
Expand Down
55 changes: 38 additions & 17 deletions lib/src/param_cleanup_rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,44 +122,65 @@ Month? _expectMonth(DateParsingParameters parameters) {
(element) => timestamp.contains(element.name.toLowerCase()),
);

return month.firstOrNullExtenstion ?? english.firstOrNullExtenstion;
return month.firstOrNullExtension ?? english.firstOrNullExtension;
}

Weekday? _expectWeekday(DateParsingParameters parameters) {
// TODO(gbassisp): allow weekday in any part of the string
// currently unsupported because some locales can have a conflict between
// month and weekday (e.g., "Mar" in French for Mardi and Mars)
final timestamp = parameters.formattedString.toLowerCase();
final weekday = parameters.parserInfo.weekdays
var weekday = parameters.parserInfo.weekdays
.where(
(element) => timestamp.startsWith(element.name.toLowerCase()),
// (element) => timestamp
// .contains(RegExp('\\D${element.name}', caseSensitive: false)),
)
.firstOrNullExtenstion;
final english = allWeekdays
.firstOrNullExtension;
if (weekday != null) return weekday;
// weekday = parameters.parserInfo.weekdays
// .where((element) => timestamp.endsWith(element.name.toLowerCase()))
// .firstOrNullExtension;
// if (weekday != null) return weekday;

// english
weekday = allWeekdays
.where(
(element) => timestamp.startsWith(element.name.toLowerCase()),
)
.firstOrNullExtenstion;
.firstOrNullExtension;
if (weekday != null) return weekday;

return weekday ?? english;
return allWeekdays
.where(
(element) => timestamp.endsWith(element.name.toLowerCase()),
)
.firstOrNullExtension;
}

final _exprs = [...idealTimePatterns]..removeLast();
final _betterTimeComponent = CleanupRule((params) {
String padLeft(String? original) => (original ?? '').padLeft(2, '0');
String padRight(String? original) => (original ?? '').padRight(3, '0');
String cleanAmpm(String? original) =>
original?.replaceAll(RegExp(r'(\.|-)'), '').toLowerCase() ?? '';

for (final e in _exprs) {
final re = RegExp(e);
final matches = re.allMatches(params.formattedString);
const ampm = r'\s*(?<ampm>(a|p)(\.|-)?m(\.|-)?\W)?';
final re = RegExp(e + ampm, caseSensitive: false);
final s = '${params.formattedString} ';
final matches = re.allMatches(s);
// unsure what to do if many matches
if (matches.length == 1) {
final m = matches.first;
final newString = params.formattedString.replaceAllMapped(
re,
(match) => '${padLeft(m.namedGroup('hour'))}:'
'${padLeft(m.tryNamedGroup('minute'))}:'
'${padLeft(m.tryNamedGroup('second'))}'
'${m.tryNamedGroup('microsecond') != null ? '.'
'${padRight(m.tryNamedGroup('microsecond'))}' : ''}',
);
final newTime = ' ${padLeft(m.namedGroup('hour'))}:'
'${padLeft(m.tryNamedGroup('minute'))}:'
'${padLeft(m.tryNamedGroup('second'))}'
'${m.tryNamedGroup('microsecond') != null ? '.'
'${padRight(m.tryNamedGroup('microsecond'))}' : ''} '
'${cleanAmpm(m.tryNamedGroup('ampm'))}';
final newString = s.replaceAllMapped(re, (_) => '') + newTime;

params
..formattedString = newString
..simplifiedString = newString;
Expand Down Expand Up @@ -198,6 +219,6 @@ extension _GroupNames on RegExpMatch {
}

extension _IterableX<T> on Iterable<T> {
T? get firstOrNullExtenstion => isEmpty ? null : first;
T? get firstOrNullExtension => isEmpty ? null : first;
// T? get lastOrNull => isEmpty ? null : last;
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: any_date
description: A package for parsing String into DateTime in any format. Easy way to parse a date in any format and in any language, while always respecting ISO and RFC formats.
version: 1.0.3
version: 1.0.4
repository: https://github.com/gbassisp/any_date
homepage: https://github.com/gbassisp/any_date

Expand Down
29 changes: 23 additions & 6 deletions test/locale_based_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,11 @@ Future<void> main() async {
final englishMonths = AnyDate.defaultSettings.months;
final englishWeekdays = AnyDate.defaultSettings.weekdays;
final longWeekdays = englishWeekdays.sublist(0, 7);
final shortWeekdays = englishWeekdays.sublist(7)
..removeWhere((element) => element.name == 'Sept');
final shortWeekdays = englishWeekdays.sublist(7);
test('english speaking - american format', () {
final locale = Locale.fromSubtags(languageCode: 'en', countryCode: 'US');
final longMonths = englishMonths.sublist(0, 12);
final shortMonths = englishMonths.sublist(12)
..removeWhere((element) => element.name == 'Sept');
final shortMonths = englishMonths.sublist(12);

expect(locale.usesMonthFirst, isTrue);
expect(locale.usesYearFirst, isFalse);
Expand All @@ -122,8 +120,7 @@ Future<void> main() async {
test('english speaking - normal format', () {
final locale = Locale.fromSubtags(languageCode: 'en', countryCode: 'AU');
final longMonths = englishMonths.sublist(0, 12);
final shortMonths = englishMonths.sublist(12)
..removeWhere((element) => element.name == 'Sep');
final shortMonths = englishMonths.sublist(12);

expect(locale.usesMonthFirst, isFalse);
expect(locale.usesYearFirst, isFalse);
Expand All @@ -135,6 +132,26 @@ Future<void> main() async {
});

group('locale specific cases', () {
// this is to ensure 日 is not mis-interpreted between day of the week and
// day of the month
test(
'2024年8月31日 on ja with format y年M月d日 resulted in null, '
'but expected 2024-08-31 00:00:00.000', () {
const locale = 'ja';
const formatted = '2024年8月31日';
const format = 'y年M月d日';
final formatter = DateFormat(format);
final expected = DateTime.parse('2024-08-31 00:00:00.000');
final parser = AnyDate.fromLocale(locale);

expect(
formatter.parseLoose(formatted),
equals(expected),
reason: 'sanity check that DateFormat $format can parse $formatted',
);
expect(parser.tryParse(formatted), equals(expected));
});

const unambiguousEnglish = {
'March 27, 2024',
'March 27 2024',
Expand Down
61 changes: 61 additions & 0 deletions test/nonsense_formats_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'package:any_date/any_date.dart';
import 'package:any_date/src/extensions.dart';
import 'package:test/test.dart';

import 'test_values.dart';

void main() {
group('lack of separators formats', () {
const parser = AnyDate();
Expand Down Expand Up @@ -93,4 +96,62 @@ void main() {
expect(parser.tryParse(formatted), equals(expected));
});
});

group('git', () {
final parser = parsers.first;
test('git default nonsense 1', () {
const formatted = 'Thu May 16 10:18:07 2024 +0930';
final expected =
DateTime.utc(2024, 5, 16, 10, 18, 7).copyWithOffset('+0930');

expect(parser.tryParse(formatted), equals(expected));
});
test('git default nonsense 2', () {
const formatted = 'Thu May 16 10:18:07 2024 -0930';
final expected =
DateTime.utc(2024, 5, 16, 10, 18, 7).copyWithOffset('-0930');

expect(parser.tryParse(formatted), equals(expected));
});
for (final am in [
'am',
'AM',
'a.m.',
'a.m',
'A.M',
' am',
' AM',
' a.m.',
' a.m',
' A.M',
]) {
test('git default nonsense 3 - "$am"', () {
final formatted = 'Thu, May 16 10:18:07$am 2024 -0930';
final expected =
DateTime.utc(2024, 5, 16, 10, 18, 7).copyWithOffset('-0930');

expect(parser.tryParse(formatted), equals(expected));
});
}
for (final pm in [
'pm',
'PM',
'p.m.',
'p.m',
'P.M',
' pm',
' PM',
' p.m.',
' p.m',
' P.M',
]) {
test('git default nonsense 4 - "$pm"', () {
final formatted = 'Thu, May 16 10:18:07$pm 2024 -0930';
final expected =
DateTime.utc(2024, 5, 16, 22, 18, 7).copyWithOffset('-0930');

expect(parser.tryParse(formatted), equals(expected));
});
}
});
}

0 comments on commit 277b235

Please sign in to comment.