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