• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {
18  TimestampConverterUtils,
19  timestampEqualityTester,
20} from 'common/time/test_utils';
21import {TimestampConverter} from './timestamp_converter';
22import {TIME_UNIT_TO_NANO} from './time_units';
23
24describe('TimestampConverter', () => {
25  const MILLISECOND = BigInt(TIME_UNIT_TO_NANO.ms);
26  const SECOND = BigInt(TIME_UNIT_TO_NANO.s);
27  const MINUTE = BigInt(TIME_UNIT_TO_NANO.m);
28  const HOUR = BigInt(TIME_UNIT_TO_NANO.h);
29  const DAY = BigInt(TIME_UNIT_TO_NANO.d);
30
31  const testElapsedNs = 100n;
32  const testRealNs = 1659243341051481088n; // Sun, 31 Jul 2022 04:55:41 GMT to test timestamp conversion between different days
33  const testMonotonicTimeOffsetNs = 5n * MILLISECOND;
34  const testRealToBootTimeOffsetNs = MILLISECOND;
35
36  beforeAll(() => {
37    jasmine.addCustomEqualityTester(timestampEqualityTester);
38  });
39
40  describe('makes timestamps from ns without timezone info', () => {
41    const converterWithMonotonicOffset = new TimestampConverter(
42      TimestampConverterUtils.UTC_TIMEZONE_INFO,
43    );
44    converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
45      testMonotonicTimeOffsetNs,
46    );
47
48    const converterWithBootTimeOffset = new TimestampConverter(
49      TimestampConverterUtils.UTC_TIMEZONE_INFO,
50    );
51    converterWithBootTimeOffset.setRealToBootTimeOffsetNs(
52      testRealToBootTimeOffsetNs,
53    );
54
55    it('can create real-formatted timestamp without real-time offset set', () => {
56      const timestamp = new TimestampConverter(
57        TimestampConverterUtils.UTC_TIMEZONE_INFO,
58      ).makeTimestampFromRealNs(testRealNs);
59      expect(timestamp.getValueNs()).toBe(testRealNs);
60      expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.051');
61    });
62
63    it('can create real-formatted timestamp with real to monotonic offset', () => {
64      const timestamp =
65        converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs);
66      expect(timestamp.getValueNs()).toBe(
67        testRealNs + testMonotonicTimeOffsetNs,
68      );
69      expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.056');
70    });
71
72    it('can create real-formatted timestamp with real to boot time offset', () => {
73      const timestamp =
74        converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs);
75      expect(timestamp.getValueNs()).toBe(
76        testRealNs + testRealToBootTimeOffsetNs,
77      );
78      expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.052');
79    });
80
81    it('can create elapsed-formatted timestamp', () => {
82      const timestamp = new TimestampConverter(
83        TimestampConverterUtils.UTC_TIMEZONE_INFO,
84      ).makeTimestampFromMonotonicNs(testElapsedNs);
85      expect(timestamp.getValueNs()).toBe(testElapsedNs);
86      expect(timestamp.format()).toEqual('100ns');
87    });
88
89    it('formats real-formatted timestamp with offset correctly', () => {
90      expect(
91        converterWithMonotonicOffset
92          .makeTimestampFromMonotonicNs(100n * MILLISECOND)
93          .format(),
94      ).toEqual('1970-01-01, 00:00:00.105');
95      expect(
96        converterWithMonotonicOffset
97          .makeTimestampFromRealNs(100n * MILLISECOND)
98          .format(),
99      ).toEqual('1970-01-01, 00:00:00.100');
100    });
101  });
102
103  describe('makes timestamps from ns with timezone info', () => {
104    const converterWithMonotonicOffset = new TimestampConverter(
105      TimestampConverterUtils.ASIA_TIMEZONE_INFO,
106    );
107    converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
108      testMonotonicTimeOffsetNs,
109    );
110    converterWithMonotonicOffset.initializeUTCOffset(
111      converterWithMonotonicOffset.makeTimestampFromRealNs(testRealNs),
112    );
113
114    const converterWithBootTimeOffset = new TimestampConverter(
115      TimestampConverterUtils.ASIA_TIMEZONE_INFO,
116    );
117    converterWithBootTimeOffset.setRealToBootTimeOffsetNs(
118      testRealToBootTimeOffsetNs,
119    );
120    converterWithBootTimeOffset.initializeUTCOffset(
121      converterWithBootTimeOffset.makeTimestampFromRealNs(testRealNs),
122    );
123
124    it('can create real-formatted timestamp without real-time offset set', () => {
125      const converter = new TimestampConverter(
126        TimestampConverterUtils.ASIA_TIMEZONE_INFO,
127      );
128      converter.initializeUTCOffset(
129        converter.makeTimestampFromRealNs(testRealNs),
130      );
131
132      const timestamp = converter.makeTimestampFromRealNs(testRealNs);
133      expect(timestamp.getValueNs()).toBe(testRealNs);
134      expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.051');
135    });
136
137    it('can create real-formatted timestamp with monotonic offset', () => {
138      const timestamp =
139        converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs);
140      expect(timestamp.getValueNs()).toBe(
141        testRealNs + testMonotonicTimeOffsetNs,
142      );
143      expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.056');
144    });
145
146    it('can create real-formatted timestamp with real to boot time offset', () => {
147      const timestamp =
148        converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs);
149      expect(timestamp.getValueNs()).toBe(
150        testRealNs + testRealToBootTimeOffsetNs,
151      );
152      expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.052');
153    });
154
155    it('can create elapsed-formatted timestamp', () => {
156      const timestamp = new TimestampConverter(
157        TimestampConverterUtils.ASIA_TIMEZONE_INFO,
158      ).makeTimestampFromMonotonicNs(testElapsedNs);
159      expect(timestamp.getValueNs()).toBe(testElapsedNs);
160      expect(timestamp.format()).toEqual('100ns');
161    });
162
163    describe('adds correct offset for different timezones', () => {
164      it('creates correct real-formatted timestamps for different timezones', () => {
165        const londonConverter = new TimestampConverter(
166          {
167            timezone: 'Europe/London',
168            locale: 'en-US',
169          },
170          0n,
171        );
172        londonConverter.initializeUTCOffset(
173          londonConverter.makeTimestampFromRealNs(testRealNs),
174        );
175        expect(
176          londonConverter.makeTimestampFromRealNs(testRealNs).format(),
177        ).toEqual('2022-07-31, 05:55:41.051');
178
179        const zurichConverter = new TimestampConverter(
180          {
181            timezone: 'Europe/Zurich',
182            locale: 'en-US',
183          },
184          0n,
185        );
186        zurichConverter.initializeUTCOffset(
187          zurichConverter.makeTimestampFromRealNs(testRealNs),
188        );
189        expect(
190          zurichConverter.makeTimestampFromRealNs(testRealNs).format(),
191        ).toEqual('2022-07-31, 06:55:41.051');
192
193        const westCoastConverter = new TimestampConverter(
194          {
195            timezone: 'America/Los_Angeles',
196            locale: 'en-US',
197          },
198          0n,
199        );
200        westCoastConverter.initializeUTCOffset(
201          westCoastConverter.makeTimestampFromRealNs(testRealNs),
202        );
203        expect(
204          westCoastConverter.makeTimestampFromRealNs(testRealNs).format(),
205        ).toEqual('2022-07-30, 21:55:41.051');
206
207        const indiaConverter = new TimestampConverter(
208          {
209            timezone: 'Asia/Kolkata',
210            locale: 'en-US',
211          },
212          0n,
213        );
214        indiaConverter.initializeUTCOffset(
215          indiaConverter.makeTimestampFromRealNs(testRealNs),
216        );
217        expect(
218          indiaConverter.makeTimestampFromRealNs(testRealNs).format(),
219        ).toEqual('2022-07-31, 10:25:41.051');
220      });
221    });
222  });
223
224  describe('makes timestamps from string without timezone info', () => {
225    const converterWithoutOffsets = new TimestampConverter(
226      TimestampConverterUtils.UTC_TIMEZONE_INFO,
227    );
228
229    const converterWithMonotonicOffset = new TimestampConverter(
230      TimestampConverterUtils.UTC_TIMEZONE_INFO,
231    );
232    converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
233      testMonotonicTimeOffsetNs,
234    );
235
236    it('makeTimestampfromHumanElapsed', () => {
237      expect(converterWithoutOffsets.makeTimestampFromHuman('0ns')).toEqual(
238        converterWithoutOffsets.makeTimestampFromMonotonicNs(0n),
239      );
240      expect(converterWithoutOffsets.makeTimestampFromHuman('1000ns')).toEqual(
241        converterWithoutOffsets.makeTimestampFromMonotonicNs(1000n),
242      );
243      expect(converterWithoutOffsets.makeTimestampFromHuman('0ms')).toEqual(
244        converterWithoutOffsets.makeTimestampFromMonotonicNs(0n),
245      );
246      expect(converterWithoutOffsets.makeTimestampFromHuman('1ms')).toEqual(
247        converterWithoutOffsets.makeTimestampFromMonotonicNs(MILLISECOND),
248      );
249      expect(converterWithoutOffsets.makeTimestampFromHuman('10ms')).toEqual(
250        converterWithoutOffsets.makeTimestampFromMonotonicNs(10n * MILLISECOND),
251      );
252
253      expect(converterWithoutOffsets.makeTimestampFromHuman('999ms')).toEqual(
254        converterWithoutOffsets.makeTimestampFromMonotonicNs(
255          999n * MILLISECOND,
256        ),
257      );
258      expect(converterWithoutOffsets.makeTimestampFromHuman('1s')).toEqual(
259        converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND),
260      );
261      expect(converterWithoutOffsets.makeTimestampFromHuman('1s0ms')).toEqual(
262        converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND),
263      );
264      expect(
265        converterWithoutOffsets.makeTimestampFromHuman('1s0ms0ns'),
266      ).toEqual(converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND));
267      expect(
268        converterWithoutOffsets.makeTimestampFromHuman('1s0ms1ns'),
269      ).toEqual(
270        converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND + 1n),
271      );
272      expect(converterWithoutOffsets.makeTimestampFromHuman('0d1s1ms')).toEqual(
273        converterWithoutOffsets.makeTimestampFromMonotonicNs(
274          SECOND + MILLISECOND,
275        ),
276      );
277
278      expect(converterWithoutOffsets.makeTimestampFromHuman('1m0s0ms')).toEqual(
279        converterWithoutOffsets.makeTimestampFromMonotonicNs(MINUTE),
280      );
281      expect(converterWithoutOffsets.makeTimestampFromHuman('1m1s1ms')).toEqual(
282        converterWithoutOffsets.makeTimestampFromMonotonicNs(
283          MINUTE + SECOND + MILLISECOND,
284        ),
285      );
286
287      expect(converterWithoutOffsets.makeTimestampFromHuman('1h0m')).toEqual(
288        converterWithoutOffsets.makeTimestampFromMonotonicNs(HOUR),
289      );
290      expect(
291        converterWithoutOffsets.makeTimestampFromHuman('1h1m1s1ms'),
292      ).toEqual(
293        converterWithoutOffsets.makeTimestampFromMonotonicNs(
294          HOUR + MINUTE + SECOND + MILLISECOND,
295        ),
296      );
297
298      expect(converterWithoutOffsets.makeTimestampFromHuman('1d0s1ms')).toEqual(
299        converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND),
300      );
301      expect(
302        converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s1ms'),
303      ).toEqual(
304        converterWithoutOffsets.makeTimestampFromMonotonicNs(
305          DAY + HOUR + MINUTE + SECOND + MILLISECOND,
306        ),
307      );
308
309      expect(converterWithoutOffsets.makeTimestampFromHuman('1d')).toEqual(
310        converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY),
311      );
312      expect(converterWithoutOffsets.makeTimestampFromHuman('1d1ms')).toEqual(
313        converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND),
314      );
315    });
316
317    it('makeTimestampfromHumanElapsed throws on invalid input format', () => {
318      const invalidFormatError = new Error('Invalid timestamp format');
319      expect(() =>
320        converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s0ns1ms'),
321      ).toThrow(invalidFormatError);
322      expect(() =>
323        converterWithoutOffsets.makeTimestampFromHuman('1dns'),
324      ).toThrow(invalidFormatError);
325      expect(() =>
326        converterWithoutOffsets.makeTimestampFromHuman('100'),
327      ).toThrow(invalidFormatError);
328      expect(() => converterWithoutOffsets.makeTimestampFromHuman('')).toThrow(
329        invalidFormatError,
330      );
331    });
332
333    it('makeTimestampFromHumanReal', () => {
334      const NOV_10_2022 = 1668038400000n * MILLISECOND;
335      expect(
336        converterWithMonotonicOffset.makeTimestampFromHuman(
337          '2022-11-10T22:04:54.186123212',
338        ),
339      ).toEqual(
340        converterWithMonotonicOffset.makeTimestampFromRealNs(
341          NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186123212n,
342        ),
343      );
344      expect(
345        converterWithMonotonicOffset.makeTimestampFromHuman(
346          '2022-11-10T22:04:54.186123212Z',
347        ),
348      ).toEqual(
349        converterWithMonotonicOffset.makeTimestampFromRealNs(
350          NOV_10_2022 +
351            22n * HOUR +
352            4n * MINUTE +
353            54n * SECOND +
354            186n * MILLISECOND +
355            123212n,
356        ),
357      );
358      expect(
359        converterWithMonotonicOffset.makeTimestampFromHuman(
360          '2022-11-10T22:04:54.186000212',
361        ),
362      ).toEqual(
363        converterWithMonotonicOffset.makeTimestampFromRealNs(
364          NOV_10_2022 +
365            22n * HOUR +
366            4n * MINUTE +
367            54n * SECOND +
368            186n * MILLISECOND +
369            212n,
370        ),
371      );
372      expect(
373        converterWithMonotonicOffset.makeTimestampFromHuman(
374          '2022-11-10T22:04:54.006000002',
375        ),
376      ).toEqual(
377        converterWithMonotonicOffset.makeTimestampFromRealNs(
378          NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n,
379        ),
380      );
381      expect(
382        converterWithMonotonicOffset.makeTimestampFromHuman(
383          '2022-11-10T06:04:54.006000002',
384        ),
385      ).toEqual(
386        converterWithMonotonicOffset.makeTimestampFromRealNs(
387          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n,
388        ),
389      );
390      expect(
391        converterWithMonotonicOffset.makeTimestampFromHuman(
392          '2022-11-10T06:04:54',
393        ),
394      ).toEqual(
395        converterWithMonotonicOffset.makeTimestampFromRealNs(
396          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
397        ),
398      );
399      expect(
400        converterWithMonotonicOffset.makeTimestampFromHuman(
401          '2022-11-10T06:04:54.0',
402        ),
403      ).toEqual(
404        converterWithMonotonicOffset.makeTimestampFromRealNs(
405          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
406        ),
407      );
408      expect(
409        converterWithMonotonicOffset.makeTimestampFromHuman(
410          '2022-11-10T06:04:54.0100',
411        ),
412      ).toEqual(
413        converterWithMonotonicOffset.makeTimestampFromRealNs(
414          NOV_10_2022 +
415            6n * HOUR +
416            4n * MINUTE +
417            54n * SECOND +
418            10n * MILLISECOND,
419        ),
420      );
421      expect(
422        converterWithMonotonicOffset.makeTimestampFromHuman(
423          '2022-11-10T06:04:54.0175328',
424        ),
425      ).toEqual(
426        converterWithMonotonicOffset.makeTimestampFromRealNs(
427          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n,
428        ),
429      );
430    });
431
432    it('makeTimestampFromHumanReal throws on invalid input format', () => {
433      const invalidFormatError = new Error('Invalid timestamp format');
434      expect(() =>
435        converterWithMonotonicOffset.makeTimestampFromHuman('100'),
436      ).toThrow(invalidFormatError);
437      expect(() =>
438        converterWithMonotonicOffset.makeTimestampFromHuman(
439          '06h4m54s, 10 Nov 2022',
440        ),
441      ).toThrow(invalidFormatError);
442      expect(() =>
443        converterWithMonotonicOffset.makeTimestampFromHuman(''),
444      ).toThrow(invalidFormatError);
445      expect(() =>
446        converterWithMonotonicOffset.makeTimestampFromHuman(
447          '2022-11-10T06:04:54.',
448        ),
449      ).toThrow(invalidFormatError);
450      expect(() =>
451        converterWithMonotonicOffset.makeTimestampFromHuman(
452          '2022-11-10T06:04:54.1234567890',
453        ),
454      ).toThrow(invalidFormatError);
455      expect(() =>
456        converterWithMonotonicOffset.makeTimestampFromHuman(
457          '06:04:54.1234567890',
458        ),
459      ).toThrow(invalidFormatError);
460    });
461
462    it('can reverse-date format', () => {
463      expect(
464        converterWithMonotonicOffset
465          .makeTimestampFromHuman('2022-11-10, 22:04:54.186123212')
466          .format(),
467      ).toEqual('2022-11-10, 22:04:54.186');
468    });
469  });
470
471  describe('makes timestamps from string with timezone info', () => {
472    const converter = new TimestampConverter(
473      TimestampConverterUtils.ASIA_TIMEZONE_INFO,
474    );
475    converter.setRealToMonotonicTimeOffsetNs(testMonotonicTimeOffsetNs);
476    converter.initializeUTCOffset(
477      converter.makeTimestampFromRealNs(testRealNs),
478    );
479
480    it('makeTimestampFromHumanReal', () => {
481      const NOV_10_2022 = 1668038400000n * MILLISECOND;
482      testMakeTimestampFromHumanReal(
483        '2022-11-11T03:34:54.186123212',
484        NOV_10_2022 +
485          22n * HOUR +
486          4n * MINUTE +
487          54n * SECOND +
488          186n * MILLISECOND +
489          123212n,
490        '2022-11-11, 03:34:54.186',
491      );
492
493      testMakeTimestampFromHumanReal(
494        '2022-11-11T03:34:54.186123212Z',
495        NOV_10_2022 +
496          22n * HOUR +
497          4n * MINUTE +
498          54n * SECOND +
499          186n * MILLISECOND +
500          123212n,
501        '2022-11-11, 03:34:54.186',
502      );
503
504      testMakeTimestampFromHumanReal(
505        '2022-11-10T11:34:54',
506        NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
507        '2022-11-10, 11:34:54.000',
508      );
509
510      testMakeTimestampFromHumanReal(
511        '2022-11-10T11:34:54.0',
512        NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
513        '2022-11-10, 11:34:54.000',
514      );
515
516      testMakeTimestampFromHumanReal(
517        '2022-11-10T11:34:54.0100',
518        NOV_10_2022 +
519          6n * HOUR +
520          4n * MINUTE +
521          54n * SECOND +
522          10n * MILLISECOND,
523        '2022-11-10, 11:34:54.010',
524      );
525
526      testMakeTimestampFromHumanReal(
527        '2022-11-10T11:34:54.0175328',
528        NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n,
529        '2022-11-10, 11:34:54.018',
530      );
531    });
532
533    it('can reverse-date format', () => {
534      expect(
535        converter
536          .makeTimestampFromHuman('2022-11-11, 03:34:54.186123212')
537          .format(),
538      ).toEqual('2022-11-11, 03:34:54.186');
539    });
540
541    function testMakeTimestampFromHumanReal(
542      timestampHuman: string,
543      expectedNs: bigint,
544      expectedFormattedTimestamp: string,
545    ) {
546      const timestamp = converter.makeTimestampFromHuman(timestampHuman);
547      expect(timestamp.getValueNs()).toEqual(expectedNs);
548      expect(timestamp.format()).toEqual(expectedFormattedTimestamp);
549    }
550  });
551});
552