Skip to content

Commit dddde17

Browse files
authored
using Daystamp when considering deadline (#510)
``Daystamp`` contains the logic for calculating the date (all of year, month, and day) that should be reported as the daystamp of a newly created datapoint, including accounting for the goal's deadline. This merge request uses Daystamp and this feature of it to replace the three instances of other areas in the app tackling this same calculation: in Today View extension, in Goal / add datapoint, and in Timer. Furthermore, three places with their own implementations all suffered the same logic error of not accounting for certain deadlines correctly - in particular deadlines just after midnight (midnight until 1 in the morning): ```swift if self.goal.deadline > 0 && currentHour! < 6 && currentHour! < self.goal.deadline/3600 { self.dateStepper.value = -1 } ``` If there was not a whole hour of difference, then the deadline was effectively ignored. Fixes #17 Fixes #228 -- There have been many reports of the app not handling deadlines well or properly. Commit 5f3278c introduces logic for handling the deadline in the Goal/add and Timer screens. Other, possibly related issues: #17, #35, #228, #450
1 parent 4e5c5eb commit dddde17

File tree

4 files changed

+43
-83
lines changed

4 files changed

+43
-83
lines changed

BeeKit/Daystamp.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,13 @@ public struct Daystamp: CustomStringConvertible, Strideable, Comparable, Equatab
4040

4141
self.init(year: year, month: month, day: day)
4242
}
43-
44-
init(fromDate date: Date, deadline: Int) {
43+
44+
/// Creates a Daystamp of having submitted a datapoint on a particular date given the goal's deadline
45+
///
46+
/// - Parameters:
47+
/// - date: a calendar date
48+
/// - deadline: a ``Goal/deadline``
49+
public init(fromDate date: Date, deadline: Int) {
4550
let secondsAfterMidnight =
4651
Daystamp.calendar.component(.hour, from: date) * 60 * 60
4752
+ Daystamp.calendar.component(.minute, from: date) * 60
@@ -104,12 +109,16 @@ public struct Daystamp: CustomStringConvertible, Strideable, Comparable, Equatab
104109

105110
// Trait: CustomStringConvertible
106111

112+
/// Daystamp formatted as a YYYYMMdd string
107113
public var description: String {
108114
return String(format: "%04d%02d%02d", year, month, day)
109115
}
110116

111117
// Trait: Strideable
112118

119+
/// how many days apart two daystamps are
120+
/// - Parameter other: another daystamp
121+
/// - Returns: number of days as distance between the daystamps
113122
public func distance(to other: Daystamp) -> Int {
114123
let selfDate = Daystamp.calendar.date(from: DateComponents(calendar: Daystamp.calendar, year: year, month: month, day: day))!
115124
let otherDate = Daystamp.calendar.date(from: DateComponents(calendar: Daystamp.calendar, year: other.year, month: other.month, day: other.day))!
@@ -147,4 +156,20 @@ public struct Daystamp: CustomStringConvertible, Strideable, Comparable, Equatab
147156

148157
// Trait: Hashable
149158
// This is generated automatically for structs by the compiler
159+
160+
161+
/// generates the daystamp as a string for use in urtext, considering both the submission date and goal's deadline
162+
/// - Parameters:
163+
/// - submissionDate: calendar date on which the datapoint would be submitted
164+
/// - goal: the goal for which the urtext is to be created
165+
/// - Returns: string of the daystamp, suitable for use in urtext
166+
public static func makeUrtextDaystamp(submissionDate: Date = Date(), goal: Goal) -> String {
167+
let daystamp = Daystamp(fromDate: submissionDate,
168+
deadline: goal.deadline)
169+
170+
return String(format: "%04d %02d %02d",
171+
daystamp.year,
172+
daystamp.month,
173+
daystamp.day)
174+
}
150175
}

BeeSwift/GoalViewController.swift

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -209,30 +209,7 @@ class GoalViewController: UIViewController, UIScrollViewDelegate, DatapointTabl
209209
self.dateStepper.tintColor = UIColor.Beeminder.gray
210210
dataEntryView.addSubview(self.dateStepper)
211211
self.dateStepper.addTarget(self, action: #selector(GoalViewController.dateStepperValueChanged), for: .valueChanged)
212-
self.dateStepper.value = 0
213-
214-
// if the goal's deadline is after midnight, and it's after midnight,
215-
// but before the deadline,
216-
// default to entering data for the "previous" day.
217-
let now = Date()
218-
let calendar = Calendar.current
219-
let components = (calendar as NSCalendar).components([.hour, .minute], from: now)
220-
let currentHour = components.hour
221-
if self.goal.deadline > 0 && currentHour! < 6 && currentHour! < self.goal.deadline/3600 {
222-
self.dateStepper.value = -1
223-
}
224-
225-
// if the goal's deadline is before midnight and has already passed for this calendar day, default to entering data for the "next" day
226-
if self.goal.deadline < 0 {
227-
let deadlineSecondsAfterMidnight = 24*3600 + self.goal.deadline
228-
let deadlineHour = deadlineSecondsAfterMidnight/3600
229-
let deadlineMinute = (deadlineSecondsAfterMidnight % 3600)/60
230-
let currentMinute = components.minute
231-
if deadlineHour < currentHour! ||
232-
(deadlineHour == currentHour! && deadlineMinute < currentMinute!) {
233-
self.dateStepper.value = 1
234-
}
235-
}
212+
self.dateStepper.value = Self.makeInitialDateStepperValue(for: goal)
236213

237214
self.dateStepper.snp.makeConstraints { (make) -> Void in
238215
make.top.equalTo(self.dateTextField.snp.bottom).offset(elementSpacing)
@@ -513,6 +490,15 @@ class GoalViewController: UIViewController, UIScrollViewDelegate, DatapointTabl
513490
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
514491
return self.goalImageView
515492
}
493+
494+
private static func makeInitialDateStepperValue(date: Date = Date(), for goal: Goal) -> Double {
495+
let daystampAccountingForTheGoalsDeadline = Daystamp(fromDate: date,
496+
deadline: goal.deadline)
497+
let daystampAssumingMidnightDeadline = Daystamp(fromDate: date,
498+
deadline: 0)
499+
500+
return Double(daystampAccountingForTheGoalsDeadline.distance(to: daystampAssumingMidnightDeadline))
501+
}
516502

517503
// MARK: - SFSafariViewControllerDelegate
518504

BeeSwift/TimerViewController.swift

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -143,33 +143,7 @@ class TimerViewController: UIViewController {
143143
}
144144

145145
func urtext() -> String {
146-
// if the goal's deadline is after midnight, and it's after midnight,
147-
// but before the deadline,
148-
// default to entering data for the "previous" day.
149-
let now = Date()
150-
var offset: Double = 0
151-
let calendar = Calendar.current
152-
let components = (calendar as NSCalendar).components([.hour, .minute], from: now)
153-
let currentHour = components.hour
154-
if self.goal.deadline > 0 && currentHour! < 6 && self.goal.deadline/3600 < currentHour! {
155-
offset = -1
156-
}
157-
158-
// if the goal's deadline is before midnight and has already passed for this calendar day, default to entering data for the "next" day
159-
if self.goal.deadline < 0 {
160-
let deadlineSecondsAfterMidnight = 24*3600 + self.goal.deadline
161-
let deadlineHour = deadlineSecondsAfterMidnight/3600
162-
let deadlineMinute = (deadlineSecondsAfterMidnight % 3600)/60
163-
let currentMinute = components.minute
164-
if deadlineHour < currentHour! ||
165-
(deadlineHour == currentHour! && deadlineMinute < currentMinute!) {
166-
offset = 1
167-
}
168-
}
169-
170-
let formatter = DateFormatter()
171-
formatter.locale = Locale(identifier: "en_US")
172-
formatter.dateFormat = "d"
146+
let urtextDaystamp = Daystamp.makeUrtextDaystamp(submissionDate: Date(), goal: goal)
173147

174148
let value: Double
175149

@@ -182,7 +156,7 @@ class TimerViewController: UIViewController {
182156

183157
let comment = "Automatically entered from iOS timer interface"
184158

185-
return "\(formatter.string(from: Date(timeIntervalSinceNow: offset*24*3600))) \(value) \"\(comment)\""
159+
return "\(urtextDaystamp) \(value) \"\(comment)\""
186160
}
187161

188162
@objc func addDatapointButtonPressed() {

BeeSwiftToday/TodayTableViewCell.swift

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -125,36 +125,11 @@ class TodayTableViewCell: UITableViewCell {
125125
hud.mode = .indeterminate
126126
self.addDataButton.isUserInteractionEnabled = false
127127

128-
// if the goal's deadline is after midnight, and it's after midnight,
129-
// but before the deadline,
130-
// default to entering data for the "previous" day.
131-
let now = Date()
132-
var offset: Double = 0
133-
let calendar = Calendar.current
134-
let components = (calendar as NSCalendar).components([.hour, .minute], from: now)
135-
let currentHour = components.hour
136-
let goalDeadline = goal.deadline
137-
if goalDeadline > 0 && currentHour! < 6 && goalDeadline/3600 < currentHour! {
138-
offset = -1
139-
}
140-
141-
// if the goal's deadline is before midnight and has already passed for this calendar day, default to entering data for the "next" day
142-
if goalDeadline < 0 {
143-
let deadlineSecondsAfterMidnight = 24*3600 + goalDeadline
144-
let deadlineHour = deadlineSecondsAfterMidnight/3600
145-
let deadlineMinute = (deadlineSecondsAfterMidnight % 3600)/60
146-
let currentMinute = components.minute
147-
if deadlineHour < currentHour! ||
148-
(deadlineHour == currentHour! && deadlineMinute < currentMinute!) {
149-
offset = 1
150-
}
151-
}
152-
153-
let formatter = DateFormatter()
154-
formatter.locale = Locale(identifier: "en_US")
155-
formatter.dateFormat = "d"
128+
let urtextDaystamp = Daystamp.makeUrtextDaystamp(submissionDate: Date(), goal: goal)
129+
let value = Int(self.valueStepper.value)
130+
let comment = "Added via iOS widget"
156131

157-
let params = ["urtext": "\(formatter.string(from: Date(timeIntervalSinceNow: offset*24*3600))) \(Int(self.valueStepper.value)) \"Added via iOS widget\"", "requestid": UUID().uuidString]
132+
let params = ["urtext": "\(urtextDaystamp) \(value) \"\(comment)\"", "requestid": UUID().uuidString]
158133
let slug = goal.slug
159134

160135
Task { @MainActor in

0 commit comments

Comments
 (0)