Format date with 7 fractional seconds - ios

I have the need of parsing a date that looks like this:
"2018-03-08T17:37:09.2694781-00:00"
Notice the fractional seconds, I need to keep all 7 digits because then I need to send back to my server exactly the same date.
So, I need to be able to convert that string to Date, do some stuff and then send it back to another server thus converting that Date back to String again and keeping the same format and fractional seconds, keeping the original date as String is not an option here.
I already tried this but it does not work because the original Date/Time gets modified for some unknown reason
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.Axxx"
let dateObj = formatter.date(from:("2018-03-08T17:37:09.2694781-00:00"))
//This produces: "2018-03-08 00:44:54 UTC", nothing to do with the original date, it would be fine if converted to UTC, though
I also tried a formatter like
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSZZZZZ"
But that truncates my fractional seconds to 3 digits.

Related

How to know Date format getting from sever [duplicate]

This question already has answers here:
How can I parse / create a date time stamp formatted with fractional seconds UTC timezone (ISO 8601, RFC 3339) in Swift?
(13 answers)
Closed 1 year ago.
I am trying to get date format of a date that I am getting from server.
What will be the format of this date:
2021-10-14T17:53:03.753588+05:30
What I tried:
yyyy-MM-dd'T'HH:mm:ssXXX
But Its not working.
How to (my tips & steps):
When you are struggling to find the date format for String -> Date ask you this: What my format is really doing? What's it's parsing/interpreting? Just let's see with Date -> String...
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXX"
print(formatter.string(from: Date()))
Output: 2021-10-14T13:06:38+02:00: Is it the "same" as the string we have? No, some are the same, but not all...
Let's continue with another tip:
Let's put our format and the string one above the other one:
2021-10-14T17:53:03.753588+05:30
yyyy-MM-dd'T'HH:mm:ssXXX
Then, let's add "spaces", to make each pattern match its corresponding input:
2021-10-14 T 17:53:03.753588 +05:30
yyyy-MM-dd 'T' HH:mm:ss XXX
Then, let's check the doc (it's bookmarked in my web browser) for interpretation of the pattern and check if they correspond if needed, and to find the missing one if needed too.
Ok, so we aren't interpreting .753588 at all, that's why it's failing...
It's for the fractional seconds, so if we change the format to: "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", it should work. Note, you can replace XXX with Z if you want...
Now, remember that patterns are case sensitive, so if you have strange hours, minutes, or nil because of that, check if you didn't misuse minutes vs month, 12h format vs 24 hour format...
If you have hours diff (or usually 30min diff), the issue could be then a timezone issue.
If you have a day diff, it could also be a timezone issue (interpret it as hours diffs around midnight, so there is a day change).
If you have a year diff, check if you didn't misuse yyyy vs YYYY.
Etc. But that should cover most of your cases (basic issues).
Try this format.
"yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX"
As the date format you mentioned in your question contains milliseconds as well as timezone offset info.
Once date is parsed then based on your need you can set output date format and will get formatted date.
let sampleDate = "2021-10-14T17:53:03.753588+05:30"
let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX" // Set input date format.
let inputDate = dateFormatter.date(from: sampleDate)
print("Input Date:- \(inputDate)")
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss" // Set output date format as per need
let outputDate = dateFormatter.string(from: inputDate ?? Date())
print("Output Date:- \(outputDate)")

How does function `DateFormatter.dateFormat(fromTemplate:)` work?

With some digging, and help from a thread on SO, I was able to figure out that if you set up a DateFormatter with code like this:
let locale = NSLocale.current
let format : String = DateFormatter.dateFormat(fromTemplate: "j:mm.ss.SSS", options:0, locale:locale)!
print("format = \(format)")
let formatter = DateFormatter()
formatter.dateFormat = format
You get a date formatter with a date string that adapts to the user's 12/24 hour clock setting.
If you run that code in the US with the device set to 12 hour time, you see the following on the console:
format = h:mm:ss.SSS a
If you set your device to 24 hour time, you instead see
format = HH:mm:ss.SSS
That makes sense.
However, once you assign the resulting format string to your date formatter, it should be fixed. I would expect the date formatter to generate 12 or 24 hour times until I explicitly change it's format string.
That is not what happens. If I switch to settings and change the clock settings from 12 to 24 hours, the date formatter magically starts producing 24 hour time strings. I can log the date formatter's format string, and it sill shows the 12 hour string "h:mm:ss.SSS a". What's going on?

Issue with 24hour date format

I am trying to convert Date object in string using DateFormatter but I am getting some weird behavior. I have a timestamp value (1513356296) which I want to convert in 24hour format along with date and timeZone (My current is -0800). Here is my code:
let date = Date(timeIntervalSince1970: Double(1513356296))
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
dateFormatter.timeZone = TimeZone(secondsFromGMT: -(8*3600))
dateFormatter.locale = Locale.current
I am expecting a result value of 2017-12-15T08:44:56-0800 but I am getting 2017-12-15T8:44:56-0800. Everything is same except the hour value. The hour value is 8 instead of 08.
This is not happing with all the test, it happened in only one device and rest are fine.
I also tried by checking if hour value changes if I change device time setting to 12hour format from 24hour format to see what I get for HH and it was always giving me 08 in my case. Only one user was facing this issue.
What's wrong with my code? or is there something wrong with HH I am using?

Swift date formatting, getting the date back from server is not the same as sent to the server

I am trying to parse a date that is sent to the server.
Now the date that is recieved from the server is "2016-05-10T22:34:00.000Z"
And here is my code to get a formatted date out of the above date string :
let format="yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let dateFmt = NSDateFormatter()
dateFmt.dateFormat = format
let newreadableDate = dateFmt.dateFromString(dateString)
print(newreadableDate!)
dateFmt.dateFormat = "MMM dd hh:mm a"
print("Formatted date is : \(dateFmt.stringFromDate(newreadableDate!))")
The print statement prints the following result : May 11 04:04 AM
The above result is totally wrong. I should get back the same date that I recieved from server but with different format.
But the newReadableDate variable prints the correct date : 2016-05-10 22:34:00 +0000
But after I format it, it gives me wrong date.
What is wrong in the above code ?
Your code is fine. The output is correct. The date string you parse is in UTC time. But you log the final result in local time. You must live in a timezone that is 5.5 hours ahead of UTC.
The Z in the date string represents "Zulu" time which it UTC time. So the date formatter parses the string as a time in UTC time.
You then choose to print the resulting NSDate. By default, NSDateFormatter will generate a string from an NSDate in locale time.
May 10, 2016 at 22:34 UTC time is exactly the same time as May 11, 2016 at 04:04 am in your local (+0530) timezone.

Getting a NSDate object from a string

Before flagging this question as a duplicate, please read on.
I need to compare two NSDates. A date I get from a server with the current date.
From NSDate.date() I get this date 2014-09-25 12:48:23 +0000 which is wrong (the time part). I needed to add 5 hours to get the current time so I did the following.
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd hh:mm:ss ZZZZZ"
let dateString = formatter.stringFromDate(NSDate.date())
The result is the correct date - 2014-09-25 06:21:56 +05:30
But there's a little hitch. This date is a String, not a NSDate. I need it to be a NSDate object to compare it with another date.
I tried converting it back like this,
let date = formatter.dateFromString(dateString)
And I get a wrong result - 2014-09-25 00:55:53 +0000. I tried passing the date string to a new NSDateFormatter to see if that works but again I still I get the wrong date.
My question is, how can I convert this date string to a NSDate object which also retains the correct time.
Thank you.
You are thoroughly confused about NSDate.
An NSDate is a point in time. It has no time zone information. If we both call [NSDate date] right now, we will get the same NSDate, even when you are in India and I'm in the UK. That's intentional. It's the same time. The time displayed on my watch and on your watch is different, but NSDate is the same. You can't convert NSDate to an "Indian" date.
You use calendars and timezones to convert NSDates to strings that you display to a user, in the way your users expect it. That's what you have done. You got a string that makes sense to Indian users. If an Indian user types a time, you take that string and convert it to an NSDate. The NSDate will be in Universal time. If you and I both typed in the time on our watch right now and converted it, you would type a time that looks like 5 1/2 hours earlier than mine. But it's the same time. If you convert it to NSDate, we will both get the exact same NSDate.
So how do you change your NSDate? Quite simple: You don't. NSDate is absolute time, independent of your location on earth.
Upon further Googling, I came across this post. The method described in it does exactly what I want. The original code is in Objective-C and since my question is in Swift, I'm going to post its Swift translation. \
func toLocalTime() -> NSDate {
let timeZone = NSTimeZone.localTimeZone()
let seconds = timeZone.secondsFromGMTForDate(self)
return NSDate(timeInterval: Double(seconds), sinceDate: self)
}
I added these as extension methods of NSDate so you can simply call them like this.
NSDate.date().toLocalTime()
You can compare two dates using any of the following NSDate functions: compare, earlierDate, laterDate, isEqualToDate. You should not compare date strings (oh, goodness, no, think of the nightmare); convert 'date strings' into 'NSDate' as soon as inputed.
In order to compare two dates that arose from strings correctly, you'll need the date strings to be unambiguous. In practice, that requires the date to have a time zone attached. If your server isn't providing a time zone and can't be modified to provide one, then you'll be forced to assume one (which would typically be the time zone where the server is located, assuming one server).

Resources