Skip to content

Commit 0037104

Browse files
committed
tests for all time functions
1 parent 4aa2b41 commit 0037104

File tree

1 file changed

+275
-12
lines changed

1 file changed

+275
-12
lines changed

packages/cubejs-backend-shared/test/time.test.ts

Lines changed: 275 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import moment from 'moment-timezone';
2-
import { localTimestampToUtc, timeSeries, isPredefinedGranularity, timeSeriesFromCustomInterval } from '../src';
2+
import {
3+
localTimestampToUtc,
4+
timeSeries,
5+
isPredefinedGranularity,
6+
timeSeriesFromCustomInterval,
7+
extractDate,
8+
utcToLocalTimeZone,
9+
addSecondsToLocalTimestamp,
10+
reformatInIsoLocal,
11+
} from '../src';
312

4-
describe('time', () => {
13+
describe('timeSeries', () => {
514
it('time series - day', () => {
615
expect(timeSeries('day', ['2021-01-01', '2021-01-02'])).toEqual([
716
['2021-01-01T00:00:00.000', '2021-01-01T23:59:59.999'],
@@ -177,19 +186,273 @@ describe('time', () => {
177186
timeSeriesFromCustomInterval('10 minutes 15 seconds', ['1970-01-01', '2021-01-02'], moment('2021-02-01 09:59:45'));
178187
}).toThrowError(/The count of generated date ranges.*for the request.*is over limit/);
179188
});
189+
});
180190

181-
it('inDbTimeZone', () => {
182-
expect(localTimestampToUtc('UTC', 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', '2020-01-01T00:00:00.000000')).toEqual(
183-
'2020-01-01T00:00:00.000000Z'
184-
);
185-
186-
expect(localTimestampToUtc('UTC', 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', '2020-01-31T23:59:59.999999')).toEqual(
187-
'2020-01-31T23:59:59.999999Z'
188-
);
189-
});
190-
191+
describe('predefinedGranularity', () => {
191192
it('isPredefinedGranularity', () => {
192193
expect(isPredefinedGranularity('day')).toBeTruthy();
193194
expect(isPredefinedGranularity('fiscal_year_by_1st_feb')).toBeFalsy();
194195
});
195196
});
197+
198+
describe('extractDate', () => {
199+
it('should return null if data is empty', () => {
200+
expect(extractDate(null)).toBeNull();
201+
expect(extractDate(undefined)).toBeNull();
202+
expect(extractDate([])).toBeNull();
203+
expect(extractDate('')).toBeNull();
204+
});
205+
206+
it('should return null if no valid date is found in data', () => {
207+
expect(extractDate([{}])).toBeNull();
208+
expect(extractDate([{ someKey: 'invalid date' }])).toBeNull();
209+
});
210+
211+
it('should parse a date with UTC timezone', () => {
212+
const input = [{ date: '2025-02-28T12:00:00Z' }];
213+
const result = extractDate(input);
214+
expect(result).toBe('2025-02-28T12:00:00.000');
215+
});
216+
217+
it('should parse a date with an offset timezone', () => {
218+
const input = [{ date: '2025-02-28T12:00:00+03:00' }];
219+
const result = extractDate(input);
220+
expect(result).toBe('2025-02-28T09:00:00.000');
221+
});
222+
223+
it('should parse a date without timezone as UTC', () => {
224+
const input = [{ date: '2025-02-28 12:00:00' }];
225+
const result = extractDate(input);
226+
expect(result).toBe('2025-02-28T12:00:00.000');
227+
});
228+
229+
it('should handle multiple formats', () => {
230+
const input1 = [{ date: '2025-02-28 12:00:00' }];
231+
const input2 = [{ date: '2025-02-28T12:00:00.000' }];
232+
const input3 = [{ date: '2025-02-28T12:00:00Z' }];
233+
const input4 = [{ date: '2025-02-28T12:00:00+03:00' }];
234+
235+
expect(extractDate(input1)).toBe('2025-02-28T12:00:00.000');
236+
expect(extractDate(input2)).toBe('2025-02-28T12:00:00.000');
237+
expect(extractDate(input3)).toBe('2025-02-28T12:00:00.000');
238+
expect(extractDate(input4)).toBe('2025-02-28T09:00:00.000');
239+
});
240+
});
241+
242+
describe('localTimestampToUtc', () => {
243+
it('should return null if timestamp is empty', () => {
244+
expect(localTimestampToUtc('UTC', 'YYYY-MM-DDTHH:mm:ss.SSS', '')).toBeNull();
245+
expect(localTimestampToUtc('UTC', 'YYYY-MM-DDTHH:mm:ss.SSS')).toBeNull();
246+
});
247+
248+
it('should throw an error for unknown timezone', () => {
249+
expect(() => localTimestampToUtc('Invalid/Timezone', 'YYYY-MM-DDTHH:mm:ss.SSS', '2025-02-28T12:00:00.000'))
250+
.toThrowError('Unknown timezone: Invalid/Timezone');
251+
});
252+
253+
it('should convert timestamp with timezone to UTC for format YYYY-MM-DD[T]HH:mm:ss.SSS[Z]', () => {
254+
const timestamp = '2025-02-28T11:00:00.000';
255+
const timezone = 'Europe/Kiev';
256+
const result = localTimestampToUtc(timezone, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]', timestamp);
257+
expect(result).toBe('2025-02-28T09:00:00.000Z');
258+
});
259+
260+
it('should convert timestamp with timezone to UTC for format YYYY-MM-DDTHH:mm:ss.SSSZ', () => {
261+
const timestamp = '2025-02-28T11:00:00.000';
262+
const timezone = 'Europe/Kiev';
263+
const result = localTimestampToUtc(timezone, 'YYYY-MM-DDTHH:mm:ss.SSSZ', timestamp);
264+
expect(result).toBe('2025-02-28T09:00:00.000Z');
265+
});
266+
267+
it('should convert timestamp with microseconds to UTC for format YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', () => {
268+
const timezone = 'Europe/Kiev';
269+
let timestamp = '2025-02-28T11:00:00.123456';
270+
let result = localTimestampToUtc(timezone, 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', timestamp);
271+
expect(result).toBe('2025-02-28T09:00:00.123000Z'); // microseconds are zeroed :(
272+
273+
timestamp = '2025-02-28T11:00:00.000000';
274+
result = localTimestampToUtc(timezone, 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', timestamp);
275+
expect(result).toBe('2025-02-28T09:00:00.000000Z'); // microseconds are zeroed :(
276+
277+
timestamp = '2025-02-28T11:00:00.999999';
278+
result = localTimestampToUtc(timezone, 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', timestamp);
279+
expect(result).toBe('2025-02-28T09:00:00.999999Z'); // microseconds are zeroed :(
280+
});
281+
282+
it('should convert timestamp with timezone to UTC for format YYYY-MM-DDTHH:mm:ss.SSSSSS', () => {
283+
const timezone = 'Europe/Kiev';
284+
let timestamp = '2025-02-28T11:00:00.123456';
285+
let result = localTimestampToUtc(timezone, 'YYYY-MM-DDTHH:mm:ss.SSSSSS', timestamp);
286+
expect(result).toBe('2025-02-28T09:00:00.123000');
287+
288+
timestamp = '2025-02-28T11:00:00.000000';
289+
result = localTimestampToUtc(timezone, 'YYYY-MM-DDTHH:mm:ss.SSSSSS', timestamp);
290+
expect(result).toBe('2025-02-28T09:00:00.000000');
291+
292+
timestamp = '2025-02-28T11:00:00.999999';
293+
result = localTimestampToUtc(timezone, 'YYYY-MM-DDTHH:mm:ss.SSSSSS', timestamp);
294+
expect(result).toBe('2025-02-28T09:00:00.999999');
295+
});
296+
297+
it('should convert timestamp without timezone to UTC for format YYYY-MM-DDTHH:mm:ss.SSS', () => {
298+
const timestamp = '2025-02-28T12:00:00.000';
299+
const timezone = 'UTC';
300+
const result = localTimestampToUtc(timezone, 'YYYY-MM-DDTHH:mm:ss.SSS', timestamp);
301+
expect(result).toBe('2025-02-28T12:00:00.000'); // UTC
302+
});
303+
304+
it('should correctly handle timestamp without time zone', () => {
305+
const timestamp = '2025-02-28T12:00:00.000';
306+
const timezone = 'America/New_York';
307+
const result = localTimestampToUtc(timezone, 'YYYY-MM-DDTHH:mm:ss.SSS', timestamp);
308+
expect(result).toBe('2025-02-28T17:00:00.000'); // America/New_York is UTC-5 during daylight saving time
309+
});
310+
});
311+
312+
describe('utcToLocalTimeZone', () => {
313+
it('should return null if no timestamp is provided', () => {
314+
expect(utcToLocalTimeZone('Europe/Kiev', 'YYYY-MM-DDTHH:mm:ss.SSS', undefined)).toBeNull();
315+
});
316+
317+
it('should throw an error for an unknown timezone', () => {
318+
expect(() => utcToLocalTimeZone('Unknown/Zone', 'YYYY-MM-DDTHH:mm:ss.SSS', '2025-02-28T10:00:00.000'))
319+
.toThrow('Unknown timezone: Unknown/Zone');
320+
});
321+
322+
it('should convert UTC to specified timezone with timestamp format "YYYY-MM-DDTHH:mm:ss.SSS"', () => {
323+
const timestamp = '2025-02-28T10:00:00.000';
324+
const expected = '2025-02-28T12:00:00.000';
325+
326+
expect(utcToLocalTimeZone('Europe/Kiev', 'YYYY-MM-DDTHH:mm:ss.SSS', timestamp)).toBe(expected);
327+
});
328+
329+
it('should convert UTC to specified timezone with timestamp format "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"', () => {
330+
const timestamp = '2025-02-28T10:00:00.000';
331+
const expected = '2025-02-28T12:00:00.000Z';
332+
333+
expect(utcToLocalTimeZone('Europe/Kiev', 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]', timestamp)).toBe(expected);
334+
});
335+
336+
it('should handle timestamps with microseconds correctly', () => {
337+
const timestamp = '2025-02-28T10:00:00.123456Z';
338+
const expected = '2025-02-28T12:00:00.123000'; // microseconds are zeroed
339+
340+
expect(utcToLocalTimeZone('Europe/Kiev', 'YYYY-MM-DDTHH:mm:ss.SSSSSS', timestamp)).toBe(expected);
341+
});
342+
343+
it('should handle UTC timestamp and correctly shift to target timezone', () => {
344+
const timestamp = '2025-02-28T12:00:00.000Z';
345+
const expected = '2025-02-28T14:00:00.000';
346+
347+
expect(utcToLocalTimeZone('Europe/Kiev', 'YYYY-MM-DDTHH:mm:ss.SSS', timestamp)).toBe(expected);
348+
});
349+
350+
it('should correctly handle UTC to timezone conversion with timestamp format without milliseconds', () => {
351+
const timestamp = '2025-02-28T10:00:00Z';
352+
const expected = '2025-02-28T12:00:00';
353+
354+
expect(utcToLocalTimeZone('Europe/Kiev', 'YYYY-MM-DDTHH:mm:ss', timestamp)).toBe(expected);
355+
});
356+
357+
it('should return the local time as UTC format if no milliseconds are present', () => {
358+
const timestamp = '2025-02-28T10:00:00Z';
359+
const expected = '2025-02-28T12:00:00.000Z';
360+
361+
expect(utcToLocalTimeZone('Europe/Kiev', 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]', timestamp)).toBe(expected);
362+
});
363+
});
364+
365+
describe('addSecondsToLocalTimestamp', () => {
366+
it('should throw an error for an unknown timezone', () => {
367+
const timestamp = '2025-02-28T12:00:00.000';
368+
const unknownTimezone = 'Unknown/Zone';
369+
const seconds = 10;
370+
371+
expect(() => addSecondsToLocalTimestamp(timestamp, unknownTimezone, seconds))
372+
.toThrow('Unknown timezone: Unknown/Zone');
373+
});
374+
375+
it('should correctly add seconds to a timestamp in the specified timezone', () => {
376+
const timestamp = '2025-02-28T12:00:00.000';
377+
const timezone = 'Europe/Kiev';
378+
const seconds = 10;
379+
const expected = new Date('2025-02-28T12:00:10.000');
380+
381+
expect(addSecondsToLocalTimestamp(timestamp, timezone, seconds)).toEqual(expected);
382+
});
383+
384+
it('should correctly add seconds to a timestamp with UTC timezone', () => {
385+
const timestamp = '2025-02-28T12:00:00.000Z';
386+
const timezone = 'Europe/Kiev';
387+
const seconds = 60;
388+
const expected = new Date('2025-02-28T14:01:00.000');
389+
390+
expect(addSecondsToLocalTimestamp(timestamp, timezone, seconds)).toEqual(expected);
391+
});
392+
393+
it('should correctly add seconds to a timestamp without milliseconds', () => {
394+
const timestamp = '2025-02-28T12:00:00';
395+
const timezone = 'Europe/Kiev';
396+
const seconds = 30;
397+
const expected = new Date('2025-02-28T12:00:30.000');
398+
399+
expect(addSecondsToLocalTimestamp(timestamp, timezone, seconds)).toEqual(expected);
400+
});
401+
402+
it('should correctly handle timestamp with microseconds and add seconds', () => {
403+
const timestamp = '2025-02-28T12:00:00.123456Z';
404+
const timezone = 'Europe/Kiev';
405+
const seconds = 60;
406+
const expected = new Date('2025-02-28T14:01:00.123456');
407+
408+
expect(addSecondsToLocalTimestamp(timestamp, timezone, seconds)).toEqual(expected);
409+
});
410+
411+
it('should correctly add seconds to timestamp with long format', () => {
412+
const timestamp = '2025-02-28T12:00:00.000';
413+
const timezone = 'Europe/Kiev';
414+
const seconds = 100;
415+
const expected = new Date('2025-02-28T12:01:40.000');
416+
417+
expect(addSecondsToLocalTimestamp(timestamp, timezone, seconds)).toEqual(expected);
418+
});
419+
420+
it('should return the same time if seconds to add is 0', () => {
421+
const timestamp = '2025-02-28T12:00:00.000';
422+
const timezone = 'Europe/Kiev';
423+
const seconds = 0;
424+
const expected = new Date('2025-02-28T12:00:00.000');
425+
426+
expect(addSecondsToLocalTimestamp(timestamp, timezone, seconds)).toEqual(expected);
427+
});
428+
});
429+
430+
describe('reformatInIsoLocal', () => {
431+
it('should return the same timestamp if its length is 23 characters', () => {
432+
const timestamp = '2025-02-28T12:00:00.000';
433+
const expected = '2025-02-28T12:00:00.000';
434+
435+
expect(reformatInIsoLocal(timestamp)).toBe(expected);
436+
});
437+
438+
it('should return timestamp without the "Z" if its length is 24 characters', () => {
439+
const timestamp = '2025-02-28T12:00:00.000Z';
440+
const expected = '2025-02-28T12:00:00.000';
441+
442+
expect(reformatInIsoLocal(timestamp)).toBe(expected);
443+
});
444+
445+
it('should reformat timestamp in UTC to ISO 8601 local format', () => {
446+
const timestamp = '2025-02-28T12:00:00';
447+
const expected = '2025-02-28T12:00:00.000';
448+
449+
expect(reformatInIsoLocal(timestamp)).toBe(expected);
450+
});
451+
452+
it('should return the same timestamp if it is an empty string', () => {
453+
const timestamp = '';
454+
const expected = '';
455+
456+
expect(reformatInIsoLocal(timestamp)).toBe(expected);
457+
});
458+
});

0 commit comments

Comments
 (0)