Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 1.0.4 #7

Merged
merged 13 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
});
}
});
}
Loading