Elikos
Elikos

Reputation: 103

Converting unix time (as NSString) to "X Hours ago" in Objective-C

I get my data from an API which received the data as strings.

I have NSString type variable name timeAgo which represents the unix time in seconds (for example: 1455893366). My goal is to change it to "X minutes/hours/days ago" format.

I've couldn't find something useful, and I don't want to change my API to do it (Well, im learning Objective-C and iOS..).

Anyone can help me how it can be done?

I've been trying: int interval = (int) ([[NSDate date] timeIntervalSinceDate:update.timeAgo] / 3600); but I get the following error: Incompatible pointer types sending 'NSString *' to parameter of type 'NSDate * _Nonnull'

Upvotes: 0

Views: 565

Answers (2)

CRD
CRD

Reputation: 53010

im learning Objective-C and iOS

Well let's see if we can help you along, without just giving the final answer.

The code you tried:

int interval = (int) ([[NSDate date] timeIntervalSinceDate:update.timeAgo] / 3600);

gives you the error:

Incompatible pointer types sending 'NSString *' to parameter of type 'NSDate * _Nonnull'

You've already stated that timeAgo is an NSString *, that is some text. Computers represent text and numbers differently, e.g. the string @"42" is represented differently to the integer 42, and also to the floating-point number 42.0.

As you just have the textual representation of a number the first thing you need to do is produce the equivalent actual number, which you might store in a variable of type int, long, double etc. depending on the magnitude of the number you expect.

Which type should you pick? Well Cocoa uses the type NSTimeInterval to represent a time interval in seconds, and if you follow the link you'll find it is just a convenient name for double so that would be a good choice.

Now look at the NSString documentation for a method which parses (interprets) your text as a double value and returns the result.

So now you have your seconds as a double (or NSTimeInterval). But that won't completely address the error as the message says a value of type NSDate * (ignore the _Nonnull for now, you can learn about that in the future). Computers use different representations for different types, not just for text and numbers, and here you need an NSDate * value, a double (aka NSTimeInterval - don't forget) will do you no better than the NSString * did.

Rather than figure out how to produce an NSDate value from an NSTimeInterval, in this case there is a more direct way. You selected the method timeIntervalSinceDate why? Take another look at the NSDate documentation. You are interested in time differences, or intervals, not in absolute dates. You have a Unix time interval, which is a number of seconds, and you wish to know the number of seconds it differs from now. This should suggest you wish to be generating/adding/subtracting/comparing NSTimeInterval values, and there are some methods in NSDate which take/return NSTimeInterval values.

Before you go any further you need to understand about how time is measured on Unix and OS X. Unix stores time as the number of seconds since the Unix epoch, which is the very start (00:00) of the 1st Jan 1970 in the UTC time zone. OS X also stores time as the number of seconds since the epoch, however the OS X epoch is the start of 1st Jan 2001. What this means is you must be careful that given two NSTimeInterval values that they are based on the same epoch, if not your calculations might have an error of 31 years!

Can you find a method in NSDate which will give you now as a Unix time, that is an NSTimeInterval counting from 1970?

Got it? You can take the difference between your Unix time (which came from timeAgo and you've converted to double/NSTimeInterval) and the Unix time for now (from NSDate methods) and you've got the number of seconds you wish to convert to "X minutes/hours/days ago" format. (Note: if you might have future times you'll need to be aware of the sign of the difference, if only past times just subtract in the right order so you always get a positive difference.)

So the next step, how do you convert your seconds to the text phrase you want? Provided you're on at least iOS 8 there is a class NSDateComponentsFormatter which can convert values of type NSDateComponents (hence the class name) and NSTimeInterval to the kind of text you want. Just read the documentation and you'll figure it out.

If you follow this through, produce some code, and still have problems you can ask a new question, showing your new code, and explaining why you are stuck. Someone will probably help you along.

(Note: You don't say if you know other programming languages; if you do apologies if some of the above is a bit basic for you, also if you get stuck you might want to "cheat" and look at Rob's answer which is written in Swift and covers much of what is said above.)

HTH

Upvotes: 0

rob mayoff
rob mayoff

Reputation: 385890

First, turn the string into a number like this:

let timestamp = NSTimeInterval(timeAgo)

Then, you can use a function like this to format it as a relative string:

var currentUnixTime: NSTimeInterval { return CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970 }

func relativeStringForUnixTime(time: NSTimeInterval) -> String {
    struct Static {
        static var once: dispatch_once_t = 0
        static var formatter: NSDateComponentsFormatter!
    }
    dispatch_once(&Static.once) {
        Static.formatter = NSDateComponentsFormatter()
        Static.formatter.allowedUnits = [ .Day, .Hour, .Minute, .Second ]
        Static.formatter.maximumUnitCount = 1
        Static.formatter.unitsStyle = .Full
    }

    let offset = currentUnixTime - time
    if offset > 0 {
        return Static.formatter.stringFromTimeInterval(offset)! + " ago"
    } else {
        return Static.formatter.stringFromTimeInterval(-offset)! + " from now"
    }
}

relativeStringForUnixTime(currentUnixTime)          // "0 seconds ago"
relativeStringForUnixTime(currentUnixTime - 10)     // "10 seconds ago"
relativeStringForUnixTime(currentUnixTime - 100)    // "2 minutes ago"
relativeStringForUnixTime(currentUnixTime - 1000)   // "17 minutes ago"
relativeStringForUnixTime(currentUnixTime - 10000)  // "3 hours ago"
relativeStringForUnixTime(currentUnixTime - 100000) // "1 day ago"

relativeStringForUnixTime(currentUnixTime + 10)     // "9 seconds from now"
relativeStringForUnixTime(currentUnixTime + 100)    // "2 minutes from now"
relativeStringForUnixTime(currentUnixTime + 1000)   // "17 minutes from now"
relativeStringForUnixTime(currentUnixTime + 10000)  // "3 hours from now"
relativeStringForUnixTime(currentUnixTime + 100000) // "1 day from now"

Upvotes: 1

Related Questions