UIDatePicker shows incorrect time - swift

I'm trying to set the minimumDate to the datepicker, but the datepicker adds +1 hour when showing.
I suspect that the timezone of the Datepicker is different from firstPossibleOrderTime.
I get this from api: 20181127151122.
Then use SwiftyDate pod to init date.
DateInRegion(firstPossiblePickup, format: "yyyyMMddHHmmss",
region: .currentIn()).
let datePickerFrame = UIDatePicker(frame: .zero)
datePickerFrame.locale = Locale.init(identifier: "nb")
print(self.firstPossibleOrderTime.date)
print(self.firstPossibleOrderTime.date.add(28.days))
datePickerFrame.minimumDate = self.firstPossibleOrderTime.date
datePickerFrame.maximumDate = self.firstPossibleOrderTime.date.add(28.days)
How can i prevent the Datepicker from increment the time by 1 hour?

I guess that probably you are using the wrong date formatter to convert the date you are receiving from the server.
Is the time you receive from the server a string?
If it is a string, in which time zone is set?
You must take that into account before parsing it, usually servers returns a en_US_POSIX date string that you can parse with this date formatter.
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
dateFormatter.isLenient = false
If it is not in this format you probably must take into account time zone or daylight saving.

Related

I want to format a date into a string, but its giving me nil, i want to use the date in a UITextField.text

// the creationdate is coming from an api call
var creationDate = "2020-11-04T16:46:59.439212Z"
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none
formatter.dateFormat = "dd-MM-yyyy"
var creationDateFormattedInToDate = formatter.date(from:
creationDate)
print("date \(creationDateFormattedInToDate)")
So i want that date in the format 04-11-2020 and pass in a UITextField.text
You will need two formatters, one to parse the input date to a Date object and one to convert the date object to a string of the right format.
The input date seems to be a variant of a internet date/time so we use a ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
The second formatter is a basic DateFormatter with a custom format
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = "dd-MM-yyyy"
And then we can use them like this
if let date = formatter.date(from: creationDate) {
someTextField.text = outputFormatter.string(from: date)
}
You will want to use one formatter for parsing the response from the server (which is in what’s called and “ISO 8601” or “RFC 3339” format), and another for preparing the string representation of the date in the UI.
Regarding the date formatter for parsing the server response:
Set the formatter’s locale to Locale(identifier: "en_US_POSIX").
The setting of the styles when parsing this date string are irrelevant if you’re going to set dateFormat.
When parsing the date from the string, set dateFormat to yyyy-MM-dd'T'HH:mm:ss.SSSSSSX.
If you ever plan on using this formatter for the reverse date-to-string conversion (for preparing date strings to be sent to the server) you might want to set the timeZone of the formatter to TimeZone(secondsFromGMT: 0).
Regarding the date formatter used to prepare the string representation of the date in your UI:
I would not advise ever using a fixed dd-MM-yyyy format in your UI. That might be natural for European users, but it may be unnatural to most US users, who generally expect to see month before the day.
I would suggest not using dateFormat for this second date formatter, but rather using a dateStyle (e.g. of .medium or .long). It results in a nice, localized, and natural reading date string.
If you insist in using dd and MM and yyyy in your UI, I’d localize it with setLocalizedDateFormatFromTemplate so that the day and the month appear in the logical order that this particular user would expect (month-followed-by-day for US users, day-followed-by-month for most other locales).
Thus:
let serverDateFormatter = DateFormatter()
serverDateFormatter.locale = Locale(identifier: "en_US_POSIX")
serverDateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
serverDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSX"
let uiFormatter = DateFormatter()
uiFormatter.dateStyle = .medium // or uiFormatter.setLocalizedDateFormatFromTemplate("ddMMyyyy")
if let date = serverDateFormatter.date(from: creationDateString) {
let string = uiFormatter.string(from: date)
// use that `string` in your UI
}

Swift convert Unix timestamp to Date with timezone and save it to EKEvent

I trying to convert my unix timestamp(Int) to the Date type in my app. I found a solution which is
let str = timeValue as? NSNumber
return Date(timeIntervalSince1970: str.doubleValue)
This solution works but how can I set the timezone. I found another solution that used the formatter but the formatter return string.
func convertDateTime(timeValue: Int) -> String {
let truncatedTime = Int(timeValue)
let date = Date(timeIntervalSince1970: TimeInterval(truncatedTime))
let formatter = DateFormatter()
formatter.timeZone = TimeZone(abbreviation: "GMT+8")
formatter.dateFormat = "dd/MM/yyyy hh:mm a"
return formatter.string(from: date)
}
Anyone can answer me how to do so?
Edited: I want to save it as EKEvent.
Dates represent instants/points in time - "x seconds since a reference point". They are not "x seconds since a reference point at a location", so the timezone is not part of them. It makes no sense to "set the timezone of a Date", the same way it makes no sense to "set the number of decimal places of a Double".
It seems like you actually want to store a EKCalendarEvent. Well, EKCalendarEvents do have a timezone, because they are events that occur at a particular instant/day (occurrenceDate), in some timezone (timeZone). So you just need to set the timeZone property of the EKEvent, rather than the Date.

How to get daylight saving time with using pod TimeZoneLocate in Swift

I'm converting local timezone to string to display it on screen. For this purpose I use TimeZoneLocate library. Problem: I'm getting date result one hour less than it is because of not implementing daylight saving time.
I'm getting JSON from sunrise-sunset.org, and using this lines: sunrise = "3:22:31 AM"; sunset = "5:23:25 PM".
I thought about using function isDaylightSavingTime() with if statement, but I can't figure out where to add this one hour.
This is function where does the magic happen:
func UTCToLocal(incomingFormat: String, outgoingFormat: String, location: CLLocation?) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = incomingFormat
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let dt = dateFormatter.date(from: self)
let timeZone = location?.timeZone ?? TimeZone.current
dateFormatter.timeZone = timeZone
dateFormatter.dateFormat = outgoingFormat
return dateFormatter.string(from: dt ?? Date())
}
I use local "location" from CLLocation, TimeZone.current is provided by TimeZoneLocate library.
This is how I use it in code:
func parce(json: Data, location: CLLocation) {
let decoder = JSONDecoder()
if let sunriseData = try? decoder.decode(Results.self, from: json) {
self.sunriseLbl.text = sunriseData.results?.sunrise.UTCToLocal(incomingFormat: "h:mm:ss a",
outgoingFormat: "HH:mm",
location: location)
sunriseLbl is printing the sunrise data from JSON for current location by default and for any place by GooglePlaces. But, in both, I get wrong date.
Also, here is a link to my project on GitHub, if it might help you help me: https://github.com/ArtemBurdak/Sunrise-Sunset.
Thanks in advance
An interesting thing I noted: TimeZone.current is returning the correct time zone, but location?.timeZone is not returning the correct time zone. If there is a way to implement TimeZone.current, i.e. the application will always be using the user's current location, then I would advise using that. If users can enter a custom location, however, then you need to get a workaround for the apparent incorrect time zone returned by location?.timeZone.
My workaround is as follows. Notice that we manually adjust the location of the time zone we want by changing the .secondsFromGMT() property. This is how I adjusted your code, and it was returning the correct time zone for my personal location.
extension String {
func UTCToLocal(incomingFormat: String, outgoingFormat: String, location: CLLocation?) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = incomingFormat
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let dt = dateFormatter.date(from: self)
var timeZone = location?.timeZone ?? TimeZone.current
if timeZone.isDaylightSavingTime() {
timeZone = TimeZone(secondsFromGMT: timeZone.secondsFromGMT() - 7200)!
}
dateFormatter.timeZone = timeZone
dateFormatter.dateFormat = outgoingFormat
let output = dateFormatter.string(from: dt ?? Date())
return output
}
}
NOTE:
Time zones are very complex and change from place to place and from the current time of year. Just because this workaround works for my current location on this current day, doesn't mean that this workaround always works. However, you can look at the timeZone.isDaylightSavingTime() value returned as well as the current location to create a new time zone via timeZone = TimeZone(secondsFromGMT: timeZone.secondsFromGMT() - x as needed. This is the way you can implement the
"I thought about using function isDaylightSavingTime() with if statement, but I can't figure out where to add this one hour."
idea that you had.
EDIT:
For the record, the time zone I was using was CST, or Chicago time. The date I wrote this code at was April 19, 2019.

can't convert time stamp date to string in swift 4

I'm trying to convert a timeStamp string date to Date.
The result always returns nil.
func getDatefromTimeStamp (str_date : String , strdateFormat: String) -> String {
// stringDate '2018-01-01T00:00:00.000+03:00'
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone!
let date = dateFormatter.date(from: str_date)
dateFormatter.dateFormat = strdateFormat
let datestr = dateFormatter.string(from: date!)
return datestr
}
Your primary issue is that the format "yyyy-MM-dd'T'HH:mm:ssZ" does not match a string such as "2018-01-01T00:00:00.000+03:00". That string contains milliseconds but your format doesn't.
Update your format to "yyyy-MM-dd'T'HH:mm:ss.SSSZ".
That will fix the nil result.
Then you should clean-up your use of NSTimeZone. Just use TimeZone.
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
But there is no need to set the timezone when parsing this string because the string includes timezone information.
However, you may or may not want a timezone set when converting the resulting Date into the new String. It depends on what result you want.
Do you want the final string in UTC time (which is what you will get with your current code) or do you want the final string in the user's local time?
If you want the final string in the user's local time, don't set the timezone property at all. It will default to local time.
First of i highly recommend that you use guards instead of forces
Secondly why are you setting the date format twice? the first time is dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" then a few lines later resetting it with the parameter you passed in dateFormatter.dateFormat = strdateFormat. pick one and set it in the beginning - that may be causing your problem
Thirdly if that above is not the problem - make sure that your date is exactly in the necessary format if it is at all wrong it will return nil. even spaces and colons have to be perfect, i suggest using string builder to make sure they are consistant

Using DateFormatter produces a result which is off by a day

Using DateFormatter produces a result that is off by a day (actually 12 hours). Using the following code consistently produces dates that show as the previous day. I've been getting this in a number of applications for a while but just finally got around to digging into it.
extension Date
{
func display() -> String
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy"
print(dateFormatter.locale)
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
let txt = dateFormatter.string(from: self)
print(txt)
return txt
}
}
Other questions that were in this vein suggested changing the locale, thus the added code for that, but I checked the locale and the actual date. If I add 8 hours to the date, I get the correct display result, but adding less than that does nothing. Some dates are being retrieved from the birthday field in the Contacts app, which yields dates that have a time of day 00:00:00 UTC. It would seem that I need to convert the date to local time? The timezone on the device is set to the local timezone (Pacific). That wouldn't seem so bad, but dates retrieved from a date picker aren't in UTC time, they're in local time. I haven't been able to figure out how to tell which timezone the date is in since using the calendar class and trying to extract the .timezone component says that "NSCalendarUnitTimeZone cannot be gotten by this method". Any suggestions on how to create a universal date formatter that works in all cases?
A couple of observations:
If your Date object is in UTC time zone, then set your formatter’s timeZone to TimeZone(secondsFromGMT: 0), too.
If you’re showing the string representation of a Date object in the UI, you do not want to use a locale of en_US_POSIX. You want to show it in the default locate of the device (i.e., don’t change the formatter’s locale at all). You only use en_US_POSIX when dealing with ISO 8601 and RFC 3339 date strings that are used internally or, for example, for exchanging date strings with a web service).
Finally, I would not specify a dateFormat string because not all users expect dates in MMMM dd, yyyy format. For example, UK users expect it in d MMMM yyyy format. When presenting dates in the UI, specify a dateStyle instead. Or, if none of those styles work, go ahead and specify dateFormat, but set it using setLocalizedDateFormatFromTemplate(_:) rather than a fixed string.
Thus, for your purpose, you would do:
extension Date {
var dateString: String {
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter.string(from: self)
}
}
Or, if you're calling this a lot, you may want to reuse the formatter:
extension Date {
private static let formatterForDateString: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
var dateString: String {
return Date.formatterForDateString.string(from: self)
}
}
Use the timeZone property, to get the exact date, as shown as below:
formatter.timeZone = TimeZone(secondsFromGMT: 0)
it will solve your purpose!