Как рассчитать дни между 2 датами без 31 числа любого месяца
Основная цель:
Я хочу рассчитывать заработную плату на основе 30 дней в каждом месяце
в месяц с 31 он должен рассчитывать максимум 30 дней
let monthlyWage = someValue
let perDay : Double = Double(monthlyWage/30)
let components = calendar.dateComponents([.day], from:startDateDay, to: currentDateDay)
let daysPassed = Double(components.day! + 1)
let componentsForStatement = calendar.dateComponents([.day, .month], from: currentDateDay)
if ((componentsForStatement.month == 01) ||
(componentsForStatement.month == 03) ||
(componentsForStatement.month == 05) ||
(componentsForStatement.month == 07) ||
(componentsForStatement.month == 08) ||
(componentsForStatement.month == 10) ||
(componentsForStatement.month == 12)
) && componentsForStatement.day == 31 {
daysPassed -= 1
}
let currentMoney = Double(lround(perDay * daysPassed))
2 ответа
(я обновил оригинальный вопрос до ответов в комментариях)
в 2016 году
10.01.16 Jan ?? (max 31) 21
02.16 Feb +1 (max 29) 30
03.16 Mar -1 (max 31) 30
04.16 Apr +0 (max 30) 30
12.05.16 May +0 (max 31) 12
или в 2017 году
10.01.17 Jan ?? (max 31) 21
02.17 Feb +2 (max 28) 30
03.17 Mar -1 (max 31) 30
04.17 Apr +0 (max 30) 30
12.05.17 May +0 (max 31) 12
я реализовал это так:
- проверить, если начало до конца
- проверить, если в том же месяце -> рассчитать рабочие дни со средним
- в противном случае: извлечь первый месяц / месяц между / последним месяцем и сложить вместе
вот код:
import Foundation
func day(_ dateString:String) -> Date {
let dateStringFormatter = DateFormatter()
dateStringFormatter.dateFormat = "yyyy-MM-dd"
let d = dateStringFormatter.date(from: dateString)!
return Date(timeInterval:0, since:d)
}
func createDate(day: Int, month: Int, year: Int) -> Date {
let userCalendar = Calendar.current
var dateComponents = DateComponents()
dateComponents.day = day
dateComponents.month = month
dateComponents.year = year
return userCalendar.date(from: dateComponents)!
}
func printDate(date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
return formatter.string(from: date)
}
func check(from startDate: String, to endDate: String, expected: Int){
let days = daysBeetweenMax30(from: day(startDate), to: day(endDate))
let ok = expected == days ? "OK" : "!!"
let error = expected == days ? "" : "(current: \(days) expected: \(expected))"
print(" \(ok): \(startDate) -> \(endDate) => \(days) \(error)")
}
func daysInMonth(of date: Date) -> Int {
let userCalendar = Calendar.current
return userCalendar.range(of: .day, in: .month, for: date)!.count
}
func setDayInDate(day: Int, of date: Date) -> Date {
let userCalendar = Calendar.current
var components = userCalendar.dateComponents([.day, .month, .year], from: date)
components.day = day
return userCalendar.date(from: components)!
}
func daysBeetweenMax30(from startDate: Date, to endDate: Date) -> Int {
// print("start: \(printDate(date: startDate)) ende:\(printDate(date: endDate))")
let userCalendar = Calendar.current
// check order
guard userCalendar.compare(startDate, to: endDate, toGranularity: .day) != .orderedDescending else {
print("startDate should be before endDate")
return 0
}
// check if in same month
guard userCalendar.compare(startDate, to: endDate, toGranularity: .month) != .orderedSame else {
let components = userCalendar.dateComponents([.day], from:startDate, to: endDate)
let days = (components.day ?? 0) + 1
let calculatedDays = Double(30.0 * Double(days) / Double(daysInMonth(of: startDate)))
let averageDays = Int(calculatedDays.rounded(.toNearestOrAwayFromZero))
// print("same month")
// print("daysInMonth: \(daysInMonth(of: startDate))")
// print("days: \(days)")
// print( "calculatedDays: \(calculatedDays)")
return averageDays
// maximum 30 days - but need to adjust for february .....
// return min((components.day ?? 0) + 1,30)
}
let startDateEnd = setDayInDate(day: daysInMonth(of: startDate), of: startDate)
let startMonthDays = daysBeetweenMax30(from: startDate, to: startDateEnd)
let endDateStart = setDayInDate(day: 1, of: endDate)
let endMonthDays = daysBeetweenMax30(from: endDateStart, to: endDate)
let components = userCalendar.dateComponents([.month], from:startDateEnd, to: endDateStart)
let monthBetween = components.month ?? 0
// print("monthBetween: \(monthBetween)")
return startMonthDays + monthBetween*30 + endMonthDays
}
проверять:
check(from: "2017-05-01", to: "2017-04-01", expected: 0)
print("----")
check(from: "2017-05-01", to: "2017-05-10", expected: 10)
check(from: "2017-05-05", to: "2017-05-09", expected: 5)
check(from: "2017-05-16", to: "2017-05-20", expected: 5)
check(from: "2017-05-30", to: "2017-05-31", expected: 2)
print("----")
check(from: "2017-05-01", to: "2017-05-31", expected: 30)
check(from: "2017-02-01", to: "2017-02-28", expected: 30)
print("----")
check(from: "2017-02-01", to: "2017-02-27", expected: 29)
check(from: "2017-02-01", to: "2017-02-02", expected: 2)
check(from: "2017-02-01", to: "2017-02-13", expected: 14)
check(from: "2017-02-01", to: "2017-02-14", expected: 15)
check(from: "2017-02-01", to: "2017-02-18", expected: 19)
check(from: "2017-02-26", to: "2017-02-28", expected: 3)
print("----")
check(from: "2017-05-30", to: "2017-06-02", expected: 2+2)
check(from: "2017-02-26", to: "2017-03-03", expected: 3+3)
check(from: "2017-05-30", to: "2017-07-02", expected: 2+1*30+2)
check(from: "2017-05-30", to: "2017-08-02", expected: 2+2*30+2)
print("----")
check(from: "2017-01-10", to: "2017-01-31", expected: 21)
check(from: "2017-02-01", to: "2017-02-28", expected: 30)
check(from: "2017-03-01", to: "2017-03-31", expected: 30)
check(from: "2017-04-01", to: "2017-04-30", expected: 30)
check(from: "2017-05-01", to: "2017-05-12", expected: 12)
print("----")
check(from: "2017-01-10", to: "2017-05-12", expected: 123)
check(from: "2017-01-10", to: "2017-05-12", expected: 21+3*30+12)
check(from: "2016-01-10", to: "2016-05-12", expected: 123) // 2016 with leap year
print(" ----")
check(from: "2016-01-10", to: "2017-05-12", expected: 21+(12+3)*30+12)
результат:
startDate should be before endDate
OK: 2017-05-01 -> 2017-04-01 => 0
----
OK: 2017-05-01 -> 2017-05-10 => 10
OK: 2017-05-05 -> 2017-05-09 => 5
OK: 2017-05-16 -> 2017-05-20 => 5
OK: 2017-05-30 -> 2017-05-31 => 2
----
OK: 2017-05-01 -> 2017-05-31 => 30
OK: 2017-02-01 -> 2017-02-28 => 30
----
OK: 2017-02-01 -> 2017-02-27 => 29
OK: 2017-02-01 -> 2017-02-02 => 2
OK: 2017-02-01 -> 2017-02-13 => 14
OK: 2017-02-01 -> 2017-02-14 => 15
OK: 2017-02-01 -> 2017-02-18 => 19
OK: 2017-02-26 -> 2017-02-28 => 3
----
OK: 2017-05-30 -> 2017-06-02 => 4
OK: 2017-02-26 -> 2017-03-03 => 6
OK: 2017-05-30 -> 2017-07-02 => 34
OK: 2017-05-30 -> 2017-08-02 => 64
----
OK: 2017-01-10 -> 2017-01-31 => 21
OK: 2017-02-01 -> 2017-02-28 => 30
OK: 2017-03-01 -> 2017-03-31 => 30
OK: 2017-04-01 -> 2017-04-30 => 30
OK: 2017-05-01 -> 2017-05-12 => 12
----
OK: 2017-01-10 -> 2017-05-12 => 123
OK: 2017-01-10 -> 2017-05-12 => 123
OK: 2016-01-10 -> 2016-05-12 => 123
----
OK: 2016-01-10 -> 2017-05-12 => 483
Благодаря идеям @muescha я делаю этот код хорошим, чтобы не считать 31 день в месяце:
let components = calendar.dateComponents([.day], from:startContractDateDay, to: currentDateDay)
var daysPassed = Double(components.day! + 1)
var startDay = startContractDateDay
while startDay <= currentDateDay {
let startDayComponent = calendar.component(.day, from: startDay)
if startDayComponent == 31 {
daysPassed -= 1
}
startDay = calendar.date(byAdding: .day, value: 1, to: startDay)!
}