Is there a daylight savings check in Swift? - swift

I need to check to see if the current date is during daylight savings time. In pseudocode that would be like this:
let date = NSDate()
if date.isDaylightSavingsTime {
print("Success")
}
I haven't been able to find the solution to this anywhere on the internet.

An NSDate alone represents an absolute point in time.
To decide if a date is during daylight savings time or not
it needs to be interpreted in the context of a time zone.
Therefore you'll find that method in the NSTimeZone class and not
in the NSDate class. Example:
let date = NSDate()
let tz = NSTimeZone.localTimeZone()
if tz.isDaylightSavingTimeForDate(date) {
}
Update for Swift 3/4:
let date = Date()
let tz = TimeZone.current
if tz.isDaylightSavingTime(for: date) {
print("Summertime, and the livin' is easy ... 🎶")
}

Swift 4.0 or later
You can check a date isDaylightSavingTime in two ways by time zone identifier or abbreviation.
let timeZone = TimeZone(identifier: "America/New_York")!
if timeZone.isDaylightSavingTime(for: Date()) {
print("Yes, daylight saving time at a given date")
}
let timeZone = TimeZone(abbreviation: "EST")!
if timeZone.isDaylightSavingTime(for: Date()) {
print("Yes, daylight saving time at a given date")
}

Related

Where the time coming from when converting date string without any time in to Date() in swift?

I'm using this extension to convert a string containing date to Date() object:
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/YYYY"
let date = dateFormatter.date(from: self)
return date
}
}
the result always containing a time in it. I'm curious where is the time coming from, why it is not all zero?
print("11/12/2021".toDate())
result is ->
2020-12-19 21:00:00 +0000
In the time that I run the code, it is showing 21:00:00, so why it is 21? I believe It is not related to my time because I run it at different times.
A Date object indicates an instant in time anywhere on the planet, independent of time zone.
A DateFormatter can convert a String to a Date (or a Date to a String, but ignore that for now). When it converts a String to a Date, it may make assumptions about the time of day if that is not included in the String. I believe it assumes that the time is midnight (00:00:00) in the date formatter's time zone. (And by the way, midnight is the starting point of a day, so midnight is zero hours/minutes/seconds into the day. Essentially midnight IS zeros for time.)
So when you call your String extension to convert "11/12/2021" to a Date, the extension creates a DateFormatter which defaults to the device time zone. It creates a Date assuming Midnight in the local time zone.
When you print that date, it gets displayed in GMT.
It looks like your format string has problems though. You're getting the wrong year and month. I think you must be using the wrong month or day string in your formatter. (I always have to look those up when I use them.)
Edit:
You likely want a format string of "MM-dd-yyyy"
(2-digit month, 2-digit day of month, and 4-digit year.)
Lower-case "m" or "mm" is minutes. Upper-case "Y" is for "week of year" based calendars, which you probably don't want.
Try this code:
func toDate() -> Date?{
let dateFormatter = DateFormatter()
let posixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM-dd-yyyy"
dateFormatter.locale = posixLocale
let date = dateFormatter.date(from: self)
return date
}
}
And to use it:
let dateString = "12/11/2021"
let date = dateString.toDate()
print(date)
if let date = date {
let convertedDateString = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .medium)
print(convertedDateString)
} else {
print("Can't convert \(dateString) to a date")
}
That displays "Dec 11, 2021 at 12:00:00 AM" in my US locale (US Date formatting.) Note that since I use the DateFormatter class method localizedString(from:dateStyle:timeStyle:) I see midnight as the displayed time (The time you get from a DateFormatter when you don't specify a time, but displayed in the local time zone.)
The answer is:
when we are converting a string to a Date Object the important part is the time zone that we are converting it to.
for example, if you convert your string date to a UTC time zone when you want to bring it back you have to set the time zone of the date to UTC.
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(identifier: "UTC")
so this is the reason why when we are printing the Date() object it is deferred from our string date.
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
let date = dateFormatter.date(from: self)
return date
}
}
extension Date {
func toString() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
return dateFormatter.string(from: self)
}
}
let stringDate = "01-12-2021"
let date = "01-12-2021".toDate()
let convertBack = date?.toString()
print("(\(stringDate)) -> (\(date!)) -> (\(convertBack!))")
and the result is:
(01-12-2021) -> (2021-01-11 21:01:00 +0000) -> (01-12-2021)
so at the end when we convert back the Date object it will be the same. because that 2 dateFormatter in the extensions are using the default time zone. and if you want to specify a specific time zone you have to declare it in converting from and to string together.

DateFormatter date from string returns nil when iPhone Date & Time 24-Hour Time is off [duplicate]

This question already has an answer here:
DateFormatter doesn't return date for "HH:mm:ss"
(1 answer)
Closed 2 years ago.
I am working on an app that initializes dates from strings returned from the backend. The dateString is returned using the following format: "2020-03-05T09:00:00+00:00"
The method I have to do the conversion is:
extension Date {
static func convertDate(_ dateString: String?) -> Date? {
guard let dateString = dateString else { return nil }
let dateFormatter = DateFormatter()
dateFormatter.setLocalizedDateFormatFromTemplate("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
return dateFormatter.date(from: dateString)
}
}
Everything was working fine until someone reported that if the user switches off "24-Hour Time" in settings the method above returns nil.
What am I doing wrong?
Thank you
You're using a very standardized timestamp format, which allows you to take advantage of the ISO8601DateFormatter.
let dateString = "2020-03-05T09:00:00+00:00"
let df = ISO8601DateFormatter()
df.formatOptions = [.withInternetDateTime]
if let date = df.date(from: dateString) {
print(date) // 2020-03-05 09:00:00 +0000
}
If a machine (like your server) is generating the timestamp then it will (should) always be in zulu time (GMT) so you don't need to do anything beyond this. You could specify a time zone but there isn't a point since the string will always zero it out for you.
df.timeZone = TimeZone(secondsFromGMT: 0)
This string represents an absolute moment in time. If you need a relative moment in time, such as the local time from the source, you'll need to identify that time zone and apply it here, which is also very straighforward.

Swift 4.1 couldn't not convert String to local Date, always return UTC Date

I want to transfer a date string to Date.
let a = DateFormatter()
a.dateFormat = "yyyy-MM-dd HH:mm:ss"
guard let datea = a.date(from: "2018-06-21 00:00:00") else {
fatalError("ERROR: Date conversion failed due to mismatched format.")
}
print("ans", datea)
But it always print "ans 2018-06-20 16:00:00 +0000"
Why it could not print the original string date "2018-06-21 00:00:00"?
What wrong with my code ?
A Date is not a string. A Date is a moment in time. It has no clock. It has no time zones. It has no calendar. It is just an instant in time, independent of location or localization.
As a debugging convenience, a Date can be easily converted to a string in a pre-defined format using its .description (which is what print calls). As with all .description methods, you should never use this string for anything but debugging (or possibly logging). There is no promise about what format this string is in.
If you need some specific string representation, then you should use the DateFormatter:
print("ans", a.string(from: datea))
You need to provide timeZone to get the time according to that provided timeZone so to convert UTC time to local time your code should be look like that.
let a = DateFormatter()
a.dateFormat = "yyyy-MM-dd HH:mm:ss"
a.timeZone = TimeZone(abbreviation: "UTC")
let dt = a.date(from: "2018-06-21 00:00:00")
a.timeZone = TimeZone.current
a.dateFormat = "yyyy-MM-dd HH:mm:ss" //change the dateFormat according to your need
let dateString = a.string(from: dt!)
print("now the dateString is \(dateString)")
//printed result (now the dateString is 2018-06-21 05:30:00 )
As Rob Napier says in his answer, a Date object does not have a time zone. It represents a moment in time anywhere on the planet.
If you want to display a Date as a month, day, year, and time, you need to specify a particular time zone.
If you just print a date, like print(Date()), you get the default description property of the date object, which shows the date expressed in UTC. That's probably not what you want.
I defined an extension to Date that lets me see dates expressed in the user's current locale and time zone:
extension Date {
func localString(dateStyle: DateFormatter.Style = .medium, timeStyle: DateFormatter.Style = .medium) -> String {
return DateFormatter.localizedString(from: self, dateStyle: dateStyle, timeStyle: timeStyle)
}
func timeString(timeStyle: DateFormatter.Style = .medium) -> String {
return localString(dateStyle: .none, timeStyle: timeStyle)
}
}
If you add that extension to your project you can use it like this:
print(print("ans", datea.localString())
And you'll see your Date in the device's current time zone. It's very useful for debugging.

How to find midnight for a given Date in Swift

I creating an itinerary generation app where the user is required to enter the dates of his/her trip. The only problem is, using UIDatePicker the dates are always given as the current time for a given day/month/year.
In a separate file I've extended Date class to try and write a simple method that will return midnight for a given date.
First I tried
var midnight:Date{
let cal = Calendar(identifier: .gregorian)
return cal.startOfDay(for: self)
}
However this always gave me either 04:00 or 05:00 depending on daylights savings, which gave me the idea that I should simply remove 4 or 5 hours depending on daylight savings, and so I created the following methods:
var timezone:TimeZone{
return TimeZone.current
}
///Returns the first instance of the date, e.g. 2018-02-26 00:00:00
var trueMidnight:Date{
let cal = Calendar(identifier: .gregorian)
let midnight = cal.startOfDay(for: self)
let secondsFromGMT = TimeZone.current.secondsFromGMT()
print("Daylight savings? \(daylightSavings)")
return midnight.addingTimeInterval(Double(secondsFromGMT))
}
///If this var returns true, then daylight savings time is active and an hour of daylight is gained (during the summer).
var isDaylightSavings:Bool{
return timezone.daylightSavingTimeOffset(for: self) == 0 ? false : true
}
var daylightSavings:Double{
return isDaylightSavings ? 3600.0 : 0.0
}
However these methods sometimes return midnight, 23:00, or even 22:00 the previous day.
I'm a relatively inexperienced programmer so I feel like I'm lacking a basic understanding for the date class or missing a large concept. Why is it so difficult for me to simply find midnight on a given date?
I even forsook the idea of returning midnight and tried to just find noon on a given day with the code:
var noon:Date{
let gregorian = Calendar(identifier: .gregorian)
var components = gregorian.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self)
components.hour = 12
components.minute = 0
components.second = 0
return gregorian.date(from: components)!
}
But this returns 16:00 or 17:00 as opposed to noon. Any help would be appreciated.
When you print a date, it is printed in UTC time. So when you print your Dates, they differ from your local time by 4/5 hours.
If you use the following code instead
print(yourDate.description(with: .current))
Where yourDate is your date, it will be in the correct time zone.
You're confused.
If you use
print(Date())
You will get a date in UTC. If you're in the UTC+5 time zone, that date will be 5 hours greater than your local time. Thus if you try to display midnight local time in UTC, it will show up as 5:00 AM in UTC.
Try this:
extension Date {
func localString(dateStyle: DateFormatter.Style = .medium,
timeStyle: DateFormatter.Style = .medium) -> String {
return DateFormatter.localizedString(
from: self,
dateStyle: dateStyle,
timeStyle: timeStyle)
}
var midnight:Date{
let cal = Calendar(identifier: .gregorian)
return cal.startOfDay(for: self)
}
}
print("Tonight at midnight is " + Date().midnight.localString())
That code uses a function localString() that takes advantage of a DateFormatter method localizedString(from:dateStyle:timeStyle:) that converts a Date to a string in the current locale (which includes the local time zone.
I suggest adding that extension to your apps.

How do I turn an arbitrary time zone into UTC?

I'm allowing my users to be able to select a random Date and time, using their local time zone. I want to be able to send this date string to the server in UTC Format so it can be read by anyone else around the world using their local time zone. I've read lots online on how to turn UTC to local time but not the other way around. How can I accomplish this?
Edit:
First I use the first function to take the concatenated string with a user selected date and time, turn it into a NSDate, and then I convert this NSDate into a UTC string. Is this the best method of achieving my goal?
public class func localTimeZoneStringToDate(string: String) -> NSDate {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
return dateFormatter.dateFromString(string)!
}
public class func UTCStringFromDate(date: NSDate) -> String {
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return dateFormatter.stringFromDate(date)
}
If you're using the UIDatePicker, getting the selected date and time in UTC is quite simple. The first line of code below will return the selected time in YYYY-MM-DD hh:mm:ss The second line of code will return the time interval in seconds since January 1, 1970 at 12:00am GMT.
// Returns Selecteed Date //
datePicker.date
// Returns Seconds Since Jan. 1, 1970 //
datePicker.date.timeIntervalSince1970
However, if you are not using the UIDatePicker, you can get the same information that the above code will return by simply using:
//** Variable "pastDate" must be NSDate **//
// Returns Selected Date //
pastDate
// Returns Seconds Since Jan.1, 1970 //
pastDate.timeIntervalSince1970
EDIT:
I think I understand what you're attempting to do now, and the following code should return the user's selected date in UTC form.
func UTCStringFromDate(date: NSDate) -> String {
// Get User's Time //
let calendar = NSCalendar.currentCalendar()
// Get User's TimeZone Difference //
let difference = calendar.timeZone.secondsFromGMT
// Get UTC Time //
let adjustedTime = calendar.dateByAddingUnit(.Second, value: -difference, toDate: date, options: [])!
// Format Date //
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return dateFormatter.stringFromDate(adjustedTime)
}