1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2018 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Workerimport {BigintMath} from './bigint_math'; 16*6dbdd20aSAndroid Build Coastguard Workerimport {Brand} from './brand'; 17*6dbdd20aSAndroid Build Coastguard Workerimport {assertTrue} from './logging'; 18*6dbdd20aSAndroid Build Coastguard Worker 19*6dbdd20aSAndroid Build Coastguard Worker// The |time| type represents trace time in the same units and domain as trace 20*6dbdd20aSAndroid Build Coastguard Worker// processor (i.e. typically boot time in nanoseconds, but most of the UI should 21*6dbdd20aSAndroid Build Coastguard Worker// be completely agnostic to this). 22*6dbdd20aSAndroid Build Coastguard Workerexport type time = Brand<bigint, 'time'>; 23*6dbdd20aSAndroid Build Coastguard Worker 24*6dbdd20aSAndroid Build Coastguard Worker// The |duration| type is used to represent the duration of time between two 25*6dbdd20aSAndroid Build Coastguard Worker// |time|s. The domain is irrelevant because a duration is relative. 26*6dbdd20aSAndroid Build Coastguard Workerexport type duration = bigint; 27*6dbdd20aSAndroid Build Coastguard Worker 28*6dbdd20aSAndroid Build Coastguard Worker// The conversion factor for converting between different time units. 29*6dbdd20aSAndroid Build Coastguard Workerconst TIME_UNITS_PER_SEC = 1e9; 30*6dbdd20aSAndroid Build Coastguard Workerconst TIME_UNITS_PER_MILLISEC = 1e6; 31*6dbdd20aSAndroid Build Coastguard Workerconst TIME_UNITS_PER_MICROSEC = 1e3; 32*6dbdd20aSAndroid Build Coastguard Worker 33*6dbdd20aSAndroid Build Coastguard Workerexport class Time { 34*6dbdd20aSAndroid Build Coastguard Worker // Negative time is never found in a trace - so -1 is commonly used as a flag 35*6dbdd20aSAndroid Build Coastguard Worker // to represent a value is undefined or unset, without having to use a 36*6dbdd20aSAndroid Build Coastguard Worker // nullable or union type. 37*6dbdd20aSAndroid Build Coastguard Worker static readonly INVALID = Time.fromRaw(-1n); 38*6dbdd20aSAndroid Build Coastguard Worker 39*6dbdd20aSAndroid Build Coastguard Worker // The min and max possible values, considering times cannot be negative. 40*6dbdd20aSAndroid Build Coastguard Worker static readonly MIN = Time.fromRaw(0n); 41*6dbdd20aSAndroid Build Coastguard Worker static readonly MAX = Time.fromRaw(BigintMath.INT64_MAX); 42*6dbdd20aSAndroid Build Coastguard Worker 43*6dbdd20aSAndroid Build Coastguard Worker static readonly ZERO = Time.fromRaw(0n); 44*6dbdd20aSAndroid Build Coastguard Worker 45*6dbdd20aSAndroid Build Coastguard Worker // Cast a bigint to a |time|. Supports potentially |undefined| values. 46*6dbdd20aSAndroid Build Coastguard Worker // I.e. it performs the following conversions: 47*6dbdd20aSAndroid Build Coastguard Worker // - `bigint` -> `time` 48*6dbdd20aSAndroid Build Coastguard Worker // - `bigint|undefined` -> `time|undefined` 49*6dbdd20aSAndroid Build Coastguard Worker // 50*6dbdd20aSAndroid Build Coastguard Worker // Use this function with caution. The function is effectively a no-op in JS, 51*6dbdd20aSAndroid Build Coastguard Worker // but using it tells TypeScript that "this value is a time value". It's up to 52*6dbdd20aSAndroid Build Coastguard Worker // the caller to ensure the value is in the correct units and time domain. 53*6dbdd20aSAndroid Build Coastguard Worker // 54*6dbdd20aSAndroid Build Coastguard Worker // If you're reaching for this function after doing some maths on a |time| 55*6dbdd20aSAndroid Build Coastguard Worker // value and it's decayed to a |bigint| consider using the static math methods 56*6dbdd20aSAndroid Build Coastguard Worker // in |Time| instead, as they will do the appropriate casting for you. 57*6dbdd20aSAndroid Build Coastguard Worker static fromRaw(v: bigint): time; 58*6dbdd20aSAndroid Build Coastguard Worker static fromRaw(v?: bigint): time | undefined; 59*6dbdd20aSAndroid Build Coastguard Worker static fromRaw(v?: bigint): time | undefined { 60*6dbdd20aSAndroid Build Coastguard Worker return v as time | undefined; 61*6dbdd20aSAndroid Build Coastguard Worker } 62*6dbdd20aSAndroid Build Coastguard Worker 63*6dbdd20aSAndroid Build Coastguard Worker // Convert seconds (number) to a time value. 64*6dbdd20aSAndroid Build Coastguard Worker // Note: number -> BigInt conversion is relatively slow. 65*6dbdd20aSAndroid Build Coastguard Worker static fromSeconds(seconds: number): time { 66*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigInt(Math.floor(seconds * TIME_UNITS_PER_SEC))); 67*6dbdd20aSAndroid Build Coastguard Worker } 68*6dbdd20aSAndroid Build Coastguard Worker 69*6dbdd20aSAndroid Build Coastguard Worker // Convert time value to seconds and return as a number (i.e. float). 70*6dbdd20aSAndroid Build Coastguard Worker // Warning: This function is lossy, i.e. precision is lost when converting 71*6dbdd20aSAndroid Build Coastguard Worker // BigInt -> number. 72*6dbdd20aSAndroid Build Coastguard Worker // Note: BigInt -> number conversion is relatively slow. 73*6dbdd20aSAndroid Build Coastguard Worker static toSeconds(t: time): number { 74*6dbdd20aSAndroid Build Coastguard Worker return Number(t) / TIME_UNITS_PER_SEC; 75*6dbdd20aSAndroid Build Coastguard Worker } 76*6dbdd20aSAndroid Build Coastguard Worker 77*6dbdd20aSAndroid Build Coastguard Worker // Convert milliseconds (number) to a time value. 78*6dbdd20aSAndroid Build Coastguard Worker // Note: number -> BigInt conversion is relatively slow. 79*6dbdd20aSAndroid Build Coastguard Worker static fromMillis(millis: number): time { 80*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigInt(Math.floor(millis * TIME_UNITS_PER_MILLISEC))); 81*6dbdd20aSAndroid Build Coastguard Worker } 82*6dbdd20aSAndroid Build Coastguard Worker 83*6dbdd20aSAndroid Build Coastguard Worker // Convert time value to milliseconds and return as a number (i.e. float). 84*6dbdd20aSAndroid Build Coastguard Worker // Warning: This function is lossy, i.e. precision is lost when converting 85*6dbdd20aSAndroid Build Coastguard Worker // BigInt -> number. 86*6dbdd20aSAndroid Build Coastguard Worker // Note: BigInt -> number conversion is relatively slow. 87*6dbdd20aSAndroid Build Coastguard Worker static toMillis(t: time): number { 88*6dbdd20aSAndroid Build Coastguard Worker return Number(t) / TIME_UNITS_PER_MILLISEC; 89*6dbdd20aSAndroid Build Coastguard Worker } 90*6dbdd20aSAndroid Build Coastguard Worker 91*6dbdd20aSAndroid Build Coastguard Worker // Convert microseconds (number) to a time value. 92*6dbdd20aSAndroid Build Coastguard Worker // Note: number -> BigInt conversion is relatively slow. 93*6dbdd20aSAndroid Build Coastguard Worker static fromMicros(millis: number): time { 94*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigInt(Math.floor(millis * TIME_UNITS_PER_MICROSEC))); 95*6dbdd20aSAndroid Build Coastguard Worker } 96*6dbdd20aSAndroid Build Coastguard Worker 97*6dbdd20aSAndroid Build Coastguard Worker // Convert time value to microseconds and return as a number (i.e. float). 98*6dbdd20aSAndroid Build Coastguard Worker // Warning: This function is lossy, i.e. precision is lost when converting 99*6dbdd20aSAndroid Build Coastguard Worker // BigInt -> number. 100*6dbdd20aSAndroid Build Coastguard Worker // Note: BigInt -> number conversion is relatively slow. 101*6dbdd20aSAndroid Build Coastguard Worker static toMicros(t: time): number { 102*6dbdd20aSAndroid Build Coastguard Worker return Number(t) / TIME_UNITS_PER_MICROSEC; 103*6dbdd20aSAndroid Build Coastguard Worker } 104*6dbdd20aSAndroid Build Coastguard Worker 105*6dbdd20aSAndroid Build Coastguard Worker // Convert a Date object to a time value, given an offset from the unix epoch. 106*6dbdd20aSAndroid Build Coastguard Worker // Note: number -> BigInt conversion is relatively slow. 107*6dbdd20aSAndroid Build Coastguard Worker static fromDate(d: Date, offset: duration): time { 108*6dbdd20aSAndroid Build Coastguard Worker const millis = d.getTime(); 109*6dbdd20aSAndroid Build Coastguard Worker const t = Time.fromMillis(millis); 110*6dbdd20aSAndroid Build Coastguard Worker return Time.add(t, offset); 111*6dbdd20aSAndroid Build Coastguard Worker } 112*6dbdd20aSAndroid Build Coastguard Worker 113*6dbdd20aSAndroid Build Coastguard Worker // Convert time value to a Date object, given an offset from the unix epoch. 114*6dbdd20aSAndroid Build Coastguard Worker // Warning: This function is lossy, i.e. precision is lost when converting 115*6dbdd20aSAndroid Build Coastguard Worker // BigInt -> number. 116*6dbdd20aSAndroid Build Coastguard Worker // Note: BigInt -> number conversion is relatively slow. 117*6dbdd20aSAndroid Build Coastguard Worker static toDate(t: time, offset: duration): Date { 118*6dbdd20aSAndroid Build Coastguard Worker const timeSinceEpoch = Time.sub(t, offset); 119*6dbdd20aSAndroid Build Coastguard Worker const millis = Time.toMillis(timeSinceEpoch); 120*6dbdd20aSAndroid Build Coastguard Worker return new Date(millis); 121*6dbdd20aSAndroid Build Coastguard Worker } 122*6dbdd20aSAndroid Build Coastguard Worker 123*6dbdd20aSAndroid Build Coastguard Worker // Find the closest previous midnight for a given time value. 124*6dbdd20aSAndroid Build Coastguard Worker static getLatestMidnight(time: time, offset: duration): time { 125*6dbdd20aSAndroid Build Coastguard Worker const date = Time.toDate(time, offset); 126*6dbdd20aSAndroid Build Coastguard Worker const floorDay = new Date( 127*6dbdd20aSAndroid Build Coastguard Worker Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()), 128*6dbdd20aSAndroid Build Coastguard Worker ); 129*6dbdd20aSAndroid Build Coastguard Worker 130*6dbdd20aSAndroid Build Coastguard Worker return Time.fromDate(floorDay, offset); 131*6dbdd20aSAndroid Build Coastguard Worker } 132*6dbdd20aSAndroid Build Coastguard Worker 133*6dbdd20aSAndroid Build Coastguard Worker static add(t: time, d: duration): time { 134*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(t + d); 135*6dbdd20aSAndroid Build Coastguard Worker } 136*6dbdd20aSAndroid Build Coastguard Worker 137*6dbdd20aSAndroid Build Coastguard Worker static sub(t: time, d: duration): time { 138*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(t - d); 139*6dbdd20aSAndroid Build Coastguard Worker } 140*6dbdd20aSAndroid Build Coastguard Worker 141*6dbdd20aSAndroid Build Coastguard Worker static diff(a: time, b: time): duration { 142*6dbdd20aSAndroid Build Coastguard Worker return a - b; 143*6dbdd20aSAndroid Build Coastguard Worker } 144*6dbdd20aSAndroid Build Coastguard Worker 145*6dbdd20aSAndroid Build Coastguard Worker static min(a: time, b: time): time { 146*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigintMath.min(a, b)); 147*6dbdd20aSAndroid Build Coastguard Worker } 148*6dbdd20aSAndroid Build Coastguard Worker 149*6dbdd20aSAndroid Build Coastguard Worker static max(a: time, b: time): time { 150*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigintMath.max(a, b)); 151*6dbdd20aSAndroid Build Coastguard Worker } 152*6dbdd20aSAndroid Build Coastguard Worker 153*6dbdd20aSAndroid Build Coastguard Worker static quantFloor(a: time, b: duration): time { 154*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigintMath.quantFloor(a, b)); 155*6dbdd20aSAndroid Build Coastguard Worker } 156*6dbdd20aSAndroid Build Coastguard Worker 157*6dbdd20aSAndroid Build Coastguard Worker static quantCeil(a: time, b: duration): time { 158*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigintMath.quantCeil(a, b)); 159*6dbdd20aSAndroid Build Coastguard Worker } 160*6dbdd20aSAndroid Build Coastguard Worker 161*6dbdd20aSAndroid Build Coastguard Worker static quant(a: time, b: duration): time { 162*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw(BigintMath.quant(a, b)); 163*6dbdd20aSAndroid Build Coastguard Worker } 164*6dbdd20aSAndroid Build Coastguard Worker 165*6dbdd20aSAndroid Build Coastguard Worker static formatSeconds(time: time): string { 166*6dbdd20aSAndroid Build Coastguard Worker return Time.toSeconds(time).toString() + ' s'; 167*6dbdd20aSAndroid Build Coastguard Worker } 168*6dbdd20aSAndroid Build Coastguard Worker 169*6dbdd20aSAndroid Build Coastguard Worker static formatMilliseconds(time: time): string { 170*6dbdd20aSAndroid Build Coastguard Worker return Time.toMillis(time).toString() + ' ms'; 171*6dbdd20aSAndroid Build Coastguard Worker } 172*6dbdd20aSAndroid Build Coastguard Worker 173*6dbdd20aSAndroid Build Coastguard Worker static formatMicroseconds(time: time): string { 174*6dbdd20aSAndroid Build Coastguard Worker return Time.toMicros(time).toString() + ' us'; 175*6dbdd20aSAndroid Build Coastguard Worker } 176*6dbdd20aSAndroid Build Coastguard Worker 177*6dbdd20aSAndroid Build Coastguard Worker static toTimecode(time: time): Timecode { 178*6dbdd20aSAndroid Build Coastguard Worker return new Timecode(time); 179*6dbdd20aSAndroid Build Coastguard Worker } 180*6dbdd20aSAndroid Build Coastguard Worker} 181*6dbdd20aSAndroid Build Coastguard Worker 182*6dbdd20aSAndroid Build Coastguard Workerexport class Duration { 183*6dbdd20aSAndroid Build Coastguard Worker // The min and max possible duration values - durations can be negative. 184*6dbdd20aSAndroid Build Coastguard Worker static MIN = BigintMath.INT64_MIN; 185*6dbdd20aSAndroid Build Coastguard Worker static MAX = BigintMath.INT64_MAX; 186*6dbdd20aSAndroid Build Coastguard Worker static ZERO = 0n; 187*6dbdd20aSAndroid Build Coastguard Worker 188*6dbdd20aSAndroid Build Coastguard Worker // Cast a bigint to a |duration|. Supports potentially |undefined| values. 189*6dbdd20aSAndroid Build Coastguard Worker // I.e. it performs the following conversions: 190*6dbdd20aSAndroid Build Coastguard Worker // - `bigint` -> `duration` 191*6dbdd20aSAndroid Build Coastguard Worker // - `bigint|undefined` -> `duration|undefined` 192*6dbdd20aSAndroid Build Coastguard Worker // 193*6dbdd20aSAndroid Build Coastguard Worker // Use this function with caution. The function is effectively a no-op in JS, 194*6dbdd20aSAndroid Build Coastguard Worker // but using it tells TypeScript that "this value is a duration value". It's 195*6dbdd20aSAndroid Build Coastguard Worker // up to the caller to ensure the value is in the correct units. 196*6dbdd20aSAndroid Build Coastguard Worker // 197*6dbdd20aSAndroid Build Coastguard Worker // If you're reaching for this function after doing some maths on a |duration| 198*6dbdd20aSAndroid Build Coastguard Worker // value and it's decayed to a |bigint| consider using the static math methods 199*6dbdd20aSAndroid Build Coastguard Worker // in |duration| instead, as they will do the appropriate casting for you. 200*6dbdd20aSAndroid Build Coastguard Worker static fromRaw(v: bigint): duration; 201*6dbdd20aSAndroid Build Coastguard Worker static fromRaw(v?: bigint): duration | undefined; 202*6dbdd20aSAndroid Build Coastguard Worker static fromRaw(v?: bigint): duration | undefined { 203*6dbdd20aSAndroid Build Coastguard Worker return v as duration | undefined; 204*6dbdd20aSAndroid Build Coastguard Worker } 205*6dbdd20aSAndroid Build Coastguard Worker 206*6dbdd20aSAndroid Build Coastguard Worker static min(a: duration, b: duration): duration { 207*6dbdd20aSAndroid Build Coastguard Worker return BigintMath.min(a, b); 208*6dbdd20aSAndroid Build Coastguard Worker } 209*6dbdd20aSAndroid Build Coastguard Worker 210*6dbdd20aSAndroid Build Coastguard Worker static max(a: duration, b: duration): duration { 211*6dbdd20aSAndroid Build Coastguard Worker return BigintMath.max(a, b); 212*6dbdd20aSAndroid Build Coastguard Worker } 213*6dbdd20aSAndroid Build Coastguard Worker 214*6dbdd20aSAndroid Build Coastguard Worker static fromMillis(millis: number) { 215*6dbdd20aSAndroid Build Coastguard Worker return BigInt(Math.floor((millis / 1e3) * TIME_UNITS_PER_SEC)); 216*6dbdd20aSAndroid Build Coastguard Worker } 217*6dbdd20aSAndroid Build Coastguard Worker 218*6dbdd20aSAndroid Build Coastguard Worker // Convert time to seconds as a number. 219*6dbdd20aSAndroid Build Coastguard Worker // Use this function with caution. It loses precision and is slow. 220*6dbdd20aSAndroid Build Coastguard Worker static toSeconds(d: duration) { 221*6dbdd20aSAndroid Build Coastguard Worker return Number(d) / TIME_UNITS_PER_SEC; 222*6dbdd20aSAndroid Build Coastguard Worker } 223*6dbdd20aSAndroid Build Coastguard Worker 224*6dbdd20aSAndroid Build Coastguard Worker // Convert time to seconds as a number. 225*6dbdd20aSAndroid Build Coastguard Worker // Use this function with caution. It loses precision and is slow. 226*6dbdd20aSAndroid Build Coastguard Worker static toMilliseconds(d: duration) { 227*6dbdd20aSAndroid Build Coastguard Worker return Number(d) / TIME_UNITS_PER_MILLISEC; 228*6dbdd20aSAndroid Build Coastguard Worker } 229*6dbdd20aSAndroid Build Coastguard Worker 230*6dbdd20aSAndroid Build Coastguard Worker // Convert time to seconds as a number. 231*6dbdd20aSAndroid Build Coastguard Worker // Use this function with caution. It loses precision and is slow. 232*6dbdd20aSAndroid Build Coastguard Worker static toMicroSeconds(d: duration) { 233*6dbdd20aSAndroid Build Coastguard Worker return Number(d) / TIME_UNITS_PER_MICROSEC; 234*6dbdd20aSAndroid Build Coastguard Worker } 235*6dbdd20aSAndroid Build Coastguard Worker 236*6dbdd20aSAndroid Build Coastguard Worker // Print duration as as human readable string - i.e. to only a handful of 237*6dbdd20aSAndroid Build Coastguard Worker // significant figues. 238*6dbdd20aSAndroid Build Coastguard Worker // Use this when readability is more desireable than precision. 239*6dbdd20aSAndroid Build Coastguard Worker // Examples: 1234 -> 1.23ns 240*6dbdd20aSAndroid Build Coastguard Worker // 123456789 -> 123ms 241*6dbdd20aSAndroid Build Coastguard Worker // 123,123,123,123,123 -> 34h 12m 242*6dbdd20aSAndroid Build Coastguard Worker // 1,000,000,023 -> 1 s 243*6dbdd20aSAndroid Build Coastguard Worker // 1,230,000,023 -> 1.2 s 244*6dbdd20aSAndroid Build Coastguard Worker static humanise(dur: duration): string { 245*6dbdd20aSAndroid Build Coastguard Worker const sec = Duration.toSeconds(dur); 246*6dbdd20aSAndroid Build Coastguard Worker const units = ['s', 'ms', 'us', 'ns']; 247*6dbdd20aSAndroid Build Coastguard Worker const sign = Math.sign(sec); 248*6dbdd20aSAndroid Build Coastguard Worker let n = Math.abs(sec); 249*6dbdd20aSAndroid Build Coastguard Worker let u = 0; 250*6dbdd20aSAndroid Build Coastguard Worker while (n < 1 && n !== 0 && u < units.length - 1) { 251*6dbdd20aSAndroid Build Coastguard Worker n *= 1000; 252*6dbdd20aSAndroid Build Coastguard Worker u++; 253*6dbdd20aSAndroid Build Coastguard Worker } 254*6dbdd20aSAndroid Build Coastguard Worker return `${sign < 0 ? '-' : ''}${Math.round(n * 10) / 10}${units[u]}`; 255*6dbdd20aSAndroid Build Coastguard Worker } 256*6dbdd20aSAndroid Build Coastguard Worker 257*6dbdd20aSAndroid Build Coastguard Worker // Print duration with absolute precision. 258*6dbdd20aSAndroid Build Coastguard Worker static format(duration: duration): string { 259*6dbdd20aSAndroid Build Coastguard Worker let result = ''; 260*6dbdd20aSAndroid Build Coastguard Worker if (duration < 1) return '0s'; 261*6dbdd20aSAndroid Build Coastguard Worker const unitAndValue: [string, bigint][] = [ 262*6dbdd20aSAndroid Build Coastguard Worker ['h', 3_600_000_000_000n], 263*6dbdd20aSAndroid Build Coastguard Worker ['m', 60_000_000_000n], 264*6dbdd20aSAndroid Build Coastguard Worker ['s', 1_000_000_000n], 265*6dbdd20aSAndroid Build Coastguard Worker ['ms', 1_000_000n], 266*6dbdd20aSAndroid Build Coastguard Worker ['us', 1_000n], 267*6dbdd20aSAndroid Build Coastguard Worker ['ns', 1n], 268*6dbdd20aSAndroid Build Coastguard Worker ]; 269*6dbdd20aSAndroid Build Coastguard Worker unitAndValue.forEach(([unit, unitSize]) => { 270*6dbdd20aSAndroid Build Coastguard Worker if (duration >= unitSize) { 271*6dbdd20aSAndroid Build Coastguard Worker const unitCount = duration / unitSize; 272*6dbdd20aSAndroid Build Coastguard Worker result += unitCount.toLocaleString() + unit + ' '; 273*6dbdd20aSAndroid Build Coastguard Worker duration = duration % unitSize; 274*6dbdd20aSAndroid Build Coastguard Worker } 275*6dbdd20aSAndroid Build Coastguard Worker }); 276*6dbdd20aSAndroid Build Coastguard Worker return result.slice(0, -1); 277*6dbdd20aSAndroid Build Coastguard Worker } 278*6dbdd20aSAndroid Build Coastguard Worker 279*6dbdd20aSAndroid Build Coastguard Worker static formatSeconds(dur: duration): string { 280*6dbdd20aSAndroid Build Coastguard Worker return Duration.toSeconds(dur).toString() + ' s'; 281*6dbdd20aSAndroid Build Coastguard Worker } 282*6dbdd20aSAndroid Build Coastguard Worker 283*6dbdd20aSAndroid Build Coastguard Worker static formatMilliseconds(dur: duration): string { 284*6dbdd20aSAndroid Build Coastguard Worker return Duration.toMilliseconds(dur).toString() + ' ms'; 285*6dbdd20aSAndroid Build Coastguard Worker } 286*6dbdd20aSAndroid Build Coastguard Worker 287*6dbdd20aSAndroid Build Coastguard Worker static formatMicroseconds(dur: duration): string { 288*6dbdd20aSAndroid Build Coastguard Worker return Duration.toMicroSeconds(dur).toString() + ' us'; 289*6dbdd20aSAndroid Build Coastguard Worker } 290*6dbdd20aSAndroid Build Coastguard Worker} 291*6dbdd20aSAndroid Build Coastguard Worker 292*6dbdd20aSAndroid Build Coastguard Worker// This class takes a time and converts it to a set of strings representing a 293*6dbdd20aSAndroid Build Coastguard Worker// time code where each string represents a group of time units formatted with 294*6dbdd20aSAndroid Build Coastguard Worker// an appropriate number of leading zeros. 295*6dbdd20aSAndroid Build Coastguard Workerexport class Timecode { 296*6dbdd20aSAndroid Build Coastguard Worker public readonly sign: string; 297*6dbdd20aSAndroid Build Coastguard Worker public readonly days: string; 298*6dbdd20aSAndroid Build Coastguard Worker public readonly hours: string; 299*6dbdd20aSAndroid Build Coastguard Worker public readonly minutes: string; 300*6dbdd20aSAndroid Build Coastguard Worker public readonly seconds: string; 301*6dbdd20aSAndroid Build Coastguard Worker public readonly millis: string; 302*6dbdd20aSAndroid Build Coastguard Worker public readonly micros: string; 303*6dbdd20aSAndroid Build Coastguard Worker public readonly nanos: string; 304*6dbdd20aSAndroid Build Coastguard Worker 305*6dbdd20aSAndroid Build Coastguard Worker constructor(time: time) { 306*6dbdd20aSAndroid Build Coastguard Worker this.sign = time < 0 ? '-' : ''; 307*6dbdd20aSAndroid Build Coastguard Worker 308*6dbdd20aSAndroid Build Coastguard Worker const absTime = BigintMath.abs(time); 309*6dbdd20aSAndroid Build Coastguard Worker 310*6dbdd20aSAndroid Build Coastguard Worker const days = absTime / 86_400_000_000_000n; 311*6dbdd20aSAndroid Build Coastguard Worker const hours = (absTime / 3_600_000_000_000n) % 24n; 312*6dbdd20aSAndroid Build Coastguard Worker const minutes = (absTime / 60_000_000_000n) % 60n; 313*6dbdd20aSAndroid Build Coastguard Worker const seconds = (absTime / 1_000_000_000n) % 60n; 314*6dbdd20aSAndroid Build Coastguard Worker const millis = (absTime / 1_000_000n) % 1_000n; 315*6dbdd20aSAndroid Build Coastguard Worker const micros = (absTime / 1_000n) % 1_000n; 316*6dbdd20aSAndroid Build Coastguard Worker const nanos = absTime % 1_000n; 317*6dbdd20aSAndroid Build Coastguard Worker 318*6dbdd20aSAndroid Build Coastguard Worker this.days = days.toString(); 319*6dbdd20aSAndroid Build Coastguard Worker this.hours = hours.toString().padStart(2, '0'); 320*6dbdd20aSAndroid Build Coastguard Worker this.minutes = minutes.toString().padStart(2, '0'); 321*6dbdd20aSAndroid Build Coastguard Worker this.seconds = seconds.toString().padStart(2, '0'); 322*6dbdd20aSAndroid Build Coastguard Worker this.millis = millis.toString().padStart(3, '0'); 323*6dbdd20aSAndroid Build Coastguard Worker this.micros = micros.toString().padStart(3, '0'); 324*6dbdd20aSAndroid Build Coastguard Worker this.nanos = nanos.toString().padStart(3, '0'); 325*6dbdd20aSAndroid Build Coastguard Worker } 326*6dbdd20aSAndroid Build Coastguard Worker 327*6dbdd20aSAndroid Build Coastguard Worker // Get the upper part of the timecode formatted as: [-]DdHH:MM:SS. 328*6dbdd20aSAndroid Build Coastguard Worker get dhhmmss(): string { 329*6dbdd20aSAndroid Build Coastguard Worker const days = this.days === '0' ? '' : `${this.days}d`; 330*6dbdd20aSAndroid Build Coastguard Worker return `${this.sign}${days}${this.hours}:${this.minutes}:${this.seconds}`; 331*6dbdd20aSAndroid Build Coastguard Worker } 332*6dbdd20aSAndroid Build Coastguard Worker 333*6dbdd20aSAndroid Build Coastguard Worker // Get the subsecond part of the timecode formatted as: mmm uuu nnn. 334*6dbdd20aSAndroid Build Coastguard Worker // The "space" char is configurable but defaults to a normal space. 335*6dbdd20aSAndroid Build Coastguard Worker subsec(spaceChar: string = ' '): string { 336*6dbdd20aSAndroid Build Coastguard Worker return `${this.millis}${spaceChar}${this.micros}${spaceChar}${this.nanos}`; 337*6dbdd20aSAndroid Build Coastguard Worker } 338*6dbdd20aSAndroid Build Coastguard Worker 339*6dbdd20aSAndroid Build Coastguard Worker // Formats the entire timecode to a string. 340*6dbdd20aSAndroid Build Coastguard Worker toString(spaceChar: string = ' '): string { 341*6dbdd20aSAndroid Build Coastguard Worker return `${this.dhhmmss}.${this.subsec(spaceChar)}`; 342*6dbdd20aSAndroid Build Coastguard Worker } 343*6dbdd20aSAndroid Build Coastguard Worker} 344*6dbdd20aSAndroid Build Coastguard Worker 345*6dbdd20aSAndroid Build Coastguard Workerexport function currentDateHourAndMinute(): string { 346*6dbdd20aSAndroid Build Coastguard Worker const date = new Date(); 347*6dbdd20aSAndroid Build Coastguard Worker return `${date 348*6dbdd20aSAndroid Build Coastguard Worker .toISOString() 349*6dbdd20aSAndroid Build Coastguard Worker .substr(0, 10)}-${date.getHours()}-${date.getMinutes()}`; 350*6dbdd20aSAndroid Build Coastguard Worker} 351*6dbdd20aSAndroid Build Coastguard Worker 352*6dbdd20aSAndroid Build Coastguard Workerexport class TimeSpan { 353*6dbdd20aSAndroid Build Coastguard Worker static readonly ZERO = new TimeSpan(Time.ZERO, Time.ZERO); 354*6dbdd20aSAndroid Build Coastguard Worker 355*6dbdd20aSAndroid Build Coastguard Worker readonly start: time; 356*6dbdd20aSAndroid Build Coastguard Worker readonly end: time; 357*6dbdd20aSAndroid Build Coastguard Worker 358*6dbdd20aSAndroid Build Coastguard Worker constructor(start: time, end: time) { 359*6dbdd20aSAndroid Build Coastguard Worker assertTrue( 360*6dbdd20aSAndroid Build Coastguard Worker start <= end, 361*6dbdd20aSAndroid Build Coastguard Worker `Span start [${start}] cannot be greater than end [${end}]`, 362*6dbdd20aSAndroid Build Coastguard Worker ); 363*6dbdd20aSAndroid Build Coastguard Worker this.start = start; 364*6dbdd20aSAndroid Build Coastguard Worker this.end = end; 365*6dbdd20aSAndroid Build Coastguard Worker } 366*6dbdd20aSAndroid Build Coastguard Worker 367*6dbdd20aSAndroid Build Coastguard Worker static fromTimeAndDuration(start: time, duration: duration): TimeSpan { 368*6dbdd20aSAndroid Build Coastguard Worker return new TimeSpan(start, Time.add(start, duration)); 369*6dbdd20aSAndroid Build Coastguard Worker } 370*6dbdd20aSAndroid Build Coastguard Worker 371*6dbdd20aSAndroid Build Coastguard Worker get duration(): duration { 372*6dbdd20aSAndroid Build Coastguard Worker return this.end - this.start; 373*6dbdd20aSAndroid Build Coastguard Worker } 374*6dbdd20aSAndroid Build Coastguard Worker 375*6dbdd20aSAndroid Build Coastguard Worker get midpoint(): time { 376*6dbdd20aSAndroid Build Coastguard Worker return Time.fromRaw((this.start + this.end) / 2n); 377*6dbdd20aSAndroid Build Coastguard Worker } 378*6dbdd20aSAndroid Build Coastguard Worker 379*6dbdd20aSAndroid Build Coastguard Worker contains(t: time): boolean { 380*6dbdd20aSAndroid Build Coastguard Worker return this.start <= t && t < this.end; 381*6dbdd20aSAndroid Build Coastguard Worker } 382*6dbdd20aSAndroid Build Coastguard Worker 383*6dbdd20aSAndroid Build Coastguard Worker containsSpan(start: time, end: time): boolean { 384*6dbdd20aSAndroid Build Coastguard Worker return this.start <= start && end <= this.end; 385*6dbdd20aSAndroid Build Coastguard Worker } 386*6dbdd20aSAndroid Build Coastguard Worker 387*6dbdd20aSAndroid Build Coastguard Worker overlaps(start: time, end: time): boolean { 388*6dbdd20aSAndroid Build Coastguard Worker return !(end <= this.start || start >= this.end); 389*6dbdd20aSAndroid Build Coastguard Worker } 390*6dbdd20aSAndroid Build Coastguard Worker 391*6dbdd20aSAndroid Build Coastguard Worker equals(span: TimeSpan): boolean { 392*6dbdd20aSAndroid Build Coastguard Worker return this.start === span.start && this.end === span.end; 393*6dbdd20aSAndroid Build Coastguard Worker } 394*6dbdd20aSAndroid Build Coastguard Worker 395*6dbdd20aSAndroid Build Coastguard Worker translate(x: duration): TimeSpan { 396*6dbdd20aSAndroid Build Coastguard Worker return new TimeSpan(Time.add(this.start, x), Time.add(this.end, x)); 397*6dbdd20aSAndroid Build Coastguard Worker } 398*6dbdd20aSAndroid Build Coastguard Worker 399*6dbdd20aSAndroid Build Coastguard Worker pad(padding: duration): TimeSpan { 400*6dbdd20aSAndroid Build Coastguard Worker return new TimeSpan( 401*6dbdd20aSAndroid Build Coastguard Worker Time.sub(this.start, padding), 402*6dbdd20aSAndroid Build Coastguard Worker Time.add(this.end, padding), 403*6dbdd20aSAndroid Build Coastguard Worker ); 404*6dbdd20aSAndroid Build Coastguard Worker } 405*6dbdd20aSAndroid Build Coastguard Worker} 406*6dbdd20aSAndroid Build Coastguard Worker 407*6dbdd20aSAndroid Build Coastguard Worker// Print the date only for a given date in ISO format. 408*6dbdd20aSAndroid Build Coastguard Workerexport function toISODateOnly(date: Date) { 409*6dbdd20aSAndroid Build Coastguard Worker const year = date.getUTCFullYear(); 410*6dbdd20aSAndroid Build Coastguard Worker const month = String(date.getUTCMonth() + 1).padStart(2, '0'); 411*6dbdd20aSAndroid Build Coastguard Worker const day = String(date.getUTCDate()).padStart(2, '0'); 412*6dbdd20aSAndroid Build Coastguard Worker 413*6dbdd20aSAndroid Build Coastguard Worker return `${year}-${month}-${day}`; 414*6dbdd20aSAndroid Build Coastguard Worker} 415