0

I am new to iOS development, being an Android developer I am used to use have an object that saves a datetime with a given timezone (from Joda-Time library).

After reading the iOS documentation about dates and times (https://developer.apple.com/documentation/foundation/dates_and_times) I still have doubts about which class should I use to save datetimes. Given the Date/NSDate class description "A specific point in time, independent of any calendar or time zone." it seems very useless because it is timezone independent and time without a timezone does not make any sense, since it does not have any context.

My real problem (TL;DR):

I have a database where date times are stored in UTC like this "yyyy-MM-dd hh:mm:ss". I would like to init an object with some kind of DateFormatter (string with this format "yyyy-MM-dd hh:mm:ss") plus a timezone (UTC) to easily convert to any Timezone that I want (to show to the user on his default timezone time). How can I accomplish this in iOS?

Edit: Imagine I have a class Event with a title and a start time. Title is a String, what start time should be?

Exprove
  • 1,301
  • 2
  • 18
  • 32
  • 4
    You state *"time without a timezone does not make any sense"*. That's plain wrong. Of course it makes sense. A `Date` is a point in time. If I say "now", that is a point in time that is the same for everyone in the world. The timezone is irrelevant until someone wants to know "what time was that 'now' in my timezone?". Then you simply format that `Date` into a `String` with the given timezone. – rmaddy Oct 28 '17 at 23:00
  • @rmaddy I might have to disagree with you. Imagine that someone gives you this time annotation "1970-12-12 11:40:30". You don't know when it started, it's inconclusive. What you are describing are seconds passed after an event, which is the same for all contexts. You have to give some start point, which is what timezone does. – Exprove Oct 28 '17 at 23:12
  • But the string "1970-12-12 11:40:30" isn't a `Date`. It's a string. You will convert that string to a `Date` based on a specific timezone. Once you have the `Date`, you store it. Later you retrieve it and you can convert that `Date` to a string again for any timezone you want. That's the point. The `Date` object has no specific timezone and it doesn't need one. – rmaddy Oct 28 '17 at 23:15
  • @Exprove in iOS you have a date picker that would display the date to the user at local time. When you store that date you store relative date time at utc. When displaying it you display that date formatting it to the user local time zone. – Leo Dabus Oct 28 '17 at 23:16
  • Thanks guys for trying to make me understand. But then NSDate/Date have UTC as default or we have to "imagine" that it is UTC? – Exprove Oct 28 '17 at 23:20
  • Yes date in fact it is UTC. Only when displaying it you use the date formatter which the default timezone it is the current one. – Leo Dabus Oct 28 '17 at 23:24
  • So to convert your date to string to store in your server use this https://stackoverflow.com/questions/28016578/swift-how-to-create-a-date-time-stamp-and-format-as-iso-8601-rfc-3339-utc-tim/28016692#28016692 – Leo Dabus Oct 28 '17 at 23:25
  • If you need to display a date object to the user use this https://stackoverflow.com/questions/28332946/nsdateformatter-stringfromdatensdate-returns-empty-string/28347285?s=1%7C24.2119#28347285 – Leo Dabus Oct 28 '17 at 23:26
  • 2
    @Exprove No, don't think about `Date` being UTC. It's not any timezone. It's just a point in time. Period. – rmaddy Oct 28 '17 at 23:38
  • @rmaddy in fact the date it is stored as a time interval and UTC it is used. https://developer.apple.com/documentation/foundation/date/1779647-init – Leo Dabus Oct 29 '17 at 00:17
  • 1
    @LeoDabus The fact that internally a `Date` is represented as a time interval since a certain epoch in no way means that a `Date` can be thought of as being in UTC time. – rmaddy Oct 29 '17 at 02:32
  • @LeoDabus Think of `Date` as a UNIX timestamp except with `2001-01-01 00:00:00` as the reference date instead of `1970-01-01 00:00:00`. It's a count of the number of seconds which have passed since then, but what actual _human_ date it corresponds to is up to interpretation (with a calendar, locale, time zone, etc.). – Itai Ferber Oct 29 '17 at 05:25
  • `Date` is just a `Double` — how it's interpreted depends on the `Calendar` and `TimeZone` you later apply. It doesn't mean anything on its own. – Itai Ferber Oct 29 '17 at 05:26
  • @ItaiFerber think about it as a person who was born at 2001-01-01 00:00:00 UTC time. That person can travel around the world. The time elapsed won't change doesn't matter where that person goes. You can say whatever you want to me a date object it is UTC. What will be displayed to the user is its local time representation of that point in time (UTC) – Leo Dabus Oct 29 '17 at 05:30
  • @LeoDabus Your example is a good way to think about it, but I think the conclusion isn't quite right. Take Alice, who was born on a date `D`. Alice was born somewhere on Earth in a time zone which wasn't UTC, but the moment of her birth corresponds to `2001-01-01 00:00:00`. If you ask Alice when she was born, she could give you her birth date in her local time zone, but it wouldn't even make sense to you because her local calendar isn't even the Gregorian calendar... So instead, to make it easy to agree on "when" `D` actually is, you can both agree to use UTC to represent it. – Itai Ferber Oct 29 '17 at 05:36
  • @ItaiFerber I mean Date().timeIntervalSince1970 1509255492.314381 for me here in Brazil would be the same for you anywhere in the world – Leo Dabus Oct 29 '17 at 05:38
  • @LeoDabus Alternatively rephrased: I just came up with a new timekeeping system called `ItaiDate`. An `ItaiDate` represents the number of seconds since _now_ (_now_ being the moment this comment is posted). Every second, the current `ItaiDate` goes up by 1. But when is _now_? If you look at your clock, chances are, _now_ says something which is different from what my clock says. But who's _now_ is right? Well, they're both _now_. If we want to coordinate, we can just refer to the moment in time represented by _now_ in a time zone we both agree on (UTC). – Itai Ferber Oct 29 '17 at 05:39
  • @LeoDabus Ah, agreed then. I'm just trying to explain that UTC doesn't have much to do with it. We could also agree to have `ItaiDate` be represented as a moment in time relative to the `Brazil/East` time zone, for instance. It doesn't matter, as long as the time zone doesn't change (UTC doesn't) and we both agree to use it. Having it be in UTC just makes it easy for the both of us to agree on what moment in time it is. – Itai Ferber Oct 29 '17 at 05:40
  • @ItaiFerber `Date(timeIntervalSince1970: 0)` would give me "Dec 31, 1969 at 9:00 PM" -03:00 not "Jan 1st, 1970 at 12:00 AM" -03:00 thats what I mean – Leo Dabus Oct 29 '17 at 05:41

2 Answers2

1

You use a DateFormatter for this.

let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.timeZone = TimeZone(secondsFromGMT: 0)

The formatter.locale sets the current locale for the user and formatter.dateFormat sets the desired date format. In your case yyyy-MM-dd HH:mm:ss.

To call it simply:

let utcDateFromServer = "2017-01-01 22:10:10"
let date = formatter.date(from: utcDateFromServer)
Rashwan L
  • 38,237
  • 7
  • 103
  • 107
  • I don't think a dateformatter would solve my problem (atleast the "model" part). I maybe was not very clear. Imagine I have a class Event with a title and a start time. Title is a String, what start time should be (I am almost sure that it is not a dateformatter)? – Exprove Oct 28 '17 at 22:59
  • @Exprove, checkout the updated post it describes it more in detail what you want to do. – Rashwan L Oct 28 '17 at 23:06
0

A Date is a point in time, as mentioned in other comments & in the documentation.

If you want to convert the UTC time into local time, you'll need to first convert the String "yyyy-MM-dd hh:mm:ss" from your database into a Date using DateFormatter.

let dateStringUTC = "2018-01-01 00:00:00"

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
//Set the input timezone (if you don't set anything, the default is user's local time)
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let date : Date = dateFormatter.date(from: dateStringUTC)! 

Then convert the Date back into String using DateFormatter with the respective TimeZone

let outputDateFormatter = DateFormatter()
outputDateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
//Set the output timezone (if you don't set anything, the default is user's local time)
//outputDateFormatter.timeZone = someTimeZone
let dateString = outputDateFormatter.string(from: date)

print(dateString)

Output: 2017-12-31 17:00:00

And you can just change the input and output timezone to do the opposite.

XW_
  • 472
  • 1
  • 5
  • 11
  • It's highly unlikely that you want to set the formatter's timezone to "GMT" when converting the original string to a `Date`. Most likely you want the user's timezone since it is likely a local time you are parsing. – rmaddy Oct 28 '17 at 23:16
  • @rmaddy I've edited the answer to provide more info. I meant that is for when the original date String is in UTC, as the questioner probably wants to convert UTC/GMT dates from the database to local timezone – XW_ Oct 28 '17 at 23:24
  • @LWJ Imagine I have a class Event with a title and a start time. Title is a String, what start time should be? – Exprove Oct 29 '17 at 00:08
  • @Exprove You should use Date for the start time. It's only when you display the date to the end user where you convert the `Date` into a `String` using `DateFormatter` – XW_ Oct 29 '17 at 01:37