Date in darwin standard library giving me the wrong date? - ios

What is wrong with this code in swift accessing the time and date functions in C? The date it gives me is off by 3 days even though the difftime function is correct on the time difference.
import Darwin
var time1 = tm(tm_sec: 00, tm_min: 00, tm_hour: 00, tm_mday: 13, tm_mon: 06, tm_year: 1977, tm_wday: 0, tm_yday: 0, tm_isdst: 0, tm_gmtoff: 0, tm_zone: nil)
var time1secs = timegm(&time1)
var time2secs = timegm(&time1) + 1_000_000_000
var time2 = gmtime(&time2secs).memory
difftime(time2secs, time1secs) // 1,000,000,000
print("\(time2.tm_year)-\(time2.tm_mon)-\(time2.tm_mday)") //2009-2-22
// The correct answer is 2009-02-19

In the struct tm, the tm_year field is the number of years
since 1900, and tm_mon is the month in the range 0 .. 11:
// struct tm for 1977/06/13:
var time1 = tm()
time1.tm_year = 1977 - 1900
time1.tm_mon = 06 - 1
time1.tm_mday = 13
// Add 10^9 seconds:
var time2secs = timegm(&time1) + 1_000_000_000
var time2 = gmtime(&time2secs).memory
// Extract year/month/day:
let year = time2.tm_year + 1900
let month = time2.tm_mon + 1
let day = time2.tm_mday
print("\(year)-\(month)-\(day)") // 2009-2-19

Related

Array containing the 24 sequence of 24 hours before 24 hour time

I have found a way to find the current 24 hour time of the user through
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date) --> e.g.(15)
I am wanting to create an array of a sequence of 24 hours before that time. An example of this is below-
15
14
...
2
1
24
23
... and so on
I have been able
to create the list to 1 but instead of going to 0, I have not been able to create a formula that will go to 24.
I currently have -
for i in 0...24 {
var previousHours = 0
if i >= hour{
previousHours = hour - 24 + i
} else {
previousHours = hour - i
}
hoursArray.append(previousHours)
}
Which for hour 20, this list is formed-
21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,18,19,20,21
Instead of
hour - 24 + I
What formula would produce the correct results? Is there an easier way to have the same result?
Try like this
for i in 1...24 {
hoursArray.append(i)
}
let myHour = 20 //like your example
let hoursAfter = hoursArray.filter { $0 > myHour }
let hoursBefore = hoursArray.filter { $0 < myHour }
let cycle = hoursAfter + hoursBefore
Here you have hours from 21 - 24 and then from 1 - 19
or if you wnat another way around, make
cycle.reversed()
Maybe like something like this?
var hrs = [Int]()
var start = 15
while hrs.count < 24 {
if start < 24 {
hrs.append(start)
start += 1
} else {
start = 1
}
}
hrs.reverse()
Use calendar to calculate as mentioned by the above comment, this one goes from 0-23:
var hrs = [Int]()
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
var dayComp = DateComponents()
dayComp.calendar = calendar
dayComp.hour = 0
while hrs.count < 24 {
let next = calendar.date(byAdding: dayComp, to: date)
hrs.append(calendar.component(.hour, from: next!))
dayComp.hour! -= 1
}
Output:
18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 23, 22, 21, 20, 19
A small change in your formula from
hour - 24 + i
to
hour - i + 24
may solve the problem and will provide output you requires.

Are there time zone abbreviations for UTC-12 and UTC+12?

I'm working on a problem where I want to do calculations on NSDates where a single NSDate gives different dd/mm/yyyy values in different time zones.
To do that I'm currently using New York City (EST) and Aukland, NZ, since they are frequently on different dates.
I'd like to be able to use the time zones on either side of the international date line, UTC+12, and UTC-12. There appears to be a standard abbreviation for UTC+12, ANAT, for Anadyr, Russia. However, the iOS implementation of TimeZone/NSTimeZone doesn't seem to recognize it. There also does not seem to be an abbreviation for UTC-12 (which would be in Alaska).
Does anybody know if there are such abbreviations for UTC+12 and UTC-12 that iOS (or Mac OS, for that matter) recognizes?
It looks like the answer is no.
I wrote some code to fetch all the system time zones, sort them by offset, and print them:
typealias timeZoneTuple = (abbreviation: String, name: String, offset: Int)
let timeZones = TimeZone.abbreviationDictionary
let mappedTimeZones: [timeZoneTuple] = timeZones
.map {key, value in
var offset = 0
if let timeZone = TimeZone(abbreviation: key) {
offset = timeZone.secondsFromGMT() / 3600
}
return (abbreviation: key, name: value, offset:offset)}
.sorted {$0.offset < $1.offset}
mappedTimeZones.forEach {
let abbreviation = $0.abbreviation.padding(toLength: 4, withPad: " ", startingAt: 0)
let name = $0.name.padding(toLength: 20, withPad: " ", startingAt: 0)
print("abbreviation = \(abbreviation), offset = \(name), val = \($0.offset)")}
The output of the above code is:
abbreviation = HST , offset = Pacific/Honolulu , val = -10
abbreviation = AKDT, offset = America/Juneau , val = -9
abbreviation = AKST, offset = America/Juneau , val = -9
abbreviation = PST , offset = America/Los_Angeles , val = -8
abbreviation = PDT , offset = America/Los_Angeles , val = -8
abbreviation = MDT , offset = America/Denver , val = -7
abbreviation = MST , offset = America/Denver , val = -7
abbreviation = CDT , offset = America/Chicago , val = -6
abbreviation = CST , offset = America/Chicago , val = -6
abbreviation = EDT , offset = America/New_York , val = -5
abbreviation = PET , offset = America/Lima , val = -5
abbreviation = EST , offset = America/New_York , val = -5
abbreviation = COT , offset = America/Bogota , val = -5
abbreviation = ADT , offset = America/Halifax , val = -4
abbreviation = AST , offset = America/Halifax , val = -4
abbreviation = CLT , offset = America/Santiago , val = -3
abbreviation = CLST, offset = America/Santiago , val = -3
abbreviation = ART , offset = America/Argentina/Bu, val = -3
abbreviation = BRST, offset = America/Sao_Paulo , val = -2
abbreviation = BRT , offset = America/Sao_Paulo , val = -2
abbreviation = GMT , offset = GMT , val = 0
abbreviation = WET , offset = Europe/Lisbon , val = 0
abbreviation = BST , offset = Europe/London , val = 0
abbreviation = WEST, offset = Europe/Lisbon , val = 0
abbreviation = UTC , offset = UTC , val = 0
abbreviation = CEST, offset = Europe/Paris , val = 1
abbreviation = WAT , offset = Africa/Lagos , val = 1
abbreviation = CET , offset = Europe/Paris , val = 1
abbreviation = CAT , offset = Africa/Harare , val = 2
abbreviation = MSD , offset = Europe/Moscow , val = 3
abbreviation = EAT , offset = Africa/Addis_Ababa , val = 3
abbreviation = IRST, offset = Asia/Tehran , val = 3
abbreviation = MSK , offset = Europe/Moscow , val = 3
abbreviation = EET , offset = Europe/Istanbul , val = 3
abbreviation = EEST, offset = Europe/Istanbul , val = 3
abbreviation = GST , offset = Asia/Dubai , val = 4
abbreviation = IST , offset = Asia/Calcutta , val = 5
abbreviation = PKT , offset = Asia/Karachi , val = 5
abbreviation = BDT , offset = Asia/Dhaka , val = 6
abbreviation = WIT , offset = Asia/Jakarta , val = 7
abbreviation = ICT , offset = Asia/Bangkok , val = 7
abbreviation = SGT , offset = Asia/Singapore , val = 8
abbreviation = HKT , offset = Asia/Hong_Kong , val = 8
abbreviation = PHT , offset = Asia/Manila , val = 8
abbreviation = KST , offset = Asia/Seoul , val = 9
abbreviation = JST , offset = Asia/Tokyo , val = 9
abbreviation = NZDT, offset = Pacific/Auckland , val = 13
abbreviation = NZST, offset = Pacific/Auckland , val = 13
So it looks like UTC-12, UTC-11, UTC-1, UTC+10, UTC+11, and UTC+12 are all missing from the "named" timezones that are available in Cocoa.
EDIT:
Based on a comment from #MattJohnson, it seems that the identifiers is a better way to get the list of available time zones. Modifying my code to use identifiers instead:
struct timeZoneStruct: CustomStringConvertible {
let identifier: String
var offset: Int
var description: String {
let displayOffset = String(format: "%3d", offset)
let displayIdentifier = (identifier + ",").padding(toLength: 30, withPad: " ", startingAt: 0)
return "identifier = \(displayIdentifier) offset = \(displayOffset)"
}
}
let timeZoneIDs = TimeZone.knownTimeZoneIdentifiers
let mappedTimeZones: [timeZoneStruct] = timeZoneIDs
.map {identifier in
var offset = 0
if let timeZone = TimeZone(identifier: identifier) {
offset = timeZone.secondsFromGMT() / 3600
}
return timeZoneStruct(identifier: identifier, offset: offset)}
.sorted {$0.offset < $1.offset}
mappedTimeZones.forEach {
print($0.description)
}
That yields a list of time zones ranging from UTC-11 (Pacific/Pago_pago) to UTC+14 (Pacific/Apia)
(There are quite a few duplicates for most time zones, so the list is too long to include here.)
So it seems there are defined time zones for offsets from UTC-11 to UTC+14. There is not a time zone for UTC-12 however, even though Baker Island, at Lat/Long: 0°12'N / 176°29'W, is in UTC-12. Curious.
This is the simplest way to get all timezones with their respective abbreviation.
P.S Not all timezones have their proper 3-Letter Abbreviations.
let timezoneList = NSTimeZone.knownTimeZoneNames
for i in 0...timezoneList.count - 1 {
let locale = NSTimeZone.init(name: timezoneList[i])
print("Region: \((locale?.name)!) Abbr: \((locale?.abbreviation)!)")
}
Also total 51, 3-lettered Abbreviations are present:
print(TimeZone.abbreviationDictionary.count)
You can also explore https://developer.apple.com/documentation/foundation/timezone for more.

how to query by date offset in Parse with Swift?

I want to set a constraint on a Parse query, that takes a birthday ( date), and only gathers results which are within 10 years.
So if the date is something like (1954-01-10 07:00:00 +0000)
then I want to get all records from 1944 to 1964.
Is there some way to do this using Parse query code?
Or,
do I have to obtain the date, then use swift code to offset it by 10 years, then write something like this
let currentUserBirthday = PFUser.currentUser()?.objectForKey("birthday")!
// set date 10 years greater and lower than currentUserBirthday
let datePlus10 = // add 10 years to date
let dateMinus10 = // subtract 10 years from date
dailyFourQuery?.whereKey("birthday", greaterThanOrEqualTo: datePlus10)
dailyFourQuery?.whereKey("birthday", lessThanOrEqualTo: dateMinus10)
edit: hey guys, i solved this by getting the age from the date, then adding or subtracting the integer offset from that number,
then using NSCalendar and creating components with modified values.
Thanks for all the help.
let currentUserBirthdayNSDate = currentUserBirthday as! NSDate
let dateComponents = calendar.components([NSCalendarUnit.Day, NSCalendarUnit.Month, NSCalendarUnit.Year, NSCalendarUnit.WeekOfYear, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second, NSCalendarUnit.Nanosecond], fromDate: currentUserBirthdayNSDate)
let componentsPlus10 = NSDateComponents()
componentsPlus10.day = dateComponents.day
componentsPlus10.month = dateComponents.month
componentsPlus10.year = dateComponents.year + 10
componentsPlus10.hour = dateComponents.hour
componentsPlus10.minute = dateComponents.minute
You can refer to SwiftDate
// Reference date is: Thu, 19 Nov 2015 19:00:00 UTC (1447959600 from 1970)
let refDate = NSDate(timeIntervalSince1970: 1447959600)
// Remember: all parameters are optional; in this example we have ignored minutes and seconds
let newDate = refDate.add(years: 1, months: 2, days: 1, hours: 2)
// newdate is 2017-01-21 14:00:00 +0000
// This is equivalent to
let newDate2 = refDate + 1.years + 2.months + 1.days + 2.hours
do you want this?

Confusion about NSTimeZone.secondsFromGMT

I am developing an app that has a feature to enter dark/night mode during night hours automatically. The app asks for the user location and determines the sunrise/sunset hour (in Universal Time) using this algorithm.
The only step that is not clear is to convert from UT to local time, since this is not explained in the algorithm. Say I get a sunrise time of 8.5 (8:30 in the morning UT). How could I convert it to user's local time to check if it's day or night? Or equivalently, how could I convert user's local time to UT in order to be able to compare them?
So far I've tried to use NSCalendar to get the NSDateComponents of the current date (NSDate()). One of these components is a NSTimeZone? from which I can get the secondsFromGMT. Something like this:
let dateComponents = NSCalendar.currentCalendar().components([.TimeZone], fromDate: NSDate())
let localOffset = Double(dateComponents.timeZone?.secondsFromGMT ?? 0)/3600
where localOffset should be the time difference (in hours) from UT (i.e. GMT if I am right) to local time, defaulting to 0 if dateComponents.timeZone == nil (I don't know under which situations this could happen). The problem is that I get the same localOffset for now than for 6 months in the future (when the daylight saving time will be different than it is now at my location, Spain). Does this mean that I need to use the properties daylightSavingTime and/or daylightSavingTimeOffset together with secondsFromGMT? Doesn't secondsFromGMT itself account for this?
Things get even more confusing to me when I read the results from the algorithm. The sun setting hour (in local time) is exactly the one given by Google, but the sun rising hour is one hour ahead of what Google says (for my location and date). I share with you the whole Swift implementation of the algorithm hoping that it can help someone spot what's that I'm doing wrong.
import Foundation
import CoreLocation
enum SunriseSunsetZenith: Double {
case Official = 90.83
case Civil = 96
case Nautical = 102
case Astronomical = 108
}
func sunriseSunsetHoursForLocation(coordinate: CLLocationCoordinate2D, atDate date: NSDate = NSDate(), zenith: SunriseSunsetZenith = .Civil) -> (sunrise: Double, sunset: Double) {
// Initial values (will be changed later)
var sunriseTime = 7.5
var sunsetTime = 19.5
// Get the longitude and latitude
let latitude = coordinate.latitude
let longitude = coordinate.longitude
// Get the day, month, year and local offset
let dateComponents = NSCalendar.currentCalendar().components([.Day, .Month, .Year, .TimeZone], fromDate: date)
let day = Double(dateComponents.day)
let month = Double(dateComponents.month)
let year = Double(dateComponents.year)
let localOffset = Double(dateComponents.timeZone?.daylightSavingTimeOffset ?? 0)/3600
// Calculate the day of the year
let N1 = floor(275*month/9)
let N2 = floor((month + 9)/12)
let N3 = 1 + floor((year - 4*floor(year/4) + 2)/3)
let dayOfYear = N1 - N2*N3 + day - 30
for i in 0...1 {
// Convert the longitude to hour value and calculate an approximate time
let longitudeHour = longitude/15
let t = dayOfYear + ((i == 0 ? 6.0 : 18.0) - longitudeHour)/24
// Calculate the Sun's mean anomaly
let M = 0.9856*t - 3.289
// Calculate the Sun's true longitude
var L = M + 1.916*sind(M) + 0.020*sind(2*M) + 282.634
L %= 360
// Calculate the Sun's right ascension
var RA = atand(0.91764 * tand(L))
RA %= 360
let Lquadrant = (floor(L/90))*90
let RAquadrant = (floor(RA/90))*90
RA += Lquadrant - RAquadrant
RA /= 15
// Calculate the Sun's declination
let sinDec = 0.39782*sind(L)
let cosDec = cosd(asind(sinDec))
// Calculate the Sun's local hour angle
let cosH = (cosd(zenith.rawValue) - sinDec*sind(latitude))/(cosDec*cosd(latitude))
if cosH > 1 { // The sun never rises on this location (on the specified date)
sunriseTime = Double.infinity
sunsetTime = -Double.infinity
} else if cosH < -1 { // The sun never sets on this location (on the specified date)
sunriseTime = -Double.infinity
sunsetTime = Double.infinity
} else {
// Finish calculating H and convert into hours
var H = ( i == 0 ? 360.0 : 0.0 ) + ( i == 0 ? -1.0 : 1.0 )*acosd(cosH)
H /= 15
// Calculate local mean time of rising/setting
let T = H + RA - 0.06571*t - 6.622
// Adjust back to UTC
let UT = T - longitudeHour
// Convert UT value to local time zone of latitude/longitude
let localT = UT + localOffset
if i == 0 { // Add 24 and modulo 24 to be sure that the results is between 0..<24
sunriseTime = (localT + 24)%24
} else {
sunsetTime = (localT + 24)%24
}
}
}
return (sunriseTime, sunsetTime)
}
func sind(valueInDegrees: Double) -> Double {
return sin(valueInDegrees*M_PI/180)
}
func cosd(valueInDegrees: Double) -> Double {
return cos(valueInDegrees*M_PI/180)
}
func tand(valueInDegrees: Double) -> Double {
return tan(valueInDegrees*M_PI/180)
}
func asind(valueInRadians: Double) -> Double {
return asin(valueInRadians)*180/M_PI
}
func acosd(valueInRadians: Double) -> Double {
return acos(valueInRadians)*180/M_PI
}
func atand(valueInRadians: Double) -> Double {
return atan(valueInRadians)*180/M_PI
}
Ans this is how I use the function to determine if it's night or not:
let latitude = ...
let longitude = ...
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let (sunriseHour, sunsetHour) = sunriseSunsetHoursForLocation(coordinate)
let componetns = NSCalendar.currentCalendar().components([.Hour, .Minute], fromDate: NSDate())
let currentHour = Double(componetns.hour) + Double(componetns.minute)/60
let isNight = currentHour < sunriseHour || currentHour > sunsetHour
I'm not sure why your code to get the offset isn't working (I got the same result). But there's a simpler solution that does work. Just ask the local time zone, using secondsFromGMTForDate. With dates six months apart I get different results:
let now = NSDate()
let future = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Month, value: 6, toDate: now, options: NSCalendarOptions(rawValue: 0))!
let nowOffset = NSTimeZone.localTimeZone().secondsFromGMTForDate(now)/3600
let futureOffset = NSTimeZone.localTimeZone().secondsFromGMTForDate(future)/3600

Adding time Strings in swift

I have an array of timeStrings of the following format:
x Hr(s) xx min.
I need to add these up to give a total. I'm wondering what is the best way to do this?
My thoughts were to find the index of "Hr(s)". Then if i substring between index 0 and the index of "Hr(s)", I have my hrs var and then add 6 to index of "Hr(s)" and find the index of "min" - 1, to give me the min var.
Then I need to take into account if seconds is greater than 60. So if I divide my seconds var by 60 and the answer is great than 1, I add that answer to my hrs var?
Can anyone see any flaws in this logic?
Sample implementation:
JSON response:
{"status":"OK","hrs":[{"scheduleDate":"2015-11-09","userName":"John Doe","company":"Company X","hrsWorked":"0 Hr(s) 42 min"},{"scheduleDate":"2015-11-10","userName":"Jane Doe","company":"Company Y","hrsWorked":"0 Hr(s) 47 min"},{"scheduleDate":"2015-11-10","userName":"Bob loblaw","company":"Company X","hrsWorked":"0 Hr(s) 37 min"},{"scheduleDate":"2015-11-10","userName":"Joe Soap","company":"Company Y","hrsWorked":"0 Hr(s) 50 min"},{"scheduleDate":"2015-11-10","userName":"Test","company":"Company Y","hrsWorked":"0 Hr(s) 40 min"}],"queryStatus":"OK","message":null,"count":5}
var hrsVar = 0
var minsVar = 0
loop through hrsArray{
hrsMinStr = hrsWorkedInJson
if let endHrsIndex = hrsMinStr.lowercaseString.characters.indexOf("Hr(s)") {
print("Index: \(index)")
let hrsStr = Int(hrsMinStr.substringWithRange(Range<String.Index>(start: 0, end: endHrsIndex)))
hrsVar += hrsStr
let minStr = Int(hrsMinStr.substringWithRange(Range<String.Index>(start: endHrsIndex + 1, end: hrsMinStr.length - 3)))
minsVar += minStr
}
}
if minsVar/60 > 1 {
hrsVar = hrsVar + minsVar/60
minsVar = minsVar%60
}
Update
It seems as though I cannot pass in "Hr(s)" and instead only a single character "h". Because of this, I was trying to use the advancedBy(x) method to get the right endIndex. But I'm getting the error:
Cannot invoke initializer for type 'Range<Index>' with an argument list of type '(start: Int, end: String.CharacterView.Index)'
Updated code:
if let endHrsIndex = hrsMinStr.lowercaseString.characters.indexOf("h") {
print("Index: \(endHrsIndex)")
let hrsStr = Int(hrsMinStr.substringWithRange(Range<String.Index>(start: 0, end: endHrsIndex)))
hrsVar += hrsStr
let minStr = Int(hrsMinStr.substringWithRange(Range<String.Index>(start: endHrsIndex.advancedBy(5), end: hrsMinStr.length.characters.count.advancedBy(-3))))
minsVar += minStr
}
I'm really looking for the most efficient approach as possible, so please advise if there is a better way/if you see issues with this approach
import Foundation
let str = "0 Hr(s) 42 min"
let time = str
.stringByReplacingOccurrencesOfString("Hr(s)", withString:":")
.stringByReplacingOccurrencesOfString("min", withString: "")
.stringByReplacingOccurrencesOfString(" ", withString: "")
let t = time.characters.split(Character(":"))
let h = Int(String(t[0])) // 0
let m = Int(String(t[1])) // 1
and sum
import Foundation
var time: [(hours:Int,minutes:Int,seconds:Int)] = []
for i in 0...5 {
let h = Int(arc4random_uniform(24))
let m = Int(arc4random_uniform(60))
let s = Int(arc4random_uniform(60))
time.append((h,m,s))
}
let t = time.reduce(0) { (sum, time: (hours: Int, minutes: Int, seconds: Int)) -> Int in
sum + time.seconds + time.minutes * 60 + time.hours * 60 * 60
}
let seconds = t % 60
let minutes = ((t - seconds) / 60) % 60
let hours = ((t - seconds) / 3660)
time.forEach {
print(String(format: " %6d:%02d:%02d", arguments: [$0.hours,$0.minutes, $0.seconds]))
}
print("-------------------")
print(String(format: "Sum: %6d:%02d:%02d", arguments: [hours,minutes,seconds]))
/*
12:25:04
2:43:36
14:09:35
11:59:43
10:39:19
23:32:14
-------------------
Sum: 74:29:31
*/
You should most likely try using the scanners in your case.
let string = "12 Hr(s) 37 min"
let scanner = NSScanner(string: string)
var min: Int = 0
var hour : Int = 0
scanner.scanInteger(&hour)
scanner.scanString("Hr(s) ", intoString: nil)
scanner.scanInteger(&min)
Or even easier if you create this part in Objective-C:
int min, hour;
sscanf("12 Hr(s) 37 min", "%d Hr(s) %d min", &hour, &min);

Resources