1 /* 2 * Copyright (C) 2023 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 17 package com.google.android.libraries.mobiledatadownload.internal.logging; 18 19 import static com.google.common.math.LongMath.checkedAdd; 20 import static com.google.common.math.LongMath.checkedMultiply; 21 import static com.google.common.math.LongMath.checkedSubtract; 22 23 import com.google.protobuf.Timestamp; 24 25 /** 26 * Utilities to help create/manipulate {@code protobuf/timestamp.proto}. 27 */ 28 public class TimestampsUtil { 29 30 // Timestamp for "0001-01-01T00:00:00Z" 31 static final long TIMESTAMP_SECONDS_MIN = -62135596800L; 32 33 // Timestamp for "9999-12-31T23:59:59Z" 34 static final long TIMESTAMP_SECONDS_MAX = 253402300799L; 35 36 static final int NANOS_PER_SECOND = 1000000000; 37 static final int NANOS_PER_MILLISECOND = 1000000; 38 static final int NANOS_PER_MICROSECOND = 1000; 39 static final int MILLIS_PER_SECOND = 1000; 40 static final int MICROS_PER_SECOND = 1000000; 41 42 @SuppressWarnings("GoodTime") // this is a legacy conversion API toMillis(Timestamp timestamp)43 public static long toMillis(Timestamp timestamp) { 44 checkValid(timestamp); 45 return checkedAdd( 46 checkedMultiply(timestamp.getSeconds(), MILLIS_PER_SECOND), 47 timestamp.getNanos() / NANOS_PER_MILLISECOND); 48 } 49 50 51 /** Create a Timestamp from the number of milliseconds elapsed from the epoch. */ 52 @SuppressWarnings("GoodTime") // this is a legacy conversion API fromMillis(long milliseconds)53 public static Timestamp fromMillis(long milliseconds) { 54 return normalizedTimestamp( 55 milliseconds / MILLIS_PER_SECOND, 56 (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); 57 } 58 checkValid(Timestamp timestamp)59 public static Timestamp checkValid(Timestamp timestamp) { 60 long seconds = timestamp.getSeconds(); 61 int nanos = timestamp.getNanos(); 62 if (!isValid(seconds, nanos)) { 63 throw new IllegalArgumentException( 64 String.format( 65 "Timestamp is not valid. See proto definition for valid values. " 66 + "Seconds (%s) must be in range [-62,135,596,800, +253,402," 67 + "300,799]. " 68 + "Nanos (%s) must be in range [0, +999,999,999].", 69 seconds, nanos)); 70 } 71 return timestamp; 72 } 73 74 /** 75 * Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The 76 * {@code 77 * seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between 78 * 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range 79 * [0, +999,999,999]. 80 * 81 * <p><b>Note:</b> Negative second values with fractional seconds must still have non-negative 82 * nanos values that count forward in time. 83 */ 84 @SuppressWarnings("GoodTime") // this is a legacy conversion API isValid(long seconds, int nanos)85 public static boolean isValid(long seconds, int nanos) { 86 if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) { 87 return false; 88 } 89 if (nanos < 0 || nanos >= NANOS_PER_SECOND) { 90 return false; 91 } 92 return true; 93 } 94 normalizedTimestamp(long seconds, int nanos)95 static Timestamp normalizedTimestamp(long seconds, int nanos) { 96 if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { 97 seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND); 98 nanos = (int) (nanos % NANOS_PER_SECOND); 99 } 100 if (nanos < 0) { 101 nanos = 102 (int) 103 (nanos 104 + NANOS_PER_SECOND); // no overflow since nanos is negative 105 // (and we're adding) 106 seconds = checkedSubtract(seconds, 1); 107 } 108 Timestamp timestamp = Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build(); 109 return checkValid(timestamp); 110 } 111 } 112