How to convert difference between two times into hours only? - ios

I found the answer to this, but unfortunately it's using Java. I have two times, formatted as HHmm (no colons). I need to figure out how many 15 minute time segments are in the difference. For example, I have a start time of 1000 and an end time of 1130 (military time).
When I subtract the two dates, I get 130, which is meaningless for computations.
Is there an existing method that will do this for me? (I have spent the last 6 hours trying SO and Google, but found nothing).
UPDATE: I would appreciate it if whoever downvoted me please reverse it. The question is very pertinent and others will find it useful. Thank you.

Parse each time and convert to minutes. So 1000 becomes 10 hours 0 minutes for a total of 600 minutes. 1130 becomes 11 hours 30 minutes for a total of 690 minutes. Subtract the two values for a difference of 90 minutes. Now divide by 15 to get 6.
The following assumes all times are 4 digit military times:
NSString *startTime = #"1000";
NSString *endTime = #"1130";
int startMinues = [[startTime substringToIndex:2] intValue] * 60 + [[startTime substringFromIndex:2] intValue];
int endMinues = [[endTime substringToIndex:2] intValue] * 60 + [[endTime substringFromIndex:2] intValue];
int minutes = endMinutes - startMinutes;
int units = minutes / 15;
This gives whole units of your 15 minute blocks.

Use -[NSCalendar components:fromDate:toDate:options], like this:
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalenderUnitMinute fromDate:startDate toDate:endDate options:0];
NSInteger numberOfMinutes = [components minute];
Once you have the number of minutes, it should just be a matter of dividing by 15 to get the number of 15 minute chunks.

Try using NSDateComponents:
NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *components = [calendar components:NSMinuteCalendarUnit|NSHourCalendarUnit
fromDate:dateA
toDate:dateB
options:0];
int increments = components.hour*4 + components.minute/15;

Format is rather simple:
int HHmm = [date intValue];
int HH = HHmm / 100;
int mm = HHmm % 100;
Diff, for two parsed dates:
int diff = ((HH2 * 60 + mm2) - (HH1 * 60 + mm1)) / 15;

Related

Is it NSCalendar bug?

Why are the last two lines of the output the same?
Use NSCalendar to calculate the diff between startTime and endTime, find that the diff between #"2008-02-28 00:00:00" and #"2022-02-28 00:00:00" and the diff between #"2008-02-29 00:00:00" and #"2022-02-28 00:00:00" are the same. It looks like a bug of NSCalendar, maybe about leapMonth?
code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self printDiffBetweenStartTime:#"2008-02-27 00:00:00" endTime:#"2022-02-28 00:00:00"];
[self printDiffBetweenStartTime:#"2008-02-28 00:00:00" endTime:#"2022-02-28 00:00:00"];
[self printDiffBetweenStartTime:#"2008-02-29 00:00:00" endTime:#"2022-02-28 00:00:00"];
}
- (void)printDiffBetweenStartTime:(NSString *)startTime endTime:(NSString *)endTime
{
static NSDateFormatter *dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ss";
dateFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
});
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startDate = [dateFormatter dateFromString:startTime];
NSDate *endDate = [dateFormatter dateFromString:endTime];
NSCalendarUnit unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [calendar components:unitFlags fromDate:startDate toDate:endDate options:0];
NSLog(#"\"%#\" to \"%#\" : %# year %# month %# day %# hour %# minute %# second", startTime, endTime, #(components.year), #(components.month), #(components.day), #(components.hour), #(components.minute), #(components.second));
}
output:
"2008-02-27 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 1 day 0 hour 0 minute 0 second
"2008-02-28 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 0 day 0 hour 0 minute 0 second
"2008-02-29 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 0 day 0 hour 0 minute 0 second
This is expected. There are many ways to do these period calculations, and the one that NSCalendar uses turns out to not be the one you expected.
The documentation briefly describes what it does:
Some operations can be ambiguous, and the behavior of the computation is calendar-specific, but generally larger components will be computed before smaller components; for example, in the Gregorian calendar a result might be 1 month and 5 days instead of, for example, 0 months and 35 days.
What this means is that it will compute how many years are in between the two dates first, then months, then days, and so on. "Years" is the biggest component you requested.
And NSCalendar finds that adding 14 years to 2008-02-28 makes exactly 2022-02-28. Adding 14 years to 2008-02-29 is also exactly 2022-02-28, because 2022 is not a leap year. Note that "adding a year" does not mean the same as "adding 12 months" or "adding 365 days".
For a difference to appear in this case, you need to compute the days first. One period has 5114 days, and the other has 5113.
A few more examples:
If you instead compute the year, month, day period between 2008-02-28 and 2022-02-01, and the period between 2008-02-29 and 2022-02-01. You wouldn't see a difference, both are 13 years, 11 months, and 4 days. This is because adding 13 years to both 2008-02-29 and 2008-02-28 gets you to 2021-02-28, then adding 11 months is 2022-01-28. 4 days after that is 2022-02-01.
However, if you only compute months and days, the period between 2008-02-28 and 2022-02-01, and the period between 2008-02-29 and 2022-02-01 are different.
The period between 2008-02-28 and 2022-02-01 is 167 months and 4 days. Adding 167 months to 2008-02-28 is 2022-01-28. 4 days after that is 2022-02-01.
The period between 2008-02-29 and 2022-02-01 is 167 months and 3 days. Adding 167 months to 2008-02-29 is 2022-01-29. 3 days after that is 2022-02-01.
Period calculations are weird, aren't they! But they are consistent in a unique way.

Calculating Coordinated Mars Time v2.0

This is the follow up of a previous question of mine.
In a nutshell, I am trying to follow this tutorial step-by-step: https://jtauber.github.io/mars-clock/ to get to Coordinated Mars Time, but I got stuck right before the end. My code works fine up until the end (some values are more accurate than in the tutorial because I went back to the source from NASA: https://www.giss.nasa.gov/tools/mars24/help/algorithm.html ):
double millis = ( [[NSDate date] timeIntervalSince1970] * 1000 );
NSLog(#"millis: %f", millis);
double JDUT = ( 2440587.5 + (millis / 86400000) );
NSLog(#"JDUT: %f", JDUT);
double JDTT = ( JDUT + (37 +32.184) / 86400);
NSLog(#"JDTT: %f", JDTT);
double J2000Epoch = ( JDTT - 2451545.0 );
NSLog(#"J2000Epoch: %f", J2000Epoch);
double MSD = ( (( J2000Epoch - 4.5 ) / 1.0274912517) + 44796.0 - 0.0009626 );
NSLog(#"MSD: %f", MSD);
The only step remaining is actually calculating Coordinated Mars Time, using this equation:
MTC = mod24 { 24 h × MSD }
The problem is that I have no idea how. I tried to use modf( (double), (double *) ) but no idea how it actually works. I tried it the way below, but it gave me an incorrect answer (obviously as I have really no idea what I am doing). :(
double MSD24 = (24 * MSD);
double MCT = modf(24, &MSD24);
NSLog(#"MCT: %f", MCT); // Result: 0.000000
Any help would be much appreciated. Thank you very much!
p.s.: Notice that I use Objective-C; I do not understand swift unfortunately! :(
Carrying on from the code you gave, I tried:
CGFloat MTC = fmod(24 * MSD, 24);
and got
// 19.798515
which was right according to the web page you cited at the moment I tried it.
The sort of thing his page actually shows, e.g. "19:49:38" or whatever (at the time I tried it), is merely a string representation of that number, treating it as a number of hours and just dividing it up into minutes and seconds in the usual way. Which, I suppose, brings us to the second part of your question, i.e. how to convert a number of hours into an hours-minutes-seconds representation? But that is a simple matter, dealt with many times here. See NSNumber of seconds to Hours, minutes, seconds for example.
So, carrying on once again, I tried this:
CGFloat secs = MTC*3600;
NSDate* d = [NSDate dateWithTimeIntervalSince1970:secs];
NSDateFormatter* df = [NSDateFormatter new];
df.dateFormat = #"HH:mm:ss";
df.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSString* result = [df stringFromDate:d];
NSLog(#"%#", result); // 20:10:20
...which is exactly the same as his web page was showing at that moment.
And here's a Swift version for those who would like to know what the "mean time" is on Mars right now:
let millis = Date().timeIntervalSince1970 * 1000
let JDUT = 2440587.5 + (millis / 86400000)
let JDTT = JDUT + (37 + 32.184) / 86400
let J2000Epoch = ( JDTT - 2451545 )
let MSD = (( J2000Epoch - 4.5 ) / 1.0274912517) + 44796.0 - 0.0009626
let MTC = (24 * MSD).truncatingRemainder(dividingBy: 24)
let d = Date(timeIntervalSince1970: MTC*3600)
let df = DateFormatter()
df.dateFormat = "HH:mm:ss"
df.timeZone = TimeZone(abbreviation: "GMT")!
df.string(from:d)

swift3 Date to Data, Data to Date convert

I am working on changing the code created in objective c to swift3.
I want to change the code below to the swift3 code created with objective c.
Objective c NSDate to NSData code :
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:NSDayCalendarUnit |NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:[NSDate date]];
NSInteger year = components.year;
NSMutableData *yearData = [[NSMutableData alloc] initWithBytes:&year length:sizeof(year)];
int year1 = *(int *)[[yearData subdataWithRange:NSMakeRange(0, 1)] bytes];
int year2 = *(int *)[[yearData subdataWithRange:NSMakeRange(1, 1)] bytes];
int month = components.month;
int day = components.day;
int hour = components.hour;
int min = components.minute;
int second = components.second;
char bytes[7];
bytes[0] = year1;
bytes[1] = year2;
bytes[2] = month;
bytes[3] = day;
bytes[4] = hour;
bytes[5] = min;
bytes[6] = second;
NSData *data = [[NSData alloc] initWithBytes:&bytes length:sizeof(bytes)];
Objective c NSData to NSDate code :
NSData *date = [[NSData alloc] initWithData:characteristic.value];
int year = *(int *)[[date subdataWithRange:NSMakeRange(0, 2)] bytes];
int month = *(int *)[[date subdataWithRange:NSMakeRange(2, 1)] bytes];
int day = *(int *)[[date subdataWithRange:NSMakeRange(3, 1)] bytes];
int hour = *(int *)[[date subdataWithRange:NSMakeRange(4, 1)] bytes];
int minutes = *(int *)[[date subdataWithRange:NSMakeRange(5, 1)] bytes];
int seconds = *(int *)[[date subdataWithRange:NSMakeRange(6, 1)] bytes];
NSLog(#"year %d month %d day %d hour %d minutes %d second %d", year, month, day, hour, minutes, seconds); //year 2017 month 7 day 13 hour 16 minutes 8 second 2
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setYear:year];
[components setMonth:month];
[components setDay:day];
[components setHour:hour];
[components setMinute:minutes];
[components setSecond:seconds];
NSCalendar *calendar = [NSCalendar currentCalendar];
self.time = [calendar dateFromComponents:components];
Swift Date to Data code :
let cal = Calendar(identifier: .gregorian)
var comp = cal.dateComponents([.day,.month,.year,.hour,.minute,.second], from: Date())
var year = comp.year
let yearData:Data = Data(bytes: &year, count: MemoryLayout.size(ofValue: year))
let year1:Data = yearData.subdata(in: 0..<1)
let year2:Data = yearData.subdata(in: 1..<2)
let settingArray = [UInt8]([
UInt8(year1[0])
, UInt8(year2[0])
, UInt8(comp.month!)
, UInt8(comp.day!)
, UInt8(comp.hour!)
, UInt8(comp.minute!)
, UInt8(comp.second!)
])
let settingData:Data = Data(bytes: settingArray, count: MemoryLayout.size(ofValue: settingArray))
Swift Data to Date code :
var yearVal:UInt8 = 0
let year = characteristic.value?.subdata(in: 0..<2)
year?.copyBytes(to: &yearVal, count: MemoryLayout.size(ofValue: year))
var month = characteristic.value?.subdata(in: 2..<3)
var day = characteristic.value?.subdata(in: 3..<4)
var hour = characteristic.value?.subdata(in: 4..<5)
var minutes = characteristic.value?.subdata(in: 5..<6)
var seconds = characteristic.value?.subdata(in: 6..<7)
print("year = \(yearVal), month = \(Int((month?[0])!)), day = \(Int((day?[0])!)), hour = \(Int((hour?[0])!)), minutes = \(Int((minutes?[0])!)), seconds = \(Int((seconds?[0])!))") // year = 225, month = 7, day = 13, hour = 15, minutes = 56, seconds = 56
When I modify the let year = characteristic.value?.subdata(in: 0..<2) part, the conversion value should be 2017. However, only 225 values are output. I do not know how to solve this part.
Please help me.
You are very lucky your Objective-C code works as you are reading unassigned memory and ignoring endian issues.
Consider the line:
int month = *(int *)[[date subdataWithRange:NSMakeRange(2, 1)] bytes];
Here you are taking a pointer to a single byte, casting it to a pointer to 4 bytes (the size on an int), and then reading 4 bytes and storing them in month. By luck the extra three bytes you read happen to be zero.
Then there is the endian issue, different cpu architectures store multi-byte values in different orders in memory. A little-endian architecture stores the least significant byte first, a big-endian one the most significant.
E.g. the 4-byte integer 0xDEADBEEF is stored as the byte sequence EF, BE, AD, DE on a little-endian machine and as DE, AD, BE, EF on a big-endian one. What this means in terms of your month value above is if the byte is 06 then you might get back the integer 0x06000000 when you read those 4 bytes (and only if those extra bytes are zeroes).
For the month case you could load the byte and then convert to an integer:
int month = (int *)(*(Byte *)[[date subdataWithRange:NSMakeRange(2, 1)] bytes]);
When converting the year to two bytes you go through the long winded process:
NSMutableData *yearData = [[NSMutableData alloc] initWithBytes:&year length:sizeof(year)];
int year1 = *(int *)[[yearData subdataWithRange:NSMakeRange(0, 1)] bytes];
int year2 = *(int *)[[yearData subdataWithRange:NSMakeRange(1, 1)] bytes];
This converts an integer to an NSData, makes to more NSData values containing 1 byte each, and then loads 4 bytes for each - the same issue as above, but in this case as you will only be storing 1 byte in your bytes array it doesn't matter if the extra bytes are garbage.
The process is convoluted, you would be better off sticking with integer operations to obtain the two values. You can obtain the individual bytes using division and remainder operations, or bit-wise shift and mask operations.
E.g. using decimal first to demonstrate:
int year = 2017;
int firstDigit = year % 10; // the remainder of year / 10 => 7
int secondDigit = (year / 10) % 10; // 1
int thirdDigit = (year / 100) % 10; // 0
int fourthDigit = (year / 1000) % 10; // 2
To extract the bytes just change the divisor:
int year = 2017; // = 0x7E1
int loByte = year % 256; // = 0xE1
int hiByte = (year / 256) % 256; // = 0x7
Finally you can use bit-wise shift and masking:
int year = 2017; // = 0x7E1
int loByte = year & 0xFF; // = 0xE1
int hiByte = (year >> 8) & 0xFF; // = 0x7
Using bit-wise operations makes the byte splitting more obvious, but divide and remainder achieve the same result.
What does all this mean in terms of your Objective-C code? Well the second of your two methods can be written:
+ (NSDate *) dataToDate:(NSData *)data
{
NSDateComponents *components = [[NSDateComponents alloc] init];
const Byte *bytes = data.bytes;
components.year = (NSInteger)bytes[0] | ((NSInteger)bytes[1] << 8); // reassemble 2-byte value
components.month = (NSInteger)bytes[2];
components.day = (NSInteger)bytes[3];
components.hour = (NSInteger)bytes[4];
components.minute = (NSInteger)bytes[5];
components.second = (NSInteger)bytes[6];
NSCalendar *calendar = [NSCalendar currentCalendar];
return[calendar dateFromComponents:components];
}
This is a lot less complex, doesn't read random memory, and is easier to convert to Swift.
Following the same approach here is your first method in Swift:
func toData(_ date : Date) -> Data
{
let cal = Calendar(identifier: .gregorian)
let comp = cal.dateComponents([.day,.month,.year,.hour,.minute,.second], from: date)
let year = comp.year!
let yearLo = UInt8(year & 0xFF) // mask to avoid overflow error on conversion to UInt8
let yearHi = UInt8(year >> 8)
let settingArray = [UInt8]([
yearLo
, yearHi
, UInt8(comp.month!)
, UInt8(comp.day!)
, UInt8(comp.hour!)
, UInt8(comp.minute!)
, UInt8(comp.second!)
])
return Data(bytes: settingArray)
}
Finally, you can index the Data type in Swift lust like an array, so the above Objective-C line:
components.month = (NSInteger)bytes[2];
where bytes came from calling NSData's bytes can be written directly in Swift as:
components.month = Int(data[2])
where data is the Data value.
The above approach doesn't answer the issue you actually had, because it avoids messing with splitting data values into bits and trying to extra values from them - just index the byte and convert with a cast.
The rest of the code you need is left as an excercise!
HTH
you are fetching year value as UInt8 which only have range of 0-255 so use UInt32
var yearVal: UInt32 = 0
(year as! NSData).getBytes(&yearVal, length: MemoryLayout.size(ofValue: year))

Query on dataPicker

Suppose my business hours are from 7am to 7pm, then default selection of time date is :
A) During business hours, a hour later than current time
B) After business hour, 7 am the next business day.
How will you calculate value for Minimum and maximum date ??
Sol -A)
NSDate *minDate = [[NSDate date]dateByAddingTimeInterval:60 * 60 *1];
NSDate *maxDate = [[NSDate date]dateByAddingTimeInterval:60 * 60 * 24 * 2];
Is above solution for (A) is correct ?? is there any other way
Can anyone provide solution for (B) ??
Please provide proper programming code in objective C with explanation ??

Display a negative time in Obj-C

It may sound simple however I am unsure how to do this. How do I display time in the following format?:
-00:00:00
I have tried using float and int values of the interval difference between two times however neither give a consistent display in the 00:00:00 format. I have also tried converting the time difference into date and then display as String.
This is the code I have used to convert my intervals :
NSDate * now = [NSDate date];
NSTimeInterval totalTime1 = [now timeIntervalSinceDate: timeEntry1];
NSTimeInterval totalTime2 = [now timeIntervalSinceDate: timeEntry2];
//must always be this way
int adjustedTime = totalTime1 - totalTime2;
int hours = adjustedTime / 3600;
int minutes = (adjustedTime / 60) % 60;
int seconds = adjustedTime % 60;
NSString * newTime = [NSString stringWithFormat:#"%02u:%02u:%02u", hours, minutes, seconds];
The above works fine for displaying positive time differences. However presents a variety of 00:423456:978098 and so on when it goes negative in both the NSLog and the Label.
When I convert and save as a type of NSDate I get (null) in NSLog and nothing in my Label.
When I use float it works but does not consistently display in the 00:00:00 format.
NOTE
The code I am using works immaculately for positive time differences. I need negative time differences to also display.
I also need to be able to save the negative time to CoreData. If this is not possible then I will work around, but displaying negative time formatted correctly is the main issue.
EDIT
My new revised code:
NSDate * now = [NSDate date];
NSTimeInterval totalTime1 = [now timeIntervalSinceDate: timeEntry1];
NSTimeInterval totalTime2 = [now timeIntervalSinceDate: timeEntry2];
//must always be this way
int adjustedTime = (int) (totalTime1 - totalTime2);
NSLog (#"What is the adjustedTime? %d", adjustedTime);
int hours = adjustedTime / 3600;
int minutes = (adjustedTime / 60) % 60;
int seconds = adjustedTime % 60;
NSString * newTime = [NSString stringWithFormat:#"%#%02d:%02d:%02d", adjustedTime < 0 ?#"-":#"", hours, minutes, seconds];
NSLog(#"What is the newTime? %#", newTime);
It is closer as now it displays negative numbers however the display is still incorrect when negative.
EDIT 2
A person who answered below suggested I try checking for negative if it is a boolean. Displaying did not change. Below are more log statements to demonstrate. NOTE I stopped using an updated seconds for sake of working out whether it affected the display and stored seconds separate to test, which is why there is no - sign or alteration to the seconds.
2015-01-09 09:30:14.526 App2.0[8398:498707] What is time 2 : 720
2015-01-09 09:30:14.526 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:14.526 App2.0[8398:498707] What is the adjusted time? 51
2015-01-09 09:30:14.527 App2.0[8398:498707] New Time: 00:00:51
2015-01-09 09:30:18.249 App2.0[8398:498707] What is time 2 : 900
2015-01-09 09:30:18.249 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:18.249 App2.0[8398:498707] What is the adjusted time? -129
2015-01-09 09:30:18.249 App2.0[8398:498707] New Time: -00:-2:51
2015-01-09 09:30:20.281 App2.0[8398:498707] What is time 2 : 840
2015-01-09 09:30:20.281 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:20.281 App2.0[8398:498707] What is the adjusted time? -69
2015-01-09 09:30:20.281 App2.0[8398:498707] New Time: -00:-1:51
2015-01-09 09:30:21.725 App2.0[8398:498707] What is time 2 : 780
2015-01-09 09:30:21.726 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:21.726 App2.0[8398:498707] What is the adjusted time? -9
2015-01-09 09:30:21.726 App2.0[8398:498707] New Time: -00:00:51
2015-01-09 09:30:30.161 App2.0[8398:498707] What is time 2 : 1080
2015-01-09 09:30:30.161 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:30.162 App2.0[8398:498707] What is the adjusted time? -309
2015-01-09 09:30:30.162 App2.0[8398:498707] New Time: -00:-5:51
2015-01-09 09:30:33.389 App2.0[8398:498707] What is time 2 : 4680
2015-01-09 09:30:33.389 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:33.390 App2.0[8398:498707] What is the adjusted time? -3909
2015-01-09 09:30:33.390 App2.0[8398:498707] New Time: --1:-5:51
2015-01-09 09:30:36.186 App2.0[8398:498707] What is time 2 : 8280
2015-01-09 09:30:36.187 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:36.187 App2.0[8398:498707] What is the adjusted time? -7509
2015-01-09 09:30:36.187 App2.0[8398:498707] New Time: --2:-5:51
2015-01-09 09:30:43.918 App2.0[8398:498707] What is time 2 : 660
2015-01-09 09:30:43.918 App2.0[8398:498707] What is time 1 : 771
2015-01-09 09:30:43.919 App2.0[8398:498707] What is the adjusted time? 111
2015-01-09 09:30:43.919 App2.0[8398:498707] New Time: 00:01:51
Since you're displaying the time components separately, you'll probably need some conditional logic to adjust the display depending on whether totalTime2 is before or after totalTime1
:
NSString *newTime = nil;
if (adjustedTime < 0) {
newTime = [NSString stringWithFormat:#"-%02d:%02d:%02d", hours, minutes, seconds];
}
else {
newTime = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
}
or if you prefer something more compact:
NSString *newTime = adjustedTime < 0 ? #"-" : #"";
newTime = [newTime stringByAppendingFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
Also, as Wain points out in the comments, you'll need to take the absolute value of each component before using it to display:
int hours = abs(adjustedTime / 3600);
int minutes = abs((adjustedTime / 60) % 60);
int seconds = abs(adjustedTime % 60);
That should do the trick:
NSDate *now = [NSDate date];
NSTimeInterval totalTime1 = [now timeIntervalSinceDate: timeEntry1];
NSTimeInterval totalTime2 = [now timeIntervalSinceDate: timeEntry2];
NSLog(#"totalTime1: %f", totalTime1); // => -60.00;
NSLog(#"totalTime2: %f", totalTime2); // => 6023.00;
//must always be this way
int timeOffset = (int) (totalTime1 - totalTime2);
NSLog(#"timeOffset: %d", timeOffset); // => -6083;
BOOL showNegativePrefix = timeOffset < 0;
int hours = abs(timeOffset / 3600); // => 01
int minutes = abs((timeOffset / 60 ) % 60); // => 41
int seconds = abs(timeOffset % 60); // => 23
NSString * newTime = [NSString stringWithFormat:#"%#%02d:%02d:%02d", showNegativePrefix ? #"-" : #"", hours, minutes, seconds];
NSLog(#"%#", newTime); // => -01:41:23
You are almost there, all you need extra is a flag if the value is negative and to then format the positive difference preceded by a sign if needed. First set a flag and make the difference always positive:
int adjustedTime = ...;
BOOL isNeg;
if (adjustedTime < 0)
{
isNeg = YES;
adjustedTime = -adjustedTime; // make value +ve
}
else
isNeg = NO;
then do your math as before and change the format line to:
NSString *newTime = [NSString stringWithFormat:#"%#%02d:%02d:%02d", (isNeg ? #"-" : #""), hours, minutes, seconds];
Note: you need to use %d as your values are int not unsigned int.
HTH
Addendum
Here is the code again, including the bit of yours I skipped ("do your math as before"), with added comments:
int adjustedTime = totalTime1 - totalTime2;
// At this point adjustedTime may be negative, use the standard approach
// test if it is -ve, and if so set a flag and make the value +ve
BOOL isNeg;
if (adjustedTime < 0)
{
// we are here if -ve, set flag
isNeg = YES;
// and make the value +ve
adjustedTime = -adjustedTime;
}
else
isNeg = NO;
// at this point adjustedValue is ALWAYS +ve, isNeg is set if it was originally -ve
int hours = adjustedTime / 3600;
int minutes = (adjustedTime / 60) % 60;
int seconds = adjustedTime % 60;
// at this point hours, minutes & seconds MUST BE +VE as adjustedTime is +ve
// format the values, an optional sign based on isNeg followed by three POSITIVE numbers
NSString *newTime = [NSString stringWithFormat:#"%#%02d:%02d:%02d", (isNeg ? #"-" : #""), hours, minutes, seconds];
This can only print AT MOST ONE minus sign at the start from the string.
This approach (though with a test for the minimum negative integer as that cannot be negated) is the standard way to handle this issue.
The solutions presented by Sebastian Keller and CRD both suffer a problem when rolling over from negative to positive values, for example when displaying a countdown that starts with a negative value:
-1.896893 -00:00:01
-1.498020 -00:00:01
-1.099442 -00:00:01
-0.996686 00:00:00
-0.896971 00:00:00
-0.195021 00:00:00
-0.095020 00:00:00
0.004988 00:00:00
0.104940 00:00:00
0.504980 00:00:00
0.904981 00:00:00
1.000516 00:00:01
1.104926 00:00:01
As can be seen, both solutions actually display 00:00:00 for 2 seconds.
The code below instead produces the correct output:
-1.899441 -00:00:02
-1.499838 -00:00:02
-1.095019 -00:00:02
-0.994941 -00:00:01
-0.899903 -00:00:01
-0.195743 -00:00:01
-0.097564 -00:00:01
0.001758 00:00:00
0.100495 00:00:00
0.503691 00:00:00
0.904986 00:00:00
1.004998 00:00:01
1.103652 00:00:01
2.004936 00:00:02
It's actually a category I wrote on NSNumber but the principle should also be applicable to any floating point number:
- (NSString *)ut_hmsString {
NSString *sign = (self.floatValue < 0.0f) ? #"-" : #" ";
float corr = (self.floatValue < 0.0f) ? 1.0000001f : 0;
int seconds = fmodf(fabsf(floorf(self.floatValue)), 60.0f);
int fakesec = fabsf(self.floatValue - corr);
int minutes = fakesec / 60 % 60;
int hours = fakesec / 3600;
return [NSString stringWithFormat:#"%#%02i:%02i:%02i", sign, hours, minutes, seconds];
}

Resources