#158 guard DateTime against weird timestamps

This commit is contained in:
Thibault Deckers 2022-01-16 18:21:25 +09:00
parent dbc3b1d68d
commit d183541b58
8 changed files with 62 additions and 27 deletions

View file

@ -16,6 +16,7 @@ import 'package:aves/services/geocoding_service.dart';
import 'package:aves/services/metadata/svg_metadata_service.dart';
import 'package:aves/theme/format.dart';
import 'package:aves/utils/change_notifier.dart';
import 'package:aves/utils/time_utils.dart';
import 'package:collection/collection.dart';
import 'package:country_code/country_code.dart';
import 'package:flutter/foundation.dart';
@ -349,6 +350,7 @@ class AvesEntry {
DateTime? get bestDate {
if (_bestDate == null) {
try {
if ((_catalogDateMillis ?? 0) > 0) {
_bestDate = DateTime.fromMillisecondsSinceEpoch(_catalogDateMillis!);
} else if ((sourceDateTakenMillis ?? 0) > 0) {
@ -356,6 +358,10 @@ class AvesEntry {
} else if ((dateModifiedSecs ?? 0) > 0) {
_bestDate = DateTime.fromMillisecondsSinceEpoch(dateModifiedSecs! * 1000);
}
} catch (e, stack) {
// date millis may be out of range
reportService.recordError(e, stack);
}
}
return _bestDate;
}
@ -748,13 +754,11 @@ class AvesEntry {
return c != 0 ? c : compareAsciiUpperCase(a.extension ?? '', b.extension ?? '');
}
static final _epoch = DateTime.fromMillisecondsSinceEpoch(0);
// compare by:
// 1) date descending
// 2) name descending
static int compareByDate(AvesEntry a, AvesEntry b) {
var c = (b.bestDate ?? _epoch).compareTo(a.bestDate ?? _epoch);
var c = (b.bestDate ?? epoch).compareTo(a.bestDate ?? epoch);
if (c != 0) return c;
return compareByName(b, a);
}

View file

@ -15,13 +15,13 @@ import 'package:aves/theme/format.dart';
import 'package:aves/utils/file_utils.dart';
import 'package:aves/utils/math_utils.dart';
import 'package:aves/utils/string_utils.dart';
import 'package:aves/utils/time_utils.dart';
import 'package:aves/widgets/viewer/video/fijkplayer.dart';
import 'package:collection/collection.dart';
import 'package:fijkplayer/fijkplayer.dart';
import 'package:flutter/foundation.dart';
class VideoMetadataFormatter {
static final _epoch = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
static final _anotherDatePattern = RegExp(r'(\d{4})[-/](\d{2})[-/](\d{2}) (\d{2}):(\d{2}):(\d{2})');
static final _durationPattern = RegExp(r'(\d+):(\d+):(\d+)(.\d+)');
static final _locationPattern = RegExp(r'([+-][.0-9]+)');
@ -349,7 +349,7 @@ class VideoMetadataFormatter {
static String? _formatDate(String value) {
final date = DateTime.tryParse(value);
if (date == null) return value;
if (date == _epoch) return null;
if (date == epoch) return null;
return date.toIso8601String();
}

View file

@ -237,7 +237,14 @@ class PlatformMetadataFetchService implements MetadataFetchService {
'sizeBytes': entry.sizeBytes,
'field': field.toExifInterfaceTag(),
});
if (result is int) return DateTime.fromMillisecondsSinceEpoch(result);
if (result is int) {
try {
return DateTime.fromMillisecondsSinceEpoch(result);
} catch (e, stack) {
// date millis may be out of range
await reportService.recordError(e, stack);
}
}
} on PlatformException catch (e, stack) {
if (!entry.isMissingAtPath) {
await reportService.recordError(e, stack);

View file

@ -1,3 +1,5 @@
import 'package:aves/services/common/services.dart';
extension ExtraDateTime on DateTime {
bool isAtSameYearAs(DateTime? other) => year == other?.year;
@ -14,6 +16,8 @@ extension ExtraDateTime on DateTime {
bool get isThisYear => isAtSameYearAs(DateTime.now());
}
final epoch = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
final _unixStampMillisPattern = RegExp(r'\d{13}');
final _unixStampSecPattern = RegExp(r'\d{10}');
final _plainPattern = RegExp(r'(\d{8})([_-\s](\d{6})([_-\s](\d{3}))?)?');
@ -23,22 +27,32 @@ DateTime? parseUnknownDateFormat(String? s) {
var match = _unixStampMillisPattern.firstMatch(s);
if (match != null) {
final stampString = match.group(0);
if (stampString != null) {
final stampMillis = int.tryParse(stampString);
final stampMillisString = match.group(0);
if (stampMillisString != null) {
final stampMillis = int.tryParse(stampMillisString);
if (stampMillis != null) {
try {
return DateTime.fromMillisecondsSinceEpoch(stampMillis, isUtc: false);
} catch (e, stack) {
// date millis may be out of range
reportService.recordError(e, stack);
}
}
}
}
match = _unixStampSecPattern.firstMatch(s);
if (match != null) {
final stampString = match.group(0);
if (stampString != null) {
final stampMillis = int.tryParse(stampString);
if (stampMillis != null) {
return DateTime.fromMillisecondsSinceEpoch(stampMillis * 1000, isUtc: false);
final stampSecString = match.group(0);
if (stampSecString != null) {
final stampSec = int.tryParse(stampSecString);
if (stampSec != null) {
try {
return DateTime.fromMillisecondsSinceEpoch(stampSec * 1000, isUtc: false);
} catch (e, stack) {
// date millis may be out of range
reportService.recordError(e, stack);
}
}
}
}

View file

@ -76,18 +76,18 @@ class _RenderSliverKnownExtentBoxAdaptor extends RenderSliverMultiBoxAdaptor {
SectionLayout? sectionAtIndex(int index) => sectionLayouts.firstWhereOrNull((section) => section.hasChild(index));
SectionLayout sectionAtOffset(double scrollOffset) => sectionLayouts.firstWhere((section) => section.hasChildAtOffset(scrollOffset), orElse: () => sectionLayouts.last);
SectionLayout? sectionAtOffset(double scrollOffset) => sectionLayouts.firstWhereOrNull((section) => section.hasChildAtOffset(scrollOffset)) ?? sectionLayouts.lastOrNull;
double indexToLayoutOffset(int index) {
return (sectionAtIndex(index) ?? sectionLayouts.last).indexToLayoutOffset(index);
}
int getMinChildIndexForScrollOffset(double scrollOffset) {
return sectionAtOffset(scrollOffset).getMinChildIndexForScrollOffset(scrollOffset);
return sectionAtOffset(scrollOffset)?.getMinChildIndexForScrollOffset(scrollOffset) ?? 0;
}
int getMaxChildIndexForScrollOffset(double scrollOffset) {
return sectionAtOffset(scrollOffset).getMaxChildIndexForScrollOffset(scrollOffset);
return sectionAtOffset(scrollOffset)?.getMaxChildIndexForScrollOffset(scrollOffset) ?? 0;
}
double estimateMaxScrollOffset(

View file

@ -1,6 +1,8 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums.dart';
import 'package:aves/utils/constants.dart';
import 'package:aves/utils/time_utils.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/common/providers/selection_provider.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
@ -63,7 +65,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
}
static int compareFiltersByDate(FilterGridItem<CollectionFilter> a, FilterGridItem<CollectionFilter> b) {
final c = (b.entry?.bestDate ?? DateTime.fromMillisecondsSinceEpoch(0)).compareTo(a.entry?.bestDate ?? DateTime.fromMillisecondsSinceEpoch(0));
final c = (b.entry?.bestDate ?? epoch).compareTo(a.entry?.bestDate ?? epoch);
return c != 0 ? c : a.filter.compareTo(b.filter);
}

View file

@ -53,7 +53,11 @@ class ViewerDebugPage extends StatelessWidget {
String toDateValue(int? time, {int factor = 1}) {
var value = '$time';
if (time != null && time > 0) {
try {
value += ' (${DateTime.fromMillisecondsSinceEpoch(time * factor)})';
} catch (e) {
value += ' (invalid DateTime})';
}
}
return value;
}

View file

@ -57,7 +57,11 @@ class _MetadataTabState extends State<MetadataTab> {
if (secondTimestampKeys.contains(key)) {
v *= 1000;
}
try {
value += ' (${DateTime.fromMillisecondsSinceEpoch(v)})';
} catch (e) {
value += ' (invalid DateTime})';
}
}
if (key == 'xmp' && v != null && v is Uint8List) {
value = String.fromCharCodes(v);