NSDateComponentsFormatter to get "An hour ago" or "A day ago" - ios7

Can I use NSDateComponentsFormatter to get strings like "An hour ago" or "A day ago"?
I mean, I think it should be easy to get "1 hour ago" or "1 day ago", but no idea.

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSLocale *locale = [NSLocale currentLocale];
[dateFormatter setLocale:locale];
[dateFormatter setDoesRelativeDateFormatting:YES];

You can use this function
- (NSString *)timeAgo
{
NSDate *now = [NSDate date];
double deltaSeconds = fabs([self timeIntervalSinceDate:now]);
double deltaMinutes = deltaSeconds / 60.0f;
int minutes;
if(deltaSeconds < 5)
{
return NSDateTimeAgoLocalizedStrings(#"Just now");
}
else if(deltaSeconds < 60)
{
return [self stringFromFormat:#"%%d %#seconds ago" withValue:deltaSeconds];
}
else if(deltaSeconds < 120)
{
return NSDateTimeAgoLocalizedStrings(#"A minute ago");
}
else if (deltaMinutes < 60)
{
return [self stringFromFormat:#"%%d %#minutes ago" withValue:deltaMinutes];
}
else if (deltaMinutes < 120)
{
return NSDateTimeAgoLocalizedStrings(#"An hour ago");
}
else if (deltaMinutes < (24 * 60))
{
minutes = (int)floor(deltaMinutes/60);
return [self stringFromFormat:#"%%d %#hours ago" withValue:minutes];
}
else if (deltaMinutes < (24 * 60 * 2))
{
return NSDateTimeAgoLocalizedStrings(#"Yesterday");
}
else if (deltaMinutes < (24 * 60 * 7))
{
minutes = (int)floor(deltaMinutes/(60 * 24));
return [self stringFromFormat:#"%%d %#days ago" withValue:minutes];
}
else if (deltaMinutes < (24 * 60 * 14))
{
return NSDateTimeAgoLocalizedStrings(#"Last week");
}
else if (deltaMinutes < (24 * 60 * 31))
{
minutes = (int)floor(deltaMinutes/(60 * 24 * 7));
return [self stringFromFormat:#"%%d %#weeks ago" withValue:minutes];
}
else if (deltaMinutes < (24 * 60 * 61))
{
return NSDateTimeAgoLocalizedStrings(#"Last month");
}
else if (deltaMinutes < (24 * 60 * 365.25))
{
minutes = (int)floor(deltaMinutes/(60 * 24 * 30));
return [self stringFromFormat:#"%%d %#months ago" withValue:minutes];
}
else if (deltaMinutes < (24 * 60 * 731))
{
return NSDateTimeAgoLocalizedStrings(#"Last year");
}
minutes = (int)floor(deltaMinutes/(60 * 24 * 365));
return [self stringFromFormat:#"%%d %#years ago" withValue:minutes];
}

It's very sad, but there is no such functionality in formatters from Foundation framework. Buy you can use FormattersKit. This kit contains TTTTimeIntervalFormatter which allows you to do following:
TTTTimeIntervalFormatter *timeIntervalFormatter = [[TTTTimeIntervalFormatter alloc] init];
[timeIntervalFormatter stringForTimeInterval:0]; // "just now"
[timeIntervalFormatter stringForTimeInterval:-100]; // "1 minute ago"

Related

Convert the for loop to accept ID type

I want to be able to reuse this method so it's not restricted by the class being called in the for loop declaration.
The problem is the compiler has no idea what id actually is, so I cannot use . notation on it. I need to cast it to a whatever type of event class i'll be passing it, instead of having to repeat the method for SpecialEvent1 SpecialEvent2 SpecialEvent3
So instead of for (SpecialEvent1 *event in day) I want to be able to do something like for (id event in day) but problem occurs when I try to access the time or duration property
- (NSArray *)sumDay:(NSArray *)day {
NSInteger morning = 0, afternoon = 0, evening = 0, night = 0;
for (SpecialEvent1 *event in day) {
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:event.time];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitHour fromDate:date];
NSInteger hour = [components hour];
if (hour < 12) {
morning += event.duration;
}
else if (hour < 18) {
afternoon += event.duration;
}
else if (hour < 20) {
evening += event.duration;
}
else {
night += event.duration;
}
}
NSArray *sums = #[#(morning), #(afternoon), #(evening), #(night)];
return sums;
}
Assuming that you aren't interested in objects that don't have a time or duration property, you could define a protocol which says this:
#protocol TimedEvent <NSObject>
-(NSTimeInterval)time;
-(NSTimeInterval)duration;
#end
Then go through the array like this:
- (NSArray *)sumDay:(NSArray *)day {
NSInteger morning = 0, afternoon = 0, evening = 0, night = 0;
for (id<TimedEvent> event in day) {
if (![event conformsToProtocol:#protocol(TimedEvent)]) {
continue;
}
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:event.time];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitHour fromDate:date];
NSInteger hour = [components hour];
if (hour < 12) {
morning += event.duration;
}
else if (hour < 18) {
afternoon += event.duration;
}
else if (hour < 20) {
evening += event.duration;
}
else {
night += event.duration;
}
}
NSArray *sums = #[#(morning), #(afternoon), #(evening), #(night)];
return sums;
}

How to convert timestamp to nsdate

I have a timestamp as below. Its in a string format
"/Date(1402987019190+0000)/"
I would like to convert it into NSDate. Can somebody help me into this one?
Use dateWithTimeIntervalSince1970:,
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeStamp];
NSLog(#"%#", date);
Use this function to convert string to timestamp Ref. https://stackoverflow.com/a/932130/1868660
-(NSString *)dateDiff:(NSString *)origDate {
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setFormatterBehavior:NSDateFormatterBehavior10_4];
[df setDateFormat:#"EEE, dd MMM yy HH:mm:ss VVVV"];
NSDate *convertedDate = [df dateFromString:origDate];
[df release];
NSDate *todayDate = [NSDate date];
double ti = [convertedDate timeIntervalSinceDate:todayDate];
ti = ti * -1;
if(ti < 1) {
return #"never";
} else if (ti < 60) {
return #"less than a minute ago";
} else if (ti < 3600) {
int diff = round(ti / 60);
return [NSString stringWithFormat:#"%d minutes ago", diff];
} else if (ti < 86400) {
int diff = round(ti / 60 / 60);
return[NSString stringWithFormat:#"%d hours ago", diff];
} else if (ti < 2629743) {
int diff = round(ti / 60 / 60 / 24);
return[NSString stringWithFormat:#"%d days ago", diff];
} else {
return #"never";
}
}
If I correctly understood what you mean, you need to parse timestamp from the string with given format.
You can use regular expression to extract timestamp value.
NSString * timestampString = #"/Date(1402987019190+0000)/";
NSString *pattern = #"/Date\\(([0-9]{1,})\\+0000\\)/";
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:nil];
NSTextCheckingResult *textCheckingResult = [regex firstMatchInString:timestampString options:0 range:NSMakeRange(0, timestampString.length)];
NSRange matchRange = [textCheckingResult rangeAtIndex:1];
NSString *match = [timestampString substringWithRange:matchRange];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[match intValue];
NSLog(#"%#", date);

Compare time in objective c

I want to compare different times of outlets with ipad current time so what i did was this
if(CT!=nil && startTime!=nil &&endTime!=nil)
{
/* If current time is greater than begin time but less than end time then he can give feedack
*/
if (([CT compare:startTime]== NSOrderedDescending) && ([CT compare:endTime]== NSOrderedAscending) ) {
return YES;
}
else if (([CT compare:startTime]== NSOrderedSame) || ([CT compare:endTime]== NSOrderedSame))
{
return YES;
}
else if (([CT compare:startTime]== NSOrderedDescending) && ([CT compare:endTime]== NSOrderedDescending))
{
return NO; // out of outlet timings
}
This seems to be working good till now but when i change the outlet time to
start time = 18:30PM
end time = 2:00 AM
Then in that case the above code fails, when the ipad clocks at 1:00AM then the first if condition fails so i added this code below
if (([CT compare:startTime]== NSOrderedAscending) && ([CT compare:endTime]== NSOrderedAscending))
{
}
Now as a developer i am sure that if the device clocks at 1:00 AM then it wont fail but what if somebody updated the outlet timing to
start time = 9:00AM
end time = 11:00 AM
and current device time = 8:00 AM
then in the above case my the second code will fail.
what i want is some suggestion as in what i can do in the second if condition
Code that creates CT is :
NSDate *sysDate = [NSDate date];
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"HH:mm:ss"];
NSString *timeString = [df stringFromDate:sysDate];
NSDate *CT = [df dateFromString:timeString];
startTime and endTime are date objects stored in the plist.
Rewrite your conditions as if there were numbers instead of dates and see what they actually do (NSOrderedDescending becomes > "greater than", NSOrderedAscending becomes < "less than"):
if (ctNumber > startNumber && ctNumber < endNumber)
{ ... }
else if (ctNumber == startNumber || ctNumber == endNumber)
{ ... }
else if (ctNumber > startNumber && ctNumber > endNumber)
{ ... }
I hope this way it's obvious the last one is not what you mean at all, and the other condition you tried later was still incorrect. The correct one is (ctNumber < startNumber || ctNumber > endNumber) which is exactly what standalone else would do, so you can just drop it:
if (ctNumber > startNumber && ctNumber < endNumber)
{ ... }
else if (ctNumber == startNumber || ctNumber == endNumber)
{ ... }
else
{ /* otherwise ; everything else ; in all other cases */ ... }
But you really should follow #ILYA2606 advice and compare time intervals (which are numbers) in future to avoid further confusions like this. It will also allow you to use <= and >= and so drop the middle condition:
NSTimeInterval ctNumber = [CT timeIntervalSince1970];
NSTimeInterval startNumber = [startDate timeIntervalSince1970];
NSTimeInterval endNumber = [endDate timeIntervalSince1970];
BOOL ctLiesInsideInterval = (ctNumber >= startNumber && ctNumber <= endNumber);
return ctLiesInsideInterval;
And finally, I have to point out that the date formatter code that creates CT is nonsense and is equivalent to simply using current time:
NSDate* CT = [NSDate date];
Convert NSString to NSDate, compare timeIntervals:
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"HH:mm:ss"];
NSDate *CTDate = [df dateFromString:CT];
NSDate *startDate = [df dateFromString:startTime];
NSTimeInterval CTInterval = [CTDate timeIntervalSince1970];
NSTimeInterval startInterval = [startDate timeIntervalSince1970];
NSTimeInterval deltaInterval = startInterval - CTInterval;
if(deltaInterval < 0){
//Ascending (CT < startTime)
}
else if(deltaInterval > 0){
//Descending (CT > startTime)
}
else{
//Some (CT = startTime)
}
Try this one..
NSDate *currentDate=[NSDate date];
NSTimeInterval daysInSeconds = 60*60;
NSDate *starttime= [currentDate dateByAddingTimeInterval:daysInSeconds];
daysInSeconds = 2*60*60;
NSDate *endtime= [currentDate dateByAddingTimeInterval:daysInSeconds];
if( [starttime compare:currentDate]==NSOrderedAscending ){
//Ascending (CT < startTime)
}
if( [starttime compare:currentDate]==NSOrderedDescending ){
//Descending (CT > startTime)
}
if( [endtime compare:currentDate]==NSOrderedAscending ){
//Ascending (CT < endtime)
}
if( [endtime compare:currentDate]==NSOrderedDescending ){
//Descending (CT > endtime)
}

NSDate countdown from NOW to NSDate

How can I show a countdown in HH:mm:ss format from NOW to a desired NSDate that will happen in the future?
Start at the documentation.
NSDate *future = // whatever
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:) userInfo:nil repeats:YES];
- (void)updateCounter:(NSTimer *)tmr
{
NSTimeInterval iv = [future timeIntervalSinceNow];
int h = iv / 3600;
int m = (iv - h * 3600) / 60;
int s = iv - h * 3600 - m * 60;
aUILabel.text = [NSString stringWithFormat:#"%02d:%02d:%02d", h, m, s];
if (h + m + s <= 0) {
[tmr invalidate];
}
}
You have to use a timer for ticking the date.
Store a future date, and keep on substracting future date - [nsdate today]...
Calculate the time in seconds and calculate it into Hours, minutes, seconds...
//Make two properties NSDate *nowDate, *futureDate
futureDate=...;
nowDate=[NSDate date];
long elapsedSeconds=[nowDate timeIntervalSinceDate:futureDate];
NSLog(#"Elaped seconds:%ld seconds",elapsedSeconds);
NSInteger seconds = elapsedSeconds % 60;
NSInteger minutes = (elapsedSeconds / 60) % 60;
NSInteger hours = elapsedSeconds / (60 * 60);
NSString *result= [NSString stringWithFormat:#"%02ld:%02ld:%02ld", hours, minutes, seconds];
This will come handy for you...kindly check the project...
Give this code a shot:
NSTimer* timer= [NSTimer scheduledTimerWithInterval: [self.futureDate timeIntervalSinceNow] target: self selector: #selector(countdown:) userInfo: nil, repeats: YES];
The countdown: method:
- (void) countdown: (NSTimer*) timer
{
if( [self.futureDate timeIntervalSinceNow] <= 0)
{
[timer invalidate];
return;
}
NSDateComponents* comp= [ [NSCalendar currentCalendar] components: NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit startingDate: [NSDate date] toDate: self.futureDate options: 0];
NSLog(#"%lu:%lu:%lu", comp.hour,comp.minute.comp.second);
}

Converting CMTime to human readable time in objective-c

So I have a CMTime from a video. How do I convert it into a nice string like in the video time duration label in the Photo App. Is there some convenience methods that handle this? Thanks.
AVURLAsset* videoAsset = [AVURLAsset URLAssetWithURL:url options:nil];
CMTime videoDuration = videoAsset.duration;
float videoDurationSeconds = CMTimeGetSeconds(videoDuration);
You can use this as well to get a video duration in a text format if you dont require a date format
AVURLAsset *videoAVURLAsset = [AVURLAsset assetWithURL:url];
CMTime durationV = videoAVURLAsset.duration;
NSUInteger dTotalSeconds = CMTimeGetSeconds(durationV);
NSUInteger dHours = floor(dTotalSeconds / 3600);
NSUInteger dMinutes = floor(dTotalSeconds % 3600 / 60);
NSUInteger dSeconds = floor(dTotalSeconds % 3600 % 60);
NSString *videoDurationText = [NSString stringWithFormat:#"%i:%02i:%02i",dHours, dMinutes, dSeconds];
There is always an extension ;)
import CoreMedia
extension CMTime {
var durationText:String {
let totalSeconds = Int(CMTimeGetSeconds(self))
let hours:Int = Int(totalSeconds / 3600)
let minutes:Int = Int(totalSeconds % 3600 / 60)
let seconds:Int = Int((totalSeconds % 3600) % 60)
if hours > 0 {
return String(format: "%i:%02i:%02i", hours, minutes, seconds)
} else {
return String(format: "%02i:%02i", minutes, seconds)
}
}
}
to use
videoPlayer?.addPeriodicTimeObserverForInterval(CMTime(seconds: 1, preferredTimescale: 1), queue: dispatch_get_main_queue()) { time in
print(time.durationText)
}
You can use CMTimeCopyDescription, it work really well.
NSString *timeDesc = (NSString *)CMTimeCopyDescription(NULL, self.player.currentTime);
NSLog(#"Description of currentTime: %#", timeDesc);
edit: okay, i read the question too fast, this is not what your wanted but could be helpful anyway for debuging.
edit: as #bcattle commented, the implementation i suggested contain a memory leak with ARC. Here the corrected version :
NSString *timeDesc = (NSString *)CFBridgingRelease(CMTimeCopyDescription(NULL, self.player.currentTime));
NSLog(#"Description of currentTime: %#", timeDesc);
Based on combination of the question and comments above, this is concise:
AVURLAsset* videoAsset = [AVURLAsset URLAssetWithURL:url options:nil];
CMTime videoDuration = videoAsset.duration;
float videoDurationSeconds = CMTimeGetSeconds(videoDuration);
NSDate* date = [NSDate dateWithTimeIntervalSince1970:videoDurationSeconds];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
[dateFormatter setDateFormat:#"HH:mm:ss"]; //you can vary the date string. Ex: "mm:ss"
NSString* result = [dateFormatter stringFromDate:date];
Swift 3.0 ios 10 answer based codingrhythm answer...
extension CMTime {
var durationText:String {
let totalSeconds = CMTimeGetSeconds(self)
let hours:Int = Int(totalSeconds / 3600)
let minutes:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 3600) / 60)
let seconds:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 60))
if hours > 0 {
return String(format: "%i:%02i:%02i", hours, minutes, seconds)
} else {
return String(format: "%02i:%02i", minutes, seconds)
}
}
}
Swift 4.2 extension
extension CMTime {
var timeString: String {
let sInt = Int(seconds)
let s: Int = sInt % 60
let m: Int = (sInt / 60) % 60
let h: Int = sInt / 3600
return String(format: "%02d:%02d:%02d", h, m, s)
}
var timeFromNowString: String {
let d = Date(timeIntervalSinceNow: seconds)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm:ss"
return dateFormatter.string(from: d)
}
}
Simple extension I use for displaying video file duration.
import CoreMedia
extension CMTime {
var stringValue: String {
let totalSeconds = Int(self.seconds)
let hours = totalSeconds / 3600
let minutes = totalSeconds % 3600 / 60
let seconds = totalSeconds % 3600 % 60
if hours > 0 {
return String(format: "%i:%02i:%02i", hours, minutes, seconds)
} else {
return String(format: "%02i:%02i", minutes, seconds)
}
}
}
For example you can use NSDate and it's description method. You can specify any output format you want.
> `
// First, create NSDate object using
NSDate* d = [[NSDate alloc] initWithTimeIntervalSinceNow:seconds];
// Then specify output format
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:#"HH:mm:ss"];
// And get output with
NSString* result = [dateFormatter stringWithDate:d];`
here is the code for getting seconds from cmtime
NSLog(#"seconds = %f", CMTimeGetSeconds(cmTime));
Swift 3:
let time = kCMTimeZero
let timeString = time.toString()
A simplest way (without using NSDate and NSDateFormatter) to do this:-
Using Swift:-
func updateRecordingTimeLabel()
{
// Result Output = MM:SS(01:23)
let cmTime = videoFileOutput.recordedDuration
var durationInSeconds = Int(CMTimeGetSeconds(cmTime))
let durationInMinutes = Int(CMTimeGetSeconds(cmTime)/60)
var strDuMin = String(durationInMinutes)
durationInSeconds = durationInSeconds-(60*durationInMinutes)
var strDuSec = String(durationInSeconds)
if durationInSeconds < 10
{
strDuSec = "0"+strDuSec
}
if durationInMinutes < 10
{
strDuMin = "0"+strDuMin
}
// Output string
let str_output = strDuMin+":"+strDuSec
print("Result Output : [\(str_output)]")
}