SOLVED: Calculating Dates and Double

 Mar '23 I have a `struct` with amount and a date and then do some calculation to find the total amount based on a frequency of the amount ``````enum Frequency: String, CaseIterable, Equatable { case daily, weekly, fortnightly, fourWeekly, monthly, yearly } struct ExpenseItem: Identifiable, Hashable { var id = UUID() var name: String var amount: Double var dueDate: Date var frequency: Frequency var note: String? } let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy" let dailyDate = formatter.date(from: "17-03-1923")! let weeklyDate = formatter.date(from: "24-03-1923")! let fortnightDate = formatter.date(from: "24-03-1923")! let fourWeeklyDate = formatter.date(from: "31-03-1923")! let hwsDate = formatter.date(from: "02-04-1923")! let devDate = formatter.date(from: "12-04-1923")! var expenseItems = [ ExpenseItem(name: "Daily", amount: 3, dueDate: dailyDate, frequency: .daily), ExpenseItem(name: "Weekly", amount: 10.45, dueDate: weeklyDate, frequency: .weekly), ExpenseItem(name: "Fortnightly", amount: 12.5, dueDate: fortnightDate, frequency: .fortnightly), ExpenseItem(name: "Four weekly", amount: 100.45, dueDate: fourWeeklyDate, frequency: .fourWeekly), ExpenseItem(name: "Hacking with Swift +", amount: 20, dueDate: hwsDate, frequency: .monthly), ExpenseItem(name: "Developer Fee", amount: 100, dueDate: devDate, frequency: .yearly) ] // today = 18/03/23 will be using Date.now let now = formatter.date(from: "18-03-1923")! func totalItem() -> Double { let calendar = Calendar.current var total = 0.0 for item in expenseItems { switch item.frequency { case .daily: // amount times number of days to next pay day(Friday) + if not paid add following weeks // total = amount * (days to payDate) + (amount * 7 * number of payDate since dueDate) total += item.amount * 7 case .weekly: // zero until pay day is reached then the amount + (amount * number of payDate since dueDate) total += item.amount case .fortnightly: // the number day of week (eg Fridays) from `dueDate` minus two weeks ago divides number of a day of week (eg Fridays) in a two period // eg dueDate is 24 Mar then between 24 Mar and 10 Mar there two Fridays // Today date is 18 Mar then there has been one Fridays total += item.amount * (1 / 2) case .fourWeekly: // the number day of week (eg Fridays) from `dueDate` minus four weeks ago divides number of a day of week (eg Fridays) in a four week // eg dueDate is 31 Mar then between 31 Mar and 10 Mar there four Fridays // Today date is 18 Mar then there has been two Fridays total += item.amount * (2 / 4) case .monthly: // the number day of week (eg Fridays) from `dueDate` minus month ago divides number of a day of week (eg Fridays) in a month // eg dueDate is 3 Apr then between 3 Mar and 3 Apr there five Fridays // Today date is 18 Mar then there has been three Fridays total += item.amount * (3 / 5) case .yearly: // the number day of week (eg Fridays) from `dueDate` minus year ago divides number of a day of week (eg Fridays) in a year // eg dueDate is 3 Apr then between 3 Mar and 3 Apr there five Fridays // Today date is 18 Mar then there has been three Fridays total += item.amount * (49 / 52) } } return total } print(totalItem())`````` I have used "Magic" numbers but would like it to work out the numbers instead 2 Mar '23 Hi, Hopefully im not too late, i managed to work out the numbers and found an error on `.monthly`, between 3 Mar and 3 Apr there are four Fridays so there has been two Fridays up to 18 Mar, besides that everything should work as expected. ``````enum Frequency: String, CaseIterable, Equatable { case daily, weekly, fortnightly, fourWeekly, monthly, yearly } struct ExpenseItem: Identifiable, Hashable { var id = UUID() var name: String var amount: Double var dueDate: Date var frequency: Frequency var note: String? } let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy" let dailyDate = formatter.date(from: "17-03-1923")! let weeklyDate = formatter.date(from: "24-03-1923")! let fortnightDate = formatter.date(from: "24-03-1923")! let fourWeeklyDate = formatter.date(from: "31-03-1923")! let hwsDate = formatter.date(from: "03-04-1923")! let devDate = formatter.date(from: "12-04-1923")! var expenseItems = [ ExpenseItem(name: "Daily", amount: 3, dueDate: dailyDate, frequency: .daily), ExpenseItem(name: "Weekly", amount: 10.45, dueDate: weeklyDate, frequency: .weekly), ExpenseItem(name: "Fortnightly", amount: 12.5, dueDate: fortnightDate, frequency: .fortnightly), ExpenseItem(name: "Four weekly", amount: 100.45, dueDate: fourWeeklyDate, frequency: .fourWeekly), ExpenseItem(name: "Hacking with Swift +", amount: 20, dueDate: hwsDate, frequency: .monthly), ExpenseItem(name: "Developer Fee", amount: 100, dueDate: devDate, frequency: .yearly) ] // today = 18/03/23 will be using Date.now let now = formatter.date(from: "18-03-1923")! func totalItem() -> Double { let calendar = Calendar.current var total = 0.0 for item in expenseItems { switch item.frequency { case .daily: // amount times number of days to next pay day(Friday) + if not paid add following weeks // total = amount * (days to payDate) + (amount * 7 * number of payDate since dueDate) total += item.amount * daysUntilFriday(from: item.dueDate) + (item.amount * 7 * fridaysFrom(item.dueDate, to: now)) case .weekly: // zero until pay day is reached then the amount + (amount * number of payDate since dueDate) total += item.amount + (item.amount * fridaysFrom(item.dueDate, to: now)) case .fortnightly: // the number day of week (eg Fridays) from `dueDate` minus two weeks ago divides number of a day of week (eg Fridays) in a two period // eg dueDate is 24 Mar then between 24 Mar and 10 Mar there two Fridays // Today date is 18 Mar then there has been one Fridays let twoWeeksAgo = calendar.date(byAdding: Calendar.Component.weekOfMonth, value: -2, to: item.dueDate) total += item.amount * (fridaysFrom(twoWeeksAgo!, to: now) / fridaysFrom(twoWeeksAgo!, to: item.dueDate)) case .fourWeekly: // the number day of week (eg Fridays) from `dueDate` minus four weeks ago divides number of a day of week (eg Fridays) in a four week // eg dueDate is 31 Mar then between 31 Mar and 10 Mar there four Fridays // Today date is 18 Mar then there has been two Fridays let fourWeeksAgo = calendar.date(byAdding: Calendar.Component.weekOfMonth, value: -4, to: item.dueDate) total += item.amount * (fridaysFrom(fourWeeksAgo!, to: now) / fridaysFrom(fourWeeksAgo!, to: item.dueDate)) case .monthly: // the number day of week (eg Fridays) from `dueDate` minus month ago divides number of a day of week (eg Fridays) in a month // eg dueDate is 3 Apr then between 3 Mar and 3 Apr there five Fridays <-- between 3 Mar and 3 Apr there are four Fridays // Today date is 18 Mar then there has been three Fridays <-- there has been two Fridays let aMonthAgo = calendar.date(byAdding: Calendar.Component.month, value: -1, to: item.dueDate) total += item.amount * (fridaysFrom(aMonthAgo!, to: now) / fridaysFrom(aMonthAgo!, to: item.dueDate)) case .yearly: // the number day of week (eg Fridays) from `dueDate` minus year ago divides number of a day of week (eg Fridays) in a year // eg dueDate is 3 Apr then between 3 Mar and 3 Apr there five Fridays // Today date is 18 Mar then there has been three Fridays let aYearAgo = calendar.date(byAdding: Calendar.Component.year, value: -1, to: item.dueDate) total += item.amount * (fridaysFrom(aYearAgo!, to: now) / fridaysFrom(aYearAgo!, to: item.dueDate)) } } return total } func daysUntilFriday(from date: Date) -> Double { let calendar = Calendar.current let startOfDate = Calendar.current.startOfDay(for: date) let nextFriday = calendar.nextDate(after: startOfDate, matching: DateComponents.init(weekday: 6), matchingPolicy: .strict)! return (DateInterval(start: startOfDate, end: nextFriday).duration / 86400) + 1 } func fridaysFrom(_ date1: Date, to date2: Date) -> Double { let calendar = Calendar.current var fridays = 0.0 calendar.enumerateDates(startingAfter: date1, matching: DateComponents.init(weekday: 6), matchingPolicy: .strict) { result, exactMatch, stop in if result! > date2 { stop = true } else { fridays += 1 } } return fridays } print(totalItem())`````` 2 Mar '23 Thank you so much. I already spent two days and just got a very complicated code just for the `.daily`. I only done checked that one but works well (So marking as SOLVED). Will do the other when got time. Amended slightly (not your fault) I did say Friday however it might another day! and put it a Date extension ``````extension Date { func daysUntilPayDay(of weekday: Weekday) -> Double { let calendar = Calendar.current let startOfDate = Calendar.current.startOfDay(for: self) let nextPayDay = calendar.nextDate(after: startOfDate, matching: DateComponents.init(weekday: weekday.rawValue), matchingPolicy: .strict)! return (DateInterval(start: startOfDate, end: nextPayDay).duration / 86400) + 1 } func forPayDay(of weekday: Weekday, to date: Date) -> Double { let calendar = Calendar.current var days = 0.0 calendar.enumerateDates(startingAfter: self, matching: DateComponents.init(weekday: weekday.rawValue), matchingPolicy: .strict) { result, exactMatch, stop in if result! > date { stop = true } else { days += 1 } } return days } }`````` So the call is ``total += (item.amount * item.dueDate.daysUntilPayDay(of: .friday)) + (item.amount * item.dueDate.forPayDay(of: .friday, to: .now) * 7)`` Have an enum ``````enum Weekday: Int { case sunday = 1 case monday case tuesday case wednesday case thursday case friday case saturday }`````` 3

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

You are not logged in