1// Copyright (C) 2018 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {assertTrue} from '../base/logging'; 16import {ColumnType} from './query_result'; 17 18// TODO(hjd): Combine with timeToCode. 19export function timeToString(sec: number) { 20 const units = ['s', 'ms', 'us', 'ns']; 21 const sign = Math.sign(sec); 22 let n = Math.abs(sec); 23 let u = 0; 24 while (n < 1 && n !== 0 && u < units.length - 1) { 25 n *= 1000; 26 u++; 27 } 28 return `${sign < 0 ? '-' : ''}${Math.round(n * 10) / 10} ${units[u]}`; 29} 30 31export function tpTimeToString(time: TPTime) { 32 return timeToString(tpTimeToSeconds(time)); 33} 34 35export function fromNs(ns: number) { 36 return ns / 1e9; 37} 38 39export function toNsFloor(seconds: number) { 40 return Math.floor(seconds * 1e9); 41} 42 43export function toNsCeil(seconds: number) { 44 return Math.ceil(seconds * 1e9); 45} 46 47export function toNs(seconds: number) { 48 return Math.round(seconds * 1e9); 49} 50 51// 1000000023ns -> "1.000 000 023" 52export function formatTimestamp(sec: number) { 53 const parts = sec.toFixed(9).split('.'); 54 parts[1] = parts[1].replace(/\B(?=(\d{3})+(?!\d))/g, ' '); 55 return parts.join('.'); 56} 57 58export function formatTPTime(time: TPTime) { 59 return formatTimestamp(tpTimeToSeconds(time)); 60} 61 62// TODO(hjd): Rename to formatTimestampWithUnits 63// 1000000023ns -> "1s 23ns" 64export function timeToCode(sec: number): string { 65 let result = ''; 66 let ns = Math.round(sec * 1e9); 67 if (ns < 1) return '0s'; 68 const unitAndValue = [ 69 ['m', 60000000000], 70 ['s', 1000000000], 71 ['ms', 1000000], 72 ['us', 1000], 73 ['ns', 1], 74 ]; 75 unitAndValue.forEach((pair) => { 76 const unit = pair[0] as string; 77 const val = pair[1] as number; 78 if (ns >= val) { 79 const i = Math.floor(ns / val); 80 ns -= i * val; 81 result += i.toLocaleString() + unit + ' '; 82 } 83 }); 84 return result.slice(0, -1); 85} 86 87export function tpTimeToCode(time: TPTime) { 88 return timeToCode(tpTimeToSeconds(time)); 89} 90 91export function currentDateHourAndMinute(): string { 92 const date = new Date(); 93 return `${date.toISOString().substr(0, 10)}-${date.getHours()}-${ 94 date.getMinutes()}`; 95} 96 97// Aliased "Trace Processor" time and duration types. 98// Note(stevegolton): While it might be nice to type brand these in the future, 99// for now we're going to keep things simple. We do a lot of maths with these 100// timestamps and type branding requires a lot of jumping through hoops to 101// coerse the type back to the correct format. 102export type TPTime = bigint; 103export type TPDuration = bigint; 104 105export function tpTimeFromNanos(nanos: number): TPTime { 106 return BigInt(Math.floor(nanos)); 107} 108 109export function tpTimeFromSeconds(seconds: number): TPTime { 110 return BigInt(Math.floor(seconds * 1e9)); 111} 112 113export function tpTimeToNanos(time: TPTime): number { 114 return Number(time); 115} 116 117export function tpTimeToMillis(time: TPTime): number { 118 return Number(time) / 1e6; 119} 120 121export function tpTimeToSeconds(time: TPTime): number { 122 return Number(time) / 1e9; 123} 124 125// Create a TPTime from an arbitrary SQL value. 126// Throws if the value cannot be reasonably converted to a bigint. 127// Assumes value is in nanoseconds. 128export function tpTimeFromSql(value: ColumnType): TPTime { 129 if (typeof value === 'bigint') { 130 return value; 131 } else if (typeof value === 'number') { 132 return tpTimeFromNanos(value); 133 } else if (value === null) { 134 return 0n; 135 } else { 136 throw Error(`Refusing to create Timestamp from unrelated type ${value}`); 137 } 138} 139 140export function tpDurationToSeconds(dur: TPDuration): number { 141 return tpTimeToSeconds(dur); 142} 143 144export function tpDurationToNanos(dur: TPDuration): number { 145 return tpTimeToSeconds(dur); 146} 147 148export function tpDurationFromNanos(nanos: number): TPDuration { 149 return tpTimeFromNanos(nanos); 150} 151 152export function tpDurationFromSql(nanos: ColumnType): TPDuration { 153 return tpTimeFromSql(nanos); 154} 155 156export interface Span<Unit, Duration = Unit> { 157 get start(): Unit; 158 get end(): Unit; 159 get duration(): Duration; 160 get midpoint(): Unit; 161 contains(span: Unit|Span<Unit, Duration>): boolean; 162 intersects(x: Span<Unit>): boolean; 163 equals(span: Span<Unit, Duration>): boolean; 164 add(offset: Duration): Span<Unit, Duration>; 165 pad(padding: Duration): Span<Unit, Duration>; 166} 167 168export class TPTimeSpan implements Span<TPTime, TPDuration> { 169 readonly start: TPTime; 170 readonly end: TPTime; 171 172 constructor(start: TPTime, end: TPTime) { 173 assertTrue( 174 start <= end, 175 `Span start [${start}] cannot be greater than end [${end}]`); 176 this.start = start; 177 this.end = end; 178 } 179 180 get duration(): TPDuration { 181 return this.end - this.start; 182 } 183 184 get midpoint(): TPTime { 185 return (this.start + this.end) / 2n; 186 } 187 188 contains(x: TPTime|Span<TPTime, TPDuration>): boolean { 189 if (typeof x === 'bigint') { 190 return this.start <= x && x < this.end; 191 } else { 192 return this.start <= x.start && x.end <= this.end; 193 } 194 } 195 196 intersects(x: Span<TPTime, TPDuration>): boolean { 197 return !(x.end <= this.start || x.start >= this.end); 198 } 199 200 equals(span: Span<TPTime, TPDuration>): boolean { 201 return this.start === span.start && this.end === span.end; 202 } 203 204 add(x: TPTime): Span<TPTime, TPDuration> { 205 return new TPTimeSpan(this.start + x, this.end + x); 206 } 207 208 pad(padding: TPDuration): Span<TPTime, TPDuration> { 209 return new TPTimeSpan(this.start - padding, this.end + padding); 210 } 211} 212