Swift Правильный расчет времени как пропорции дня - PullRequest
1 голос
/ 07 февраля 2020

Я пытаюсь рассчитать долю дня, к которой относится указанное c время. Например, 06:00 - это 0,25, 18:00 - 0,75 и т. Д. c.

Я оцениваю ряд дат типа «Дата», созданных в timeZone = «GMT». Подпрограмма ниже работает отлично. Однако, когда я оцениваю время после 23:00 для дат в летнее время, тогда вычисление идет не так, как время оценивается как следующий день (например, 23:08 оценивается как 00:08)

Есть ли любой способ, который я могу узнать, когда переход от GMT к DST переносит дату на следующий день? Затем я могу соответствующим образом скорректировать расчет.

Моя функция для определения пропорции, которую представляет время ввода, равна:

func getTimeAsProportionOfDay(time: Date) -> Double {
    //calculates the amount of day, between 0 and 1 given the input date

    let calendar =  Calendar.current
    let hours = calendar.component(.hour, from: time)
    let minutes = calendar.component(.minute, from: time)
    let seconds = calendar.component(.second, from: time)
    let totalSeconds = Double(hours * 60 * 60 + minutes * 60 + seconds)

    return Double(totalSeconds) / Double(secondsInDay)
}

Кроме того, я знаю, что моя константа secondsInDay (= 24 * 60 * 60) может быть технически неверно, но я не уверен, какой системной константой заменить его.

Спасибо.

Ответы [ 3 ]

1 голос
/ 07 февраля 2020

Вам просто нужно получить день после первоначальной даты и вычесть секунду. Затем подсчитайте количество секунд в этой дате, используя метод календаря

func ordinality(of smaller: Calendar.Component, in larger: Calendar.Component, for date: Date) -> Int?

. С некоторыми помощниками вы можете упростить свою жизнь

extension Date {
    var dayAfter: Date { Calendar.current.date(byAdding: .day, value: 1, to: noon)!}
    var noon: Date { Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! }
    var startOfDay: Date { Calendar.current.startOfDay(for: self) }
    var endOfDay: Date { Calendar.current.date(byAdding: .init(second: -1), to: dayAfter.startOfDay)! }
}

Тестирование endOfDay

Date().endOfDay  // "Feb 7, 2020 at 11:59 PM"

И ваш метод:

func getTimeAsProportionOfDay(time: Date) -> Double {
    // discarding the fractional seconds
    let time = Calendar.current.date(bySetting: .nanosecond, value: 0, of: time)!
    return Double(Calendar.current.ordinality(of: .second, in: .day, for: time)!-1) /
        Double(Calendar.current.ordinality(of: .second, in: .day, for: time.endOfDay)!-1)
}

Тестирование игровой площадки:

let date = DateComponents(calendar: .current, year: 2020, month: 2, day: 7, hour: 23, minute: 08).date!
date.endOfDay
let result = getTimeAsProportionOfDay(time: date)  // 0.9639000451394113
1 голос
/ 07 февраля 2020

Вообще говоря, я бы сделал что-то вроде этого:

let date = Date()

var dayStart = Date()
var dayDuration: TimeInterval = 0

Calendar.current.dateInterval(of: .day, start: &dayStart, interval: &dayDuration, for: date)

let timeInterval = date.timeIntervalSince(dayStart)
let percentage = timeInterval / dayDuration

print(percentage)
0 голосов
/ 10 февраля 2020

Все, спасибо за вашу помощь. Я думаю, что нашел решение этой проблемы, используя фиксированное начало дня для сравнения.

func getTimeAsProportionOfDay(time: Date) -> Double {
    //calculates the amount of day, between 0 and 1 given the input date


    if Int(time.timeIntervalSince(tides.tideDate)) > secondsInDay { //time has been moved to next day by BST change) so return 1 for gradient
        return 1.0
    } else {/// this was my original code
        let calendar =  Calendar.current
        let hours = calendar.component(.hour, from: time)
        let minutes = calendar.component(.minute, from: time)
        let seconds = calendar.component(.second, from: time)
        let totalSeconds = Double(hours * 60 * 60 + minutes * 60 + seconds)

        return Double(totalSeconds) / Double(secondsInDay)
    }
}
...