day for Date() and Calendar.dateComponents don't match up - ios

I'm trying to get the current date to print in a particular format (YYYYMMD) for AWS security credentials and I noticed that when I do Date(), the day is the 4th which is the correct value:
let date = Date()
print("\(date)") //2016-10-04 00:56:28 +0000
Now, I want to print the date in the format I desire so, but I keep getting the day value as the 3rd:
let calendar = Calendar.current
let date = Date()
let components = calendar.dateComponents([.day], from: date)
print("\(components.day)") //Optional(3)
S3 is expecting the date to be the 4th. How can I fix this?

It's because of time zone difference. date will return the UTC time and date but calendar will return the date and time based on your device's time zone. If you need the day number in UTC just set the time zone of the calendar object to UTC after you create it:
let calendar = Calendar.current
calendar.timeZone = TimeZone(abbreviation: "UTC")!
Now it will always match the value that is returned by Date()

When you print a date using
print("\(date)")
You get the date and time in UTC, which is probably not what you want.
If you want to display your date in your local time zone, create a date formatter and use that:
let dateFormatter = NSdateFormatter()
let dateFormatter.dateStyle = .medium
let dateFormatter.timeStyle = .medium
let dateString = dateFormatter.StringFromDate(date)
print ("date = \(dateString)")
If you do this a lot, you might want to create an extension on NSDate displayString so you can use that to display your dates without having to write additional code.

Related

How to convert a GMT date from server to a current user's time zone?

I have such a response from server
date: "2021-04-24T16:00:34.969"
timezone: "Asia/Saigon"
How can I convert this date with specified time zone to a user's time zone?
You can create a time zone object from the given string and use it when parsing the date
let dateString = "2021-04-24T16:00:34.969"
let timezoneString = "Asia/Saigon"
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(identifier: timezoneString)
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
let date = dateFormatter.date(from: dateString)
Then when you want to display the date to the user you use another DateFormatter instance where the timezone property is set to .current

Convert from string to Date with current timezone

I am using these lines of code to convert Date and time, to the current time zone:
let calendar = Calendar(identifier: .gregorian)
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss:Z"
dateFormatter.timeZone = calendar.timeZone
dateFormatter.string(from:currentDate)
print(dateFormatter.string(from: currentDate))
The print output gives me:
2019-11-19 17:22:55:+0100
I am going to store the date in Realm, so how can I convert this back to a Date()?
Edit; I tried converting it, but it doesn't work:
let calendar = Calendar(identifier: .gregorian)
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss:Z"
dateFormatter.timeZone = calendar.timeZone
let dateString = dateFormatter.string(from:currentDate)
let finalDate = dateFormatter.date(from: dateString)
print(dateString) -> Gives me output: 2019-11-19 18:09:05:+0100 - This is the correct time!
print(finalDate!) -> Gives me output: 2019-11-19 17:09:05 +0000
So the finalDate should store the current time as a Date(), but it doesn't get the correct time.
DateFormatter has an option to convert string to date as well:
let calendar = Calendar(identifier: .gregorian)
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss:Z"
dateFormatter.timeZone = calendar.timeZone
dateFormatter.date(from: yourStringDate)
DateFormatter converts in both directions. Using the same formatter, to convert it back, use .date(from:)
let date = dateFormatter.date(from: string)
Based on your comments, you may be misunderstanding what a Date is. A Date is nothing more and nothing less than a number of seconds since the reference time. It doesn't know anything about time zones. It doesn't know anything about calendars. It doesn't even know anything about minutes or hours. It is only a number of seconds since the reference time (it is literally a small wrapper around a single Double value).
When you print a Date, it calls .description, and as a programmer convenience that is generated by a default DateFormatter that turns it into a human readable string purely for debugging purposes. That string does not imply that the Date has calendar or a time zone attached to it. It's just a programmer convenience. It should never be used by the program itself.
If you want to deal with time zones, you must use a Calendar to compute various DateComponents, or a DateFormatter (which uses a Calendar) to generate a String.

Storing UTC time and Daylight saving time

It's generaly accepted as a good practise to store dates as UTC/GMT and convert them back to local time when they need to be displayed to the user. Working currently on calendar app I am wondering how to do this for future/past dates taking into account the current Daylight saving time.
Date to UTC:
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let dt = dateFormatter.date(from: date)
Date back to local time:
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.current
let dt = dateFormatter.date(from: date)
Example:
When a user living in Amsterdam schedules a future meeting from 4pm to 5pm (UTC+2). I'll be storing the UTC equivalent which is 2pm to 3pm.
Now if he would like check his meeting in two months when Amsterdam has passed from summer- to winter-time (UTC-1), the user will see that his meeting has shifted to 3pm - 4pm.
How would I best tackle this ?

Preventing Date from being localized

I have the following string:
let dateString = "2018-04-18T04:54:00-04:00"
I initialize a Date via the ISO8601DateForamtter by doing the following:
let formatter = ISO8601DateFormatter()
let date = formatter.date(from: dateString)
If I print the date, I get the following:
Apr 18, 2018 at 1:54am
The formatter is automatically converting the time into my local time. How can I prevent accounting for my time zone? For example, I want the Date object to show the following instead:
Apr 18, 2018 at 4:54am
With ISO8601, 2018-04-18T04:54:00-04:00 means 2018-04-18 04:54:00 in GMT -4h. To print the time as it is in the original string, you need to create a date formatter with the specific time zone which is -4.
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(secondsFromGMT: -4 * 60 * 60)
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dateFormatter.string(from: date))
You will get
2018-04-17 04:54:00
FYI, I'm adding a link for ISO8601
You need to parse the timezone from your date string and use it to set the timezone from your date formatter:
func secondsFromGMT(from string: String) -> Int {
guard !string.hasSuffix("Z") else { return 0 }
let timeZone = string.suffix(6)
let comps = timeZone.components(separatedBy: ":")
guard let hours = comps.first,
let minutes = comps.last,
let hr = Int(hours),
let min = Int(minutes) else { return 0 }
return hr * 3600 + min * 60
}
let dateString = "2018-04-18T04:54:00-04:00"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
formatter.locale = Locale(identifier: "en_US_POSIX")
if let dateFromString = formatter.date(from: dateString) {
formatter.timeZone = TimeZone(secondsFromGMT: secondsFromGMT(from: dateString))
formatter.dateFormat = "MMM dd, yyyy 'at' h:mma"
formatter.amSymbol = "am"
formatter.pmSymbol = "pm"
print(formatter.string(from: dateFromString)) // Apr 18, 2018 at 4:54am
}
Instead of logging the Date directly, have a look at the string(from:timeZone:formatOptions:) method on ISO8601DateFormatter. With this, you should be able to get a date string for any time zone you desire.
You should set your formatter to the appropriate timezone such as (UTC example below):
formatter.timeZone = TimeZone(identifier: "UTC")
or alternatively specify against GMT:
formatter.timeZone = TimeZone(secondsFromGMT: 0)
The date that you are receiving from your current formatter is technically correct. Setting the date backwards as described in the currently accepted answer is not advised because you are effectively hard-coding an intended time zone. As soon as your device enters another time zone (or if a user downloads your app outside of the current time zone), your information will be incorrect.
If you are trying to display this time in the UTC time zone, you need to use another formatter to correctly format the output in the target time zone.
let utcFormatter = DateFormatter()
utcFormatter.timeZone = TimeZone(secondsFromGMT: 0)
// Perform any other transformations you'd like
let output = utcFormatter.string(from: date)
But why is your original date correct?
The Date API is incredibly robust and doing a lot of things under-the-hood, but is effectively implemented using a simple Double. The automaic time-zone information that it's displaying to you is an abstraction to make it easier to reason about. A date technically has no knowledge of what time zone it's in – but converting it to a string implicitly applies an inferred date formatter on the date and returns information it thinks will be most useful to you.
If you're doing manipulations on a date, you're likely using the Calendar API. You typically get a new instance from using Calendar.current, which will create a new calendar with your current time zone information. You can change the represented time zone of the calendar like this:
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 0)
This will give you relative dates that will work in any time zone without modifying the base Date object that you're working with.

getting wrong time while set date and time in one nsdate separately in ios

when i'm going set event from app to device calendar. i got wrong time.
i have three date picker one for date and other two for start time and end time for event. i set start date as end date in EKEvent because i have to set event on that day only.
get date from date-picker and store it as startdate and end date as nsdate type. below is my date-picker method
func pickerDate()
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd"
routineStartDate = dateFormatter.string(from: self.startDatePicker.date)
// it is for database entry in string and i get right string
print(routineStartDate)
startDate = self.startDatePicker.date as NSDate
print(startDate)
endDate = startDate
}
below method is for start time where i get time and convert to Time Interval and set it to start date.
func starttime() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
let then: Date? = self.startTimePicker.date
let difference: TimeInterval? = then?.timeIntervalSinceNow
startDate.addingTimeInterval(difference!)
routineStartTime = dateFormatter.string(from: self.startTimePicker.date)
// it is for database entry in string and i get right string
print(routineStartTime)
}
below method is for end time where i get time from picker and convert to Time Interval and set Time Interval to enddate
func endtime() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
routineEndTime = dateFormatter.string(from: self.endTimePicker.date)
print(routineEndTime)
// it is for database entry in string and i get right string
let then: Date? = self.endTimePicker.date
let difference: TimeInterval? = then?.timeIntervalSinceNow
endDate.addingTimeInterval(difference!)
}
below image showing which date i set in picker
below is My EKEvent method where i create event.
existevent.title = tempDescription
existevent.startDate = startDate as Date
existevent.endDate = endDate as Date
existevent.isAllDay = false
existevent.notes = "This is a note"
existevent.calendar = cal
when i check event in calendar i got Problem, i get wrong time in event.i set start time 12:50 pm end time 1:50 pm on date 27 june 2017 in caledar app. date is set perfectly but why time is not set perfectly ? below image for calendar app.
i have doubt in conversion of time interval and set to date. but what i missing dont know.
please suggest me solution and ideas to solve.
Thank you
you need to convert the time to the desired time zone. Because now the date is set correctly in your timezone, but is displayed in +0000 Screenshot. Use calendar for date representation this
And change your code like this in both methods:
startDate.addingTimeInterval(difference!)
to
self.startDate = startDate.addingTimeInterval(difference!)
and
endDate.addingTimeInterval(difference!)
to
self.endDate = endDate.addingTimeInterval(difference!)
in your case Xcode Warning "Result of call to 'addingTimeInterval' is unused"
Try to convert date, before set it to you "existevent", or when you show it
func convertDate(date:Date) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm" // or other format
var comp = DateComponents()
let calendar = Calendar.current
comp.hour = Calendar.current.component(.hour, from: date)
comp.minute = Calendar.current.component(.minute, from: date)
comp.timeZone = TimeZone(abbreviation: "GMT")!
return calendar.date(from: comp)!
}

Resources