Coverage Report

Created: 2025-11-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/internal/time.cc
Line
Count
Source
1
// Copyright 2021 Google LLC
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
//     https://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
#include "internal/time.h"
16
17
#include <cstdint>
18
#include <string>
19
20
#include "absl/status/status.h"
21
#include "absl/status/statusor.h"
22
#include "absl/strings/str_cat.h"
23
#include "absl/strings/str_format.h"
24
#include "absl/strings/string_view.h"
25
#include "absl/time/time.h"
26
#include "internal/status_macros.h"
27
#include "google/protobuf/util/time_util.h"
28
29
namespace cel::internal {
30
31
namespace {
32
33
0
std::string RawFormatTimestamp(absl::Time timestamp) {
34
0
  return absl::FormatTime("%Y-%m-%d%ET%H:%M:%E*SZ", timestamp,
35
0
                          absl::UTCTimeZone());
36
0
}
37
38
}  // namespace
39
40
0
absl::Duration MaxDuration() {
41
  // This currently supports a larger range then the current CEL spec. The
42
  // intent is to widen the CEL spec to support the larger range and match
43
  // google.protobuf.Duration from protocol buffer messages, which this
44
  // implementation currently supports.
45
  // TODO(google/cel-spec/issues/214): revisit
46
0
  return absl::Seconds(google::protobuf::util::TimeUtil::kDurationMaxSeconds) +
47
0
         absl::Nanoseconds(google::protobuf::util::TimeUtil::kDurationMaxNanoseconds);
48
0
}
49
50
0
absl::Duration MinDuration() {
51
  // This currently supports a larger range then the current CEL spec. The
52
  // intent is to widen the CEL spec to support the larger range and match
53
  // google.protobuf.Duration from protocol buffer messages, which this
54
  // implementation currently supports.
55
  // TODO(google/cel-spec/issues/214): revisit
56
0
  return absl::Seconds(google::protobuf::util::TimeUtil::kDurationMinSeconds) +
57
0
         absl::Nanoseconds(google::protobuf::util::TimeUtil::kDurationMinNanoseconds);
58
0
}
59
60
0
absl::Time MaxTimestamp() {
61
0
  return absl::UnixEpoch() +
62
0
         absl::Seconds(google::protobuf::util::TimeUtil::kTimestampMaxSeconds) +
63
0
         absl::Nanoseconds(google::protobuf::util::TimeUtil::kTimestampMaxNanoseconds);
64
0
}
65
66
0
absl::Time MinTimestamp() {
67
0
  return absl::UnixEpoch() +
68
0
         absl::Seconds(google::protobuf::util::TimeUtil::kTimestampMinSeconds) +
69
0
         absl::Nanoseconds(google::protobuf::util::TimeUtil::kTimestampMinNanoseconds);
70
0
}
71
72
0
absl::Status ValidateDuration(absl::Duration duration) {
73
0
  if (duration < MinDuration()) {
74
0
    return absl::InvalidArgumentError(
75
0
        absl::StrCat("Duration \"", absl::FormatDuration(duration),
76
0
                     "\" below minimum allowed duration \"",
77
0
                     absl::FormatDuration(MinDuration()), "\""));
78
0
  }
79
0
  if (duration > MaxDuration()) {
80
0
    return absl::InvalidArgumentError(
81
0
        absl::StrCat("Duration \"", absl::FormatDuration(duration),
82
0
                     "\" above maximum allowed duration \"",
83
0
                     absl::FormatDuration(MaxDuration()), "\""));
84
0
  }
85
0
  return absl::OkStatus();
86
0
}
87
88
0
absl::StatusOr<absl::Duration> ParseDuration(absl::string_view input) {
89
0
  absl::Duration duration;
90
0
  if (!absl::ParseDuration(input, &duration)) {
91
0
    return absl::InvalidArgumentError("Failed to parse duration from string");
92
0
  }
93
0
  return duration;
94
0
}
95
96
0
absl::StatusOr<std::string> FormatDuration(absl::Duration duration) {
97
0
  CEL_RETURN_IF_ERROR(ValidateDuration(duration));
98
0
  return absl::FormatDuration(duration);
99
0
}
100
101
0
std::string DebugStringDuration(absl::Duration duration) {
102
0
  return absl::FormatDuration(duration);
103
0
}
104
105
0
absl::Status ValidateTimestamp(absl::Time timestamp) {
106
0
  if (timestamp < MinTimestamp()) {
107
0
    return absl::InvalidArgumentError(
108
0
        absl::StrCat("Timestamp \"", RawFormatTimestamp(timestamp),
109
0
                     "\" below minimum allowed timestamp \"",
110
0
                     RawFormatTimestamp(MinTimestamp()), "\""));
111
0
  }
112
0
  if (timestamp > MaxTimestamp()) {
113
0
    return absl::InvalidArgumentError(
114
0
        absl::StrCat("Timestamp \"", RawFormatTimestamp(timestamp),
115
0
                     "\" above maximum allowed timestamp \"",
116
0
                     RawFormatTimestamp(MaxTimestamp()), "\""));
117
0
  }
118
0
  return absl::OkStatus();
119
0
}
120
121
0
absl::StatusOr<absl::Time> ParseTimestamp(absl::string_view input) {
122
0
  absl::Time timestamp;
123
0
  std::string err;
124
0
  if (!absl::ParseTime(absl::RFC3339_full, input, absl::UTCTimeZone(),
125
0
                       &timestamp, &err)) {
126
0
    return err.empty() ? absl::InvalidArgumentError(
127
0
                             "Failed to parse timestamp from string")
128
0
                       : absl::InvalidArgumentError(absl::StrCat(
129
0
                             "Failed to parse timestamp from string: ", err));
130
0
  }
131
0
  CEL_RETURN_IF_ERROR(ValidateTimestamp(timestamp));
132
0
  return timestamp;
133
0
}
134
135
0
absl::StatusOr<std::string> FormatTimestamp(absl::Time timestamp) {
136
0
  CEL_RETURN_IF_ERROR(ValidateTimestamp(timestamp));
137
0
  return RawFormatTimestamp(timestamp);
138
0
}
139
140
0
std::string FormatNanos(int32_t nanos) {
141
0
  constexpr int32_t kNanosPerMillisecond = 1000000;
142
0
  constexpr int32_t kNanosPerMicrosecond = 1000;
143
144
0
  if (nanos % kNanosPerMillisecond == 0) {
145
0
    return absl::StrFormat("%03d", nanos / kNanosPerMillisecond);
146
0
  } else if (nanos % kNanosPerMicrosecond == 0) {
147
0
    return absl::StrFormat("%06d", nanos / kNanosPerMicrosecond);
148
0
  }
149
0
  return absl::StrFormat("%09d", nanos);
150
0
}
151
152
0
absl::StatusOr<std::string> EncodeDurationToJson(absl::Duration duration) {
153
  // Adapted from protobuf time_util.
154
0
  CEL_RETURN_IF_ERROR(ValidateDuration(duration));
155
0
  std::string result;
156
0
  int64_t seconds = absl::IDivDuration(duration, absl::Seconds(1), &duration);
157
0
  int64_t nanos = absl::IDivDuration(duration, absl::Nanoseconds(1), &duration);
158
159
0
  if (seconds < 0 || nanos < 0) {
160
0
    result = "-";
161
0
    seconds = -seconds;
162
0
    nanos = -nanos;
163
0
  }
164
165
0
  absl::StrAppend(&result, seconds);
166
0
  if (nanos != 0) {
167
0
    absl::StrAppend(&result, ".", FormatNanos(nanos));
168
0
  }
169
170
0
  absl::StrAppend(&result, "s");
171
0
  return result;
172
0
}
173
174
0
absl::StatusOr<std::string> EncodeTimestampToJson(absl::Time timestamp) {
175
  // Adapted from protobuf time_util.
176
0
  static constexpr absl::string_view kTimestampFormat = "%E4Y-%m-%dT%H:%M:%S";
177
0
  CEL_RETURN_IF_ERROR(ValidateTimestamp(timestamp));
178
  // Handle nanos and the seconds separately to match proto JSON format.
179
0
  absl::Time unix_seconds =
180
0
      absl::FromUnixSeconds(absl::ToUnixSeconds(timestamp));
181
0
  int64_t n = (timestamp - unix_seconds) / absl::Nanoseconds(1);
182
183
0
  std::string result =
184
0
      absl::FormatTime(kTimestampFormat, unix_seconds, absl::UTCTimeZone());
185
186
0
  if (n > 0) {
187
0
    absl::StrAppend(&result, ".", FormatNanos(n));
188
0
  }
189
190
0
  absl::StrAppend(&result, "Z");
191
0
  return result;
192
0
}
193
194
0
std::string DebugStringTimestamp(absl::Time timestamp) {
195
0
  return RawFormatTimestamp(timestamp);
196
0
}
197
198
}  // namespace cel::internal