Saving Date Components in Swift 3 - swift

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")!.

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.

Swift Date formating changes to next month when using end of month data?

I live by end of month dates such as "2019-02-28 23:59:59"
print when I print out that date it tells me "Mar-2019". No it is still "Feb-2019"
print(dt1.toString(dateFormat: "MMM-YYYY")
I do use SwiftDate. I also get this issue with the DateFormatter().
so instead of clean code I end up having to subtracting a day to get the correct month-year to display.
Why?
You need to set Timezone & date format properly as below,
let string = "2019-02-28 23:59:59"
let df = DateFormatter()
df.timeZone = .current
df.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = df.date(from: string)
print(date?.description(with: .current)) //Thursday, February 28, 2019 at 11:59:59 PM Gulf Standard Time
df.dateFormat = "MMM-yyyy"
print(df.string(from: date!)) // Feb-2019

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

Swift Calendar class returning wrong date for weekday

I have a question regarding the Calendar class in Swift 3:
Basically, I wanted to get the Date of the next Sunday. However, the following code gives me the wrong output:
let cal = Calendar.current //gregorian
let today = Date(timeIntervalSinceNow: 0)
let date_c = DateComponents(calendar: cal, weekday: 1)
let today_weekday = cal.component(Calendar.Component.weekday, from: today)
let next_sunday = cal.nextDate(after: today, matching: date_c, matchingPolicy: Calendar.MatchingPolicy.nextTime)!
print(today_weekday) //prints out: 1 (today is Sunday, that's true)
print(next_sunday) //prints out: 2017-10-28 which is a Saturday
Please note that I tested the code yesterday, that's why I wrote "today is Sunday".
I was expecting the second print output to be the 2017-10-29 (next Sunday) however, it is giving me Saturday. What am I doing wrong?
Dates are always printed as if they are in the GMT timezone.
Suppose your time zone is UTC+1, then next Sunday will be 29-10-2017 00:00:00 in your time zone. However, this Date instance is expressed as 28-10-2017 23:00:00 in GMT.
So nextDate does return the correct date, it's just not formatted in your time zone. To fix this, just use a DateFormatter:
let formatter = DateFormatter()
formatter.dateStyle = .short
print(formatter.string(from: next_sunday))

NSDate startOfDay is 4AM?

I'm trying to write a loop for every 10 minutes of a given 24 hour day, starting at midnight and ending at ten minutes before midnight. So I tried this...
let x = Calendar.current.component(.year, from: Date())
let dateFormatter = DateFormatter()
dateFormatter.dateFormat="dd-MM-yyyy"
let june = dateFormatter.date(from: "21-06-" + String(x))
The result for june is "2017-06-21 04:00:00 UTC". Now technically this is correct, my local day will be 4 AM UTZ, but the code I'm passing this into, from the Astronomical Almanac, already handles local/global conversion.
So then I tried this:
var UTZCal = Calendar.current
UTZCal.timeZone = TimeZone(abbreviation: "GMT")!
let x = UTZCal.component(.year, from: Date())
let dateFormatter = DateFormatter()
dateFormatter.dateFormat="dd-MM-yyyy"
dateFormatter.calendar = UTZCal
let june = dateFormatter.date(from: "21-06-" + String(x))
This produced the exact same result. What am I missing?
It seems that the date formatter does not use the timezone of the
assigned calendar, and adding
dateFormatter.timeZone = UTZCal.timeZone
to your code makes it produce the expected result. But note that you
can simplify the calculation to
var utzCal = Calendar(identifier: .gregorian)
utzCal.timeZone = TimeZone(secondsFromGMT: 0)!
let year = utzCal.component(.year, from: Date())
let june = DateComponents(calendar: utzCal, year: year, month: 6, day: 21).date!
print(june) // 2017-06-21 00:00:00 +0000