39

I am trying:

NSDate *currentDateInLocal = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:SS.SSS'Z'"];
NSString *currentLocalDateAsStr = [dateFormatter stringFromDate:currentDateInLocal];

NSDateFormatter * dateFormatter2 = [[NSDateFormatter alloc] init];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
[dateFormatter2 setTimeZone:timeZone];
[dateFormatter2 setDateFormat:@"yyyy-MM-dd'T'HH:mm:SS.SSS'Z'"];
NSDate *currentDateInUTC = [dateFormatter2 dateFromString:currentLocalDateAsStr];

but It's still does not represent the current UTC time, how can I achieve this?

Thanks

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
williamsandonz
  • 15,864
  • 23
  • 100
  • 186

8 Answers8

53

You're overcomplicating things.

NSDates don't have time zones or calendars. [NSDate date] gets the current date, which is a measurement of a moment in history. If I run [NSDate date] in Europe at exactly the same time as you run it in America then we'll get exactly the same value.

How you print a date depends on the calendar and the time zone. So a date printed in the Gregorian calendar looks different from the same one printed in the Julian calendar. And a date printed in the UTC Gregorian calendar looks different from the same one printed in the PST Gregorian calendar. But they're still the same date.

So you want to jump straight to your dateFormatter2.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • 11
    This should be the accepted answer. `NSDate`s simply represent an absolute moment in time, and do not come with a timezone attached. The other answers currently here, including the currently accepted one, incorrectly assert that `NSDate`s are 'in UTC', which is incorrect. Only this answer is right. – Mark Amery Jul 28 '13 at 16:30
  • @Mark Amery UTC is not (only) a time zone! UTC is a definition how time is measured, and which time reference is used. And NSDate is related to UTC. Altough you always simply state the uncomplete Apple Definition: the first instant of 1 January 2001, GMT. GMT = UTC,. Is that so difficult to understand? The explanation "a measurement of moment in history is uncomplete". The refernece has to be defined. And the reference is 1.1.1970 UTC – AlexWien Jun 17 '15 at 17:20
  • @MarkAmery This answer is not correct. You have still not understood that GMT = UTC. An NSDate is related to a reference time, and this refercence is UTC. IT is not just a moment in time. Further UTC is not only a time zone. It is a time system that defines how time on earth is measured and coordinated, related to other time refernce system like solar time. Compare that to GPS time reference system. A gps receiver interernally uses GPS Time reference and later converts to UTC. NSDate uses UTC. And both systems currently deviate by 18 seconds, although they mean the same moment in time. – AlexWien Jun 17 '15 at 17:35
  • 2
    @AlexWien I understand perfectly that GMT = UTC. The reason the docs specify the epoch for `NSDate` as "midnight, 1 January 2001, GMT" instead of just "midnight, 1 January 2001" is that "midnight, 1 January 2001" does not define a single instant in time. That doesn't mean the reference date is "in UTC", it just means that UTC has been used to specify it. They could equally have written "1AM on 1 January 2001, Central European Time" and it would have had *precisely* the same meaning as what they in fact wrote. It wouldn't then follow that all `NSDate`s were "in CET". – Mark Amery Jun 17 '15 at 17:38
  • It is documente that the reference Date is 1.1.2001, GMT and that clearly means that the time scale is in UTC. Do not interchange time scale with time zones. Even CET is UTC based, but with an offset. GPS time has another time scale (no leap seconds), UT has yet another time scale. It is therfore alsways neccessary to precisle specify what time scale a date or time is based on. All methods (messages)of NSDate are UTC based, altough they call it GMT. NSDate is neither based on GPS time, nor on UT nor on sideral time, it is based on universal coordinated time. (UTC) – AlexWien Jun 17 '15 at 21:54
  • 1
    @AlexWien the answer is nevertheless complete; `NSDate` is an opaque type that makes no promises about time zone or scale and each instance of `NSDate` contractually promises to identify a moment in history only. Just as `NSString` doesn't necessarily use `unichar` storage, an `NSNumber` could store everything except `uint64_t` as a `double` if it really wanted, a `UIColor +colorWithHue:...` might store the nominated colour in RGB or YCrCb or anything else, etc. Or I can speak with a German person but think in English. There's nothing inherently UTC about e.g. `[NSDate distantFuture]`. – Tommy Jun 18 '15 at 12:03
  • The point is that you have to look at the methods that NSDate provides, and they are documented and explictly state that they are related to GMT which is UTC. The methods explictly explain and mention GMT as reference system. – AlexWien Jun 18 '15 at 19:37
  • @AlexWien: I think we're digressing. The point is: my answer states that dates don't have time zones or calendars. They don't. It describes a date as "a measurement of a moment in history". That's accurate. Regardless of from where the scale comes from. I think you're over-extending by stating that the answer is "not correct". Indeed I would say that claim itself was not correct. – Tommy Jun 18 '15 at 22:10
  • 1
    The only thing in your answer which is to vague is "a measurement of a moment in history". that not accurate. Each measurement needs its unit. and in all methods that NSDate provides the unit is well defined. The unit is UTC time. Object oriented thinking when dealing with times is problematic. That is also the reason why you never should store a date as an object. Comercial systems use "long millisecondsUTC" instead, which is well defined and acurate. But that also typical for Apple, they dont want to be to specific, resulting in a situation where nobody knows whats inside. – AlexWien Jun 19 '15 at 14:59
  • @AlexWien too vague, whether one agrees with that assessment or not, does not mean "not correct". – Tommy Jun 19 '15 at 16:06
  • You do not answer his question: he wants the time in utc. As all Apis in all Programming languages provide: ([NSDate date] timeIntervalSince1970]) That is the time in utc – AlexWien Jun 22 '15 at 14:52
  • 1
    @AlexWien: you're obviously not the sort of person it's worth having much of a debate with so I give up. At the minute every Internet comment thread cliche is being ticked. 28 people seem to think my answer has merit; 18 people think yours does. Probably with a large degree of overlap. That'll do as a conclusion for me. – Tommy Jun 22 '15 at 18:31
  • Accepted alternate interpretation of that comment: it's not worth debating with me any more. Read it as you wish. I've fully understood your argument — that UTC defines a scale as well as a zone and that `NSDate` impliedly uses that scale hence is in UTC in a sense even if zone independent — but I don't think your conclusion about my answer being "not correct" is sound. You've imputed an assumption not backed by the documentation. Apple is very careful to state GMT only in defining the reference date, otherwise explicitly stating that NSDates merely "represent an invariant point in time". – Tommy Jun 22 '15 at 18:56
  • Yes, you understood well , but this is not visibile in the text of your answer. Further the internal representaion (not documented) of NSDate (you could call it Apple Time) is of little interest. You stated this above as "opaque object". The point is, that the NSDate object is measurable, transferable, gets a specified unit and is portable to other systems, only AFTER you use a method of it. (The most usefull method is called timeIntervalSince1970) and it delivers the time since 1970 utc.. – AlexWien Jun 22 '15 at 19:32
  • Yes, I understood from your comments. Those are subsequent to my answer. I just don't think your comments invalidate the answer. If Apple considers the UTC _scale_ so obvious as not to be worth mentioning then I don't think my answer crosses the threshold into "not correct" through failure to mention scale _at all_. Your case, at its strongest, is that the answer is incomplete. Nobody has alleged there's any incorrect content in it. But the only ways in which it is incomplete are the same ways in which the `NSDate` documentation are incomplete. – Tommy Jun 22 '15 at 19:55
44

The accepted answer by Alex Wien is incorrect.

By default, NSDateFormatter adjusts the NSDate’s date-time value from UTC to the user's local time zone. To prevent that adjustment, tell the NSDateFormatter to use the time zone for UTC.

To verify results, google "current time utc".

My source code below should do the job, meaning get the current date-time as a string in ISO 8601 format in the UTC (Zulu) time zone signified by a Z on the end.

NSDate* datetime = [NSDate date];
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; // Prevent adjustment to user's local time zone.
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSString* dateTimeInIsoFormatForZuluTimeZone = [dateFormatter stringFromDate:datetime];

You could put this logic in a pair of convenience methods somewhere in your app.

- (NSString*)now
{
    // Purpose: Return a string of the current date-time in UTC (Zulu) time zone in ISO 8601 format.
    return [self toStringFromDateTime:[NSDate date]];
}

…and…

- (NSString*)toStringFromDateTime:(NSDate*)datetime
{
    // Purpose: Return a string of the specified date-time in UTC (Zulu) time zone in ISO 8601 format.
    // Example: 2013-10-25T06:59:43.431Z
    NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
    NSString* dateTimeInIsoFormatForZuluTimeZone = [dateFormatter stringFromDate:datetime];
    return dateTimeInIsoFormatForZuluTimeZone;
}

Example of usage…

NSString* now = [self now];

Or turn those minus signs into plus signs to use as class methods rather than instance methods…

NSString* now = [SomeClassNameHere now];

Tip: For better readability by humans, change that T in the format to a SPACE. For better interoperability by software, keep the T. The ISO 8601 spec tolerates a space but recommends keeping the T.


Tip: I've not tested, but… Some people say instantiating [NSDateFormatter][4] is expensive. If doing so often (such as in a loop) consider caching a single instance for re-use.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • You interchanged time scale with time zone. All NSDates and all formatted NSDates whatever timezone ever they are, are specified by beeing UTC based. That is the time we humans usually use. Such local times having a time zone just add an offset to UTC. The time scale stays in UTC. UTC has leap seconds, about 1 every 1-2 years. Other time scales like UT or GPS Time behave different. GPS satellites for example use GPS Time, not UTC, they have an currenlty an offset of about 18s. – AlexWien Jun 17 '15 at 22:00
  • 1
    Please fix date format string from `"yyyy-MM-dd'T'HH:mm:SS.SSS'Z'"` to `"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"`, because `SS` represents milliseconds, not seconds. – Skydan Feb 22 '16 at 13:55
  • 1
    @Skydan Thanks for the correction. FYI, StackOverflow welcomes you to make such edits yourself. – Basil Bourque Feb 22 '16 at 18:12
42
NSDate *currentDate = [[NSDate alloc] init];

Now it is in UTC, (at least after using the method below)
To store this time as UTC (since refernce date 1970) use

double secsUtc1970 = [[NSDate date]timeIntervalSince1970];

Set Date formatter to output local time:

NSTimeZone *timeZone = [NSTimeZone defaultTimeZone];
// or Timezone with specific name like
// [NSTimeZone timeZoneWithName:@"Europe/Riga"] (see link below)
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:timeZone];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSString *localDateString = [dateFormatter stringFromDate:currentDate];

Available NSTimeZone names

A NSDate object always uses UTC as time reference, but the string representation of a date is not neccessarily based on UTC timezone.

Please note that UTC is not (only) a timeZone, It is a system how time on earth is measured, how it is coordinated (The C in UTC stands for coordinated).
The NSDate is related to a reference Date of midnight 1.1.1970 UTC, altough slightly wrongly described by Apple as 1.1.1970 GMT.

In the original question the last word timeZone is not perfect.

Max P
  • 25
  • 6
AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • Thanks, Ok I've tried printing out currentDate.description (using your line) and its saying: Current:2013-01-30 19:16:02 +0000, but google says 29-01-2013 10:14 PM UTC – williamsandonz Jan 29 '13 at 22:17
  • Found the problem, my stupid system clock was about 18 hours wrong from my actual local time, ugh :(. Thanks guys! – williamsandonz Jan 29 '13 at 23:04
  • 7
    -1 for not answering the question; he asked for the date in UTC, and you've given him code that spits out the date in his local timezone. – Mark Amery Jul 26 '13 at 13:08
  • @MarkAmery remove your downvote, you neither have understood his question nor my answer. The first line in my answer, as decribed, is the date in UTC. He was not asking for a formatted date output in UTC. – AlexWien Jul 26 '13 at 13:16
  • @AlexWien An `NSDate` purely represents a time interval from a fixed reference instant in time (specifically the first instant of 1 January 2001, GMT), similar to how UNIX Timestamps represent an interval from the UNIX Epoch. Hence an `NSDate` is timezone-agnostic, not in UTC. Timezones are only specified when formatting an `NSDate` as a string or converting to `NSDateComponents`, through the timezone properties of `NSCalendar` or `NSDateFormatter`. It's conceptually incorrect to refer to an `NSDate` as being 'in' any particular timezone, and practically misleading too. – Mark Amery Jul 26 '13 at 13:48
  • UTC is the reference, your setence "specifically the first instant of 1 January 2001, GMT" is half correct although used in the unix world. GMT = UTC. So it should read "specifically the first instant of 1 January 2001, UTC". GMT also denotes a time zone in Africa which is not equal to UTC. so today you should not use the term GMT anymore. Use UTC instead. Java defines this mroe corrrect that in ios, there currentTimeMillis() is defined as number of milliseconds from midnight 1.1.1970 UTC. See the last three letters – AlexWien Jul 26 '13 at 13:54
  • I notice that you're right though, that he wants an `NSDate` that is 'in UTC'; the last line of his example code clearly shows this. I hadn't noticed that before. The correct answer to give him, then, is that this doesn't make sense as a question, since an `NSDate` doesn't have a timezone (unlike date objects in some languages, like Python). If he wants a timezone-relative representation of an `NSDate`, he should create an `NSDateComponents` instance using an `NSCalendar` with the correct timezone instead. – Mark Amery Jul 26 '13 at 13:54
  • Yes his question is not perfect. But i understood what he wanted to achieve, and posted the full code that he needs. – AlexWien Jul 26 '13 at 13:57
  • @AlexWien just for correction your time format should be `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` not `yyyy-MM-dd'T'HH:mm:SS.SSS'Z'`, coz SS is for milliseconds and you are using it as your seconds. – Mayur Shrivas Sep 20 '13 at 14:27
  • 5
    The example code as written is incorrect. The 'Z' means Zulu (UTC) time, but this code returns the local time. I suggest adding the line: `[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];` – Basil Bourque Oct 25 '13 at 06:17
  • @BasilBourque the goal of the second part of the code is to output Local time. 'Z' should output the current local timezone tje computer is set to. – AlexWien Oct 25 '13 at 09:57
  • 2
    @AlexWien No, 'Z' means [Zulu time](https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC) which is a short way of saying a time zone offset of zero ("+00:00"). In other words, UTC time. You should **never** put a 'Z' on the end of a local time. That is nonsensical, like putting a USD dollar sign on a price in Yen. – Basil Bourque Oct 25 '13 at 10:08
12

PLEASE SET UP Calendar Identifier !!!

I am not too late! Because I saw no one set up the Calendar Identifier. It is really important for worldwide users. Many users using a non-Gregorian calendar. They will get wrong year string. Especially, when you need store it into your own database. (We met this problem before)

NSCalendarIdentifierGregorian
NSCalendarIdentifierBuddhist
NSCalendarIdentifierChinese
NSCalendarIdentifierHebrew
NSCalendarIdentifierIslamic
NSCalendarIdentifierIslamicCivil
NSCalendarIdentifierJapanese
NSCalendarIdentifierRepublicOfChina
NSCalendarIdentifierPersian
NSCalendarIdentifierIndian
NSCalendarIdentifierISO8601

Code:

-(NSString *)getUTCFormateDate:(NSDate *)localDate
{
    NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
    NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
    [dateFormatter setCalendar:gregorianCalendar];
    [dateFormatter setTimeZone:timeZone];
    [dateFormatter setLocale:locale];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *dateString = [dateFormatter stringFromDate:localDate];
    return dateString;
}  
Community
  • 1
  • 1
Yi Jiang
  • 3,938
  • 6
  • 30
  • 62
  • I found it also necessary to set the NSDateFormatter locale to en_US_POSIX else the date will appear with non english numbers. – dwxw Nov 19 '15 at 11:01
3

Swift 3

let utcTimestamp = Date().timeIntervalSince1970
print("timeStamp = \(utcTimestamp)")

May following extension would be easier.

Swift 4: UTC/GMT ⟺ Local (Current/System)

extension Date {

    // Convert local time to UTC (or GMT)
    func toGlobalTime() -> Date {
        let timezone = TimeZone.current
        let seconds = -TimeInterval(timezone.secondsFromGMT(for: self))
        return Date(timeInterval: seconds, since: self)
    }

    // Convert UTC (or GMT) to local time
    func toLocalTime() -> Date {
        let timezone = TimeZone.current
        let seconds = TimeInterval(timezone.secondsFromGMT(for: self))
        return Date(timeInterval: seconds, since: self)
    }

}


// Try it
let utcDate = Date().toGlobalTime()
let localDate = utcDate.toLocalTime()

print("utcDate - (utcDate)")
print("localDate - (localDate)")
Krunal
  • 77,632
  • 48
  • 245
  • 261
2

[NSDate date] is UTC. Maybe you get fooled by looking in the locals? Then it gets converted to your timezone.

If you see the value in the locals, you see it in local time, but if you print it in the console, you see it in UTC.

When you see '+0000' after the time, you know it is in UTC

Cub71
  • 313
  • 2
  • 14
  • I'm printing date.description is that ok? – williamsandonz Jan 29 '13 at 22:15
  • 1
    These two seems to be the same: NSLog(@"%@", date); and NSLog(@"%@", [date description]); They give the same answer: 2013-01-29 22:54:15 +0000 – Cub71 Jan 29 '13 at 22:54
  • 2
    when your doing this NSLog(@"%@", date), your actually calling [date description], if you want to change how the logs appear you can override the function description. – Underdog Oct 02 '13 at 08:06
0

Still another way to do it is like so in a C++ class in your Objective C project. (So, make a .mm file and build a C++ class with public and private parts, and stick this in the public part (unless you need it private).) Then, reference it like NSString *sNowUTC = MyClass::getUTCTimestamp();.

static NSString *getUTCTimestamp(){
  time_t rawtime;
  struct tm * timeinfo;
  char buffer [80];
  time (&rawtime);
  timeinfo = gmtime (&rawtime);
  // make format like "2016-06-16 02:37:00" for 
  //   June 16, 2016 @ 02:37:00 UTC time
  strftime (buffer,80,"%F %T",timeinfo);
  std::string sTime(buffer);
  NSString *sUTC = @(sTime.c_str());
  return sUTC;
}
Volomike
  • 23,743
  • 21
  • 113
  • 209
0

This is what i used.

static func makeISO8601Date(isoDateString: String) -> Date {
    let formatter = DateFormatter()
    formatter.calendar = Calendar(identifier: .iso8601)
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"

    return formatter.date(from: isoDateString)
}

makeISO8601Date(isoDateString: "2017-12-31T23:59:59+00:00")

amol-c
  • 435
  • 4
  • 10