在Swift中使用货币

4
我希望您能够用Swift创建一个函数,该函数接受一个整数作为参数并返回当地货币的double值,例如:
输入:1250
输出:£12.50
输入:12500
输出:£125.00
我注意到有一种第三方库支持此功能,但不幸的是该仓库已经存档。所使用的货币单位是最小的货币类型,即minorUnits。
网络调用
/// GET - Retrive a feed of transactions during a certain period
static func get(accountUID: String, categoryUID: String, queryStartDate: String, queryEndDate: String , completionHandler: @escaping ([FeedItem], Error?) -> Void) {
    let sessionObject : URLSession = URLSession.shared
    let taskObject = sessionObject.dataTask(with: URLS().feedURLObject(accountUID,categoryUID,queryStartDate,queryEndDate)) { (Data, Response, Error) in
        guard Error == nil else {
            return
        }
        guard let Data = Data else {
            return
        }
        do {
            let feed = try JSONDecoder().decode(Feed.self, from: Data).feedItems.filter({ (item) -> Bool in
                item.direction == Direction.out
            })
            completionHandler(feed, nil)
        } catch let error  {
            print(error.localizedDescription)
        }
    }
    taskObject.resume()
}

模型馈送结构量

struct Amount: Codable {
let currency: Currency
let minorUnits: Int}

单个项目的JSON响应

FeedItem(feedItemUid: "0651afe9-f568-4623-ad26-31974e26015c", categoryUid: "a68f9445-4d59-44e5-9c3f-dce2df0f53d2", amount: Banking_App.Amount(currency: Banking_App.Currency.gbp, minorUnits: 551), sourceAmount: Banking_App.Amount(currency: Banking_App.Currency.gbp, minorUnits: 551), direction: Banking_App.Direction.out, updatedAt: "2020-02-04T14:09:49.072Z", transactionTime: "2020-02-04T14:09:48.743Z", settlementTime: "2020-02-04T14:09:48.992Z", source: Banking_App.Source.fasterPaymentsOut, status: Banking_App.Status.settled, counterPartyType: Banking_App.CounterPartyType.payee, counterPartyUid: Optional("fed4d40b-9ccc-411d-81c7-870164876d04"), counterPartyName: Banking_App.CounterPartyName.mickeyMouse, counterPartySubEntityUid: Optional("d6d444c0-942f-4f85-b076-d30c2f745a6f"), counterPartySubEntityName: Banking_App.CounterPartySubEntityName.ukAccount, counterPartySubEntityIdentifier: "204514", counterPartySubEntitySubIdentifier: "00000825", reference: Banking_App.Reference.externalPayment, country: Banking_App.Country.gb, spendingCategory: Banking_App.SpendingCategory.payments)

感谢您的选择。

这个回答解决了你的问题吗?如何将Double格式化为货币-Swift 3 - chirag90
不,该值已经预先从API调用中确定,并且底层值的类型为Int。我尝试使用数字格式化程序,但对于1250的值返回的值是£1,250,而应该返回£12.50。 - Furqan Agwan
分享你的代码和 JSON 响应。 - chirag90
2
@FurqanAgwan 您可以将值转换为 Double,然后除以 100 - Sulthan
欢迎来到SO!请阅读[提问指南]并提供一个[示例],展示您已经尝试过什么以及您的问题具体是什么。当前的描述不够清晰,请明确您的问题。 - koen
显示剩余4条评论
3个回答

3

货币代码在标准ISO 4217中定义,作为该标准的一部分,每种货币所使用的小数位数也已定义。由于Swift的NumberFormatter有一个适用于此ISO标准的样式,因此我们可以将其用于此情况。

创建一个实例并设置样式。

let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currencyISOCode

设置货币代码

currencyFormatter.currencyCode = "SEK"

现在,currencyFormatter.minimumFractionDigitscurrencyFormatter.maximumFractionDigits 都将包含为给定货币定义的小数位数。因此,我们现在可以将它们放在一起作为一个函数。
func convertMinorUnits(_ units: Int, currencyCode: String) -> Decimal {
    let currencyFormatter = NumberFormatter()
    currencyFormatter.numberStyle = .currencyISOCode
    currencyFormatter.currencyCode = currencyCode.uppercased()

    return Decimal(units) / pow(10, currencyFormatter.minimumFractionDigits)
}

示例

print(convertMinorUnits(551, currencyCode: "GBP")

5.51


虽然这是一个有效的解决方案,但如果我想四舍五入到最近的英镑,比如20.64四舍五入应该产生21。我尝试使用NSDecimalRounding,但没有成功,结果还应该能够计算原始值和四舍五入值之间的差异。如果可以用算术运算来实现,比如说21-20.64=0.36。 - Furqan Agwan
1
@FurqanAgwan,你在问题中没有提到四舍五入,而且这样扩展你的问题是不可接受的,请尊重我们试图帮助你的人,而是发布一个新的问题。 - Joakim Danielson

2
货币不应该使用浮点数类型如double来表示。简单的原因是浮点数是基于2进制的数字,使用它们来表示基于10进制的数字会导致舍入误差。
货币应该使用正确的数据类型,如INCurrencyAmount或类似的类型。它的属性是金额的NSDecimalNumber和一个NSString来表示货币。
除了NSDecimalNumber,你也可以使用Swift类型Decimal。它也定义了常见的数学运算符,如+*等。

在Swift中,它被称为“Decimal”。无论如何,当金额表示为整数时,它也可以工作。我更关心的是没有将货币与金额一起存储。 - Sulthan

0

正如其他人所指出的那样,任何代表货币金额的数字都应该使用小数。网站0.30000000000000004.com很好地解释了这一点。

当涉及将以便士或美分表示的整数值转换为以英镑或美元表示的值时,您需要知道乘数,即主要货币单位中有多少个分数货币单位(例如,1英镑=100便士,在这种情况下,乘数为100)。对于大多数世界货币,它是100,但也有一些货币没有任何分数单位(请参见维基百科)。

通常,您可以从API中获取乘数,但如果API没有提供它,则可以在应用程序中存储已知货币及其乘数的字典,但这样就成为您保持其最新状态的责任。

为了避免将您的金额除以乘数,我建议计算指数并像这样初始化小数:

let amountInCents = 55123
let multiplicator = 100
let exponent = -1 * Int(log10(Double(multiplicator)))
let sign: FloatingPointSign = amountInCents < 0 ? .minus : .plus
let decimalAmount = Decimal(sign: sign, exponent: exponent, significand: Decimal(amountInCents))

如果您需要格式化十进制数以便在用户界面中显示它,您需要使用数字格式化程序,并向其传递适当的区域设置:

let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_GB")
formatter.numberStyle = .currency
let formattedAmount = formatter.string(from: NSDecimalNumber(decimal: decimalAmount)) ?? decimalAmount.description
print(formattedAmount) // prints "£551.23"

这也是一个有效的解决方案,如果我想四舍五入到最近的英镑,比如20.64四舍五入应该得到21。我尝试使用NSDecimalRounding但没有成功,结果还应该能够计算原始值和四舍五入值之间的差异。如果可以通过算术运算实现,比如21-20.64=0.36,那么就可以进行向上或向下取整。 - Furqan Agwan
如果您需要帮助四舍五入小数值(这在问题中没有提到),我建议在SO上搜索“NSDecimalRound”,如果您在那里找不到解决方案,请提交一个新问题,我相信社区会很乐意帮助。您可以在此处提及新问题的链接,我也会尽力帮忙。 - Vadim Belyaev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接