fix: dateTimeOriginal timezone updates

This commit is contained in:
Daniel Dietzler
2025-12-19 14:44:33 +01:00
parent 125de91c71
commit 97f89a7c1c
2 changed files with 124 additions and 6 deletions

View File

@@ -144,14 +144,28 @@ export class AssetService extends BaseService {
await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids }); await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids });
const assetDto = _.omitBy({ isFavorite, visibility, duplicateId }, _.isUndefined); const assetDto = _.omitBy({ isFavorite, visibility, duplicateId }, _.isUndefined);
const exifDto = _.omitBy({ latitude, longitude, rating, description, dateTimeOriginal }, _.isUndefined); const exifDto = _.omitBy(
{
latitude,
longitude,
rating,
description,
dateTimeOriginal,
},
_.isUndefined,
);
const extractedTimeZone = dateTimeOriginal ? DateTime.fromISO(dateTimeOriginal, { setZone: true }).zone : undefined;
if (Object.keys(exifDto).length > 0) { if (Object.keys(exifDto).length > 0) {
await this.assetRepository.updateAllExif(ids, exifDto); await this.assetRepository.updateAllExif(ids, exifDto);
} }
if ((dateTimeRelative !== undefined && dateTimeRelative !== 0) || timeZone !== undefined) { if (
await this.assetRepository.updateDateTimeOriginal(ids, dateTimeRelative, timeZone); (dateTimeRelative !== undefined && dateTimeRelative !== 0) ||
timeZone !== undefined ||
extractedTimeZone?.type === 'fixed'
) {
await this.assetRepository.updateDateTimeOriginal(ids, dateTimeRelative, timeZone ?? extractedTimeZone?.name);
} }
if (Object.keys(assetDto).length > 0) { if (Object.keys(assetDto).length > 0) {
@@ -436,7 +450,19 @@ export class AssetService extends BaseService {
rating?: number; rating?: number;
}) { }) {
const { id, description, dateTimeOriginal, latitude, longitude, rating } = dto; const { id, description, dateTimeOriginal, latitude, longitude, rating } = dto;
const writes = _.omitBy({ description, dateTimeOriginal, latitude, longitude, rating }, _.isUndefined); const extractedTimeZone = dateTimeOriginal ? DateTime.fromISO(dateTimeOriginal, { setZone: true }).zone : undefined;
const writes = _.omitBy(
{
description,
dateTimeOriginal,
timeZone: extractedTimeZone?.type === 'fixed' ? extractedTimeZone.name : undefined,
latitude,
longitude,
rating,
},
_.isUndefined,
);
if (Object.keys(writes).length > 0) { if (Object.keys(writes).length > 0) {
await this.assetRepository.upsertExif( await this.assetRepository.upsertExif(
updateLockedColumns({ updateLockedColumns({

View File

@@ -285,7 +285,7 @@ describe(AssetService.name, () => {
.where('assetId', '=', asset.id) .where('assetId', '=', asset.id)
.executeTakeFirstOrThrow(), .executeTakeFirstOrThrow(),
).resolves.toEqual({ lockedProperties: null }); ).resolves.toEqual({ lockedProperties: null });
await sut.update(auth, asset.id, { dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' }); await sut.update(auth, asset.id, { dateTimeOriginal: '2023-11-19T18:11:00' });
await expect( await expect(
ctx.database ctx.database
@@ -296,7 +296,38 @@ describe(AssetService.name, () => {
).resolves.toEqual({ lockedProperties: ['dateTimeOriginal'] }); ).resolves.toEqual({ lockedProperties: ['dateTimeOriginal'] });
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual( await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
expect.objectContaining({ expect.objectContaining({
exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-20T01:11:00+00:00' }), exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-19T18:11:00+00:00', timeZone: null }),
}),
);
});
it('should update dateTimeOriginal with time zone', async () => {
const { sut, ctx } = setup();
ctx.getMock(JobRepository).queue.mockResolvedValue();
const { user } = await ctx.newUser();
const auth = factory.auth({ user });
const { asset } = await ctx.newAsset({ ownerId: user.id });
await ctx.newExif({ assetId: asset.id, description: 'test' });
await expect(
ctx.database
.selectFrom('asset_exif')
.select('lockedProperties')
.where('assetId', '=', asset.id)
.executeTakeFirstOrThrow(),
).resolves.toEqual({ lockedProperties: null });
await sut.update(auth, asset.id, { dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' });
await expect(
ctx.database
.selectFrom('asset_exif')
.select('lockedProperties')
.where('assetId', '=', asset.id)
.executeTakeFirstOrThrow(),
).resolves.toEqual({ lockedProperties: ['timeZone', 'dateTimeOriginal'] });
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
expect.objectContaining({
exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-20T01:11:00+00:00', timeZone: 'UTC-7' }),
}), }),
); );
}); });
@@ -329,4 +360,65 @@ describe(AssetService.name, () => {
); );
}); });
}); });
it('should update dateTimeOriginal', async () => {
const { sut, ctx } = setup();
ctx.getMock(JobRepository).queueAll.mockResolvedValue();
const { user } = await ctx.newUser();
const auth = factory.auth({ user });
const { asset } = await ctx.newAsset({ ownerId: user.id });
await ctx.newExif({ assetId: asset.id, description: 'test' });
await expect(
ctx.database
.selectFrom('asset_exif')
.select('lockedProperties')
.where('assetId', '=', asset.id)
.executeTakeFirstOrThrow(),
).resolves.toEqual({ lockedProperties: null });
await sut.updateAll(auth, { ids: [asset.id], dateTimeOriginal: '2023-11-19T18:11:00' });
await expect(
ctx.database
.selectFrom('asset_exif')
.select('lockedProperties')
.where('assetId', '=', asset.id)
.executeTakeFirstOrThrow(),
).resolves.toEqual({ lockedProperties: ['dateTimeOriginal'] });
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
expect.objectContaining({
exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-19T18:11:00+00:00', timeZone: null }),
}),
);
});
it('should update dateTimeOriginal with time zone', async () => {
const { sut, ctx } = setup();
ctx.getMock(JobRepository).queueAll.mockResolvedValue();
const { user } = await ctx.newUser();
const auth = factory.auth({ user });
const { asset } = await ctx.newAsset({ ownerId: user.id });
await ctx.newExif({ assetId: asset.id, description: 'test' });
await expect(
ctx.database
.selectFrom('asset_exif')
.select('lockedProperties')
.where('assetId', '=', asset.id)
.executeTakeFirstOrThrow(),
).resolves.toEqual({ lockedProperties: null });
await sut.updateAll(auth, { ids: [asset.id], dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' });
await expect(
ctx.database
.selectFrom('asset_exif')
.select('lockedProperties')
.where('assetId', '=', asset.id)
.executeTakeFirstOrThrow(),
).resolves.toEqual({ lockedProperties: ['timeZone', 'dateTimeOriginal'] });
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
expect.objectContaining({
exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-20T01:11:00+00:00', timeZone: 'UTC-7' }),
}),
);
});
}); });