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))
Related
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)
I have the following variable:
NSNumber *consumption = [dict objectForKey:#"con"];
Which returns 42. How can I pad this number to 10 digits on the left, leading with zeros. The output should look as,
0000000042
or if it were 420,
0000000420
NSString *paddedStr = [NSString stringWithFormat:#"%010d", 42];
EDIT: It's C style formatting. %nd means the width is at least n. So if the integer is 2 digit long, then you will have length 3 string (when %3d is used). By default the left empty spaces are filled by space. %0nd (0 between % and n) means 0 is used for padding instead of space. Here n is the total length. If the integer is less than n digits then left padding is used.
The Objective-C way,
NSNumberFormatter * numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[numberFormatter setPaddingPosition:NSNumberFormatterPadBeforePrefix];
[numberFormatter setPaddingCharacter:#"0"];
[numberFormatter setMinimumIntegerDigits:10];
NSNumber * number = [NSNumber numberWithInt:42];
NSString * theString = [numberFormatter stringFromNumber:number];
NSLog(#"%#", theString);
The C way is faster though.
You can't in the NSNumber itself. If you're creating a string from the number or using NSLog(), simply use the appropriate format, e.g.
NSLog(#"%010d", [consumption intValue]);
You can do pretty much any number formatting you would ever want with NSNumberFormatter. In this case I think you would want to use the setFormatWidth: and setPaddingCharacter: functions.
with variable num_digits
NSString* format =[NSString stringWithFormat:#"%%0%zdzd", num_digits];
NSString *theString = [NSString stringWithFormat:format, 42];
E.g. Fill the rest with zeros, 5 digits:
NSInteger someValue = 10;
[NSString stringWithFormat:#"%05ld", someValue];
Equivalent of .02f for float number when you need only 2 digits after the dot.
So there you have 0 = fill with zeros, 5 = the number of digits and type.
Solution for Swift 3
let x = 1078.1243
let numFormatter = NumberFormatter()
numFormatter.minimumFractionDigits = 1 // for float
numFormatter.maximumFractionDigits = 1 // for float
numFormatter.minimumIntegerDigits = 10 // how many digits do want before decimal
numFormatter.paddingPosition = .beforePrefix
numFormatter.paddingCharacter = "0"
let s = numFormatter.string(from: NSNumber(floatLiteral: x))!
OUTPUT
"0000001078.1"
Not sure how to word the title correctly... but what I am wondering is if there is some clever format specifier that will take the number 4.5 and give me #"4.5" but also take the number 2 and give me #"2".
Using the %.1f specifier gives me #"4.5" but also #"2.0". I am trying to get rid of the ".0" bit.
Does such a beast exist, or am I going to have to do some math on this? FWIW, I am trying to iterate over an array of values ranging from 0 to 5 increasing in half-steps, so 0, 0.5, 1, 1.5, ..., 4.5, 5
Cheers!
NSNumberFormatter is a good choice here. You can configure it to not show the fractional digits if the number is an integer. For example:
NSArray *numbers = #[#0, #0.5, #1.0, #1.5, #2.0, #2.5];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.alwaysShowsDecimalSeparator = NO;
numberFormatter.minimumFractionDigits = 0;
numberFormatter.maximumFractionDigits = 1;
numberFormatter.minimumIntegerDigits = 1;
for (NSNumber *number in numbers) {
NSLog(#"%#", [numberFormatter stringFromNumber:number]);
}
Output:
>> 0
>> 0.5
>> 1
>> 1.5
>> 2
>> 2.5
This is even easier (Swift):
let num1: Double = 5
let num2: Double = 5.52
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .DecimalStyle
print(numberFormatter.stringFromNumber(NSNumber(double: num1)))
print(numberFormatter.stringFromNumber(NSNumber(double: num2)))
This will print 5 and then 5.52.
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;
Hi I have the code to separate hour,min,sec
Now i have to convert it in to seconds.and nsnumber
NSRange range = [string rangeOfString:#":"];
NSString *hour = [string substringToIndex:range.location];
NSLog(#"time %#",hour);
NSRange range1= NSMakeRange(2,2);
NSString *min = [string substringWithRange:range1];
NSLog(#"time %#",min);
NSRange range2 = NSMakeRange(5,2);
NSString *sec = [string substringWithRange:range2];
NSLog(#"time %#",sec);
If you want to find out how many seconds the hours, minutes and seconds total, you can do something like this:
- (NSNumber *)secondsForTimeString:(NSString *)string {
NSArray *components = [string componentsSeparatedByString:#":"];
NSInteger hours = [[components objectAtIndex:0] integerValue];
NSInteger minutes = [[components objectAtIndex:1] integerValue];
NSInteger seconds = [[components objectAtIndex:2] integerValue];
return [NSNumber numberWithInteger:(hours * 60 * 60) + (minutes * 60) + seconds];
}
Just an alternative if you have to handle both "HH:mm:ss" and "mm:ss"
extension String {
/**
Converts a string of format HH:mm:ss into seconds
### Expected string format ###
````
HH:mm:ss or mm:ss
````
### Usage ###
````
let string = "1:10:02"
let seconds = string.inSeconds // Output: 4202
````
- Returns: Seconds in Int or if conversion is impossible, 0
*/
var inSeconds : Int {
var total = 0
let secondRatio = [1, 60, 3600] // ss:mm:HH
for (i, item) in self.components(separatedBy: ":").reversed().enumerated() {
if i >= secondRatio.count { break }
total = total + (Int(item) ?? 0) * secondRatio[i]
}
return total
}
}
Swift 4 - improved from #Beslan Tularov's answer.
extension String{
var integer: Int {
return Int(self) ?? 0
}
var secondFromString : Int{
var components: Array = self.components(separatedBy: ":")
let hours = components[0].integer
let minutes = components[1].integer
let seconds = components[2].integer
return Int((hours * 60 * 60) + (minutes * 60) + seconds)
}
}
Usage
let xyz = "00:44:22".secondFromString
//result : 2662
You can also try like this:
extension String{
//format hh:mm:ss or hh:mm or HH:mm
var secondFromString : Int{
var n = 3600
return self.components(separatedBy: ":").reduce(0) {
defer { n /= 60 }
return $0 + (Int($1) ?? 0) * n
}
}
}
var result = "00:44:22".secondFromString //2662
result = "00:44".secondFromString //2640
result = "01:44".secondFromString //6240
result = "999:10".secondFromString //3597000
result = "02:44".secondFromString //9840
result = "00:01".secondFromString //60
result = "00:01:01".secondFromString //61
result = "01:01:01".secondFromString //3661
result = "12".secondFromString //43200
result = "abcd".secondFromString //0
From what you've,
double totalSeconds = [hour doubleValue] * 60 * 60 + [min doubleValue] * 60 + [sec doubleValue];
NSNumber * seconds = [NSNumber numberWithDouble:totalSeconds];
The following is a String extension for converting a time string (HH:mm:ss) to seconds
extension String {
func secondsFromString (string: String) -> Int {
var components: Array = string.componentsSeparatedByString(":")
var hours = components[0].toInt()!
var minutes = components[1].toInt()!
var seconds = components[2].toInt()!
return Int((hours * 60 * 60) + (minutes * 60) + seconds)
}
}
how to use
var exampleString = String().secondsFromString("00:30:00")
You can use the following extension to do that (Swift 3):
extension String {
func numberOfSeconds() -> Int {
var components: Array = self.components(separatedBy: ":")
let hours = Int(components[0]) ?? 0
let minutes = Int(components[1]) ?? 0
let seconds = Int(components[2]) ?? 0
return (hours * 3600) + (minutes * 60) + seconds
}
}
And for example, use it like:
let seconds = "01:30:10".numberOfSeconds()
print("%# seconds", seconds)
Which will print:
3790 seconds