mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
fix: handle datetime outside the valid range supported by dart (#21526)
* fix: handle datetime outside the valid range supported by dart * add tests for tryFromSecondsSinceEpoch --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
873f7921da
commit
ec2f94cae8
3 changed files with 81 additions and 3 deletions
|
|
@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
|
import 'package:immich_mobile/utils/datetime_helpers.dart';
|
||||||
import 'package:immich_mobile/utils/diff.dart';
|
import 'package:immich_mobile/utils/diff.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
|
|
@ -285,7 +286,7 @@ extension on Iterable<PlatformAlbum> {
|
||||||
(e) => LocalAlbum(
|
(e) => LocalAlbum(
|
||||||
id: e.id,
|
id: e.id,
|
||||||
name: e.name,
|
name: e.name,
|
||||||
updatedAt: e.updatedAt == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000),
|
updatedAt: tryFromSecondsSinceEpoch(e.updatedAt!) ?? DateTime.now(),
|
||||||
assetCount: e.assetCount,
|
assetCount: e.assetCount,
|
||||||
),
|
),
|
||||||
).toList();
|
).toList();
|
||||||
|
|
@ -300,8 +301,8 @@ extension on Iterable<PlatformAsset> {
|
||||||
name: e.name,
|
name: e.name,
|
||||||
checksum: null,
|
checksum: null,
|
||||||
type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other,
|
type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other,
|
||||||
createdAt: e.createdAt == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(e.createdAt! * 1000),
|
createdAt: tryFromSecondsSinceEpoch(e.createdAt!) ?? DateTime.now(),
|
||||||
updatedAt: e.updatedAt == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000),
|
updatedAt: tryFromSecondsSinceEpoch(e.updatedAt!) ?? DateTime.now(),
|
||||||
width: e.width,
|
width: e.width,
|
||||||
height: e.height,
|
height: e.height,
|
||||||
durationInSeconds: e.durationInSeconds,
|
durationInSeconds: e.durationInSeconds,
|
||||||
|
|
|
||||||
19
mobile/lib/utils/datetime_helpers.dart
Normal file
19
mobile/lib/utils/datetime_helpers.dart
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
const int _maxMillisecondsSinceEpoch = 8640000000000000; // 275760-09-13
|
||||||
|
const int _minMillisecondsSinceEpoch = -62135596800000; // 0001-01-01
|
||||||
|
|
||||||
|
DateTime? tryFromSecondsSinceEpoch(int? secondsSinceEpoch) {
|
||||||
|
if (secondsSinceEpoch == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final milliSeconds = secondsSinceEpoch * 1000;
|
||||||
|
if (milliSeconds < _minMillisecondsSinceEpoch || milliSeconds > _maxMillisecondsSinceEpoch) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return DateTime.fromMillisecondsSinceEpoch(milliSeconds);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
mobile/test/modules/utils/datetime_helpers_test.dart
Normal file
58
mobile/test/modules/utils/datetime_helpers_test.dart
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:immich_mobile/utils/datetime_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('tryFromSecondsSinceEpoch', () {
|
||||||
|
test('returns null for null input', () {
|
||||||
|
final result = tryFromSecondsSinceEpoch(null);
|
||||||
|
expect(result, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns null for value below minimum allowed range', () {
|
||||||
|
// _minMillisecondsSinceEpoch = -62135596800000
|
||||||
|
final seconds = -62135596800000 ~/ 1000 - 1; // One second before min allowed
|
||||||
|
final result = tryFromSecondsSinceEpoch(seconds);
|
||||||
|
expect(result, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns null for value above maximum allowed range', () {
|
||||||
|
// _maxMillisecondsSinceEpoch = 8640000000000000
|
||||||
|
final seconds = 8640000000000000 ~/ 1000 + 1; // One second after max allowed
|
||||||
|
final result = tryFromSecondsSinceEpoch(seconds);
|
||||||
|
expect(result, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns correct DateTime for minimum allowed value', () {
|
||||||
|
final seconds = -62135596800000 ~/ 1000; // Minimum allowed timestamp
|
||||||
|
final result = tryFromSecondsSinceEpoch(seconds);
|
||||||
|
expect(result, DateTime.fromMillisecondsSinceEpoch(-62135596800000));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns correct DateTime for maximum allowed value', () {
|
||||||
|
final seconds = 8640000000000000 ~/ 1000; // Maximum allowed timestamp
|
||||||
|
final result = tryFromSecondsSinceEpoch(seconds);
|
||||||
|
expect(result, DateTime.fromMillisecondsSinceEpoch(8640000000000000));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns correct DateTime for negative timestamp', () {
|
||||||
|
final seconds = -1577836800; // Dec 31, 1919 (pre-epoch)
|
||||||
|
final result = tryFromSecondsSinceEpoch(seconds);
|
||||||
|
expect(result, DateTime.fromMillisecondsSinceEpoch(-1577836800 * 1000));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns correct DateTime for zero timestamp', () {
|
||||||
|
final seconds = 0; // Jan 1, 1970 (epoch)
|
||||||
|
final result = tryFromSecondsSinceEpoch(seconds);
|
||||||
|
expect(result, DateTime.fromMillisecondsSinceEpoch(0));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns correct DateTime for recent timestamp', () {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final seconds = now.millisecondsSinceEpoch ~/ 1000;
|
||||||
|
final result = tryFromSecondsSinceEpoch(seconds);
|
||||||
|
expect(result?.year, now.year);
|
||||||
|
expect(result?.month, now.month);
|
||||||
|
expect(result?.day, now.day);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue