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 {Timestamp} from 'common/time'; 18import {TIME_UNITS} from './time_units'; 19 20export class TimestampUtils { 21 // (?=.) checks there is at least one character with a lookahead match 22 private static readonly REAL_TIME_ONLY_REGEX = 23 /^(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])(\.[0-9]{1,9})?Z?$/; 24 private static readonly REAL_DATE_TIME_REGEX = 25 /^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9])),\s(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])(\.[0-9]{1,9})?Z?$/; 26 private static readonly ISO_TIMESTAMP_REGEX = 27 /^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])(\.[0-9]{1,9})?Z?$/; 28 private static readonly ELAPSED_TIME_REGEX = 29 /^(?=.)([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?([0-9]+ns)?$/; 30 private static readonly NS_TIME_REGEX = /^\s*[0-9]+(\s?ns)?\s*$/; 31 32 static isNsFormat(timestampHuman: string): boolean { 33 return TimestampUtils.NS_TIME_REGEX.test(timestampHuman); 34 } 35 36 static isHumanElapsedTimeFormat(timestampHuman: string): boolean { 37 return TimestampUtils.ELAPSED_TIME_REGEX.test(timestampHuman); 38 } 39 40 static isRealTimeOnlyFormat(timestampHuman: string): boolean { 41 return TimestampUtils.REAL_TIME_ONLY_REGEX.test(timestampHuman); 42 } 43 44 static isRealDateTimeFormat(timestampHuman: string): boolean { 45 return TimestampUtils.REAL_DATE_TIME_REGEX.test(timestampHuman); 46 } 47 48 static isISOFormat(timestampHuman: string): boolean { 49 return TimestampUtils.ISO_TIMESTAMP_REGEX.test(timestampHuman); 50 } 51 52 static isHumanRealTimestampFormat(timestampHuman: string): boolean { 53 return ( 54 TimestampUtils.isISOFormat(timestampHuman) || 55 TimestampUtils.isRealDateTimeFormat(timestampHuman) || 56 TimestampUtils.isRealTimeOnlyFormat(timestampHuman) 57 ); 58 } 59 60 static extractDateFromHumanTimestamp( 61 timestampHuman: string, 62 ): string | undefined { 63 if ( 64 !TimestampUtils.isRealDateTimeFormat(timestampHuman) && 65 !TimestampUtils.isISOFormat(timestampHuman) 66 ) { 67 return undefined; 68 } 69 return timestampHuman.slice(0, 10); 70 } 71 72 static extractTimeFromHumanTimestamp( 73 timestampHuman: string, 74 ): string | undefined { 75 if (TimestampUtils.isRealDateTimeFormat(timestampHuman)) { 76 return timestampHuman.slice(12); 77 } 78 if (TimestampUtils.isISOFormat(timestampHuman)) { 79 return timestampHuman.slice(11); 80 } 81 if (TimestampUtils.isRealTimeOnlyFormat(timestampHuman)) { 82 return timestampHuman; 83 } 84 return undefined; 85 } 86 87 static compareFn(a: Timestamp, b: Timestamp): number { 88 return Number(a.getValueNs() - b.getValueNs()); 89 } 90 91 static min(ts1: Timestamp, ts2: Timestamp): Timestamp { 92 if (ts2.getValueNs() < ts1.getValueNs()) { 93 return ts2; 94 } 95 96 return ts1; 97 } 98 99 static max(ts1: Timestamp, ts2: Timestamp): Timestamp { 100 if (ts2.getValueNs() > ts1.getValueNs()) { 101 return ts2; 102 } 103 104 return ts1; 105 } 106 107 static formatElapsedNs(timestampNanos: bigint): string { 108 let leftNanos = timestampNanos; 109 const parts: Array<{value: bigint; unit: string}> = TIME_UNITS.slice() 110 .reverse() 111 .map(({nanosInUnit, unit}) => { 112 let amountOfUnit = BigInt(0); 113 if (leftNanos >= nanosInUnit) { 114 amountOfUnit = leftNanos / BigInt(nanosInUnit); 115 } 116 leftNanos = leftNanos % BigInt(nanosInUnit); 117 return {value: amountOfUnit, unit}; 118 }); 119 120 // Remove all 0ed units at start 121 while (parts.length > 1 && parts[0].value === 0n) { 122 parts.shift(); 123 } 124 125 return parts.map((part) => `${part.value}${part.unit}`).join(''); 126 } 127} 128