Skip to content

Commit 035c0c4

Browse files
authored
Add a initializer to Google_Protobuf_Duration with rounding control. (#1747)
Provide a new initializer with explicit rounding controls and map the other initializer though the single code paths.
1 parent 42195e7 commit 035c0c4

File tree

2 files changed

+60
-24
lines changed

2 files changed

+60
-24
lines changed

Sources/SwiftProtobuf/Google_Protobuf_Duration+Extensions.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,7 @@ extension Google_Protobuf_Duration: ExpressibleByFloatLiteral {
144144
/// that is interpreted as a duration in seconds, rounded to the nearest
145145
/// nanosecond.
146146
public init(floatLiteral value: Double) {
147-
let sd = trunc(value)
148-
let nd = round((value - sd) * TimeInterval(nanosPerSecond))
149-
let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd))
150-
self.init(seconds: s, nanos: n)
147+
self.init(rounding: value, rule: .toNearestOrAwayFromZero)
151148
}
152149
}
153150

@@ -156,10 +153,25 @@ extension Google_Protobuf_Duration {
156153
/// `TimeInterval` (measured in seconds), rounded to the nearest nanosecond.
157154
///
158155
/// - Parameter timeInterval: The `TimeInterval`.
156+
@available(*, deprecated, renamed: "init(rounding:rule:)")
159157
public init(timeInterval: TimeInterval) {
160-
let sd = trunc(timeInterval)
161-
let nd = round((timeInterval - sd) * TimeInterval(nanosPerSecond))
162-
let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd))
158+
self.init(rounding: timeInterval, rule: .toNearestOrAwayFromZero)
159+
}
160+
161+
/// Creates a new `Google_Protobuf_Duration` that is equal to the given
162+
/// `TimeInterval` (measured in seconds), rounded to the nearest nanosecond
163+
/// according to the given rounding rule.
164+
///
165+
/// - Parameters:
166+
/// - timeInterval: The `TimeInterval`.
167+
/// - rule: The rounding rule to use.
168+
public init(
169+
rounding timeInterval: TimeInterval,
170+
rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero
171+
) {
172+
let sd = Int64(timeInterval)
173+
let nd = ((timeInterval - Double(sd)) * TimeInterval(nanosPerSecond)).rounded(rule)
174+
let (s, n) = normalizeForDuration(seconds: sd, nanos: Int32(nd))
163175
self.init(seconds: s, nanos: n)
164176
}
165177

Tests/SwiftProtobufTests/Test_Duration.swift

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -259,50 +259,74 @@ final class Test_Duration: XCTestCase, PBTestHelpers {
259259
XCTAssertEqual("{\"optionalDuration\":\"100.000000001s\"}", try c.jsonString())
260260
}
261261

262-
func testInitializationByTimeIntervals() throws {
262+
func testInitializationRoundingTimeIntervals() throws {
263263
// Negative interval
264-
let t1 = Google_Protobuf_Duration(timeInterval: -123.456)
264+
let t1 = Google_Protobuf_Duration(rounding: -123.456)
265265
XCTAssertEqual(t1.seconds, -123)
266266
XCTAssertEqual(t1.nanos, -456_000_000)
267267

268268
// Full precision
269-
let t2 = Google_Protobuf_Duration(timeInterval: -123.999999999)
269+
let t2 = Google_Protobuf_Duration(rounding: -123.999999999)
270270
XCTAssertEqual(t2.seconds, -123)
271271
XCTAssertEqual(t2.nanos, -999_999_999)
272272

273-
// Round up
274-
let t3 = Google_Protobuf_Duration(timeInterval: -123.9999999994)
273+
// Value past percision, default and some explicit rules
274+
let t3 = Google_Protobuf_Duration(rounding: -123.9999999994)
275275
XCTAssertEqual(t3.seconds, -123)
276276
XCTAssertEqual(t3.nanos, -999_999_999)
277-
278-
// Round down
279-
let t4 = Google_Protobuf_Duration(timeInterval: -123.9999999996)
277+
let t3u = Google_Protobuf_Duration(rounding: -123.9999999994, rule: .up)
278+
XCTAssertEqual(t3u.seconds, -123)
279+
XCTAssertEqual(t3u.nanos, -999_999_999)
280+
let t3d = Google_Protobuf_Duration(rounding: -123.9999999994, rule: .down)
281+
XCTAssertEqual(t3d.seconds, -124)
282+
XCTAssertEqual(t3d.nanos, 0)
283+
284+
// Value past percision, default and some explicit rules
285+
let t4 = Google_Protobuf_Duration(rounding: -123.9999999996)
280286
XCTAssertEqual(t4.seconds, -124)
281287
XCTAssertEqual(t4.nanos, 0)
282-
283-
let t5 = Google_Protobuf_Duration(timeInterval: 0)
288+
let t4u = Google_Protobuf_Duration(rounding: -123.9999999996, rule: .up)
289+
XCTAssertEqual(t4u.seconds, -123)
290+
XCTAssertEqual(t4u.nanos, -999_999_999)
291+
let t4d = Google_Protobuf_Duration(rounding: -123.9999999996, rule: .down)
292+
XCTAssertEqual(t4d.seconds, -124)
293+
XCTAssertEqual(t4d.nanos, 0)
294+
295+
let t5 = Google_Protobuf_Duration(rounding: 0)
284296
XCTAssertEqual(t5.seconds, 0)
285297
XCTAssertEqual(t5.nanos, 0)
286298

287299
// Positive interval
288-
let t6 = Google_Protobuf_Duration(timeInterval: 123.456)
300+
let t6 = Google_Protobuf_Duration(rounding: 123.456)
289301
XCTAssertEqual(t6.seconds, 123)
290302
XCTAssertEqual(t6.nanos, 456_000_000)
291303

292304
// Full precision
293-
let t7 = Google_Protobuf_Duration(timeInterval: 123.999999999)
305+
let t7 = Google_Protobuf_Duration(rounding: 123.999999999)
294306
XCTAssertEqual(t7.seconds, 123)
295307
XCTAssertEqual(t7.nanos, 999_999_999)
296308

297-
// Round down
298-
let t8 = Google_Protobuf_Duration(timeInterval: 123.9999999994)
309+
// Value past percision, default and some explicit rules
310+
let t8 = Google_Protobuf_Duration(rounding: 123.9999999994)
299311
XCTAssertEqual(t8.seconds, 123)
300312
XCTAssertEqual(t8.nanos, 999_999_999)
301-
302-
// Round up
303-
let t9 = Google_Protobuf_Duration(timeInterval: 123.9999999996)
313+
let t8u = Google_Protobuf_Duration(rounding: 123.9999999994, rule: .up)
314+
XCTAssertEqual(t8u.seconds, 124)
315+
XCTAssertEqual(t8u.nanos, 0)
316+
let t8d = Google_Protobuf_Duration(rounding: 123.9999999994, rule: .down)
317+
XCTAssertEqual(t8d.seconds, 123)
318+
XCTAssertEqual(t8d.nanos, 999_999_999)
319+
320+
// Value past percision, default and some explicit rules
321+
let t9 = Google_Protobuf_Duration(rounding: 123.9999999996)
304322
XCTAssertEqual(t9.seconds, 124)
305323
XCTAssertEqual(t9.nanos, 0)
324+
let t9u = Google_Protobuf_Duration(rounding: 123.9999999996, rule: .up)
325+
XCTAssertEqual(t9u.seconds, 124)
326+
XCTAssertEqual(t9u.nanos, 0)
327+
let t9d = Google_Protobuf_Duration(rounding: 123.9999999996, rule: .down)
328+
XCTAssertEqual(t9d.seconds, 123)
329+
XCTAssertEqual(t9d.nanos, 999_999_999)
306330
}
307331

308332
func testGetters() throws {

0 commit comments

Comments
 (0)