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