在 Swift 中将十进制时间转换为小时和分钟



我是编程新手,这是我的第一个程序和问题。我正在尝试编写一个函数,该函数将简单地将十进制时间转换为小时和分钟。我正在删除小时并将十进制分钟乘以 60,然后将两者重新加在一起作为字符串。我需要在我的程序中使用这个工具几次,因此功能。使用此函数的计算很简单,但我得到的结果很奇怪。如果我将"计划启动燃料"维持为 450 并调整"最小土地津贴",我会得到以下结果,

185 returns 1:28
182 returns 1:29
181 returns 1:30
180 returns 2:30
179 returns 2:30
175 returns 2:32

正确答案是 1:00 的数字。我不明白为什么该程序似乎在 180 点处的结果增加了一个小时。我相信有比我使用的更好的方法来完成此计算,但是如果您能提供帮助,我将不胜感激,知道哪个部分导致了错误以及原因。我试过什么?...万事!如果你对一个7岁的孩子提出你的答案,我可能有机会理解。谢谢。

import UIKit
import Foundation
func decimalHoursConv (hours : Double) -> (_hrs:String, mins:String) {
let remainder = hours.truncatingRemainder(dividingBy: 1) * 60
let mins = (String(format: "%.0f", remainder))
let hrs = (String(format: "%.0f", hours))
return (hrs, mins)
}
var plannedStartFuel = Double (0)
var minLandAllowance = Double (0)
var flyingTimeToMLA = Double(0)
plannedStartFuel = 450
minLandAllowance = 180
flyingTimeToMLA = ((plannedStartFuel - minLandAllowance) / 3)/60
let MLAtime = (decimalHoursConv(hours: flyingTimeToMLA))
print ("Flight Time To MLA =", MLAtime.0,"hrs",MLAtime.1,"mins")

我可能会建议根本不费心计算小时和分钟,而是让DateComponentsFormatter这样做,为您创建最终字符串。

例如:

let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.allowedUnits = [.hour, .minute]
return formatter
}()

然后提供这个格式化程序以秒为单位的经过时间(一个TimeInterval,它只是Double的别名):

let remaining: TimeInterval = 90 * 60 // e.g. 90 minutes represented in seconds
if let result = formatter.string(from: remaining) {
print(result)
}

在讲英语的设备上,这将产生:

1 小时 30 分钟

这种方法的优点是,它不仅使您摆脱了自己手动计算小时和分钟的业务,而且结果也很容易本地化。因此,如果你开始本地化你的应用,这个字符串也会为你自动本地化,你不需要进一步的工作。例如,如果您将德语添加到应用本地化中,则美国用户仍会看到上述内容,但在德国设备上,它将产生:

1 尤物和 30 分钟


如果你想让它说出还剩多少时间,请设置includesTimeRemainingPhrase

let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.includesTimeRemainingPhrase = true
formatter.allowedUnits = [.hour, .minute]
return formatter
}()

这将产生:

剩余 1 小时 30 分钟

如果你想要一个"hh:mm"类型的表示:

let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
formatter.allowedUnits = [.hour, .minute]
return formatter
}()

将产生:

01:30


顺便说一下,如果您想知道如何获取商和余数(特别是避免示例中不需要的舍入),请参阅truncatingRemainder(dividingBy:)文档,其中说:

返回此值的余数除以给定值,使用 截断除法。

使用浮点值执行截断除法会导致 截断的整数商和余数。对于值xy和 它们的截断整数商q,余数r满足x == y * q + r.

下面的示例计算除法的截断余数 8.625 x 0.75:

let x = 8.625
print(x / 0.75)
// Prints "11.5"
let q = (x / 0.75).rounded(.towardZero)
// q == 11.0
let r = x.truncatingRemainder(dividingBy: 0.75)
// r == 0.375
let x1 = 0.75 * q + r
// x1 == 8.625

如果此值和other都是有限数,则截断 余数与此值具有相同的符号,并且在 比other.truncatingRemainder(dividingBy:)方法 总是准确的。

我可能倾向于写一个浮点演绎版quotientAndRemainder

extension FloatingPoint {
func quotientAndRemainder(dividingBy divisor: Self) -> (Self, Self) {
let q = (self / divisor).rounded(.towardZero)
let r = truncatingRemainder(dividingBy: divisor)
return (q, r)
}
}

然后,在您的示例中,使用该方法:

func decimalHoursConv(hours input: Double) -> (_hrs: String, mins: String) {
let (q, r) = input.quotientAndRemainder(dividingBy: 1)
let minutes = abs((r * 60).rounded(.towardZero))
let mins = String(format: "%.0f", minutes)
let hrs = String(format: "%.0f", q)
return (hrs, mins)
}

请注意,如果输入的值为负,例如 -1.75 小时,则商为 -1 小时,余数为 -0.75 小时。这在数学上是正确的,但显然,如果您要在 UI 中显示这些,您可能希望显示负值的小时数,但也不显示分钟。所以我执行了其余部分的abs

此外,您可能希望向下舍入分钟数(以便 1.9999 小时不报告 1:60,而是 1:59)。因此,我将余数乘以 60 的乘积四舍五入(为零)。

底线,如果你真的想计算分钟和秒,请随意,但如果只是为了创建一个字符串表示,你可以考虑让DateComponentFormatter为你做这件事。它正确处理正值和负值、本地化字符串等。

编辑我意识到您想知道您的方法不起作用的地方。 这是一个四舍五入的问题,在将其传递给String(format:)之前尝试 roundindhours

func decimalHoursConv (hours : Double) -> (_hrs:String, mins:String) {
let remainder = hours.truncatingRemainder(dividingBy: 1) * 60
let mins = (String(format: "%.0f", remainder))
let hours = hours.rounded(.towardZero)
let hrs = (String(format: "%.0f", hours))
return (hrs, mins)
}

它给出:

var value = (450.0-185.0)/3
decimalHoursConv(hours: value/60) // (_hrs "1", mins "28")
value = (450.0-182.0)/3
decimalHoursConv(hours: value/60) // (_hrs "1", mins "29")
value = (450.0-181.0)/3
decimalHoursConv(hours: value/60) // (_hrs "1", mins "30")
value = (450.0-180.0)/3
decimalHoursConv(hours: value/60) // (_hrs "1", mins "30")
value = (450.0-179.0)/3
decimalHoursConv(hours: value/60) // (_hrs "1", mins "30")
value = (450.0-175.0)/3
decimalHoursConv(hours: value/60) // (_hrs "1", mins "32")

但仍然如果你使用的是 Swift,你应该使用 Measurement

func convertToHoursAndMinutes(_ value: Double) -> DateComponents {
let unitMeasurement = Measurement(value: value, unit: UnitDuration.minutes)
let hours = unitMeasurement.converted(to: .hours).value
let decimalPart = hours.truncatingRemainder(dividingBy: 1)
let decimalPartMeasurement = Measurement(value: decimalPart, unit: UnitDuration.hours)
let decimalPartMeasurementInMinutes = decimalPartMeasurement.converted(to: .minutes)
let minutes = decimalPartMeasurementInMinutes.value.rounded(.toNearestOrEven)
return DateComponents(hour: Int(hours), minute: Int(minutes))
}

用法:

var value = (450.0-185.0)/3 // 88.33333333333333
convertToHoursAndMinutes(value) // hour: 1 minute: 28 isLeapMonth: false 
value = (450.0-182.0)/3 // 89.33333333333333
convertToHoursAndMinutes(value) // hour: 1 minute: 29 isLeapMonth: false 
value = (450.0-181.0)/3 // 89.66666666666667
convertToHoursAndMinutes(value) // hour: 1 minute: 30 isLeapMonth: false 
value = (450.0-180.0)/3 // 90
convertToHoursAndMinutes(value) // hour: 1 minute: 30 isLeapMonth: false 
value = (450.0-179.0)/3 // 90.33333333333333
convertToHoursAndMinutes(value) // hour: 1 minute: 30 isLeapMonth: false 
value = (450.0-175.0)/3 // 91.66666666666667
convertToHoursAndMinutes(value) // hour: 1 minute: 32 isLeapMonth: false

请注意,如果您愿意,可以随时使用元组而不是DateComponents元组。

String格式化程序四舍五入。

您可以使用Double上的.rounded(.down)将它们向下舍入。(或您需要的其他规则)

let number = (179.0/60.0) // 2.983333333333333
String(format: "%.0f", number) // returns 3
number.rounded(.up) // returns 3
number.rounded(.down) // returns 2

首先,您应该构建数据。接下来,如果您不打算显示分数,则无需将值格式化为 Double。因此,您可以简单地将双精度转换为整数。


struct FlightPlan {
let plannedStartFuel: Double
let minimumLandAllowance: Double
}

extension FlightPlan {
var mlaTime: (hours: Int, minutes: Int) {
let hours = (plannedStartFuel - minimumLandAllowance) / 180
return (Int(hours), Int(modf(hours).1 * 60))
}
}

在向用户显示时间时,您应该使用DateComponentsFormatter

extension Formatter {
static let time: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.calendar?.locale = .init(identifier: "en_US_POSIX")
formatter.unitsStyle = .brief
formatter.allowedUnits = [.hour,.minute]
return formatter
}()
}

extension FlightPlan {
var mlaTimeDescrition: String {
return "Flight Time To MLA = " + Formatter.time.string(from: .init(hour: mlaTime.hours, minute: mlaTime.minutes))!
}
}

let flightPlan = FlightPlan(plannedStartFuel: 450,
minimumLandAllowance: 180)
flightPlan.mlaTime            // (hours 1, minutes 30)
flightPlan.mlaTime.hours      // 1
flightPlan.mlaTime.minutes    // 30
flightPlan.mlaTimeDescrition  // "Flight Time To MLA = 1hr 30min"

最新更新