From 7bf5366c5f3f95ddcf26f6406d291c41eddf523a Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Tue, 16 Dec 2025 14:35:14 +0000 Subject: [PATCH] Fix date handling ensuring there's always a time Fix #40 --- src/hooks/useStacSearch.test.ts | 15 +++++++++------ src/stac-api/StacApi.test.ts | 18 +++++++++--------- src/stac-api/index.ts | 15 ++++++++------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/hooks/useStacSearch.test.ts b/src/hooks/useStacSearch.test.ts index 741df3a..75cdc2d 100644 --- a/src/hooks/useStacSearch.test.ts +++ b/src/hooks/useStacSearch.test.ts @@ -188,7 +188,10 @@ describe('useStacSearch — API supports POST', () => { await waitFor(() => expect(result.current.results).toEqual({ data: '12345' })); // Validate POST payload const postPayload = parseRequestPayload(fetch.mock.calls[1][1]); - expect(postPayload).toEqual({ datetime: '2022-01-17/2022-05-17', limit: 25 }); + expect(postPayload).toEqual({ + datetime: '2022-01-17T00:00:00Z/2022-05-17T23:59:59Z', + limit: 25, + }); }); it('includes open date range in search (no to-date)', async () => { @@ -215,7 +218,7 @@ describe('useStacSearch — API supports POST', () => { await waitFor(() => expect(result.current.results).toEqual({ data: '12345' })); // Validate POST payload const postPayload = parseRequestPayload(fetch.mock.calls[1][1]); - expect(postPayload).toEqual({ datetime: '2022-01-17/..', limit: 25 }); + expect(postPayload).toEqual({ datetime: '2022-01-17T00:00:00Z/..', limit: 25 }); }); it('includes open date range in search (no from-date)', async () => { @@ -242,7 +245,7 @@ describe('useStacSearch — API supports POST', () => { await waitFor(() => expect(result.current.results).toEqual({ data: '12345' })); // Validate POST payload const postPayload = parseRequestPayload(fetch.mock.calls[1][1]); - expect(postPayload).toEqual({ datetime: '../2022-05-17', limit: 25 }); + expect(postPayload).toEqual({ datetime: '../2022-05-17T23:59:59Z', limit: 25 }); }); it('handles error with JSON response', async () => { @@ -799,7 +802,7 @@ describe('useStacSearch — API supports GET', () => { await waitFor(() => expect(fetch).toHaveBeenCalledTimes(2)); // Assert fetch URL and results expect(fetch.mock.calls[1][0]).toEqual( - 'https://fake-stac-api.net/search?limit=25&datetime=2022-01-17%2F2022-05-17' + 'https://fake-stac-api.net/search?limit=25&datetime=2022-01-17T00%3A00%3A00Z%2F2022-05-17T23%3A59%3A59Z' ); expect(result.current.results).toEqual({ data: '12345' }); }); @@ -825,7 +828,7 @@ describe('useStacSearch — API supports GET', () => { await waitFor(() => expect(fetch).toHaveBeenCalledTimes(2)); // Assert fetch URL and results expect(fetch.mock.calls[1][0]).toEqual( - 'https://fake-stac-api.net/search?limit=25&datetime=2022-01-17%2F..' + 'https://fake-stac-api.net/search?limit=25&datetime=2022-01-17T00%3A00%3A00Z%2F..' ); expect(result.current.results).toEqual({ data: '12345' }); }); @@ -851,7 +854,7 @@ describe('useStacSearch — API supports GET', () => { await waitFor(() => expect(fetch).toHaveBeenCalledTimes(2)); // Assert fetch URL and results expect(fetch.mock.calls[1][0]).toEqual( - 'https://fake-stac-api.net/search?limit=25&datetime=..%2F2022-05-17' + 'https://fake-stac-api.net/search?limit=25&datetime=..%2F2022-05-17T23%3A59%3A59Z' ); expect(result.current.results).toEqual({ data: '12345' }); }); diff --git a/src/stac-api/StacApi.test.ts b/src/stac-api/StacApi.test.ts index e43f753..8f97b77 100644 --- a/src/stac-api/StacApi.test.ts +++ b/src/stac-api/StacApi.test.ts @@ -24,32 +24,32 @@ describe('StacApi', () => { expect(result).toBeUndefined(); }); - it('should format date range with from and to', () => { + it('should format date range with from and to appending time parts', () => { const dateRange = { from: '2025-12-01', to: '2025-12-31' }; const result = stacApi.makeDatetimePayload(dateRange); // Simple date format for STAC API compatibility - expect(result).toBe('2025-12-01/2025-12-31'); + expect(result).toBe('2025-12-01T00:00:00Z/2025-12-31T23:59:59Z'); }); it('should format date range with only from', () => { const dateRange = { from: '2025-12-01' }; const result = stacApi.makeDatetimePayload(dateRange); - expect(result).toBe('2025-12-01/..'); + expect(result).toBe('2025-12-01T00:00:00Z/..'); }); it('should format date range with only to', () => { const dateRange = { to: '2025-12-31' }; const result = stacApi.makeDatetimePayload(dateRange); - expect(result).toBe('../2025-12-31'); + expect(result).toBe('../2025-12-31T23:59:59Z'); }); it('should handle full datetime strings', () => { const dateRange = { - from: '2025-12-01T00:00:00Z', - to: '2025-12-31T23:59:59Z', + from: '2025-12-01T11:11:11Z', + to: '2025-12-31T11:11:11Z', }; const result = stacApi.makeDatetimePayload(dateRange); - expect(result).toBe('2025-12-01T00:00:00Z/2025-12-31T23:59:59Z'); + expect(result).toBe('2025-12-01T11:11:11Z/2025-12-31T11:11:11Z'); }); }); @@ -80,7 +80,7 @@ describe('StacApi', () => { }), body: JSON.stringify({ collections: ['sentinel-2-l2a'], - datetime: '2025-12-01/2025-12-31', + datetime: '2025-12-01T00:00:00Z/2025-12-31T23:59:59Z', ids: undefined, bbox: undefined, }), @@ -98,7 +98,7 @@ describe('StacApi', () => { await getStacApi.search(searchPayload); expect(mockFetch).toHaveBeenCalledWith( - 'https://api.example.com/search?collections=sentinel-2-l2a&datetime=2025-12-01%2F2025-12-31', + 'https://api.example.com/search?collections=sentinel-2-l2a&datetime=2025-12-01T00%3A00%3A00Z%2F2025-12-31T23%3A59%3A59Z', expect.objectContaining({ method: 'GET', headers: expect.objectContaining({ diff --git a/src/stac-api/index.ts b/src/stac-api/index.ts index 4abfa54..7d5c49e 100644 --- a/src/stac-api/index.ts +++ b/src/stac-api/index.ts @@ -51,17 +51,18 @@ class StacApi { } makeDatetimePayload(dateRange?: DateRange): string | undefined { - if (!dateRange) { + if (!dateRange?.from && !dateRange?.to) { return undefined; } - const { from, to } = dateRange; + const formatDate = (date: string | undefined, end?: boolean) => { + if (!date) return '..'; - if (from || to) { - return `${from || '..'}/${to || '..'}`; - } else { - return undefined; - } + const timePart = end ? 'T23:59:59Z' : 'T00:00:00Z'; + return date.includes('T') ? date : `${date}${timePart}`; + }; + + return `${formatDate(dateRange?.from)}/${formatDate(dateRange?.to, true)}`; } payloadToQuery({ sortby, ...payload }: SearchPayload): string {