Challenges of DateFormatters

Challenges of DateFormatters

Working with DateFormatters is tough - At least on iOS. Today I will show with an example how I shot myself in the foot while working with simple DateFormatters problem.

Here's the problem statement:

I had an UI where user would choose month and year through a simple picker and those values get sent to the server. So I created a date picker like this,

let formatter = DateFormatter()
formatter.dateFormat = "MM/YYYY"

In essence, we give this formatter an input string containing both month and year value and formatter spits out the `Date` object. Let's try to verify with some examples,

// Prints Optional(2017-12-24 05:00:00 +0000)
print(formatter.date(from: "12/2018"))

// Prints Optional(2017-12-24 05:00:00 +0000)
print(formatter.date(from: "11/2018"))

// Prints Optional(2017-12-24 05:00:00 +0000)
print(formatter.date(from: "10/2018"))

Hey!!! what's going on here? Looks like our string didn't get converted to expected Date object. Answer lies somewhere else. Fortunately I found this post which explains the difference between yyyy and YYYY formatter values. To say, it in Apple words,

A common mistake is to use YYYY. yyyy specifies the calendar year whereas YYYY specifies the year (of “Week of Year”), used in the ISO year-week calendar. In most cases, yyyy and YYYY yield the same number, however they may be different. Typically you should use the calendar year.

And to quote X-Factor,

Also when using a date format string using the correct format is important.
@"YYYY" is week-based calendar year.
@"yyyy" is ordinary calendar year.

So in this case we certainly want a regular calendar year which necessitates the use of yyyy instead. Let's fix the problem now,

let formatter = DateFormatter()
formatter.dateFormat = "MM/yyyy"

And verify the previous logic with new DateFormatter

// Prints Optional(2018-12-01 05:00:00 +0000)
print(formatter.date(from: "12/2018"))

// Prints Optional(2018-11-01 05:00:00 +0000)
print(formatter.date(from: "11/2018"))

// Prints Optional(2018-10-01 05:00:00 +0000)
print(formatter.date(from: "10/2018"))

Which is what we wanted and it worked like a charm!

It was a very silly mistake besides the fact that Apple has made it more confusing by using such nomenclature. We has a hard time figuring this out especially when this code was the least suspected. Something to keep in mind when I am dealing with DateFormatters next time.