Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSDateFormatter: Using custom format with relative formatting

I am trying to format NSDates in a form where it uses the relative format when applicable and the day of the week when not: "Today", "Tomorrow", "Sunday", "Monday", …

The problem is, NSDateFormatter’s doesRelativeFormatting only works when using dateStyle, and not with dateFormat. (Basically, I’d need the functionality of dateFormat = "EEEE" for all days after tomorrow.)

At the moment, I’m using the following code:

let dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .NoStyle
dateFormatter.dateStyle = .FullStyle
dateFormatter.doesRelativeDateFormatting = true

let dateString = dateFormatter.stringFromDate(theDate)
return dateString.componentsSeparatedByString(" ")[0]

Which just happens to work in my specific locale where NSDateFormatterStyle.FullStyle outputs something like "Sunday 23 August 2015", but obviously that’s not a good or general solution.

The closest thing I found would be this question, but it seems unnecessarily complex for my use case, and I’d like something more elegant, if possible.

Thanks!

like image 823
attoPascal Avatar asked Aug 21 '15 15:08

attoPascal


2 Answers

I'd use 3 date formatters:

  1. dateStyle = .FullStyle and doesRelativeDateFormatting = true
  2. dateStyle = .FullStyle and doesRelativeDateFormatting = false
  3. dateFormat = "EEEE" and doesRelativeDateFormatting = false

Get formatted strings for 1 and 2. If they are different, then use string from 1. If they are the same then get and use string from 3.

This should work reliably for all locales. For performance reasons make sure to keep all 3 formatters around instead of recreating them each time.

like image 94
pointum Avatar answered Oct 23 '22 14:10

pointum


Here's a working extension to DateFormatter():

import Foundation

extension DateFormatter {

    /*
     * Returns a string representation of a given date in relative format.
     * If the current local doesn't offer a relative format for the given date,
     * then then a given format is applied.
     *
     * - parameter _date: The date to be formatted
     * - parameter _format: The format to be applied to non-relative dates
     *
     * - returns: A string representing a formatted version of a given date
     */

    func relativeStringWithFormat(from: Date, format: String) -> String? {

        // Create date formatters
        let _formatRelative = DateFormatter()
            _formatRelative.dateStyle = .full
            _formatRelative.doesRelativeDateFormatting = true
            _formatRelative.timeStyle = .none

        let _formatFull = DateFormatter()
            _formatFull.dateStyle = .full
            _formatFull.doesRelativeDateFormatting = false
            _formatFull.timeStyle = .none

        let _formatCustom = DateFormatter()
            _formatCustom.dateFormat = format
            _formatCustom.doesRelativeDateFormatting = false

        // Get dates in available formats
        let _dateRelative = _formatRelative.string(from: from)
        let _dateFull = _formatFull.string(from: from)
        let _dateCustom = _formatCustom.string(from: from)

        // Return appropriatly formatted date/string
        if _dateRelative != _dateFull {

            return _dateRelative

        } else {

            return _dateCustom
        }
    }   
}
like image 27
eranschau Avatar answered Oct 23 '22 15:10

eranschau