【问题标题】:Convert decimal time to hours and minutes in Swift在 Swift 中将十进制时间转换为小时和分钟
【发布时间】:2020-06-14 18:15:07
【问题描述】:

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

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")

【问题讨论】:

  • String(format: "%.0f", hours) 四舍五入小时到下一个整数。
  • 太好了,谢谢。有没有快速解决方法?
  • 我不建议使用格式化字符串; NumberFormatter 专门用于帮助您将数字表示为字符串。首先把数字弄对。然后想想你希望他们如何呈现给用户。
  • ... 和 DateComponentsFormatter 专门用于帮助您以小时和分钟表示时间间隔。

标签: swift function calculation


【解决方案1】:

我可能会建议你根本不要计算小时和分钟,而是让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


底线,如果你真的想计算分钟和秒,请随意,但如果只是为了创建字符串表示,让DateComponentFormatter 为你做这件事。

【讨论】:

  • 感谢您抽出宝贵时间对此进行解释。它有效并为我提供了所需的准确性。
  • 我必须说我怀疑DateComponentFormatter 的准确性。这绝对是创建时间量的字符串表示的标准方法,但仅此而已,我不相信它会准确地舍入。
  • 这就是它的全部存在理由。它非常可靠和准确。当我需要毫秒显示(例如秒表之类的功能)时,我个人唯一一次考虑自己做,DateComponentsFormatter 不这样做。但除此之外,Apple 工程师提供了这种丰富、一致且易于本地化的解决方案。你需要一个相当有说服力的案例来重新发明轮子。
  • 好吧,我这么说是因为DateComponentsFormatter 给出的值与Measurement 的值不同,Measurement存在理由 是提供“对单位转换和单位感知计算的支持”。所以有两个选择,要么我对Measurement 的使用有缺陷,要么DateComponentsFormatter 的四舍五入不是那么准确
  • 另外DateComponentsFormatter支持打印DateComponents,而Measurement也是Foundation的一部分;)
【解决方案2】:

编辑 我知道你想知道你的方法有什么问题。 这是一个四舍五入的问题,在将它传递给 String(format:) 之前尝试 roundind hours

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

【讨论】:

    【解决方案3】:

    String 格式化程序 向上

    您可以在Doubles 上使用.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
    
    

    【讨论】:

    • 这将是迄今为止最简单的选择,它解决了小时数的问题 - 对小时数进行四舍五入将一直有效。由于问题的原因已被突出显示,我现在看到分钟也存在同样的问题。因为我需要准确,所以我需要使用 DateComponentsFormatter - 谢谢你的帮助。
    【解决方案4】:

    首先,您应该构建数据。接下来,如果您不打算显示分数,则不需要将值格式化为 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"
    

    【讨论】:

    • 您应该使用en_US_POSIX 在用户界面中显示结果。
    • @Rob 有意避免本地化
    • 只有在您将本地化添加到您的应用程序时才会对其进行本地化。作为一般规则,添加 en_US_POSIX 区域设置以与 UI 交互是错误的。它适用于仅需要固定格式的情况,例如 Web 服务或持久存储的格式化,在这种情况下它是必不可少的。但是在格式化 UI 时几乎不应该使用它。
    猜你喜欢
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多