go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/common/tools/time_utils/time_utils.ts (about)

     1  // Copyright 2020 The LUCI Authors.
     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  
    15  import { Duration } from 'luxon';
    16  
    17  import { Duration as ProtoDuration } from '@/proto/google/protobuf/duration.pb';
    18  
    19  export const SHORT_TIME_FORMAT = 'y-MM-dd HH:mm';
    20  export const LONG_TIME_FORMAT = 'HH:mm:ss ccc, MMM dd yyyy ZZZZ';
    21  export const NUMERIC_TIME_FORMAT = 'y-MM-dd HH:mm:ss ZZ';
    22  
    23  export function displayDuration(duration: Duration) {
    24    const shifted = duration.shiftTo(
    25      'days',
    26      'hours',
    27      'minutes',
    28      'seconds',
    29      'milliseconds',
    30    );
    31    const parts = [];
    32    if (shifted.days >= 1) {
    33      parts.push(shifted.days + ' ' + (shifted.days === 1 ? 'day' : 'days'));
    34    }
    35    if (shifted.hours >= 1) {
    36      parts.push(shifted.hours + ' ' + (shifted.hours === 1 ? 'hour' : 'hours'));
    37    }
    38    // We only care about minutes if days and hours are not both present
    39    if (shifted.minutes >= 1 && parts.length <= 1) {
    40      parts.push(
    41        shifted.minutes + ' ' + (shifted.minutes === 1 ? 'min' : 'mins'),
    42      );
    43    }
    44    // We only care about seconds if it is significant enough
    45    if (shifted.seconds >= 1 && parts.length <= 1) {
    46      parts.push(
    47        shifted.seconds + ' ' + (shifted.seconds === 1 ? 'sec' : 'secs'),
    48      );
    49    }
    50    // We only care about ms if there are no other part
    51    if (parts.length === 0) {
    52      parts.push(Math.floor(shifted.milliseconds) + ' ms');
    53    }
    54    return parts.join(' ');
    55  }
    56  
    57  export function displayCompactDuration(
    58    duration: Duration | null,
    59  ): [string, string] {
    60    if (duration === null) {
    61      return ['N/A', ''];
    62    }
    63  
    64    const shifted = duration.shiftTo(
    65      'days',
    66      'hours',
    67      'minutes',
    68      'seconds',
    69      'milliseconds',
    70    );
    71    if (shifted.days >= 1) {
    72      return [`${(shifted.days + shifted.hours / 24).toFixed(1)}d`, 'd'];
    73    }
    74    if (shifted.hours >= 1) {
    75      return [`${(shifted.hours + shifted.minutes / 60).toPrecision(2)}h`, 'h'];
    76    }
    77    if (shifted.minutes >= 1) {
    78      return [`${(shifted.minutes + shifted.seconds / 60).toPrecision(2)}m`, 'm'];
    79    }
    80  
    81    // Unlike other units, shifted.milliseconds may not be an integer.
    82    // shifted.milliseconds.toFixed(0) may give us 1000 when
    83    // shifted.milliseconds >= 999.5. We should display it as 1.0s in that case.
    84    if (shifted.seconds >= 1 || shifted.milliseconds >= 999.5) {
    85      return [
    86        `${(shifted.seconds + shifted.milliseconds / 1000).toPrecision(2)}s`,
    87        's',
    88      ];
    89    }
    90    return [`${shifted.milliseconds.toFixed(0)}ms`, 'ms'];
    91  }
    92  
    93  /* eslint-disable max-len */
    94  /**
    95   * Parses the JSON encoding of google.protobuf.Duration (e.g. '4.5s').
    96   * https://github.com/protocolbuffers/protobuf/blob/68cb69ea68822d96eee6d6104463edf85e70d689/src/google/protobuf/duration.proto#L92
    97   *
    98   * Returns the number of milliseconds.
    99   */
   100  /* eslint-disable max-len */
   101  export function parseProtoDurationStr(duration: string): Duration {
   102    return Duration.fromObject({
   103      second: Number(duration.substring(0, duration.length - 1)),
   104    });
   105  }
   106  
   107  /**
   108   * Converts a google.protobuf.Duration object to a luxon Duration object.
   109   */
   110  export function parseProtoDuration(duration: ProtoDuration): Duration {
   111    return Duration.fromObject({
   112      second: Number(duration.seconds),
   113      millisecond: duration.nanos / 1000000,
   114    });
   115  }