With mobile apps, incorporating the time and date in keys, signatures, and/or hashes is a common way to validate data sent between your app and your backend. To do that properly, consistency between clients and users is crucial. If you’re not careful when using dates in Swift, you’ll probably run into some lesser-known pitfalls based on your users’ device settings. Let’s walk through how to reliably get the date or time you are expecting, in a specific format, using the DateFormatter in Swift.

Setting up

Let’s start by initializing our Date and DateFormatter objects.

let date = Date()

let formatter = DateFormatter()

Simple. Now if you want to turn your Date into a string, set the format and request the string.

formatter.dateFormat = "[your format]"

let dateString = formatter.string(from: date)

Pitfalls

Easy right? Not so fast. This code is prone to unexpected issues you may not discover in your testing. Let’s talk about how to avoid them.

Calendar

In one of our projects, we discovered that a fair number of users were sending us unusual years, like “2560” or “0028.” This was causing issues with our backend, which was expecting “2018” for the year. Turns out, those are accurate years, but for the Buddhist and Japanese calendars, respectively. In the iOS settings, users can choose between the Buddhist, Japanese, or Gregorian calendar. Obviously, if you are expecting a Gregorian year and receive a Japanese or Buddhist one, your dates are not going to line up. You can fix this by forcing your DateFormatter to use a specific calendar.

formatter.calendar = Calendar(identifier: .gregorian)

Locale

After discovering the calendar issue, I wanted to see how else I could produce an unexpected date. I found that by setting the region in my iPhone to Japan, and switching off the 24-hour time setting (which is on by default for the Japanese region), I would get results like “9 AM9” where the hour was repeated twice, and the “AM/PM” designator was included, even though the “HH” format was requested. Bizarre! To fix this, set the DateFormatter’s locale manually.

formatter.locale = Locale(identifier: "en_US_POSIX")

Time zone

Most developers will already have planned for this, but I wanted to include this pitfall just in case. If your users span across multiple time zones, it’s important to work with the same one for both your app and your backend. In Swift, you can do this by explicitly setting the time zone of your DateFormatter. The industry standard time zone is UTC.

formatter.timeZone = TimeZone(identifier: "UTC")

Real time vs. device time