Некорректное поведение NumberFormatter для небольших отрицательных чисел в режиме валюты - PullRequest
2 голосов
/ 11 апреля 2019

При использовании NumberFormatter и установке numberStyle на .currency форматтер округляет все числа до 2 десятичных знаков, как и ожидалось. Однако для небольших отрицательных чисел, значение которых округлено до 2 десятичных знаков 0 (число> = -0,005), выходная строка содержит знак отрицания и, следовательно, становится -$0.00 вместо $0.00.

Есть ли способ изменить это поведение, просто переключая некоторые свойства NumberFormatter вместо использования обходного пути? В данный момент я сам округляю значение до 2 десятичных разрядов, проверяю, равен ли вывод -0, и действую соответствующим образом. Тем не менее, было бы здорово, если бы NumberFormatter можно было настроить так, чтобы он не различал -0 и 0.

Ниже код показывает проблему и мой текущий способ ее обхода (не стесняйтесь тестировать на игровой площадке) - я знаю, что Locale должен быть установлен на основе currencySymbol, но это всего лишь код для демонстрации проблемы, а не производственный код):

public class CurrencyFormatter {

    private let formatter: NumberFormatter

    public init(locale: Locale = Locale.current) {
        let formatter = NumberFormatter()
        formatter.locale = locale
        formatter.numberStyle = .currency
        self.formatter = formatter
    }

    // Adds currency symbol and returns a string e.g. input 1 output "£1"
    public func formatWithCurrencySymbol(value: Decimal, currencyCode: String) -> String? {
        formatter.currencyCode = currencyCode

        // Workaround for cases when the currency behaviour rounds small negative values (<0.0051) to -0.00, where we don't want to have a - sign
        var value = value
        if value < 0 {
            let roundedValue = round(Double(truncating: value as NSDecimalNumber) * 100.0)
            if roundedValue == 0 && roundedValue.sign == .minus {
                value = 0
            }
        }

        return formatter.string(for: value)
    }

    public func formatWithCurrencySymbolNoWorkaround(value: Decimal, currencyCode: String) -> String? {
        formatter.currencyCode = currencyCode
        return formatter.string(for: value)
    }
}

let formatter = CurrencyFormatter()

formatter.formatWithCurrencySymbol(value: 0.01, currencyCode: "USD") // "$0.01"
formatter.formatWithCurrencySymbol(value: -0.001, currencyCode: "EUR") // "€0.00"
formatter.formatWithCurrencySymbol(value: -0.0002, currencyCode: "EUR") // "€0.00"
formatter.formatWithCurrencySymbol(value: -0.01, currencyCode: "EUR") // "-€0.01"

formatter.formatWithCurrencySymbol(value: 0.01, currencyCode: "USD") // "$0.01"
formatter.formatWithCurrencySymbol(value: -0.001, currencyCode: "EUR") // "-€0.00"
formatter.formatWithCurrencySymbol(value: -0.0002, currencyCode: "EUR") // "-€0.00"
formatter.formatWithCurrencySymbol(value: -0.01, currencyCode: "EUR") // "-€0.01"
formatter.formatWithCurrencySymbolNoWorkaround(value: -0.005, currencyCode: "EUR") // "-€0.00"
...