Merge pull request #902 from grasdk/bugfix/offset-or-ignore
Bugfix/offset or ignore
BIN
demo/images/4MinsAroundTheWorld/20240413-203800-Unknown.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
demo/images/4MinsAroundTheWorld/UTC-20240413-203430-Auckland.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 61 KiB |
BIN
demo/images/4MinsAroundTheWorld/UTC-20240413-203630-London.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
demo/images/4MinsAroundTheWorld/UTC-20240413-203720-Tokyo.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 58 KiB |
BIN
demo/images/4MinsAroundTheWorld/UTC-20240413-203830-NewYork.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
@ -364,7 +364,11 @@ export class SearchManager {
|
||||
for (const sort of sortings) {
|
||||
switch (sort.method) {
|
||||
case SortByTypes.Date:
|
||||
query.addOrderBy('media.metadata.creationDate', sort.ascending ? 'ASC' : 'DESC'); //If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). If taken into account, it will alter the sort order. Probably should not be done.
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
query.addOrderBy('media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)', sort.ascending ? 'ASC' : 'DESC');
|
||||
} else {
|
||||
query.addOrderBy('media.metadata.creationDate', sort.ascending ? 'ASC' : 'DESC');
|
||||
}
|
||||
break;
|
||||
case SortByTypes.Rating:
|
||||
query.addOrderBy('media.metadata.rating', sort.ascending ? 'ASC' : 'DESC');
|
||||
@ -562,15 +566,17 @@ export class SearchManager {
|
||||
|
||||
const textParam: { [key: string]: unknown } = {};
|
||||
textParam['from' + queryId] = (query as FromDateSearch).value;
|
||||
q.where(
|
||||
`media.metadata.creationDate ${relation} :from${queryId}`, //TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-).
|
||||
//Example: -600 means in the database UTC-10:00. The time 20:00 in the evening in the UTC-10 timezone, is actually 06:00 the next morning
|
||||
//in UTC+00:00. To make search take that into account, one can subtract the offset from the creationDate to "pretend" the photo is taken
|
||||
//in UTC time. Subtracting -600 minutes (because it's the -10:00 timezone), corresponds to adding 10 hours to the photo's timestamp, thus
|
||||
//bringing it into the next day as if it was taken at UTC+00:00. Similarly subtracting a positive timezone from a timestamp will "pretend"
|
||||
//the photo is taken earlier in time (e.g. subtracting 300 from the UTC+05:00 timezone).
|
||||
textParam
|
||||
);
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`(media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)) ${relation} :from${queryId}`,
|
||||
textParam
|
||||
);
|
||||
} else {
|
||||
q.where(
|
||||
`media.metadata.creationDate ${relation} :from${queryId}`,
|
||||
textParam
|
||||
);
|
||||
}
|
||||
|
||||
return q;
|
||||
});
|
||||
@ -589,10 +595,18 @@ export class SearchManager {
|
||||
|
||||
const textParam: { [key: string]: unknown } = {};
|
||||
textParam['to' + queryId] = (query as ToDateSearch).value;
|
||||
q.where(
|
||||
`media.metadata.creationDate ${relation} :to${queryId}`, //TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
textParam
|
||||
);
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`(media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)) ${relation} :to${queryId}`,
|
||||
textParam
|
||||
);
|
||||
} else {
|
||||
q.where(
|
||||
`media.metadata.creationDate ${relation} :to${queryId}`,
|
||||
textParam
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return q;
|
||||
});
|
||||
@ -793,18 +807,34 @@ export class SearchManager {
|
||||
textParam['to' + queryId] = to.getTime();
|
||||
textParam['from' + queryId] = from.getTime();
|
||||
if (tq.negate) {
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`(media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)) >= :to${queryId}`,
|
||||
textParam
|
||||
).orWhere(`(media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)) < :from${queryId}`,
|
||||
textParam);
|
||||
} else {
|
||||
q.where(
|
||||
`media.metadata.creationDate >= :to${queryId}`,
|
||||
textParam
|
||||
).orWhere(`media.metadata.creationDate < :from${queryId}`,
|
||||
textParam);
|
||||
|
||||
q.where(
|
||||
`media.metadata.creationDate >= :to${queryId}`, //TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
textParam
|
||||
).orWhere(`media.metadata.creationDate < :from${queryId}`, //TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
textParam);
|
||||
}
|
||||
} else {
|
||||
q.where(
|
||||
`media.metadata.creationDate < :to${queryId}`, //TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
textParam
|
||||
).andWhere(`media.metadata.creationDate >= :from${queryId}`, //TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
textParam);
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`(media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)) < :to${queryId}`,
|
||||
textParam
|
||||
).andWhere(`(media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)) >= :from${queryId}`,
|
||||
textParam);
|
||||
} else {
|
||||
q.where(
|
||||
`media.metadata.creationDate < :to${queryId}`,
|
||||
textParam
|
||||
).andWhere(`media.metadata.creationDate >= :from${queryId}`,
|
||||
textParam);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -825,29 +855,51 @@ export class SearchManager {
|
||||
|
||||
if (Config.Database.type === DatabaseType.sqlite) {
|
||||
if (tq.daysLength == 0) {
|
||||
q.where(
|
||||
//TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
`CAST(strftime('${duration}',media.metadataCreationDate/1000, 'unixepoch') AS INTEGER) ${relationEql} CAST(strftime('${duration}','now') AS INTEGER)`
|
||||
);
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`CAST(strftime('${duration}',(media.metadataCreationDate + (media.metadataCreationDateOffset * 60000))/1000, 'unixepoch') AS INTEGER) ${relationEql} CAST(strftime('${duration}','now') AS INTEGER)`
|
||||
);
|
||||
} else {
|
||||
q.where(
|
||||
`CAST(strftime('${duration}',media.metadataCreationDate/1000, 'unixepoch') AS INTEGER) ${relationEql} CAST(strftime('${duration}','now') AS INTEGER)`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
q.where(
|
||||
//TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
`CAST(strftime('${duration}',media.metadataCreationDate/1000, 'unixepoch') AS INTEGER) ${relationTop} CAST(strftime('${duration}','now') AS INTEGER)`
|
||||
)[whereFN](`CAST(strftime('${duration}',media.metadataCreationDate/1000, 'unixepoch') AS INTEGER) ${relationBottom} CAST(strftime('${duration}','now','-:diff${queryId} day') AS INTEGER)`,
|
||||
textParam);
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`CAST(strftime('${duration}',(media.metadataCreationDate + (media.metadataCreationDateOffset * 60000))/1000, 'unixepoch') AS INTEGER) ${relationTop} CAST(strftime('${duration}','now') AS INTEGER)`
|
||||
)[whereFN](`CAST(strftime('${duration}',(media.metadataCreationDate + (media.metadataCreationDateOffset * 60000))/1000, 'unixepoch') AS INTEGER) ${relationBottom} CAST(strftime('${duration}','now','-:diff${queryId} day') AS INTEGER)`,
|
||||
textParam);
|
||||
} else {
|
||||
q.where(
|
||||
`CAST(strftime('${duration}',media.metadataCreationDate/1000, 'unixepoch') AS INTEGER) ${relationTop} CAST(strftime('${duration}','now') AS INTEGER)`
|
||||
)[whereFN](`CAST(strftime('${duration}',media.metadataCreationDate/1000, 'unixepoch') AS INTEGER) ${relationBottom} CAST(strftime('${duration}','now','-:diff${queryId} day') AS INTEGER)`,
|
||||
textParam);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (tq.daysLength == 0) {
|
||||
q.where(
|
||||
//TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
`CAST(FROM_UNIXTIME(media.metadataCreationDate/1000, '${duration}') AS SIGNED) ${relationEql} CAST(DATE_FORMAT(CURDATE(),'${duration}') AS SIGNED)`
|
||||
);
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`CAST(FROM_UNIXTIME((media.metadataCreationDate + (media.metadataCreationDateOffset * 60000))/1000, '${duration}') AS SIGNED) ${relationEql} CAST(DATE_FORMAT(CURDATE(),'${duration}') AS SIGNED)`
|
||||
);
|
||||
} else {
|
||||
q.where(
|
||||
`CAST(FROM_UNIXTIME(media.metadataCreationDate/1000, '${duration}') AS SIGNED) ${relationEql} CAST(DATE_FORMAT(CURDATE(),'${duration}') AS SIGNED)`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
q.where(
|
||||
//TODO: If media.metadata.creationDateOffset is defined, it is an offset of minutes (+/-). See explanation above.
|
||||
`CAST(FROM_UNIXTIME(media.metadataCreationDate/1000, '${duration}') AS SIGNED) ${relationTop} CAST(DATE_FORMAT(CURDATE(),'${duration}') AS SIGNED)`
|
||||
)[whereFN](`CAST(FROM_UNIXTIME(media.metadataCreationDate/1000, '${duration}') AS SIGNED) ${relationBottom} CAST(DATE_FORMAT((DATE_ADD(curdate(), INTERVAL -:diff${queryId} DAY)),'${duration}') AS SIGNED)`,
|
||||
textParam);
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
q.where(
|
||||
`CAST(FROM_UNIXTIME((media.metadataCreationDate + (media.metadataCreationDateOffset * 60000))/1000, '${duration}') AS SIGNED) ${relationTop} CAST(DATE_FORMAT(CURDATE(),'${duration}') AS SIGNED)`
|
||||
)[whereFN](`CAST(FROM_UNIXTIME((media.metadataCreationDate + (media.metadataCreationDateOffset * 60000))/1000, '${duration}') AS SIGNED) ${relationBottom} CAST(DATE_FORMAT((DATE_ADD(curdate(), INTERVAL -:diff${queryId} DAY)),'${duration}') AS SIGNED)`,
|
||||
textParam);
|
||||
} else {
|
||||
q.where(
|
||||
`CAST(FROM_UNIXTIME(media.metadataCreationDate/1000, '${duration}') AS SIGNED) ${relationTop} CAST(DATE_FORMAT(CURDATE(),'${duration}') AS SIGNED)`
|
||||
)[whereFN](`CAST(FROM_UNIXTIME(media.metadataCreationDate/1000, '${duration}') AS SIGNED) ${relationBottom} CAST(DATE_FORMAT((DATE_ADD(curdate(), INTERVAL -:diff${queryId} DAY)),'${duration}') AS SIGNED)`,
|
||||
textParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -277,8 +277,8 @@ export class DiskManager {
|
||||
directory.oldestMedia = Number.MIN_SAFE_INTEGER;
|
||||
|
||||
directory.media.forEach((m) => {
|
||||
directory.youngestMedia = Math.min(m.metadata.creationDate, directory.youngestMedia);
|
||||
directory.oldestMedia = Math.max(m.metadata.creationDate, directory.oldestMedia);
|
||||
directory.youngestMedia = Math.min(Utils.getTimeMS(m.metadata.creationDate, m.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset), directory.youngestMedia);
|
||||
directory.oldestMedia = Math.max(Utils.getTimeMS(m.metadata.creationDate, m.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset), directory.oldestMedia);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -70,7 +70,7 @@ export class EmailMessenger extends Messenger<{
|
||||
(media[i].metadata as PhotoMetadata).positionData?.country :
|
||||
((media[i].metadata as PhotoMetadata).positionData?.city ?
|
||||
(media[i].metadata as PhotoMetadata).positionData?.city : '');
|
||||
const caption = Utils.getFullYear(media[i].metadata.creationDate, media[i].metadata.creationDateOffset) + (location ? ', ' + location : '');
|
||||
const caption = Utils.getFullYear(Utils.getTimeMS(media[i].metadata.creationDate, media[i].metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset), undefined) + (location ? ', ' + location : '');
|
||||
attachments.push({
|
||||
filename: media[i].name,
|
||||
path: media[i].thumbnailPath,
|
||||
|
@ -123,10 +123,11 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
static makeUTCMidnight(d: number | Date) {
|
||||
static makeUTCMidnight(d: number | Date, offset: string) {
|
||||
if (!(d instanceof Date)) {
|
||||
d = new Date(d);
|
||||
}
|
||||
d = new Date(new Date(d).toISOString().substring(0,19) + (offset ? offset : '+00:00'))
|
||||
d.setUTCHours(0);
|
||||
d.setUTCMinutes(0);
|
||||
d.setUTCSeconds(0);
|
||||
@ -139,7 +140,7 @@ export class Utils {
|
||||
if (!(d instanceof Date)) {
|
||||
d = new Date(d);
|
||||
}
|
||||
return new Date(new Date(d).toISOString().substring(0,19) + (offset ? offset : '')).getUTCFullYear();
|
||||
return new Date(new Date(d).toISOString().substring(0,19) + (offset ? offset : '+00:00')).getUTCFullYear();
|
||||
}
|
||||
|
||||
static getFullYear(d: number | Date, offset: string) {
|
||||
@ -225,10 +226,21 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
static getLocalTimeMS(creationDate: number, creationDateOffset: string) {
|
||||
//Get the MS of the creationDate, adjusted for the offset. Effectively getting the MS value as if the photo did not contain an offset.
|
||||
//One can consider this "Local" time of the photo. Starting point is UTC, as MetadataLoader loads timestamps with unknown timestamps as UTC.
|
||||
static getLocalTimeMS(creationDate: number, creationDateOffset: string) {
|
||||
const offsetMinutes = Utils.getOffsetMinutes(creationDateOffset);
|
||||
return creationDate + (offsetMinutes ? (offsetMinutes * 60000) : 0);
|
||||
}
|
||||
|
||||
//Like getLocalTimeMS, but only if localTime is true, otherwise just returns creationDate (global time)
|
||||
static getTimeMS(creationDate: number, creationDateOffset: string, localTime: boolean) {
|
||||
if (localTime) {
|
||||
return Utils.getLocalTimeMS(creationDate, creationDateOffset);
|
||||
} else {
|
||||
return creationDate;
|
||||
}
|
||||
}
|
||||
|
||||
static isLeapYear(year: number) {
|
||||
return (0 == year % 4) && (0 != year % 100) || (0 == year % 400)
|
||||
|
@ -1082,6 +1082,18 @@ export class ClientGalleryConfig {
|
||||
})
|
||||
enableDirectorySortingByDate: boolean = false;
|
||||
|
||||
@ConfigProperty({
|
||||
tags: {
|
||||
name: $localize`Ignore timestamp offsets`,
|
||||
priority: ConfigPriority.advanced,
|
||||
},
|
||||
description: $localize`If enabled, timestamp offsets are ignored, meaning that the local times of pictures are used for searching, sorting and grouping. If disabled, global time is used and pictures with no timestamp are assumed to be in UTC (offset +00:00).`
|
||||
})
|
||||
//DEVELOPER NOTE: The Database model stores the timestamp (creationDate) as milliseconds since 1970-01-01 UTC (global time). And stores and offset (creationDateOffset) as minutes.
|
||||
//Ignoring timestamp for the user is the opposite for the database. If the user wants to ignore the offset, we have to add the offset to the creationDate to give the user the right experience.
|
||||
ignoreTimestampOffset: boolean = true;
|
||||
|
||||
|
||||
@ConfigProperty({
|
||||
tags: {
|
||||
name: $localize`On scroll thumbnail prioritising`,
|
||||
|
@ -6,6 +6,7 @@ import {ContentService} from '../content.service';
|
||||
import {mergeMap, Observable, shareReplay} from 'rxjs';
|
||||
import {MDFilesFilterPipe} from '../../../pipes/MDFilesFilterPipe';
|
||||
import {MDFileDTO} from '../../../../../common/entities/MDFileDTO';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
|
||||
@Injectable()
|
||||
export class BlogService {
|
||||
@ -28,7 +29,7 @@ export class BlogService {
|
||||
let firstMedia = Number.MAX_SAFE_INTEGER;
|
||||
if (content.mediaGroups.length > 0) {
|
||||
firstMedia = content.mediaGroups[0].media.reduce((p, m) =>
|
||||
Math.min(m.metadata.creationDate, p), Number.MAX_SAFE_INTEGER);
|
||||
Math.min(Utils.getTimeMS(m.metadata.creationDate, m.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset), p), Number.MAX_SAFE_INTEGER);
|
||||
}
|
||||
|
||||
const files = this.mdFilesFilterPipe.transform(content.metaFile)
|
||||
@ -65,7 +66,7 @@ export class BlogService {
|
||||
|
||||
const getDateGroup = (date: Date) => {
|
||||
// get UTC midnight date
|
||||
const dateNum = Utils.makeUTCMidnight(date).getTime();
|
||||
const dateNum = Utils.makeUTCMidnight(date, undefined).getTime();
|
||||
let groupDate = dates.find((d, i) => i > dates.length - 1 ? false : dates[i + 1] > dateNum); //dates are sorted
|
||||
|
||||
// cant find the date. put to the last group (as it was later)
|
||||
|
@ -3,6 +3,8 @@ import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||
import {DirectoryContent} from '../contentLoader.service';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
import {Config} from '../../../../../common/config/public/Config';
|
||||
import {Utils} from '../../../../../common/Utils';
|
||||
|
||||
export enum FilterRenderType {
|
||||
enum = 1,
|
||||
@ -159,11 +161,11 @@ export class FilterService {
|
||||
}
|
||||
const ret: { date: Date, endDate: Date, dateStr: string, count: number, max: number }[] = [];
|
||||
const minDate = prefiltered.media.reduce(
|
||||
(p, curr) => Math.min(p, curr.metadata.creationDate),
|
||||
(p, curr) => Math.min(p, Utils.getTimeMS(curr.metadata.creationDate, curr.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset)),
|
||||
Number.MAX_VALUE - 1
|
||||
);
|
||||
const maxDate = prefiltered.media.reduce(
|
||||
(p, curr) => Math.max(p, curr.metadata.creationDate),
|
||||
(p, curr) => Math.max(p, Utils.getTimeMS(curr.metadata.creationDate, curr.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset)),
|
||||
Number.MIN_VALUE + 1
|
||||
);
|
||||
const diff = (maxDate - minDate) / 1000;
|
||||
@ -205,7 +207,7 @@ export class FilterService {
|
||||
const startMediaDate = new Date(floorDate(minDate));
|
||||
|
||||
prefiltered.media.forEach(m => {
|
||||
const key = Math.floor((floorDate(m.metadata.creationDate) - startMediaDate.getTime()) / 1000 / usedDiv); //TODO
|
||||
const key = Math.floor((floorDate(Utils.getTimeMS(m.metadata.creationDate, m.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset)) - startMediaDate.getTime()) / 1000 / usedDiv);
|
||||
|
||||
const getDate = (index: number) => {
|
||||
let d: Date;
|
||||
@ -273,16 +275,16 @@ export class FilterService {
|
||||
if (c.media.length > 0) {
|
||||
// Update date filter range
|
||||
afilters.dateFilter.minDate = c.media.reduce(
|
||||
(p, curr) => Math.min(p, curr.metadata.creationDate),
|
||||
(p, curr) => Math.min(p, Utils.getTimeMS(curr.metadata.creationDate, curr.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset)),
|
||||
Number.MAX_VALUE - 1
|
||||
);
|
||||
afilters.dateFilter.maxDate = c.media.reduce(
|
||||
(p, curr) => Math.max(p, curr.metadata.creationDate),
|
||||
(p, curr) => Math.max(p, Utils.getTimeMS(curr.metadata.creationDate, curr.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset)),
|
||||
Number.MIN_VALUE + 1
|
||||
);
|
||||
// Add a few sec padding
|
||||
afilters.dateFilter.minDate -= (afilters.dateFilter.minDate % 1000) + 1000;
|
||||
afilters.dateFilter.maxDate += (afilters.dateFilter.maxDate % 1000) + 1000;
|
||||
afilters.dateFilter.minDate -= ((afilters.dateFilter.minDate % 1000) + 1000);
|
||||
afilters.dateFilter.maxDate += ((1000 - (afilters.dateFilter.maxDate % 1000)) + 1000);
|
||||
|
||||
if (afilters.dateFilter.minFilter === Number.MIN_VALUE) {
|
||||
afilters.dateFilter.minFilter = afilters.dateFilter.minDate;
|
||||
@ -294,8 +296,8 @@ export class FilterService {
|
||||
// Apply Date filter
|
||||
c.media = c.media.filter(
|
||||
(m) =>
|
||||
m.metadata.creationDate >= afilters.dateFilter.minFilter &&
|
||||
m.metadata.creationDate <= afilters.dateFilter.maxFilter
|
||||
Utils.getTimeMS(m.metadata.creationDate, m.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset) >= afilters.dateFilter.minFilter &&
|
||||
Utils.getTimeMS(m.metadata.creationDate, m.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset) <= afilters.dateFilter.maxFilter
|
||||
);
|
||||
} else {
|
||||
afilters.dateFilter.minDate = Number.MIN_VALUE;
|
||||
@ -398,5 +400,3 @@ export class FilterService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -508,7 +508,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
|
||||
case LightBoxTitleTexts.persons:
|
||||
return m.metadata.faces?.map(f => f.name)?.join(', ');
|
||||
case LightBoxTitleTexts.date:
|
||||
return this.datePipe.transform(m.metadata.creationDate, 'longDate', m.metadata.creationDateOffset);
|
||||
return this.datePipe.transform(Utils.getTimeMS(m.metadata.creationDate, m.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset) , 'longDate', 'UTC');
|
||||
case LightBoxTitleTexts.location:
|
||||
if (!m.metadata.positionData) {
|
||||
return '';
|
||||
|
@ -148,7 +148,7 @@ export class InfoPanelLightboxComponent implements OnInit, OnChanges {
|
||||
isThisYear(): boolean {
|
||||
return (
|
||||
new Date().getFullYear() ===
|
||||
Utils.getUTCFullYear(this.media.metadata.creationDate, this.media.metadata.creationDateOffset)
|
||||
Utils.getUTCFullYear(this.media.metadata.creationDate, this.media.metadata.creationDateOffset)
|
||||
);
|
||||
}
|
||||
|
||||
@ -202,4 +202,3 @@ export class InfoPanelLightboxComponent implements OnInit, OnChanges {
|
||||
} as TextSearch);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ export class GallerySortingService {
|
||||
break;
|
||||
case SortByTypes.Date:
|
||||
media.sort((a: PhotoDTO, b: PhotoDTO): number => {
|
||||
return a.metadata.creationDate - b.metadata.creationDate;
|
||||
return Utils.getTimeMS(a.metadata.creationDate, a.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset) - Utils.getTimeMS(b.metadata.creationDate, b.metadata.creationDateOffset, Config.Gallery.ignoreTimestampOffset);
|
||||
});
|
||||
break;
|
||||
case SortByTypes.Rating:
|
||||
@ -196,7 +196,13 @@ export class GallerySortingService {
|
||||
private getGroupByNameFn(grouping: GroupingMethod) {
|
||||
switch (grouping.method) {
|
||||
case SortByTypes.Date:
|
||||
return (m: MediaDTO) => this.datePipe.transform(m.metadata.creationDate, 'longDate', m.metadata.creationDateOffset ? m.metadata.creationDateOffset : 'UTC');
|
||||
if (Config.Gallery.ignoreTimestampOffset === true) {
|
||||
//Datepipe used this way, converts creationDate to date in local time.
|
||||
return (m: MediaDTO) => this.datePipe.transform(m.metadata.creationDate, 'longDate', m.metadata.creationDateOffset ? m.metadata.creationDateOffset : 'UTC');
|
||||
} else {
|
||||
//Grouping with global time, requires a common timeframe.
|
||||
return (m: MediaDTO) => this.datePipe.transform(m.metadata.creationDate, 'longDate', 'UTC');
|
||||
}
|
||||
|
||||
case SortByTypes.Name:
|
||||
return (m: MediaDTO) => m.name.at(0).toUpperCase();
|
||||
@ -308,7 +314,7 @@ export class GallerySortingService {
|
||||
if (grouping.method === GroupByTypes.Date) {
|
||||
// We do not need the youngest as we group by day. All photos are from the same day
|
||||
c.mediaGroups.forEach(g => {
|
||||
g.date = Utils.makeUTCMidnight(new Date(g.media?.[0]?.metadata?.creationDate));
|
||||
g.date = Utils.makeUTCMidnight(new Date(g.media?.[0]?.metadata?.creationDate), g.media?.[0]?.metadata?.creationDateOffset);
|
||||
});
|
||||
}
|
||||
|
||||
@ -338,5 +344,3 @@ export interface GroupedDirectoryContent {
|
||||
mediaGroups: MediaGroup[];
|
||||
metaFile: FileDTO[];
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 70 KiB |
@ -0,0 +1,8 @@
|
||||
{
|
||||
"creationDate": 1713040680000,
|
||||
"fileSize": 71525,
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
@ -173,7 +173,7 @@ describe('Typeorm integration', () => {
|
||||
|
||||
const photos = await pr
|
||||
.createQueryBuilder('media')
|
||||
.orderBy('media.metadata.creationDate', 'ASC')
|
||||
.orderBy('media.metadata.creationDate', 'ASC') //TODO: Offset: Create a test where it is ".orderBy('media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)', 'ASC')" instead
|
||||
.where('media.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + photo.metadata.positionData.city + '%'})
|
||||
.innerJoinAndSelect('media.directory', 'directory')
|
||||
.limit(10)
|
||||
@ -195,7 +195,7 @@ describe('Typeorm integration', () => {
|
||||
await pr.save(photo);
|
||||
const photos = await pr
|
||||
.createQueryBuilder('media')
|
||||
.orderBy('media.metadata.creationDate', 'ASC')
|
||||
.orderBy('media.metadata.creationDate', 'ASC') //TODO: Offset: Create a test where it is ".orderBy('media.metadata.creationDate + (media.metadata.creationDateOffset * 60000)', 'ASC')" instead
|
||||
.where('media.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + city + '%'})
|
||||
.innerJoinAndSelect('media.directory', 'directory')
|
||||
.limit(10)
|
||||
|
@ -128,6 +128,7 @@ describe('CoverManager', (sqlHelper: DBTestHelper) => {
|
||||
|
||||
before(async () => {
|
||||
await setUpSqlDB();
|
||||
Config.Gallery.ignoreTimestampOffset = false;
|
||||
});
|
||||
|
||||
|
||||
|