diff --git a/.gitignore b/.gitignore index 2f9ed1b7..6efad7e3 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,5 @@ test.* *.sublime-workspace .DS_Store /coverage/ -.nyc_output/ \ No newline at end of file +.nyc_output/ +.vscode* \ No newline at end of file diff --git a/src/backend/model/database/enitites/MediaEntity.ts b/src/backend/model/database/enitites/MediaEntity.ts index 57c13132..5eb17224 100644 --- a/src/backend/model/database/enitites/MediaEntity.ts +++ b/src/backend/model/database/enitites/MediaEntity.ts @@ -4,6 +4,7 @@ import {MediaDimension, MediaDTO, MediaMetadata,} from '../../../../common/entit import {PersonJunctionTable} from './PersonJunctionTable'; import {columnCharsetCS} from './EntityUtils'; import {CameraMetadata, FaceRegion, GPSMetadata, PositionMetaData,} from '../../../../common/entities/PhotoDTO'; +import { Utils } from '../../../../common/Utils'; export class MediaDimensionEntity implements MediaDimension { @Column('int') @@ -106,7 +107,12 @@ export class MediaMetadataEntity implements MediaMetadata { @Index() creationDate: number; - @Column('text') + @Column('smallint', { + transformer: { + from: (v) => Utils.getOffsetString(v), //from database repr. as smallint (minutes) to string (+/-HH:MM) + to: (v) => Utils.getOffsetMinutes(v), //from entiry repr. as string (+/-HH:MM) to smallint (minutes) + }, + }) creationDateOffset?: string; diff --git a/src/backend/model/fileaccess/MetadataLoader.ts b/src/backend/model/fileaccess/MetadataLoader.ts index cc4c05b8..908e0116 100644 --- a/src/backend/model/fileaccess/MetadataLoader.ts +++ b/src/backend/model/fileaccess/MetadataLoader.ts @@ -226,14 +226,7 @@ export class MetadataLoader { //offset in minutes is the difference between gps timestamp and given timestamp //to calculate this correctly, we have to work with the same offset const offsetMinutes = (timestampToMS(timestamp, '+00:00')- timestampToMS(UTCTimestamp, '+00:00')) / 1000 / 60; - if (-720 <= offsetMinutes && offsetMinutes <= 840) { - //valid offset is within -12 and +14 hrs (https://en.wikipedia.org/wiki/List_of_UTC_offsets) - return (offsetMinutes < 0 ? "-" : "+") + //leading +/- - ("0" + Math.trunc(Math.abs(offsetMinutes) / 60)).slice(-2) + ":" + //zeropadded hours and ':' - ("0" + Math.abs(offsetMinutes) % 60).slice(-2); //zeropadded minutes - } else { - return undefined; - } + return Utils.getOffsetString(offsetMinutes); } else { return undefined; } diff --git a/src/common/Utils.ts b/src/common/Utils.ts index 67bc435a..2e6ae0f4 100644 --- a/src/common/Utils.ts +++ b/src/common/Utils.ts @@ -124,6 +124,29 @@ export class Utils { return new Date(new Date(d).toISOString().substring(0,19) + (offset ? offset : '')).getFullYear(); } + static getOffsetString(offsetMinutes: number) { + if (-720 <= offsetMinutes && offsetMinutes <= 840) { + //valid offset is within -12 and +14 hrs (https://en.wikipedia.org/wiki/List_of_UTC_offsets) + return (offsetMinutes < 0 ? "-" : "+") + //leading +/- + ("0" + Math.trunc(Math.abs(offsetMinutes) / 60)).slice(-2) + ":" + //zeropadded hours and ':' + ("0" + Math.abs(offsetMinutes) % 60).slice(-2); //zeropadded minutes + } else { + return undefined; + } + } + + static getOffsetMinutes(offsetString: string) { //Convert offset string (+HH:MM or -HH:MM) into a minute value + const regex = /^([+\-](0[0-9]|1[0-4]):[0-5][0-9])$/; //checks if offset is between -14:00 and +14:00. + //-12:00 is the lowest valid UTC-offset, but we allow down to -14 for efficiency + if (regex.test(offsetString)) { + let hhmm = offsetString.split(":"); + let hours = parseInt(hhmm[0]); + return hours < 0 ? ((hours*60) - parseInt(hhmm[1])) : ((hours*60) + parseInt(hhmm[1])); + } else { + return undefined; + } + } + static renderDataSize(size: number): string { const postFixes = ['B', 'KB', 'MB', 'GB', 'TB']; let index = 0; diff --git a/src/common/entities/ConentWrapper.ts b/src/common/entities/ConentWrapper.ts index f0321575..aedc1794 100644 --- a/src/common/entities/ConentWrapper.ts +++ b/src/common/entities/ConentWrapper.ts @@ -79,6 +79,11 @@ export class ContentWrapper { (media as MediaDTO).metadata['t'] = (media as MediaDTO).metadata.creationDate / 1000; // skip millies delete (media as MediaDTO).metadata.creationDate; + if ((media as MediaDTO).metadata.creationDateOffset) { + // @ts-ignore + (media as MediaDTO).metadata['o'] = Utils.getOffsetMinutes((media as MediaDTO).metadata.creationDateOffset); // offset in minutes + delete (media as MediaDTO).metadata.creationDateOffset; + } if ((media as PhotoDTO).metadata.rating) { // @ts-ignore @@ -338,6 +343,14 @@ export class ContentWrapper { delete (media as PhotoDTO).metadata['t']; } + // @ts-ignore + if (typeof (media as PhotoDTO).metadata['o'] !== 'undefined') { + // @ts-ignore + (media as PhotoDTO).metadata.creationDateOffset = Utils.getOffsetString((media as PhotoDTO).metadata['o']) ;//convert offset from minutes to String + // @ts-ignore + delete (media as PhotoDTO).metadata['o']; + } + // @ts-ignore if (typeof (media as PhotoDTO).metadata['r'] !== 'undefined') { // @ts-ignore diff --git a/test/TestHelper.ts b/test/TestHelper.ts index 0c11a5cc..8c24e85c 100644 --- a/test/TestHelper.ts +++ b/test/TestHelper.ts @@ -55,6 +55,7 @@ export class TestHelper { m.caption = null; m.size = sd; m.creationDate = 1656069387772; + m.creationDateOffset = "+02:00" m.fileSize = 123456789; // m.rating = 0; no rating by default @@ -101,6 +102,7 @@ export class TestHelper { m.positionData = pd; m.size = sd; m.creationDate = 1656069387772; + m.creationDateOffset = "-05:00"; m.fileSize = 123456789; // m.rating = 0; no rating by default @@ -177,6 +179,7 @@ export class TestHelper { p.metadata.positionData.GPSData.latitude = 10; p.metadata.positionData.GPSData.longitude = 10; p.metadata.creationDate = 1656069387772 - 1000; + p.metadata.creationDateOffset = "+00:00"; p.metadata.rating = 1; p.metadata.size.height = 1000; p.metadata.size.width = 1000; @@ -215,6 +218,7 @@ export class TestHelper { p.metadata.positionData.GPSData.latitude = -10; p.metadata.positionData.GPSData.longitude = -10; p.metadata.creationDate = 1656069387772 - 2000; + p.metadata.creationDateOffset = "+11:00"; p.metadata.rating = 2; p.metadata.size.height = 2000; p.metadata.size.width = 1000; @@ -247,6 +251,7 @@ export class TestHelper { p.metadata.positionData.GPSData.latitude = 10; p.metadata.positionData.GPSData.longitude = 15; p.metadata.creationDate = 1656069387772 - 3000; + p.metadata.creationDateOffset = "-03:45"; p.metadata.rating = 3; p.metadata.size.height = 1000; p.metadata.size.width = 2000; @@ -275,6 +280,7 @@ export class TestHelper { p.metadata.positionData.GPSData.latitude = 15; p.metadata.positionData.GPSData.longitude = 10; p.metadata.creationDate = 1656069387772 - 4000; + p.metadata.creationDateOffset = "+04:30"; p.metadata.size.height = 3000; p.metadata.size.width = 2000; @@ -394,6 +400,7 @@ export class TestHelper { positionData: pd, size: sd, creationDate: Date.now() + ++TestHelper.creationCounter, + creationDateOffset: "+01:00", fileSize: rndInt(10000), caption: rndStr(), rating: rndInt(5) as any,