【问题标题】:how to use DateComponentsFormatter for showing date as "Today" or "Yesterday"?如何使用 DateComponentsFormatter 将日期显示为“今天”或“昨天”?
【发布时间】:2020-09-04 23:01:12
【问题描述】:

我在我的项目中使用 swift,我想显示这样的特定日期:5 天前或 5 个月前或... 我正在使用 DateComponentsFormatter,它运行良好,但问题是我想在 1 天前显示“昨天”或在 3 秒前显示“今天”。我怎样才能做到这一点?我可以使用 DateComponentsFormatter 解决这个问题吗?这是我的代码:

func shortDate(_ local: LocaleIdentifier = .fa) -> String {
    
    let now = Date()
    let date = self

    let formatter = DateComponentsFormatter()
    formatter.calendar?.locale = Locale(identifier: local.rawValue)
    formatter.unitsStyle = .full
    formatter.maximumUnitCount = 1
    formatter.allowedUnits = [.year, .month, .day, .hour, .minute, .second]

    guard let timeString = formatter.string(from: date, to: now) else {
         return ""
    }

    return String(format: "Ago".localized, timeString)
}

【问题讨论】:

    标签: swift


    【解决方案1】:

    对于 iOS 13 或更高版本,您可以使用 RelativeDateTimeFormatter

    let relativeDateTimeFormatter = RelativeDateTimeFormatter()
    relativeDateTimeFormatter.dateTimeStyle = .named
    relativeDateTimeFormatter.unitsStyle = .full
    let date = Date.init(timeIntervalSinceNow: -60*60*24)
    relativeDateTimeFormatter.string(for: date)  // "yesterday"
    

    编辑/更新:

    如果您想支持 iOS 11,您需要实现自己的相对日期格式化程序。您可以使用 Calendar 方法 isDateInToday 和 isDateInYesterday 将相对日期格式化程序与日期组件格式化程序结合起来。请注意,无需检查在日期组件格式化程序中设置单个单位的时间间隔,您可以设置日期组件格式化程序的允许单位,只需确保按照所需的优先级设置它们即可:

    // 这样可以避免每次调用 relativeDateFormatted 属性时都创建格式化程序

    extension Formatter {
        static let dateComponents: DateComponentsFormatter = {
            let dateComponentsFormatter = DateComponentsFormatter()
            dateComponentsFormatter.allowedUnits = [.day, .month, .year]  // check the order of the units it does matter when allowing only 1 unit to be displayed
            dateComponentsFormatter.maximumUnitCount = 1
            dateComponentsFormatter.unitsStyle = .full
            return dateComponentsFormatter
        }()
        static let relativeDate: DateFormatter = {
            let dateFormatter = DateFormatter()
            dateFormatter.doesRelativeDateFormatting = true
            dateFormatter.timeStyle = .none
            dateFormatter.dateStyle = .medium
            return dateFormatter
        }()
    }
    

    extension Date {
        var relativeDateFormatted: String {
            Calendar.current.isDateInToday(self) || Calendar.current.isDateInYesterday(self) ?
            Formatter.relativeDate.string(from: self) :
            Formatter.dateComponents.string(from: self, to:  Date()) ?? "" 
        }
    }
    

    游乐场测试:

    let date1 = DateComponents(calendar: .current, year: 2020, month: 9, day: 4, hour: 5).date!
    let date2 = DateComponents(calendar: .current, year: 2020, month: 9, day: 3, hour: 23, minute: 50).date!
    let date3 = DateComponents(calendar: .current, year: 2020, month: 8, day: 25, hour: 10).date!
    let date4 = DateComponents(calendar: .current, year: 2020, month: 8, day: 3).date!
    let date5 = DateComponents(calendar: .current, year: 2019, month: 8, day: 27).date!
    
    date1.relativeDateFormatted  // "Today"
    date2.relativeDateFormatted  // "Yesterday"
    date3.relativeDateFormatted  // "10 days"
    date4.relativeDateFormatted  // "1 month"
    date5.relativeDateFormatted  // "1 year"
    

    【讨论】:

    • 在这种情况下,您需要实现自己的相对日期时间格式化程序 stackoverflow.com/a/27184261/2303865 这可能会有所帮助
    • 也可以使用日历方法isDateInToday和isDateInYesterday
    • 这很好,但我发现我的问题更好,更干净。
    【解决方案2】:

    您正在寻找 DateFormatter 的 doesRelativeDateFormatting 属性。

    【讨论】:

    • 是的,我可以在这种情况下使用 DateComponentsFormatter 吗?
    【解决方案3】:

    我认为,我必须同时使用 DateComponentsFormatter 超过 1 天,并使用 DateFormatter 来显示 Today(day=0) 和 Yesterday(day=1) 来解决我的问题。 我找到了我的答案here

    但我把代码做得更好一点,这是我的代码:

    var calendar = Calendar.current
    calendar.locale = Locale(identifier: local.rawValue)
        
    let componentFormatter = DateComponentsFormatter()
    componentFormatter.calendar = calendar
    componentFormatter.unitsStyle = .full
    componentFormatter.maximumUnitCount = 1
    
    let interval = Calendar.current.dateComponents([.year, .month, .day], from: self, to: Date())
        
    if let year = interval.year, year > 0 {
        componentFormatter.allowedUnits = .year
    } else if let month = interval.month, month > 0 {
        componentFormatter.allowedUnits = .month
    } else if let day = interval.day, day > 1 {
        componentFormatter.allowedUnits = .day
    } else { // if let day = interval.day, day == 0 || day == 1 for Today or Yesterday
    
        let formatter = DateFormatter()
        formatter.doesRelativeDateFormatting = true
        formatter.calendar.locale = Locale(identifier: local.rawValue)
        formatter.timeStyle = .none
        formatter.dateStyle = .medium
    
        return formatter.string(from: self)
    }
    
    guard let timeString = componentFormatter.string(from: self, to: Date()) else {
            return ""
    }
    return String(format: "Ago".localized, timeString)
    

    【讨论】:

    • 这不会按预期工作,它将在 24 小时内的任何时间间隔返回第 0 天,在 24 到 48 小时之间的任何时间间隔返回第 1 天(如果没有夏令时转换)。你应该再次使用我上面提到的日历方法 isDateInToday 和 isDateInYesterday。
    • 恢复首先检查 isDateInToday 或 isDateInYesterday 并使用 DateFormatter 否则使用 DateComponentsFormatter
    • 这就是我想要的,24 小时内的任何时间间隔都应该返回今天,24 到 48 小时之间的任何时间间隔都应该返回昨天。
    • 但是使用 isDateInToday 或 isDateInYesterday 是我的问题的另一种解决方案,当我想显示今天或昨天的另一个标题时甚至比我的答案更好,比如当 isDateInToday 返回 true 时说“现在”而不是“今天” .
    • 你的回答比我的好:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-22
    • 2019-10-30
    • 1970-01-01
    • 2018-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多