Swift timezones with winter/summer times - swift

I am working on a world clock, and i have gotten the times for various countries as so:
let date = NSDate();
var beltz = NSDateFormatter();
beltz.dateFormat = "HH:mm";
beltz.timeZone = NSTimeZone(name: "Europe/Brussels")
let belTzStr = beltz.stringFromDate(date);
println(belTzStr) //<-- correct time
My question is when summer/winter times adjust +- 1 hour, will this adjustment be reflected in the code by NSTimeZone? If not, how do i achieve this?

Consider this modified code:
let cal = NSCalendar.currentCalendar()
cal.timeZone = NSTimeZone(name: "America/Phoenix")!
let date = cal.dateWithEra(1, year: 2015, month: 12, day: 24, hour: 16, minute: 0, second: 0, nanosecond: 0)!
//date = NSDate();
var beltz = NSDateFormatter();
beltz.dateFormat = "dd MMMM HH:mm zzzz";
beltz.timeZone = NSTimeZone(name: "Europe/Brussels")
let belTzStr = beltz.stringFromDate(date);
print(belTzStr) //<-- correct time
I modified your code to add time as Phoenix AZ, which does not use summer time and added some extra formatting, especially the TZ to the printed data.
Now, if you use December (no DST in either region)
let date = cal.dateWithEra(1, year: 2015, month: 12, day: 24, hour: 16, minute: 0, second: 0, nanosecond: 0)!,
you get
25 December 00:00 Central European Standard Time
and if you use July (DST in EU)
let date = cal.dateWithEra(1, year: 2015, month: 7, day: 24, hour: 16, minute: 0, second: 0, nanosecond: 0)!,
you get
25 July 01:00 Central European Summer Time
So yes, it adjusts the TZ appropriately.

Related

Convert Double (number of days since Dec 30, 1899) to Date in Swift?

I have a double value that represents the number of days since December 30, 1899 (the usual TDateTime value in Delphi). For example: 43854.4410269444
Is it possible to create a valid Date value from this double?
One way is to get a Date representing "1899-12-30", then call addingTimeInterval.
// either parse a date string...
let formatter = DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd"
let epoch = formatter.date(from: "1899-12-30")!
// or calculate the time directly from 1970
//let epoch = Date(timeIntervalSince1970: -86400 * 25569)
// now we add the quality of the number of days times the number of seconds in a day
let numberOfDays = 43854.4410269444
let result = epoch.addingTimeInterval(86400 * numberOfDays)
Here's my attempt to solve the problem (see below how I calculated the magic number):
extension Date {
init(fromDelphiTDateTime delphiDate: Double) {
//Number of seconds between 12/30/1899 12:00 AM and 1/1/1970 12:00 AM
let tDateTimeUnixTimeOffset = 2209161600.0
//24 * 60 * 60
let numberOfSecondsInDay = 86400.0
let delphiTDateTimeAsUnixTime = delphiDate * numberOfSecondsInDay - tDateTimeUnixTimeOffset
self.init(timeIntervalSince1970: delphiTDateTimeAsUnixTime)
}
}
I assumed that TDateTime doesn't have any timezone information.
Since the Unix time (timeIntervalSince1970) is in UTC, you'll need to convert your TDateTime value to UTC if it's in a different time zone.
Examples:
//Dec 30, 1899 at 12:00 AM
let date1 = Date(fromDelphiTDateTime: 0)
//Jan 24, 2020 at 11:04 AM
let date2 = Date(fromDelphiTDateTime: 43854.4410269444)
And here's how I calculated the magic number 2209161600.0 using Swift:
let zeroTDateTimeComponents = DateComponents(calendar: Calendar.init(identifier: .gregorian), timeZone: TimeZone(secondsFromGMT: 0), year: 1899, month: 12, day: 30, hour: 0, minute: 0, second: 0)
let zeroTDateTime = zeroTDateTimeComponents.date
let tDateTimeUnixTimeOffset = zeroTDateTime?.timeIntervalSince1970 //the value is 2209161600.0
Hope this helps and I'd be grateful if the community validates or further improves my answer. Some unit tests with the existing know pairs of TDateTime and its double value would certainly help.

How to check how many times the clock has shown a full hour between two dates?

I want to check how many full calendar hours have passed between two dates.
15:00 -> 15:00 = 0 Hours
15:00 -> 16:00 = 1 Hour
15:30 -> 16:15 = 1 Hour
16:45 -> 18:10 = 2 Hours
I don`t want to know the total hours that you can get with:
Calendar.current.dateComponents([.hour], from: date, to: date2).hour
Any ideas how to do this?
First compute the start of the hour for both dates:
let start1 = cal.dateInterval(of: .hour, for: date1)?.start
let start2 = cal.dateInterval(of: .hour, for: date2)?.start
Use these to compute the difference in hours, as before.
Full self-contained example:
let cal = Calendar.current
let date1 = DateComponents(calendar: cal, year: 2018, month: 6, day: 15, hour: 16, minute: 45).date!
let date2 = DateComponents(calendar: cal, year: 2018, month: 6, day: 15, hour: 18, minute: 10).date!
let start1 = cal.dateInterval(of: .hour, for: date1)!.start
let start2 = cal.dateInterval(of: .hour, for: date2)!.start
let hours = cal.dateComponents([.hour], from: start1, to: start2).hour!
print(hours) // 2
Take a look at the DateComponentsFormatter. More specifically, you might want to do something like this:
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.includesApproximationPhrase = false
formatter.includesTimeRemainingPhrase = false
formatter.allowedUnits = [.hour]
let string = formatter.string(from: date, to: date2) //prints out "2 hours"
Source

Date Formatter failing on a correct date [duplicate]

I found that DateFormatter date(from:) method can't parse a couple of specific dates. Method returns nil for the 1st april of 1981-1984 years. Is it a bug of Foundation? What can we do to perform parsing of such dates?
Xcode 8.0, iOS SDK 10.0. Here is a screenshot of a short playground example:
This problem occurs if daylight saving time starts exactly on
midnight, as it was the case in Moscow in the years 1981–1984 (see for example Clock Changes in Moscow, Russia (Moskva)).
This was also observed in
Why does NSDateFormatter return nil date for these 4 time zones? and
Why NSDateFormatter is returning null for a 19/10/2014 in a Brazilian time zone?
For example, at midnight of April 1st 1984, the clocks were adjusted one hour forward, which means that the date "1984-04-01 00:00"
does not exist in that timezone:
let dFmt = DateFormatter()
dFmt.dateFormat = "yyyy-MM-dd"
dFmt.timeZone = TimeZone(identifier: "Europe/Moscow")
print(dFmt.date(from: "1984-04-01")) // nil
As a solution, you can tell the date formatter to be "lenient":
dFmt.isLenient = true
and then it will return the first valid date on that day:
dFmt.isLenient = true
if let date = dFmt.date(from: "1984-04-01") {
dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dFmt.string(from: date))
}
// 1984-04-01 01:00:00
A different solution
was given by rob mayoff, which is to make the date formatter use noon instead of midnight as the
default date. Here is a translation of rob's code from Objective-C to Swift:
let noon = DateComponents(calendar: dFmt.calendar, timeZone: dFmt.timeZone,
year: 2001, month: 1, day: 1, hour: 12, minute: 0, second: 0)
dFmt.defaultDate = noon.date
if let date = dFmt.date(from: "1984-04-01") {
dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dFmt.string(from: date))
}
// 1984-04-01 12:00:00

Saving Date Components in Swift 3

I have troubles saving a time. Let me show you a screenshot, it will make clear what I'm trying to achieve here.
I am saving a day as an Int and from that I create a new Date, that is handled. Only thing what is missing is adding time to that date. How can I save AM or PM in a date?
Also, will that affect users who use 24-hour time in their iPhones? If I save 9 PM will their system know that I really mean 21 hrs for them?
Okay, I forgot about the code, here it is:
This is a piece of code where I am scheduling a notification
let calendar = Calendar.autoupdatingCurrent
guard let lastPeriod = RealmManager.sharedInstance.queryLastPeriod(), let predictionDate = lastPeriod.predictionDate else {
return
}
let day = predictionDate.day - DefaultsManager.getNotificationDays()
let newComponents = DateComponents(calendar: calendar, timeZone: .current, year: predictionDate.year, month: predictionDate.month, day: day, hour: time, minute: 0)
As you can see that hour part of newComponents is tricky. I only need to provide is it 9AM, 12AM or 9PM. How do I do that? (btw. time is just an Int I'm manually inputting for testing)
As far as I can tell, you use 24h-style hours (what else?).
let dc = DateComponents(year: 2016, month: 01, day: 11, hour: 14, minute: 20)
let d = Calendar.current.date(from: dc)
//> d: Date? = 2016-01-11 13:20:00 UTC
This is correct since my current timezone is CET (UTC+1).
You can use DateFormatter to print dates in different formats. For example:
let df = DateFormatter()
df.dateStyle = DateFormatter.Style.short
df.timeStyle = DateFormatter.Style.short
df.string(from: d!)
//> R4: String = "11/01/16 14:20"
Back to my timezone!
DateFormatter also has a method date(from: String) which you can use to parse dates:
df.date(from: "11/01/16 14:20")
//> R5: Date? = 2016-01-11 13:20:00 UTC
You will have to use settings of Calendar and/or DateFormatter to get 12h-style output. On the input site, you can just map xxAM to Int("xx")! and xxPM to 12 + Int("xx")!.

Swift get local date instance

please help me to get local date and the start of the day, I mean the midnight. For getting local date I'm using code below, but I dont think it is right
var calendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
calendar!.timeZone = NSTimeZone.localTimeZone()
let components = NSDateComponents()
components.second = NSTimeZone.localTimeZone().secondsFromGMT
let today = calendar!.dateByAddingComponents(components, toDate: NSDate(), options: nil)
But I can't get the midnight of the day, it keeps returning time 21.00
var comps = calendar!.components(NSCalendarUnit.YearCalendarUnit | .MonthCalendarUnit | .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit, fromDate: today!)
comps.hour = 0
comps.minute = 0
comps.second = 0
let startToday = calendar!.dateFromComponents(comps)!
even this return 21.00
calendar!.startOfDayForDate(today)
NSDate does not have an attached timezone. For some head-scratching reason, Apple decided to always display the date in GMT when you print it. After enough hair loss on this, I decided to write my own description method see the date in my local time zone:
extension NSDate {
public var localTimeString : String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
formatter.timeZone = NSTimeZone.localTimeZone()
return formatter.stringFromDate(self)
}
}
let now = NSDate()
let flags : NSCalendarUnit = [.Day, .Month, .Year]
let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = gregorian.components(flags, fromDate: now)
let today12AM = gregorian.dateFromComponents(components)!
print(today12AM)
print(today12AM.localTimeString)
Edit:
You can construct a point in time manually:
let x = gregorian.dateWithEra(1, year: 2015, month: 8, day: 15, hour: 0, minute: 0, second: 0, nanosecond: 0)!
print(x)
print(x.localTimeString)
print(x.timeIntervalSince1970) // seconds since midnight Jan 1, 1970
print(today12AM.timeIntervalSince1970)
x is no different than today12AM. As I said, NSDate has no built-in timezone. When you use print, it converts your date into GMT. That's why it appears superficially different from the same point in time when expressed in your own timezone.