NSDateFormatter parses two-digit year as 2046 instead of 1946 or 2040 instead of 1940 - ios

I have a scenario where I'm getting a date string as "46-05-24" (yy-mm-dd), and I need to re-format the date as "1946-05-24". The NSDateFormatter interprets the string as "2046-05-24".
I'm using this code:
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yy-mm-dd"
let gmt : NSTimeZone = NSTimeZone(abbreviation: "GMT")!
dateFormatter.timeZone = gmt
let dateFromString = dateFormatter.dateFromString(date as String)
dateFormatter.dateFormat = "yyyy-mm-dd"
if dateFromString != nil{
let dateString = dateFormatter.stringFromDate(dateFromString!)
print(dateString)
}
Is there something I'm missing here?

It happens because if you're giving NSDateFormatter a two-digit year, it needs to decide what century that year is in. It does this using its twoDigitStartDate property, which sets the earliest date that a two-digit year can represent. It has a default value of December 31, 1949. A date in 46 falls on the low side of 50 so it gets treated as 2046.
You can change the value of twoDigitStartDate to adjust the results. For example, you could set it to a date exactly 100 years in the past. That would mean that any two-digit year would be interpreted as the most recent year with those two digits:
let oneCenturyAgo = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Year, value: -100, toDate: NSDate(), options: NSCalendarOptions(rawValue:0))
dateFormatter.twoDigitStartDate = oneCenturyAgo
Of course if you get someone whose date is over 100 years ago, there's no good way for your code to know which year is appropriate. If the year is "10", was that person born in 1910 or 2010? You have no way of knowing, and all your code can do is make the best guess.

The cause of your problem is that your data omits some relevant information: the century. Using only the last two digits you as the dev have to decide which century the dates are in. If all of them are in the 20th century (1900 - 1999) you can use the following approach:
You could simply prepend 19 before the string to parse. Assuming date is the string that you want to parse you can use
let dateFromString = dateFormatter.dateFromString("19" + (date as String))
instead of your
let dateFromString = dateFormatter.dateFromString(date as String)
If the dates you are going to handle are in the 20th and the 21st century you are going to have a bad time because what year is 05 supposed to reflect? 1905? 2005? 2105?

Related

Formatting a date from a string in swift [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 3 years ago.
i have the following date coming from a server 2019-09-05T10:37:49.494Z as a string and i need to parse this and convert it to a format like this Fri September 13,2019 12:36 AM and back to a string again:
i found multiple question links but none of them are working for me Question One
Question Two
it tried doing this:
let dateFormatterGet = DateFormatter()
let dateFormatterPrint = DateFormatter()
var rawDate = "2019-09-05T10:37:49.494Z"
dateFormatterGet.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
dateFormatterPrint.dateFormat = "E, d MMM yyyy HH:mm"
var formattedDate = "Error Formatting date"
if let date = dateFormatterGet.date(from: rawDate) {
formattedDate = dateFormatterPrint.string(from: date)
print("Formatted Date : \(formattedDate)")
}else {
print("There was an error decoding the string")
}
this fails printing the error message, what am i doing wrong and how can i fix it?
You are almost there.
A small tip playing with (NS)DateFormatter put the dateFormat above/under the date string.
yyyy-MM-dd'T'HH:mm:ssZ
2019-09-05T10:37:49.494Z
Then, add "spaces" to align and separate them.
yyyy - MM - dd 'T' HH : mm : ss Z
2019 - 09 - 05 T 10 : 37 : 49 . 494Z
^ ^^^
I highlighted the missing ones. You need to tell the (NS)DateFormatter through the dateFormat how to interpret theses additional characters.
Let's check the documentation.
It's
Fractional Second - truncates (like other time fields) to the count of letters. (example shows display using pattern SSSS for seconds value 12.34567)
So using yyyy-MM-dd'T'HH:mm:ss.SSSZ should interpret them and fix your issue.
That's how you fix your issue. And it explained your error.
But since as pointed by #Zombie it's using a ISO format, use if available the ISO8601DateFormatter if possible (iOS10+)
If in future cases you don't have an ISO something format, you can use theses tips ;)
The format you provided seems like an iso 8601 date for this reason I would suggest using the ISO8601DateFormatter
You can specify the options to match your string
here is an example
let dateString = "2019-09-05T10:37:49.494Z"
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [
.withDashSeparatorInDate,
.withFullDate,
.withFullTime,
.withFractionalSeconds,
.withColonSeparatorInTime
]
// "Sep 5, 2019 at 12:37 PM"
let date = formatter.date(from: dateString) ?? Date()
//"Thursday, September 5, 2019 at 12:37:49 PM"
let formattedDate = DateFormatter.localizedString(
from: date,
dateStyle: .full,
timeStyle: .medium
)
The problem is you don't tell dateFormatterGet how to parse milliseconds. Modify the dateFormat to:
dateFormatterGet.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

how to make 24 hours time format from string in Swift

I want to convert a string that was generated by the user to a Date data type. I want the time to be in 24-hour format
let dateFormatter = DateFormatter()
let timeAsString : String = "22:30"
dateFormatter.dateFormat = "HH:mm"
let timeFromString = dateFormatter.date(from: timeAsString)
result : "Jan 1, 2000 at 10:30 PM"
but the result is in 12-hour format. How can I get 22:30 as a Date data type?
Date has no format, so only can change the string converted from the date
Swift 4
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
let newDateString = dateFormatter.string(from: yourDate)
for different date format, you can check nsdateformatter.com
You code is absolutely correct, there no problem in your code. String HH in date format represent 24 hours time display format.
But developer (application) has no control over time format. You can set date format string supporting 24 hours time but if user has (not enabled) turned of 24 hours support from device then it will display time for 12 hours format.
Check your simulator/mac system/iPhone device time format and set it for 24 hours display.
Refer this apple document for 24 hours time support: Date Formatters
The representation of the time may be 13:00. In iOS, however, if the user has switched 24-Hour Time to Off, the time may be 1:00 pm.
func convertToString(of dateTo: Date) -> String {
let dateFormatter = CustomDateFormatter()
//Your New Date format as per requirement change it own
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let newDate: String = dateFormatter.string(from: dateTo) //pass Date here
// print(newDate) //New formatted Date string
return newDate
}

Issue in formatting date string Swift 3 [duplicate]

This question already has answers here:
NSDateFormatter doesn't show time zone abbreviation for "Asia/Kolkata" for the "z" or "zzz" specifier, just the GMT offset
(1 answer)
What is the best way to deal with the NSDateFormatter locale "feature"?
(4 answers)
Closed 5 years ago.
I need to convert the following date string in to a Date in Swift 3.
Fri Dec 09 16:18:43 AMST 2016
Here is the code that i have been using, but it's getting cash on this particular date string conversion. (This date was logged on Android using new Date().toString() method.)
static func formatDate(date: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
dateFormatter.dateFormat = "EEE MMM dd HH:mm:ss zzz yyyy"
//Works for "Fri Sep 16 10:55:48 GMT+05:30 2016"
var myDate = dateFormatter.date(from: date)
// My date returns nil on "Fri Dec 09 16:18:43 AMST 2016"
dateFormatter.dateFormat = "dd/MM/yyyy"
return "\(dateFormatter.string(from: myDate!))"
}
There are both type of strings in the database. I tried with various types of Timezone formats (z/zz/zzz/zzzz/zzzzz) but always myDate returns nil.
Any help would be appreciated.
Thanks In Advance.
Apple doc for TimeZone(abbreviation:):
In general, you are discouraged from using abbreviations except for unique instances such as “GMT”. Time Zone abbreviations are not standardized and so a given abbreviation may have multiple meanings.
Does AMST represents "Amazon Summer Time" (UTC-3) or "Armenia Summer Time" (UTC+5)? See: https://www.timeanddate.com/time/zones
That's probably why it can't detect the proper timezone to use.
Solutions I can propose:
If you know which timezone AMST is:
replace AMST by UTC-3 or UTC+5 in the date string
remove AMST from the date string and use dateFormatter.timeZone = TimeZone(secondsFromGMT: -3 or 5 * 3600)
Have your source output a more precise timezone.
Note the following code, where AMST is understood correctly:
let df = DateFormatter()
df.locale = Locale.init(identifier: "pt_BR") // assuming AMST is Amazon Summer Time (UTC -3)
df.dateFormat = "HH:mm:ss z"
print(df.date(from: "16:18:43 AMST")) // Optional(2000-01-01 19:18:43 +0000)
But as soon as you include English day or month names (e.g. Fri or Dec) it will produce nil (because they aren't in Portuguese).

Invalid date after conversion 12h -> 24h

I want to convert AM/PM time to 24h. That should be totally trivial... however I'm standing against a weird issue:
let inputDateString = "11:05:45PM"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm:ssa"
let inputDate = dateFormatter.dateFromString(inputDateString) // "Jan 1, 2000, 12:05 PM" ???
if let inputDate = inputDate {
dateFormatter.dateFormat = "HH:mm:ss"
print(dateFormatter.stringFromDate(inputDate))
}
I thought that maybe it's converting the time using some weird timezone. So I've tried to add systemLocale or GB locale, that didn't help.
Then I've came up with the idea, that maybe it can't set date previous to 01.01.2000 12:00:00 (what is without sense, because it should work at least from year ~1970). So I've added the day, month etc to move it to 2005 year, but the issue still persists.
You can easily paste this to the Playground with import Foundation to test it out.
Why am I getting wrong hour?
-- Edit --
If you change inputDateString to any other hour like "01:05:45PM", the inputDate would be the same :/ But if you change minutes or seconds, it gets updated accordingly.
You need to change the dateFormat to "hh:mm:ssa", as the lowercase h signalizes that the input time is in 12-hour format.
DateFormatter uses the Unicode standard for formatting, so it uses the values found in the Unicode Documentation (Valid for macOS 10.9 and iOS 7 and newer, as of 08/2016).
An easier-to-use reference can be found on http://nsdateformatter.com.
let inputDateString = "11:05:45PM"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm:ssa" // Here, change the format string to "hh:mm:ssa"
let inputDate = dateFormatter.dateFromString(inputDateString) // "Jan 1, 2000, 12:05 PM" ???
if let inputDate = inputDate {
dateFormatter.dateFormat = "HH:mm:ss"
print(dateFormatter.stringFromDate(inputDate))
}

NSDateFormatter decreases the day of date

I need to store Date variable in CoreData in iOS
I need to store the Date only without the Time, So I made a formatter that discard the time partition from the NSDate variable.
But I have a strange result:
This is my code:
let dateStr = "2016-02-14 11:27:01"
let df2 = NSDateFormatter()
df2.timeZone = NSTimeZone.defaultTimeZone()
df2.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dateStr)
if let date = df2.dateFromString(dateStr) {
df2.dateFormat = "yyyy-MM-dd"
print("-> \(df2.dateFromString(df2.stringFromDate(date)))")
}
and this is the output:
2016-02-14 11:27:01
-> Optional(2016-02-13 20:00:00 +0000)
Why does the formatter decrease the day by one ?
I tried many dates with same issue
Your time zone is obviously UTC+4.
To get UTC set the time zone accordingly.
df2.timeZone = NSTimeZone(forSecondsFromGMT: 0)
But although you see a date 4 hours ago the NSDate object is treated correctly depending on your time zone. The print command displays always UTC ignoring the time zone information, because NSDate is just a wrapper for a Double number.

Resources