Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Date/Time Natural Language Approximation in Swift

I am attempting to convert a UTC formatted date from an API to a human-readable approximation format using Swift.

I'm looking for something like the following:

2015-07-14T13:51:05.423Z

to

About two weeks ago

What is the best approach to this in Swift? While optimally this could format strings directly, I understand that this will probably require casting the string to a NSDate object.

Any help would be greatly appreciated.

EDIT: My question had been identified as a possible duplicate of another question. Tom's solution below is written for Swift and much more elegant than creating a new method in regards to my situation.

like image 426
Tyler Avatar asked Jul 28 '15 20:07

Tyler


People also ask

What is the date format in Swift?

dateFormat = "yyyy-MM-dd'T'HH:mm:ss.

What is en_US_POSIX?

In most cases the best locale to choose is "en_US_POSIX", a locale that's specifically designed to yield US English results regardless of both user and system preferences.

How do I get the current date and time in a string in Swift?

Get current time in “YYYY-MM–DD HH:MM:SS +TIMEZONE” format in Swift. This is the easiest way to show the current date-time.

How do I use ISO8601DateFormatter?

ISO 8601 is the YYYY-MM-DD format to represent date. In ISO8601DateFormatter , Swift expects the time as well: YYYY-MM-DDTHH:MM:SSZ. If we simply need the date without the time, we can set the . withFullDate format option.


2 Answers

You need two steps. First, convert your date string to an NSDate:

let dateString = "2015-07-14T13:51:05.423Z"

let df = NSDateFormatter()
df.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let date = df.dateFromString(dateString)

(If that's not an exact representation of the strings you get, you'll have to change the date format string to get this to convert).

Next, use NSDateComponentsFormatter to get your desired string:

let formatter = NSDateComponentsFormatter()
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyle.Full
formatter.includesApproximationPhrase = true
formatter.includesTimeRemainingPhrase = false
formatter.allowedUnits = NSCalendarUnit.WeekOfMonthCalendarUnit

if let pastDate = date {
    let dateRelativeString = formatter.stringFromDate(pastDate, toDate: NSDate())
}

Today is July 28, so the result for that string is "About 2 weeks". The allowedUnits attribute is a bit field, so you can specify as many unit types as you want to allow.

like image 93
Tom Harrington Avatar answered Sep 23 '22 07:09

Tom Harrington


As Tom Harrington's answer notes, you can produce a colloquial representation of a moment or a time interval using NSDateComponentsFormatter.

However, if you want to do exactly what the question asks in its example, which is to produce a colloquial representation of a moment in the past, relative to the present moment, like for a timeline-oriented UI, then it seems like NSDateComponentsFormatter is not suitable. As the documentation for stringFromTimeInterval(_:) says, the time interval value "must be a finite number. Negative numbers are treated as positive numbers when creating the string."

As near as I can tell, the best choice is TTTTimeIntervalFormatter, a standalone class in Mattt Thompson's FormatterKit.

I have produced an Xcode 7 playground, RelativeDatePlayground, that compares the outputs of NSDateFormatter output to TTTTimeIntervalFormatter. Here is a table showing output for different relative times in seconds. As you can see, NSDateComponentsFormatter does not seem to handle past moments or the present moment well:

        -1488010 |               2 weeks ago |         -1 week remaining
        -1468800 |               2 weeks ago |         -1 week remaining
         -864000 |                1 week ago |       0 seconds remaining
          -86400 |                 1 day ago |          -1 day remaining
          -36000 |              10 hours ago |       -10 hours remaining
           -3600 |                1 hour ago |         -1 hour remaining
            -600 |            10 minutes ago |     -10 minutes remaining
             -60 |              1 minute ago |       -1 minute remaining
             -10 |            10 seconds ago |     -10 seconds remaining
              -1 |              1 second ago |       -1 second remaining
              -0 |                  just now |       0 seconds remaining
               0 |                  just now |       0 seconds remaining
               1 |         1 second from now |        1 second remaining
              10 |       10 seconds from now |      10 seconds remaining
              60 |         1 minute from now |        1 minute remaining
             600 |       10 minutes from now |      10 minutes remaining
            3600 |           1 hour from now |          1 hour remaining
           36000 |         10 hours from now |        10 hours remaining
           86400 |            1 day from now |           1 day remaining
          864000 |           1 week from now |          1 week remaining
         1468800 |          2 weeks from now |         2 weeks remaining
         1488010 |          2 weeks from now |         2 weeks remaining
like image 36
algal Avatar answered Sep 20 '22 07:09

algal