summaryrefslogtreecommitdiff
path: root/java/com/google/android/libraries/mobiledatadownload/internal/logging/TimestampsUtil.java
blob: e0f2205f6d1865a2541aafe1de2fa864a30c9b4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.libraries.mobiledatadownload.internal.logging;

import static com.google.common.math.LongMath.checkedAdd;
import static com.google.common.math.LongMath.checkedMultiply;
import static com.google.common.math.LongMath.checkedSubtract;

import com.google.protobuf.Timestamp;

/**
 * Utilities to help create/manipulate {@code protobuf/timestamp.proto}.
 */
public class TimestampsUtil {

    // Timestamp for "0001-01-01T00:00:00Z"
    static final long TIMESTAMP_SECONDS_MIN = -62135596800L;

    // Timestamp for "9999-12-31T23:59:59Z"
    static final long TIMESTAMP_SECONDS_MAX = 253402300799L;

    static final int NANOS_PER_SECOND = 1000000000;
    static final int NANOS_PER_MILLISECOND = 1000000;
    static final int NANOS_PER_MICROSECOND = 1000;
    static final int MILLIS_PER_SECOND = 1000;
    static final int MICROS_PER_SECOND = 1000000;

    @SuppressWarnings("GoodTime") // this is a legacy conversion API
    public static long toMillis(Timestamp timestamp) {
        checkValid(timestamp);
        return checkedAdd(
                checkedMultiply(timestamp.getSeconds(), MILLIS_PER_SECOND),
                timestamp.getNanos() / NANOS_PER_MILLISECOND);
    }


    /** Create a Timestamp from the number of milliseconds elapsed from the epoch. */
    @SuppressWarnings("GoodTime") // this is a legacy conversion API
    public static Timestamp fromMillis(long milliseconds) {
        return normalizedTimestamp(
                milliseconds / MILLIS_PER_SECOND,
                (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
    }

    public static Timestamp checkValid(Timestamp timestamp) {
        long seconds = timestamp.getSeconds();
        int nanos = timestamp.getNanos();
        if (!isValid(seconds, nanos)) {
            throw new IllegalArgumentException(
                    String.format(
                            "Timestamp is not valid. See proto definition for valid values. "
                                    + "Seconds (%s) must be in range [-62,135,596,800, +253,402,"
                                    + "300,799]. "
                                    + "Nanos (%s) must be in range [0, +999,999,999].",
                            seconds, nanos));
        }
        return timestamp;
    }

    /**
     * Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The
     * {@code
     * seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between
     * 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range
     * [0, +999,999,999].
     *
     * <p><b>Note:</b> Negative second values with fractional seconds must still have non-negative
     * nanos values that count forward in time.
     */
    @SuppressWarnings("GoodTime") // this is a legacy conversion API
    public static boolean isValid(long seconds, int nanos) {
        if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) {
            return false;
        }
        if (nanos < 0 || nanos >= NANOS_PER_SECOND) {
            return false;
        }
        return true;
    }

    static Timestamp normalizedTimestamp(long seconds, int nanos) {
        if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
            seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND);
            nanos = (int) (nanos % NANOS_PER_SECOND);
        }
        if (nanos < 0) {
            nanos =
                    (int)
                            (nanos
                                    + NANOS_PER_SECOND); // no overflow since nanos is negative
            // (and we're adding)
            seconds = checkedSubtract(seconds, 1);
        }
        Timestamp timestamp = Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build();
        return checkValid(timestamp);
    }
}