Skip to content

Commit 3772f35

Browse files
committed
round up logic, and tests - some failing
1 parent 56884c0 commit 3772f35

File tree

2 files changed

+141
-3
lines changed

2 files changed

+141
-3
lines changed

Sources/SwiftVizScale/NiceValue.swift

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,14 +236,34 @@ public enum DateMagnitude: Equatable {
236236
case months
237237
/// Years.
238238
case years
239-
239+
240240
static let subsecondThreshold: PartialRangeUpTo<Double> = ..<1
241241
static let secondsThreshold: Range<Double> = 1 ..< 60
242242
static let minutesThreshold: Range<Double> = 60 ..< 60 * 60
243243
static let hoursThreshold: Range<Double> = 60 * 60 ..< 60 * 60 * 24
244244
static let daysThreshold: Range<Double> = 60 * 60 * 24 ..< 60 * 60 * 24 * 28
245245
static let monthsThreshold: Range<Double> = 60 * 60 * 24 * 28 ..< 60 * 60 * 24 * 365
246246
static let yearsThreshold: PartialRangeFrom<Double> = (60 * 60 * 24 * 365)...
247+
248+
/// The value of the date magnitude in seconds per increment.
249+
public var seconds: Double {
250+
switch self {
251+
case .subsecond:
252+
return 0
253+
case .seconds:
254+
return 1
255+
case .minutes:
256+
return DateMagnitude.minutesThreshold.lowerBound
257+
case .hours:
258+
return DateMagnitude.hoursThreshold.lowerBound
259+
case .days:
260+
return DateMagnitude.daysThreshold.lowerBound
261+
case .months:
262+
return DateMagnitude.monthsThreshold.lowerBound
263+
default:
264+
return DateMagnitude.yearsThreshold.lowerBound
265+
}
266+
}
247267

248268
public static func magnitudeOfDateRange(_ lhs: Date, _ rhs: Date) -> DateMagnitude {
249269
let dateExtentMagnitude = abs(lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate)
@@ -269,7 +289,7 @@ public enum DateMagnitude: Equatable {
269289
public extension Date {
270290
/// Returns a Date value rounded down to the next nice "date" value based on a calendar, and optionally the step size of the range.
271291
/// - Parameters:
272-
/// - magnitude: The magnitude to which to round
292+
/// - magnitude: The magnitude to which to round.
273293
/// - calendar: The calendar to use to compute the next lower value.
274294
/// - stepSize: The nice step size, in seconds, for the date increments.
275295
func round(magnitude: DateMagnitude, calendar: Calendar, stepSize: Double? = nil) -> Self? {
@@ -347,7 +367,22 @@ public extension Date {
347367
return components.date
348368
}
349369
}
350-
370+
371+
/// Returns a date based on the current value, rounded 'up' to the next incremental step value.
372+
/// - Parameters:
373+
/// - magnitude: The magnitude to which to round.
374+
/// - calendar: The calendar to use to compute the next lower value.
375+
/// - stepSize: The nice step size, in seconds, for the date increments.
376+
func roundUp(magnitude: DateMagnitude, calendar: Calendar, stepSize: Double? = nil) -> Self? {
377+
let incDate: Date
378+
if let stepSize = stepSize, stepSize >= magnitude.seconds {
379+
incDate = self+stepSize
380+
} else {
381+
incDate = self+magnitude.seconds
382+
}
383+
return incDate.round(magnitude: magnitude, calendar: calendar, stepSize: stepSize)
384+
}
385+
351386
/// Returns a nice step size for the magnitude of date range you provide.
352387
/// - Parameters:
353388
/// - step: The step size (in seconds) for the date increment.

Tests/SwiftVizScaleTests/DateNiceValueTests.swift

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,109 @@ final class DateNiceValueTests: XCTestCase {
179179
)
180180
}
181181

182+
func testCalendricalDateRoundUp_seconds() throws {
183+
let (formatter, now) = formatterAndNow()
184+
// starting position for comparison
185+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
186+
187+
let result_1 = now.roundUp(magnitude: .seconds, calendar: testCal, stepSize: 1)
188+
// step size of 1 is same result as not providing it for seconds
189+
XCTAssertEqual(formatter.string(from: result_1!), "2022-08-08T22:43:10.000Z")
190+
191+
let result_2 = now.roundUp(magnitude: .seconds, calendar: testCal, stepSize: 2)
192+
XCTAssertEqual(formatter.string(from: result_2!), "2022-08-08T22:43:10.000Z")
193+
194+
let result_5 = now.roundUp(magnitude: .seconds, calendar: testCal, stepSize: 5)
195+
XCTAssertEqual(formatter.string(from: result_5!), "2022-08-08T22:43:10.000Z")
196+
}
197+
198+
func testCalendricalDateRoundUp_minutes() throws {
199+
let (formatter, now) = formatterAndNow()
200+
// starting position for comparison
201+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
202+
203+
XCTAssertEqual(
204+
formatter.string(from: now.roundUp(magnitude: .minutes, calendar: testCal, stepSize: 1 * 60)!),
205+
"2022-08-08T22:44:00.000Z"
206+
)
207+
208+
XCTAssertEqual(
209+
formatter.string(from: now.roundUp(magnitude: .minutes, calendar: testCal, stepSize: 2 * 60)!),
210+
"2022-08-08T22:44:00.000Z"
211+
)
212+
213+
XCTAssertEqual(
214+
formatter.string(from: now.roundUp(magnitude: .minutes, calendar: testCal, stepSize: 5 * 60)!),
215+
"2022-08-08T22:45:00.000Z"
216+
)
217+
}
218+
219+
func testCalendricalDateRoundUp_hours() throws {
220+
let (formatter, now) = formatterAndNow()
221+
// starting position for comparison
222+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
223+
224+
XCTAssertEqual(
225+
formatter.string(from: now.roundUp(magnitude: .hours, calendar: testCal, stepSize: 1 * 60 * 60)!),
226+
"2022-08-08T23:00:00.000Z"
227+
)
228+
229+
XCTAssertEqual(
230+
formatter.string(from: now.round(magnitude: .hours, calendar: testCal, stepSize: 2 * 60 * 60)!),
231+
"2022-08-09T00:00:00.000Z"
232+
)
233+
234+
XCTAssertEqual(
235+
formatter.string(from: now.round(magnitude: .hours, calendar: testCal, stepSize: 6 * 60 * 60)!),
236+
"2022-08-09T00:00:00.000Z"
237+
)
238+
}
239+
240+
func testCalendricalDateRoundUp_days() throws {
241+
let (formatter, now) = formatterAndNow()
242+
// starting position for comparison
243+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
244+
245+
XCTAssertEqual(
246+
formatter.string(from: now.round(magnitude: .days, calendar: testCal, stepSize: 1 * 24 * 60 * 60)!),
247+
"2022-08-09T00:00:00.000Z"
248+
)
249+
250+
XCTAssertEqual(
251+
formatter.string(from: now.round(magnitude: .days, calendar: testCal, stepSize: 2 * 24 * 60 * 60)!),
252+
"2022-08-10T00:00:00.000Z"
253+
)
254+
255+
// NOTE(heckj): This is rounding down to the 7th day of the month rather than to a specific "day of week"
256+
// and we might want to choose the other path in terms of "nice values" that are spanning 7 day (week)
257+
// increments.
258+
XCTAssertEqual(
259+
formatter.string(from: now.round(magnitude: .days, calendar: testCal, stepSize: 7 * 24 * 60 * 60)!),
260+
"2022-08-15T00:00:00.000Z"
261+
)
262+
}
263+
264+
func testCalendricalDateRoundUp_months() throws {
265+
let (formatter, now) = formatterAndNow()
266+
// starting position for comparison
267+
XCTAssertEqual(formatter.string(from: now), "2022-08-08T22:43:09.011Z")
268+
269+
XCTAssertEqual(
270+
formatter.string(from: now.round(magnitude: .months, calendar: testCal, stepSize: 1 * 28 * 24 * 60 * 60)!),
271+
"2022-09-01T00:00:00.000Z"
272+
)
273+
274+
XCTAssertEqual(
275+
formatter.string(from: now.round(magnitude: .months, calendar: testCal, stepSize: 2 * 28 * 24 * 60 * 60)!),
276+
"2022-09-01T00:00:00.000Z"
277+
)
278+
279+
XCTAssertEqual(
280+
formatter.string(from: now.round(magnitude: .months, calendar: testCal, stepSize: 6 * 28 * 24 * 60 * 60)!),
281+
"2022-12-01T00:00:00.000Z"
282+
)
283+
}
284+
182285
func testNiceStepForDateMagnitudes_subseconds() throws {
183286
XCTAssertEqual(Date.niceStepForMagnitude(step: 0.10, magnitude: .subsecond), 0.1)
184287
XCTAssertEqual(Date.niceStepForMagnitude(step: 0.11, magnitude: .subsecond), 0.2)

0 commit comments

Comments
 (0)