Skip to content

Commit 56884c0

Browse files
committed
verifying rounding with step size calculations included
1 parent b767f68 commit 56884c0

File tree

2 files changed

+110
-3
lines changed

2 files changed

+110
-3
lines changed

Sources/SwiftVizScale/NiceValue.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ public extension Date {
285285
case .seconds:
286286
components.setValue(0, for: .nanosecond)
287287
if let stepSize = stepSize, let seconds = components.second {
288-
let valueRoundedByStep = floor(Double(seconds) / stepSize)
288+
let valueRoundedByStep = floor(Double(seconds) / stepSize) * stepSize
289289
components.setValue(Int(valueRoundedByStep), for: .second)
290290
}
291291
assert(components.isValidDate)
@@ -294,7 +294,8 @@ public extension Date {
294294
components.setValue(0, for: .nanosecond)
295295
components.setValue(0, for: .second)
296296
if let stepSize = stepSize, let minutes = components.minute {
297-
let valueRoundedByStep = floor(Double(minutes) / stepSize * 60)
297+
let stepSizeInMinutes = stepSize / 60
298+
let valueRoundedByStep = floor(Double(minutes) / stepSizeInMinutes) * stepSizeInMinutes
298299
components.setValue(Int(valueRoundedByStep), for: .minute)
299300
}
300301
assert(components.isValidDate)
@@ -303,13 +304,23 @@ public extension Date {
303304
components.setValue(0, for: .nanosecond)
304305
components.setValue(0, for: .second)
305306
components.setValue(0, for: .minute)
307+
if let stepSize = stepSize, let hours = components.hour {
308+
let stepSizeInHours = stepSize / (60 * 60)
309+
let valueRoundedByStep = floor(Double(hours) / stepSizeInHours) * stepSizeInHours
310+
components.setValue(Int(valueRoundedByStep), for: .hour)
311+
}
306312
assert(components.isValidDate)
307313
return components.date
308314
case .days:
309315
components.setValue(0, for: .nanosecond)
310316
components.setValue(0, for: .second)
311317
components.setValue(0, for: .minute)
312318
components.setValue(0, for: .hour)
319+
if let stepSize = stepSize, let days = components.day {
320+
let stepSizeInDays = stepSize / (24 * 60 * 60)
321+
let valueRoundedByStep = floor(Double(days) / stepSizeInDays) * stepSizeInDays
322+
components.setValue(Int(valueRoundedByStep), for: .day)
323+
}
313324
assert(components.isValidDate)
314325
return components.date
315326
case .months:
@@ -318,6 +329,11 @@ public extension Date {
318329
components.setValue(0, for: .minute)
319330
components.setValue(0, for: .hour)
320331
components.setValue(1, for: .day)
332+
if let stepSize = stepSize, let months = components.month {
333+
let stepSizeInMonths = stepSize / (28 * 24 * 60 * 60)
334+
let valueRoundedByStep = floor(Double(months) / stepSizeInMonths) * stepSizeInMonths
335+
components.setValue(Int(valueRoundedByStep), for: .month)
336+
}
321337
assert(components.isValidDate)
322338
return components.date
323339
case .years:

Tests/SwiftVizScaleTests/DateNiceValueTests.swift

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ final class DateNiceValueTests: XCTestCase {
4444
}
4545

4646
func testCalendricalDateRounding() throws {
47-
print(testCal.timeZone)
4847
let (formatter, now) = formatterAndNow()
4948
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
5049
XCTAssertEqual(now.timeIntervalSinceReferenceDate, 681_691_389.011)
@@ -88,6 +87,98 @@ final class DateNiceValueTests: XCTestCase {
8887
)
8988
}
9089

90+
func testCalendricalDateRoundingSecondsWithStepsizes() throws {
91+
let (formatter, now) = formatterAndNow()
92+
// starting position for comparison
93+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
94+
95+
let result_1 = now.round(magnitude: .seconds, calendar: testCal, stepSize: 1)
96+
// step size of 1 is same result as not providing it for seconds
97+
XCTAssertEqual(now.round(magnitude: .seconds, calendar: testCal), now.round(magnitude: .seconds, calendar: testCal, stepSize: 1))
98+
XCTAssertEqual(formatter.string(from: result_1!), "2022-08-08T22:43:09.000Z")
99+
100+
let result_2 = now.round(magnitude: .seconds, calendar: testCal, stepSize: 2)
101+
XCTAssertEqual(formatter.string(from: result_2!), "2022-08-08T22:43:08.000Z")
102+
103+
let result_5 = now.round(magnitude: .seconds, calendar: testCal, stepSize: 5)
104+
XCTAssertEqual(formatter.string(from: result_5!), "2022-08-08T22:43:05.000Z")
105+
}
106+
107+
func testCalendricalDateRoundingMinutesWithStepsizes() throws {
108+
let (formatter, now) = formatterAndNow()
109+
// starting position for comparison
110+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
111+
112+
XCTAssertEqual(now.round(magnitude: .minutes, calendar: testCal), now.round(magnitude: .minutes, calendar: testCal, stepSize: 60))
113+
114+
XCTAssertEqual(
115+
formatter.string(from: now.round(magnitude: .minutes, calendar: testCal, stepSize: 2 * 60)!),
116+
"2022-08-08T22:42:00.000Z"
117+
)
118+
119+
XCTAssertEqual(
120+
formatter.string(from: now.round(magnitude: .minutes, calendar: testCal, stepSize: 5 * 60)!),
121+
"2022-08-08T22:40:00.000Z"
122+
)
123+
}
124+
125+
func testCalendricalDateRoundingHoursWithStepsizes() throws {
126+
let (formatter, now) = formatterAndNow()
127+
// starting position for comparison
128+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
129+
130+
XCTAssertEqual(now.round(magnitude: .hours, calendar: testCal), now.round(magnitude: .hours, calendar: testCal, stepSize: 60 * 60))
131+
132+
XCTAssertEqual(
133+
formatter.string(from: now.round(magnitude: .hours, calendar: testCal, stepSize: 2 * 60 * 60)!),
134+
"2022-08-08T22:00:00.000Z"
135+
)
136+
137+
XCTAssertEqual(
138+
formatter.string(from: now.round(magnitude: .hours, calendar: testCal, stepSize: 6 * 60 * 60)!),
139+
"2022-08-08T18:00:00.000Z"
140+
)
141+
}
142+
143+
func testCalendricalDateRoundingDaysWithStepsizes() throws {
144+
let (formatter, now) = formatterAndNow()
145+
// starting position for comparison
146+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
147+
148+
XCTAssertEqual(now.round(magnitude: .days, calendar: testCal), now.round(magnitude: .days, calendar: testCal, stepSize: 24 * 60 * 60))
149+
150+
XCTAssertEqual(
151+
formatter.string(from: now.round(magnitude: .days, calendar: testCal, stepSize: 2 * 24 * 60 * 60)!),
152+
"2022-08-08T00:00:00.000Z"
153+
)
154+
155+
// NOTE(heckj): This is rounding down to the 7th day of the month rather than to a specific "day of week"
156+
// and we might want to choose the other path in terms of "nice values" that are spanning 7 day (week)
157+
// increments.
158+
XCTAssertEqual(
159+
formatter.string(from: now.round(magnitude: .days, calendar: testCal, stepSize: 7 * 24 * 60 * 60)!),
160+
"2022-08-07T00:00:00.000Z"
161+
)
162+
}
163+
164+
func testCalendricalDateRoundingMonthsWithStepsizes() throws {
165+
let (formatter, now) = formatterAndNow()
166+
// starting position for comparison
167+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
168+
169+
XCTAssertEqual(now.round(magnitude: .months, calendar: testCal), now.round(magnitude: .months, calendar: testCal, stepSize: 28 * 24 * 60 * 60))
170+
171+
XCTAssertEqual(
172+
formatter.string(from: now.round(magnitude: .months, calendar: testCal, stepSize: 2 * 28 * 24 * 60 * 60)!),
173+
"2022-08-01T00:00:00.000Z"
174+
)
175+
176+
XCTAssertEqual(
177+
formatter.string(from: now.round(magnitude: .months, calendar: testCal, stepSize: 6 * 28 * 24 * 60 * 60)!),
178+
"2022-06-01T00:00:00.000Z"
179+
)
180+
}
181+
91182
func testNiceStepForDateMagnitudes_subseconds() throws {
92183
XCTAssertEqual(Date.niceStepForMagnitude(step: 0.10, magnitude: .subsecond), 0.1)
93184
XCTAssertEqual(Date.niceStepForMagnitude(step: 0.11, magnitude: .subsecond), 0.2)

0 commit comments

Comments
 (0)