diff --git a/lib/Db/TimelineQueryDays.php b/lib/Db/TimelineQueryDays.php index e0fe9a7e..5b525ace 100644 --- a/lib/Db/TimelineQueryDays.php +++ b/lib/Db/TimelineQueryDays.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace OCA\Memories\Db; use OCA\Memories\ClustersBackend; +use OCA\Memories\Util; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -222,8 +223,12 @@ trait TimelineQueryDays // All cluster transformations ClustersBackend\Manager::applyDayPostTransforms($this->request, $row); - // We don't need these fields - unset($row['datetaken']); + // Remove datetaken unless native (for sorting) + if (Util::callerIsNative()) { + $row['datetaken'] = Util::sqlUtcToTimestamp($row['datetaken']); + } else { + unset($row['datetaken']); + } } /** diff --git a/lib/Db/TimelineQuerySingleItem.php b/lib/Db/TimelineQuerySingleItem.php index 0f03b60a..54b79910 100644 --- a/lib/Db/TimelineQuerySingleItem.php +++ b/lib/Db/TimelineQuerySingleItem.php @@ -56,17 +56,9 @@ trait TimelineQuerySingleItem 'dayid' => (int) $row['dayid'], 'w' => (int) $row['w'], 'h' => (int) $row['h'], - 'datetaken' => (int) $row['datetaken'], + 'datetaken' => Util::sqlUtcToTimestamp($row['datetaken']), ]; - // Attempt to get the date in the correct timezone - try { - $utcDate = new \DateTime($row['datetaken'], new \DateTimeZone('UTC')); - $info['datetaken'] = $utcDate->getTimestamp(); - } catch (\Throwable $e) { - // Ignore - } - // Return if only basic info is needed if ($basic) { return $info; diff --git a/lib/Util.php b/lib/Util.php index 3ba19b2a..32f81182 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -304,6 +304,20 @@ class Util return mb_ereg_replace('\/\/+', '/', $path); // remove extra slashes } + /** + * Convert SQL UTC date to timestamp. + * + * @param mixed $sqlDate + */ + public static function sqlUtcToTimestamp($sqlDate): int + { + try { + return (new \DateTime($sqlDate, new \DateTimeZone('UTC')))->getTimestamp(); + } catch (\Throwable $e) { + return 0; + } + } + /** * Explode a string into fixed number of components. * diff --git a/src/components/modal/EditDate.vue b/src/components/modal/EditDate.vue index c8971a6c..8980b4c9 100644 --- a/src/components/modal/EditDate.vue +++ b/src/components/modal/EditDate.vue @@ -173,11 +173,11 @@ export default defineComponent({ }, origDateNewest() { - return new Date(this.sortedPhotos[0].datetaken!); + return new Date(this.sortedPhotos[0].datetaken! * 1000); }, origDateOldest() { - return new Date(this.sortedPhotos[this.sortedPhotos.length - 1].datetaken!); + return new Date(this.sortedPhotos[this.sortedPhotos.length - 1].datetaken! * 1000); }, origDateDiff() { @@ -206,7 +206,7 @@ export default defineComponent({ photos.sort((a, b) => b.datetaken! - a.datetaken!); // Get date of newest photo - let date = new Date(photos[0].datetaken!); + let date = new Date(photos[0].datetaken! * 1000); this.year = date.getUTCFullYear().toString(); this.month = (date.getUTCMonth() + 1).toString(); this.day = date.getUTCDate().toString(); @@ -216,7 +216,7 @@ export default defineComponent({ // Get date of oldest photo if (photos.length > 1) { - date = new Date(photos[photos.length - 1].datetaken!); + date = new Date(photos[photos.length - 1].datetaken! * 1000); this.yearLast = date.getUTCFullYear().toString(); this.monthLast = (date.getUTCMonth() + 1).toString(); this.dayLast = date.getUTCDate().toString(); @@ -259,7 +259,7 @@ export default defineComponent({ // Interpolate date const dT = this.date.getTime(); const doT = this.origDateNewest.getTime(); - const offset = (photo.datetaken || doT) - doT; + const offset = ((photo.datetaken ?? 0) * 1000 || doT) - doT; return this.getExifFormat(new Date(dT + offset * this.scaleFactor)); }, diff --git a/src/components/modal/EditMetadataModal.vue b/src/components/modal/EditMetadataModal.vue index 3882e071..0faf559c 100644 --- a/src/components/modal/EditMetadataModal.vue +++ b/src/components/modal/EditMetadataModal.vue @@ -112,14 +112,7 @@ export default defineComponent({ try { const url = API.Q(API.IMAGE_INFO(p.fileid), { tags: 1 }); const res = await axios.get(url); - - // Validate response - p.imageInfo = null; - if (typeof res.data.datetaken !== 'number') { - console.error('Invalid date for', p.fileid); - return; - } - p.datetaken = res.data.datetaken * 1000; + p.datetaken = res.data.datetaken; p.imageInfo = res.data; } catch (error) { console.error('Failed to get date info for', p.fileid, error); diff --git a/src/native.ts b/src/native.ts index 16cd0a79..dc0e354a 100644 --- a/src/native.ts +++ b/src/native.ts @@ -108,4 +108,7 @@ export async function extendDayWithLocal(dayId: number, photos: IPhoto[]) { const localOnly = localPhotos.filter((p) => !photosSet.has(p.basename)); localOnly.forEach((p) => (p.islocal = true)); photos.push(...localOnly); + + // Sort by datetaken + photos.sort((a, b) => (b.datetaken ?? 0) - (a.datetaken ?? 0)); }