diff --git a/src/app/file-browser/components/file-list-item/file-list-item.component.html b/src/app/file-browser/components/file-list-item/file-list-item.component.html index ec76d890c..4e0d8e311 100644 --- a/src/app/file-browser/components/file-list-item/file-list-item.component.html +++ b/src/app/file-browser/components/file-list-item/file-list-item.component.html @@ -90,10 +90,10 @@
@if (!showAccess || !item.ShareArchiveVO) { - {{ item.displayDT | prDate: item.TimezoneVO : 'date' }} + {{ startDisplayTime | prDate: item.TimezoneVO : 'date' }} @if (item.dataStatus > 0) { {{ - item.displayDT | prDate: item.TimezoneVO : 'time' + startDisplayTime | prDate: item.TimezoneVO : 'time' }} } @@ -112,12 +112,12 @@
@if (item.dataStatus > 0) {
- {{ item.displayDT | prDate: item.TimezoneVO : 'date' }} + {{ startDisplayTime | prDate: item.TimezoneVO : 'date' }}
} @if (item.dataStatus > 0) {
- {{ item.displayDT | prDate: item.TimezoneVO : 'time' }} + {{ startDisplayTime | prDate: item.TimezoneVO : 'time' }}
}
diff --git a/src/app/file-browser/components/file-list-item/file-list-item.component.spec.ts b/src/app/file-browser/components/file-list-item/file-list-item.component.spec.ts index 1e78be409..8a9e6b825 100644 --- a/src/app/file-browser/components/file-list-item/file-list-item.component.spec.ts +++ b/src/app/file-browser/components/file-list-item/file-list-item.component.spec.ts @@ -25,8 +25,8 @@ class MockItemTypeIconPipe implements PipeTransform { @Pipe({ name: 'prDate' }) export class MockPrDatePipe implements PipeTransform { - transform(value: any, format?: string): string { - return `mocked-date${format ? `-${format}` : ''}`; + transform(value: any, ...args: any[]): string { + return String(value ?? ''); } } @@ -342,4 +342,51 @@ describe('FileListItemComponent', () => { expect(component.recordThumbnailUrl).toBe('https://example.com/thumb.jpg'); }); + + it('should display displayTime instead of displayDT when displayTime is set', () => { + component.item.displayTime = '2020-06-10'; + component.item.displayDT = '2023-01-01T00:00:00.000Z'; + fixture.detectChanges(); + + const secondRowDate = + fixture.nativeElement.querySelector('.second-row span')?.textContent; + + expect(secondRowDate).toContain('2020-06-10'); + expect(secondRowDate).not.toContain('2023-01-01'); + }); + + it('should display displayDT when displayTime is not set', () => { + component.item.displayTime = undefined; + component.item.displayDT = '2023-01-01T00:00:00.000Z'; + fixture.detectChanges(); + + const secondRowDate = + fixture.nativeElement.querySelector('.second-row span')?.textContent; + + expect(secondRowDate).toContain('2023-01-01T00:00:00.000Z'); + }); + + it('should display only the start date when displayTime is an EDTF interval', () => { + component.item.displayTime = '2020-06-10/2026-06-15'; + component.item.displayDT = '2023-01-01T00:00:00.000Z'; + fixture.detectChanges(); + + const secondRowDate = + fixture.nativeElement.querySelector('.second-row span')?.textContent; + + expect(secondRowDate).toContain('2020-06-10'); + expect(secondRowDate).not.toContain('2026-06-15'); + expect(secondRowDate).not.toContain('2023-01-01'); + }); + + it('should fall back to displayDT when the EDTF interval has an open start', () => { + component.item.displayTime = '../2026-06-15'; + component.item.displayDT = '2023-01-01T00:00:00.000Z'; + fixture.detectChanges(); + + const secondRowDate = + fixture.nativeElement.querySelector('.second-row span')?.textContent; + + expect(secondRowDate).toContain('2023-01-01T00:00:00.000Z'); + }); }); diff --git a/src/app/file-browser/components/file-list-item/file-list-item.component.ts b/src/app/file-browser/components/file-list-item/file-list-item.component.ts index 21312a7d5..bceed689e 100644 --- a/src/app/file-browser/components/file-list-item/file-list-item.component.ts +++ b/src/app/file-browser/components/file-list-item/file-list-item.component.ts @@ -33,6 +33,7 @@ import { } from '@root/app/models'; import { DataStatus } from '@models/data-status.enum'; import { EditService } from '@core/services/edit/edit.service'; +import { EdtfService } from '@shared/services/edtf-service/edtf.service'; import { RecordResponse, FolderResponse, @@ -247,8 +248,16 @@ export class FileListItemComponent private storage: StorageService, @Inject(DOCUMENT) private document: Document, private shareLinksService: ShareLinksService, + private edtfService: EdtfService, ) {} + get startDisplayTime(): string { + return ( + this.edtfService.getEdtfIntervalStartDate(this.item.displayTime) || + this.item.displayDT + ); + } + async ngOnInit() { this.recordThumbnailUrl = GetThumbnail(this.item); const date = new Date(this.item.displayDT); diff --git a/src/app/shared/pipes/pr-date.pipe.spec.ts b/src/app/shared/pipes/pr-date.pipe.spec.ts index b65031384..0676b7c38 100644 --- a/src/app/shared/pipes/pr-date.pipe.spec.ts +++ b/src/app/shared/pipes/pr-date.pipe.spec.ts @@ -1,5 +1,13 @@ +import { TimezoneVOData } from '@models'; import { PrDatePipe } from './pr-date.pipe'; +const cdtTimezone: TimezoneVOData = { + dstAbbrev: 'CDT', + dstOffset: '-05:00', + stdAbbrev: 'CST', + stdOffset: '-06:00', +}; + describe('PrDatePipe', () => { it('create an instance', () => { const pipe = new PrDatePipe(); @@ -7,6 +15,54 @@ describe('PrDatePipe', () => { expect(pipe).toBeTruthy(); }); + it('returns undefined for empty string', () => { + const pipe = new PrDatePipe(); + + expect(pipe.transform('')).toBeUndefined(); + }); + + it('returns undefined for null', () => { + const pipe = new PrDatePipe(); + + expect(pipe.transform(null as any)).toBeUndefined(); + }); + + describe('date-only strings (no T)', () => { + it('returns the date formatted from UTC', () => { + const pipe = new PrDatePipe(); + + expect(pipe.transform('2020-06-10')).toBe('2020-06-10'); + }); + + it('returns the date when part is date', () => { + const pipe = new PrDatePipe(); + + expect(pipe.transform('2020-06-10', undefined, 'date')).toBe( + '2020-06-10', + ); + }); + + it('returns undefined when part is time', () => { + const pipe = new PrDatePipe(); + + expect(pipe.transform('2020-06-10', undefined, 'time')).toBeUndefined(); + }); + + it('does not shift date by timezone offset', () => { + const pipe = new PrDatePipe(); + + expect(pipe.transform('2020-06-10', cdtTimezone, 'date')).toBe( + '2020-06-10', + ); + }); + + it('returns undefined for time part even with a timezone', () => { + const pipe = new PrDatePipe(); + + expect(pipe.transform('2020-06-10', cdtTimezone, 'time')).toBeUndefined(); + }); + }); + // it('transforms a DST date in Pacific time', () => { // const pipe = new PrDatePipe(); // const displayDT = formatDateISOString('2017-05-13T16:36:29.000000'); diff --git a/src/app/shared/pipes/pr-date.pipe.ts b/src/app/shared/pipes/pr-date.pipe.ts index 96e38ab8a..5bc2877bf 100644 --- a/src/app/shared/pipes/pr-date.pipe.ts +++ b/src/app/shared/pipes/pr-date.pipe.ts @@ -35,6 +35,11 @@ export class PrDatePipe implements PipeTransform { return; } + if (typeof dtString === 'string' && !dtString.includes('T')) { + if (part === 'time') return; + return moment.utc(dtString).format(MOMENT_DATE_FORMAT.date); + } + const dt = moment.utc(dtString); let outputDt: moment.Moment; diff --git a/src/app/shared/services/edtf-service/edtf.service.spec.ts b/src/app/shared/services/edtf-service/edtf.service.spec.ts index 2f86e5f4a..400dc5e1c 100644 --- a/src/app/shared/services/edtf-service/edtf.service.spec.ts +++ b/src/app/shared/services/edtf-service/edtf.service.spec.ts @@ -1283,4 +1283,42 @@ describe('EdtfService', () => { expect(result).toBe(edtfString); }); }); + + describe('getEdtfIntervalStartDate', () => { + it('should return the start date of an interval', () => { + expect(service.getEdtfIntervalStartDate('1985-05-20/1990-06-15')).toBe( + '1985-05-20', + ); + }); + + it('should preserve the wall-clock time and offset of the start', () => { + expect( + service.getEdtfIntervalStartDate( + '2020-06-10T06:58:00-05:00/2026-06-15T00:00:00Z', + ), + ).toBe('2020-06-10T06:58:00-05:00'); + }); + + it('should return a non-interval value unchanged', () => { + expect(service.getEdtfIntervalStartDate('1985-05-20')).toBe('1985-05-20'); + }); + + it('should return an empty string for an empty input', () => { + expect(service.getEdtfIntervalStartDate('')).toBe(''); + }); + + it('should return an empty string for an undefined input', () => { + expect(service.getEdtfIntervalStartDate(undefined)).toBe(''); + }); + + it('should return an empty string for an open-ended start', () => { + expect(service.getEdtfIntervalStartDate('../1990-06-15')).toBe(''); + }); + + it('should return the start when the end is open-ended', () => { + expect(service.getEdtfIntervalStartDate('1985-05-20/..')).toBe( + '1985-05-20', + ); + }); + }); }); diff --git a/src/app/shared/services/edtf-service/edtf.service.ts b/src/app/shared/services/edtf-service/edtf.service.ts index 8cc7dd34d..14ed0f385 100644 --- a/src/app/shared/services/edtf-service/edtf.service.ts +++ b/src/app/shared/services/edtf-service/edtf.service.ts @@ -102,6 +102,19 @@ export class EdtfService { } } + getEdtfIntervalStartDate(edtfString: string | undefined): string { + if (!edtfString) { + return ''; + } + + if (!edtfString.includes('/')) { + return edtfString; + } + + const [startPart] = edtfString.split('/'); + return startPart === '..' ? '' : (startPart ?? ''); + } + private parseInterval(edtfString: string): DateTimeModel | null { const [startPart, endPart] = edtfString.split('/');