import 'dart:io'; import 'dart:typed_data'; import 'package:image/image.dart' show decodeJpgExif; /// X-plore 비교 i: 일괄 이름 바꾸기의 날짜 - 사진은 찍은 때 (JPEG Exif DateTimeOriginal), 없으면 null. /// 파일 앞부분만 읽는다 (Exif 는 맨 앞 APP1, 64KB 를 넘지 않음). Future photoTakenAt(String path) async { final ext = path.toLowerCase(); if (!(ext.endsWith('.jpg') || ext.endsWith('.jpeg'))) return null; RandomAccessFile? f; try { f = await File(path).open(); final head = await f.read(256 * 1024); return parseExifDate(_exifDate(head)); } catch (_) { return null; } finally { await f?.close(); } } String? _exifDate(Uint8List head) { try { final exif = decodeJpgExif(head); if (exif == null) return null; // 찍은 때 → 디지털로 만든 때 → 바뀐 때 (ifd0 의 DateTime) for (final v in [exif.exifIfd[0x9003], exif.exifIfd[0x9004], exif.imageIfd[0x0132]]) { final s = v?.toString().trim(); if (s != null && s.isNotEmpty) return s; } } catch (_) {} return null; } /// "2024:05:01 12:34:56" → DateTime (틀리거나 0 이면 null) DateTime? parseExifDate(String? s) { final m = RegExp(r'^(\d{4})[:\-](\d{2})[:\-](\d{2})[ T](\d{2}):(\d{2}):(\d{2})').firstMatch(s ?? ''); if (m == null) return null; final n = [for (var i = 1; i <= 6; i++) int.parse(m[i]!)]; if (n[0] < 1900 || n[1] < 1 || n[1] > 12 || n[2] < 1 || n[2] > 31) return null; return DateTime(n[0], n[1], n[2], n[3], n[4], n[5]); }