Current date brings back time as well - swift

I'm trying to get the date only, I wrote this:
func getCurrentDate() -> Date {
dateFormatter.dateFormat = "dd.MM.yyyy"
let result = dateFormatter.string(from: date)
print(result)
let date = dateFormatter.date(from: result)
return date!
}
printing 'result' I get:
12.07.2019
which is what I need, but converting it to Date type and printing it results in:
2019-07-11 21:00:00 +0000
Why does that happen and how can I fix it ?

Date was very unfortunately named. It has nothing to do with dates. It represents an instant in time. An instant that all observers (ignoring details like Relativity) would agree on. It isn't "a day on the calendar." It's a point in time, independent of any calendar.
The fact that it prints out in a "calendar-like" way is just a convenience for debugging.
If you want "a specific day on a calendar" then the tools you want is DateComponents and Calendar, not Date:
func currentDay() -> DateComponents {
return Calendar.current.dateComponents([.year, .month, .day], from: Date())
}
Keep in mind that the user's current calendar may not be Gregorian. If you require the Gregorian calendar, you need to say so:
func currentDay() -> DateComponents {
return Calendar(identifier: .gregorian).dateComponents([.year, .month, .day],
from: Date())
}
The Gregorian calendar is the one where the current year is 2019. In the Hebrew calendar, it's 5779. In the Buddhist calendar, it's 2562. In the Islamic calendar, it's 1440. There is no one "correct" calendar, which is why you need to provide one.
Reading your comments, it's possible you mean "the start of 'today' using the current calendar." If that's what you want, then you would use this:
func currentDay() -> Date {
return Calendar.current.startOfDay(for: Date())
}
(Or use the Gregorian calendar if needed, though I don't believe any supported calendar disagrees about what the start of the day is.)

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.

Get Date by supplying a month and year

I’m trying to create a Date extension static function that accepts two parameters: (month: Int, year: Int) and returns -> Date.
The month that is returned would be its .startOfMonth:
var startOfMonth: Date {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.year, .month], from: self)
return calendar.date(from: components)!
}
I know how to retrieve a month in the future by adding n-months to the current:
func addMonths(_ numMonths: Int) -> Date {
let cal = NSCalendar.current
return cal.date(byAdding: .month, value: numMonths, to: self)!
}
And I suppose this could be used in a roundabout way to determine what I’m looking for, by first determining how many months from the current is the month I’m interested in getting a Date returned for. But I would much prefer if I could retrieve it without needing to go through this step, especially if there’s a possibility that I’m not actually sure if the month is in fact in the future.
You can use DateComponents initializer to compose a new date just passing the month and year components. Just make sure to pass a calendar as well otherwise the result would be nil. Maybe something like:
extension Date {
static func startOfMonth(for month: Int, of year: Int, using calendar: Calendar = .current) -> Date? {
DateComponents(calendar: calendar, year: year, month: month).date
}
}
Date.startOfMonth(for: 2, of: 2021) // "Feb 1, 2021 at 12:00 AM"

Getting the Current Date in a Specific Time Zone

I am trying to get the current date in a time zone selected by the user, but I can't get anything other than the current date despite using functions from other answers on stackoverflow. What I used is this:
func dateIn(timeZone: TimeZone) -> Date {
let now = Date()
var calendar = Calendar.current
calendar.timeZone = timeZone
let components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: now)
let date = calendar.date(from: components) ?? Date()
return date
}
I would really appreciate it if someone could help me understand why I'm not getting the date in the time zone although I changed the time zone of calendar to that time zone.

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.

Check if local time is after midnight in another timezone

I want to check if my local time is after midnight in another time zone.
Specifically, if right now where I am at is 11 PM Saturday, or 1 AM Sunday local time, I want to see if it is the start of a new week in Central Time (after 12 AM Sunday).
You can use Calendar's dateComponents(in: TimeZone, from: Date) to check the time and date in another timezone. For your specific application:
// create current date, central time zone, and get the current calendar
let now = Date()
let centralTimeZone = TimeZone(abbreviation: "CST")!
let calendar = Calendar.current
let components = calendar.dateComponents(in: centralTimeZone, from: now)
if components.weekday == 1 {
print("It is Sunday in Central Standard Time.")
} else {
print("It is not Sunday in Central Standard Time.")
}
What you're doing there is asking the current calendar to give you a full set of DateComponents in the specified timezone. Then components.weekday gives the day of the week as an Int, starting with 1 for Sunday in the Gregorian calendar.
If you want to know more generally if it's "tomorrow" somewhere, here's a simple method:
func isItTomorrow(in zone: TimeZone) -> Bool {
var calendarInZone = Calendar(identifier: Calendar.current.identifier)
calendarInZone.timeZone = TimeZone(abbreviation: "CST")!
return calendarInZone.isDateInTomorrow(Date())
}
if isItTomorrow(in: centralTimeZone) {
print("It is tomorrow.")
} else {
print("It is not tomorrow.")
}
isItTomorrow(in: TimeZone) creates a new calendar of the same type as the current calendar (presumably .gregorian, but you never know) and sets its timezone to the desired one. Then it uses the neat built-in Calendar method .isDateInTomorrow() to check if the current time is "tomorrow" in the target timezone.
There are lots of other ways to do it, and depending on your specific need there may be a built-in method that will save you a lot of work, so it's well worth reading through the docs on Calendar and DateComponents to see what's available.