| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils'; |
| import {UnitTestUtils} from 'test/unit/utils'; |
| import {TimestampConverter} from './timestamp_converter'; |
| import {TIME_UNIT_TO_NANO} from './time_units'; |
| |
| describe('TimestampConverter', () => { |
| const MILLISECOND = BigInt(TIME_UNIT_TO_NANO.ms); |
| const SECOND = BigInt(TIME_UNIT_TO_NANO.s); |
| const MINUTE = BigInt(TIME_UNIT_TO_NANO.m); |
| const HOUR = BigInt(TIME_UNIT_TO_NANO.h); |
| const DAY = BigInt(TIME_UNIT_TO_NANO.d); |
| |
| const testElapsedNs = 100n; |
| const testRealNs = 1659243341051481088n; // Sun, 31 Jul 2022 04:55:41 GMT to test timestamp conversion between different days |
| const testMonotonicTimeOffsetNs = 5n * MILLISECOND; |
| const testRealToBootTimeOffsetNs = MILLISECOND; |
| |
| beforeAll(() => { |
| jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester); |
| }); |
| |
| describe('makes timestamps from ns without timezone info', () => { |
| const converterWithMonotonicOffset = new TimestampConverter( |
| TimestampConverterUtils.UTC_TIMEZONE_INFO, |
| ); |
| converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs( |
| testMonotonicTimeOffsetNs, |
| ); |
| |
| const converterWithBootTimeOffset = new TimestampConverter( |
| TimestampConverterUtils.UTC_TIMEZONE_INFO, |
| ); |
| converterWithBootTimeOffset.setRealToBootTimeOffsetNs( |
| testRealToBootTimeOffsetNs, |
| ); |
| |
| it('can create real-formatted timestamp without real-time offset set', () => { |
| const timestamp = new TimestampConverter( |
| TimestampConverterUtils.UTC_TIMEZONE_INFO, |
| ).makeTimestampFromRealNs(testRealNs); |
| expect(timestamp.getValueNs()).toBe(testRealNs); |
| expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.051'); |
| }); |
| |
| it('can create real-formatted timestamp with real to monotonic offset', () => { |
| const timestamp = |
| converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs); |
| expect(timestamp.getValueNs()).toBe( |
| testRealNs + testMonotonicTimeOffsetNs, |
| ); |
| expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.056'); |
| }); |
| |
| it('can create real-formatted timestamp with real to boot time offset', () => { |
| const timestamp = |
| converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs); |
| expect(timestamp.getValueNs()).toBe( |
| testRealNs + testRealToBootTimeOffsetNs, |
| ); |
| expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.052'); |
| }); |
| |
| it('can create elapsed-formatted timestamp', () => { |
| const timestamp = new TimestampConverter( |
| TimestampConverterUtils.UTC_TIMEZONE_INFO, |
| ).makeTimestampFromMonotonicNs(testElapsedNs); |
| expect(timestamp.getValueNs()).toBe(testElapsedNs); |
| expect(timestamp.format()).toEqual('100ns'); |
| }); |
| |
| it('formats real-formatted timestamp with offset correctly', () => { |
| expect( |
| converterWithMonotonicOffset |
| .makeTimestampFromMonotonicNs(100n * MILLISECOND) |
| .format(), |
| ).toEqual('1970-01-01, 00:00:00.105'); |
| expect( |
| converterWithMonotonicOffset |
| .makeTimestampFromRealNs(100n * MILLISECOND) |
| .format(), |
| ).toEqual('1970-01-01, 00:00:00.100'); |
| }); |
| }); |
| |
| describe('makes timestamps from ns with timezone info', () => { |
| const converterWithMonotonicOffset = new TimestampConverter( |
| TimestampConverterUtils.ASIA_TIMEZONE_INFO, |
| ); |
| converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs( |
| testMonotonicTimeOffsetNs, |
| ); |
| converterWithMonotonicOffset.initializeUTCOffset( |
| converterWithMonotonicOffset.makeTimestampFromRealNs(testRealNs), |
| ); |
| |
| const converterWithBootTimeOffset = new TimestampConverter( |
| TimestampConverterUtils.ASIA_TIMEZONE_INFO, |
| ); |
| converterWithBootTimeOffset.setRealToBootTimeOffsetNs( |
| testRealToBootTimeOffsetNs, |
| ); |
| converterWithBootTimeOffset.initializeUTCOffset( |
| converterWithBootTimeOffset.makeTimestampFromRealNs(testRealNs), |
| ); |
| |
| it('can create real-formatted timestamp without real-time offset set', () => { |
| const converter = new TimestampConverter( |
| TimestampConverterUtils.ASIA_TIMEZONE_INFO, |
| ); |
| converter.initializeUTCOffset( |
| converter.makeTimestampFromRealNs(testRealNs), |
| ); |
| |
| const timestamp = converter.makeTimestampFromRealNs(testRealNs); |
| expect(timestamp.getValueNs()).toBe(testRealNs); |
| expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.051'); |
| }); |
| |
| it('can create real-formatted timestamp with monotonic offset', () => { |
| const timestamp = |
| converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs); |
| expect(timestamp.getValueNs()).toBe( |
| testRealNs + testMonotonicTimeOffsetNs, |
| ); |
| expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.056'); |
| }); |
| |
| it('can create real-formatted timestamp with real to boot time offset', () => { |
| const timestamp = |
| converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs); |
| expect(timestamp.getValueNs()).toBe( |
| testRealNs + testRealToBootTimeOffsetNs, |
| ); |
| expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.052'); |
| }); |
| |
| it('can create elapsed-formatted timestamp', () => { |
| const timestamp = new TimestampConverter( |
| TimestampConverterUtils.ASIA_TIMEZONE_INFO, |
| ).makeTimestampFromMonotonicNs(testElapsedNs); |
| expect(timestamp.getValueNs()).toBe(testElapsedNs); |
| expect(timestamp.format()).toEqual('100ns'); |
| }); |
| |
| describe('adds correct offset for different timezones', () => { |
| it('creates correct real-formatted timestamps for different timezones', () => { |
| const londonConverter = new TimestampConverter( |
| { |
| timezone: 'Europe/London', |
| locale: 'en-US', |
| }, |
| 0n, |
| ); |
| londonConverter.initializeUTCOffset( |
| londonConverter.makeTimestampFromRealNs(testRealNs), |
| ); |
| expect( |
| londonConverter.makeTimestampFromRealNs(testRealNs).format(), |
| ).toEqual('2022-07-31, 05:55:41.051'); |
| |
| const zurichConverter = new TimestampConverter( |
| { |
| timezone: 'Europe/Zurich', |
| locale: 'en-US', |
| }, |
| 0n, |
| ); |
| zurichConverter.initializeUTCOffset( |
| zurichConverter.makeTimestampFromRealNs(testRealNs), |
| ); |
| expect( |
| zurichConverter.makeTimestampFromRealNs(testRealNs).format(), |
| ).toEqual('2022-07-31, 06:55:41.051'); |
| |
| const westCoastConverter = new TimestampConverter( |
| { |
| timezone: 'America/Los_Angeles', |
| locale: 'en-US', |
| }, |
| 0n, |
| ); |
| westCoastConverter.initializeUTCOffset( |
| westCoastConverter.makeTimestampFromRealNs(testRealNs), |
| ); |
| expect( |
| westCoastConverter.makeTimestampFromRealNs(testRealNs).format(), |
| ).toEqual('2022-07-30, 21:55:41.051'); |
| |
| const indiaConverter = new TimestampConverter( |
| { |
| timezone: 'Asia/Kolkata', |
| locale: 'en-US', |
| }, |
| 0n, |
| ); |
| indiaConverter.initializeUTCOffset( |
| indiaConverter.makeTimestampFromRealNs(testRealNs), |
| ); |
| expect( |
| indiaConverter.makeTimestampFromRealNs(testRealNs).format(), |
| ).toEqual('2022-07-31, 10:25:41.051'); |
| }); |
| }); |
| }); |
| |
| describe('makes timestamps from string without timezone info', () => { |
| const converterWithoutOffsets = new TimestampConverter( |
| TimestampConverterUtils.UTC_TIMEZONE_INFO, |
| ); |
| |
| const converterWithMonotonicOffset = new TimestampConverter( |
| TimestampConverterUtils.UTC_TIMEZONE_INFO, |
| ); |
| converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs( |
| testMonotonicTimeOffsetNs, |
| ); |
| |
| it('makeTimestampfromHumanElapsed', () => { |
| expect(converterWithoutOffsets.makeTimestampFromHuman('0ns')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(0n), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1000ns')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(1000n), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('0ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(0n), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(MILLISECOND), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('10ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(10n * MILLISECOND), |
| ); |
| |
| expect(converterWithoutOffsets.makeTimestampFromHuman('999ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs( |
| 999n * MILLISECOND, |
| ), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1s')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1s0ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND), |
| ); |
| expect( |
| converterWithoutOffsets.makeTimestampFromHuman('1s0ms0ns'), |
| ).toEqual(converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND)); |
| expect( |
| converterWithoutOffsets.makeTimestampFromHuman('1s0ms1ns'), |
| ).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND + 1n), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('0d1s1ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs( |
| SECOND + MILLISECOND, |
| ), |
| ); |
| |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1m0s0ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(MINUTE), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1m1s1ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs( |
| MINUTE + SECOND + MILLISECOND, |
| ), |
| ); |
| |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1h0m')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(HOUR), |
| ); |
| expect( |
| converterWithoutOffsets.makeTimestampFromHuman('1h1m1s1ms'), |
| ).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs( |
| HOUR + MINUTE + SECOND + MILLISECOND, |
| ), |
| ); |
| |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1d0s1ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND), |
| ); |
| expect( |
| converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s1ms'), |
| ).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs( |
| DAY + HOUR + MINUTE + SECOND + MILLISECOND, |
| ), |
| ); |
| |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1d')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY), |
| ); |
| expect(converterWithoutOffsets.makeTimestampFromHuman('1d1ms')).toEqual( |
| converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND), |
| ); |
| }); |
| |
| it('makeTimestampfromHumanElapsed throws on invalid input format', () => { |
| const invalidFormatError = new Error('Invalid timestamp format'); |
| expect(() => |
| converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s0ns1ms'), |
| ).toThrow(invalidFormatError); |
| expect(() => |
| converterWithoutOffsets.makeTimestampFromHuman('1dns'), |
| ).toThrow(invalidFormatError); |
| expect(() => |
| converterWithoutOffsets.makeTimestampFromHuman('100'), |
| ).toThrow(invalidFormatError); |
| expect(() => converterWithoutOffsets.makeTimestampFromHuman('')).toThrow( |
| invalidFormatError, |
| ); |
| }); |
| |
| it('makeTimestampFromHumanReal', () => { |
| const NOV_10_2022 = 1668038400000n * MILLISECOND; |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T22:04:54.186123212', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186123212n, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T22:04:54.186123212Z', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + |
| 22n * HOUR + |
| 4n * MINUTE + |
| 54n * SECOND + |
| 186n * MILLISECOND + |
| 123212n, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T22:04:54.186000212', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + |
| 22n * HOUR + |
| 4n * MINUTE + |
| 54n * SECOND + |
| 186n * MILLISECOND + |
| 212n, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T22:04:54.006000002', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T06:04:54.006000002', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T06:04:54', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T06:04:54.0', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T06:04:54.0100', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + |
| 6n * HOUR + |
| 4n * MINUTE + |
| 54n * SECOND + |
| 10n * MILLISECOND, |
| ), |
| ); |
| expect( |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T06:04:54.0175328', |
| ), |
| ).toEqual( |
| converterWithMonotonicOffset.makeTimestampFromRealNs( |
| NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n, |
| ), |
| ); |
| }); |
| |
| it('makeTimestampFromHumanReal throws on invalid input format', () => { |
| const invalidFormatError = new Error('Invalid timestamp format'); |
| expect(() => |
| converterWithMonotonicOffset.makeTimestampFromHuman('100'), |
| ).toThrow(invalidFormatError); |
| expect(() => |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '06h4m54s, 10 Nov 2022', |
| ), |
| ).toThrow(invalidFormatError); |
| expect(() => |
| converterWithMonotonicOffset.makeTimestampFromHuman(''), |
| ).toThrow(invalidFormatError); |
| expect(() => |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T06:04:54.', |
| ), |
| ).toThrow(invalidFormatError); |
| expect(() => |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '2022-11-10T06:04:54.1234567890', |
| ), |
| ).toThrow(invalidFormatError); |
| expect(() => |
| converterWithMonotonicOffset.makeTimestampFromHuman( |
| '06:04:54.1234567890', |
| ), |
| ).toThrow(invalidFormatError); |
| }); |
| |
| it('can reverse-date format', () => { |
| expect( |
| converterWithMonotonicOffset |
| .makeTimestampFromHuman('2022-11-10, 22:04:54.186123212') |
| .format(), |
| ).toEqual('2022-11-10, 22:04:54.186'); |
| }); |
| }); |
| |
| describe('makes timestamps from string with timezone info', () => { |
| const converter = new TimestampConverter( |
| TimestampConverterUtils.ASIA_TIMEZONE_INFO, |
| ); |
| converter.setRealToMonotonicTimeOffsetNs(testMonotonicTimeOffsetNs); |
| converter.initializeUTCOffset( |
| converter.makeTimestampFromRealNs(testRealNs), |
| ); |
| |
| it('makeTimestampFromHumanReal', () => { |
| const NOV_10_2022 = 1668038400000n * MILLISECOND; |
| testMakeTimestampFromHumanReal( |
| '2022-11-11T03:34:54.186123212', |
| NOV_10_2022 + |
| 22n * HOUR + |
| 4n * MINUTE + |
| 54n * SECOND + |
| 186n * MILLISECOND + |
| 123212n, |
| '2022-11-11, 03:34:54.186', |
| ); |
| |
| testMakeTimestampFromHumanReal( |
| '2022-11-11T03:34:54.186123212Z', |
| NOV_10_2022 + |
| 22n * HOUR + |
| 4n * MINUTE + |
| 54n * SECOND + |
| 186n * MILLISECOND + |
| 123212n, |
| '2022-11-11, 03:34:54.186', |
| ); |
| |
| testMakeTimestampFromHumanReal( |
| '2022-11-10T11:34:54', |
| NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND, |
| '2022-11-10, 11:34:54.000', |
| ); |
| |
| testMakeTimestampFromHumanReal( |
| '2022-11-10T11:34:54.0', |
| NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND, |
| '2022-11-10, 11:34:54.000', |
| ); |
| |
| testMakeTimestampFromHumanReal( |
| '2022-11-10T11:34:54.0100', |
| NOV_10_2022 + |
| 6n * HOUR + |
| 4n * MINUTE + |
| 54n * SECOND + |
| 10n * MILLISECOND, |
| '2022-11-10, 11:34:54.010', |
| ); |
| |
| testMakeTimestampFromHumanReal( |
| '2022-11-10T11:34:54.0175328', |
| NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n, |
| '2022-11-10, 11:34:54.018', |
| ); |
| }); |
| |
| it('can reverse-date format', () => { |
| expect( |
| converter |
| .makeTimestampFromHuman('2022-11-11, 03:34:54.186123212') |
| .format(), |
| ).toEqual('2022-11-11, 03:34:54.186'); |
| }); |
| |
| function testMakeTimestampFromHumanReal( |
| timestampHuman: string, |
| expectedNs: bigint, |
| expectedFormattedTimestamp: string, |
| ) { |
| const timestamp = converter.makeTimestampFromHuman(timestampHuman); |
| expect(timestamp.getValueNs()).toEqual(expectedNs); |
| expect(timestamp.format()).toEqual(expectedFormattedTimestamp); |
| } |
| }); |
| }); |