We are building an app which requires the storage of a date for an entry on the device, the app will be international so we hit two dilema's / challenges.
User Preferences
If the user chooses the 12 hour rahter than 24 hour format we are returned from [NSDate date] a date like this 2012-07-17 11:26:03 AM which for sorting in a SQLite database is less than optimal as we cannot store it as a date.
User Locale
Typically this is ok however here in blighty we have a wonderfult thing called british summertime. which adds one hour every October 25th - 30th in a cycle and removes one hour every March 25 - 31th in a cycle so if no adjustment is made for 8 months of the year the time is one hour behind.
What I need to achieve is a consistent date formatted like this: 2012-07-17 11:26:03 no matter where the device is located and also taking into account where GMT+1 comes into place.
Any help would be awesome.
EDIT*
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd'T'HH:mm";
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT+01:00"];
[dateFormatter setTimeZone:gmt];
NSString *timeStamp = [dateFormatter stringFromDate:[NSDate date]];
NSDate *localDate = [dateFormatter dateFromString:timeStamp];
NSLocale* currentLocale = [NSLocale currentLocale];
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
NSLog(#"Country Code: %#", countryCode);
NSLog(#"Current Loacle: %#", currentLocale);
if(([countryCode isEqualToString: #"GB"])){
NSDate *mydate = [NSDate date];
NSDate *fiddlyFoo = [mydate dateByAddingTimeInterval:3600];
NSLog(#"Print GMT +1 %#",fiddlyFoo);
} else {
NSLog(#"Print GMT Local %#",localDate);
}
I'm doing something like this now. Note that NSDate "knows" about the current timezone and daylight savings time etc. So you just need to get the GMT version of the time in a sortable representation. I'd suggest RFC 3339 but you can use variations on it per your needs:
This code:
NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
// Create a local date for London, for testing purposes
NSDateComponents *comps = [NSDateComponents new];
[comps setTimeZone:[NSTimeZone timeZoneWithName:#"Europe/London"]];
[comps setDay:1];
[comps setMonth:7];
[comps setYear:2012];
[comps setHour:14];
NSDate *date = [gregorian dateFromComponents:comps];
// Just want to show this date is 2PM in London July 1st
NSDateFormatter *curFormat = [NSDateFormatter new];
[curFormat setDateStyle:NSDateFormatterFullStyle];
[curFormat setTimeStyle:NSDateFormatterFullStyle];
NSLog(#"Reference date is good no: %#", [curFormat stringFromDate:date]);
// So now we get the date as a rfc3339 string, referenced to GMT
NSString *timeString;
{
NSDateFormatter *rfc3339 = [NSDateFormatter new];
[rfc3339 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
rfc3339.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
timeString = [rfc3339 stringFromDate:date];
}
// referenced to UTC (sortable with any other time), can save in SQL DB etc
NSLog(#"Date as rfc3339 string: %#", timeString);
// Now lets convert it back into a BST time
NSDate *newDate;
{
NSDateFormatter *rfc3339 = [NSDateFormatter new];
[rfc3339 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
rfc3339.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
newDate = [rfc3339 dateFromString:timeString];
// we want to show this as a string 2012-07-17 11:26:03
NSDateFormatter *newFormatter = [NSDateFormatter new];
[newFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"]; // local time
[newFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"Europe/London"]];
NSLog(#"Local string using 24 hour clock: %#", [newFormatter stringFromDate:newDate]);
}
Generates this output, which I believe is what you want:
TimeTester[58558:f803] Reference date is good no: Sunday, July 1, 2012 9:00:00 AM Eastern Daylight Time
TimeTester[58558:f803] Date as rfc3339 string: 2012-07-01T13:00:00Z
TimeTester[58558:f803] Local string using 24 hour clock: 2012-07-01 14:00:00
Related
I asked a question not too long ago about timezone and I was using EST. Users suggested me to use EDT. I want to know why I should use one or the other because they both print the same time for me. Here is the code to better illustrate what I mean.
NSDate *today = [NSDate date];
NSDateFormatter *edtDf = [[NSDateFormatter alloc] init];
[edtDf setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"EDT"]];
[edtDf setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *stringDate = [edtDf stringFromDate:today];
NSLog(#"The EDT is %#", stringDate);
NSDate *today1 = [NSDate date];
NSDateFormatter *estDf = [[NSDateFormatter alloc] init];
[estDf setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"EST"]];
[estDf setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *stringDate1 = [estDf stringFromDate:today1];
NSLog(#"The EST is %#", stringDate1);
They may print different things depending on the time of year (since time of year determines whether Daylight Saving Time is active).
Don't use EST or EDT. Use US/Eastern or America/New_York:
NSTimeZone *tz = [NSTimeZone timeZoneWithName:#"US/Eastern"];
// or
NSTimeZone *tz = [NSTimeZone timeZoneWithName:#"America/New_York"];
These time zones adjust for Daylight Saving Time at the correct times of the year.
This question already has an answer here:
NSDateFormatter and Time Zone issue?
(1 answer)
Closed 8 years ago.
-(NSTimeInterval)convertStringToDate:(NSString *) date {
NSString *dateString = date;
NSLog(#"dateString = %#", dateString);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy hh:mm a"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *date1 = [[NSDate alloc] init];
date1 = [dateFormatter dateFromString:dateString];
NSLog(#"dateFromString = %#", date1);
NSString *displayDate = [dateFormatter stringFromDate:date1];
NSLog(#"displayDate = %#", displayDate);
return [date1 timeIntervalSince1970];
}
Why I am getting NSTimeInterval with wrong timezone?
You need to read up on the internal representation of NSDates. An NSDate is saved as the number seconds since midnight on 1 Jan, 1984 GMT (The Mac OS X "epoch date") . It represents an instant in time anywhere on the earth, but using a date in GMT as it's "zero date". To display it, you need to convert it to your local time zone.
NSDate has a couple of methods to convert a date to a number: timeIntervalSince1970, which converts an NSDate to the internet standard, which is the number of seconds since Midnight 1 Jan 1970 (The UNIX "epoch date"), and timeIntervalSinceReferenceDate, which converts to the number seconds since the Mac Epoch date.
If you display a date in NSLog:
NSLog(#"Date = %#", someNSDate);
It will be displayed in GMT.
Honestly, it's unclear what you're asking and my best guess is that you just don't understand the classes at play. I've annotated your code in the hope of aiding your comprehension.
Key point: NSDate does not have a time zone. It's an opaque time stamp.
-(NSTimeInterval)convertStringToDate:(NSString *) date {
// log the input string
NSString *dateString = date;
NSLog(#"dateString = %#", dateString);
// create an object that can apply a locale and a time zone in order to
// convert an NSDate to an NSString and vice versa
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy hh:mm a"];
[dateFormatter setLocale:[NSLocale currentLocale]];
// get a date that represents exactly now, for no reason as it's about
// to be thrown away
NSDate *date1 = [[NSDate alloc] init];
// convert to the NSDate that represents the given string.
date1 = [dateFormatter dateFromString:dateString];
// log the converted date. BECAUSE NSDATE DOES NOT HAVE A TIME ZONE,
// it will arbitrarily be displayed in UTC. Because it has to be
// displayed in something
NSLog(#"dateFromString = %#", date1);
// convert date1 back into a printable date; this will again apply
// a time zone and locale
NSString *displayDate = [dateFormatter stringFromDate:date1];
NSLog(#"displayDate = %#", displayDate);
// query the date for "The interval between the date object and
// January 1, 1970 at 12:00 a.m. GMT."; return that
return [date1 timeIntervalSince1970];
}
I was trying to format a time from GMT+7 to GMT+3:
I am building an app with a world clock in specific country (the user will be at the GMT+7and I want to represent the GMT+3 time )
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[NSLocale currentLocale];
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:118800];
NSLocale *USLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[dateFormatter setLocale:USLocale];
NSLog(#"Date for locale %#: %#",
[[dateFormatter locale] localeIdentifier], [dateFormatter stringFromDate:date]);
I looked deep into NSDate class reference but I didn't understand how to make it.
Please if someone can help me I will be grateful.
There is 2 important parameters that works separately: Time and Time Zone.
e.g: Vietnam uses GMT+7
If I know that the time in Vietnam is 9:00 AM, then GMT time is 2:00 AM.
When you get the Date from your device you are getting Time and Time Zone: YYYY-MM-DD HH:MM:SS ±HHMM. Where ±HHMM is a time zone offset in hours and minutes from GMT.
Usually you are only using time. However with NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"GMT"] you can tell the NSDateFormatter that you want the GMT time related to your local Time Zone. So, with:
NSDateFormatter *dt = [[NSDateFormatter alloc] init];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"GMT"];
[dt setTimeZone:timeZone];
You can get the GMT date of your local time zone date.
So, If you have GMT+7: 9:00 AM and you want to print out GMT+3: 5:00 AM, you have 3 possibilities:
NSDate *localDate = [NSDate date];
OPTION 1
Add a time interval of -4 hours:
NSTimeInterval secondsInFourHours = -4 * 60 * 60;
NSDate *dateThreeHoursAhead = [localDate dateByAddingTimeInterval:secondsInFourHours];
NSDateFormatter *dt = [[NSDateFormatter alloc] init];
[dt setDateFormat:#"h:mm a"];
NSLog(#"GMT+7(-4) = %#", [dt stringFromDate:dateThreeHoursAhead]);
This is the easiest way to do it. If you are always at GMT+7 and you need GMT+3, this is a time interval of -4 hours.
OPTION 2
Set the time to GMT time zone and then add a +3hours time interval. The easiest way to do it is to add the 3 hours first and then move the time to GMT:
NSTimeInterval secondsInThreeHours = 3 * 60 * 60;
NSDate *dateThreeHoursAhead = [localDate dateByAddingTimeInterval:secondsInThreeHours];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"GMT"];
[dateFormatter setTimeZone:timeZone];
[dateFormatter setDateFormat:#"h:mm a"];
NSString *date = [dateFormatter stringFromDate:dateThreeHoursAhead];
NSLog(#"GMT+3 = %#", date);
OPTION 3
This is the better option. GMT+3 is EAT (East Africa Time) you can set your time zone to EAT with: [NSTimeZone timeZoneWithName:#"EAT"]
NSDateFormatter *dt = [[NSDateFormatter alloc] init];
[dt setDateFormat:#"h:mm a"];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"EAT"];
[dt setTimeZone:timeZone];
NSLog(#"EAT = %#", [dt stringFromDate:localDate]);
Option 3 is always retrieving GMT+3
An example code here.
I am trying to convert my device current time into Zone (America/New_york)
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"America/New_York"]];
NSDate *start = [NSDate date];
start now has the device current time in IST. How can I convert it into zone 'America'
I planned to do the below
start = [dateFormatter dateFromString:<start date String>];
A Day is categorised into 3 divisions based on a time period.
Say 08:00 - 15:00 AS PHASE1
15:00 - 17:00 AS PHASE2
17:00 - 20:00 AS PHASE3
20:00 - 08:00 AS TRANSITION PHASE and can be ignored.
Am just taking device's date and append the time string to identify in which phase it is.
Example: Indian Time is 05-FEB-2014 01:25 AM (Device Date)
Considering 15:00 as first cutoff.. I append the '05-FEB-2014' to 15:00 and digest it as American timezone.. Which resolves and gives me 06-FEB-2014 02:30 IST(Cut off Date).. So the difference between the both days goes greater than one day!
My expected result could be take device time also in American Timezone and compare with the compare with the nearest cut off time.
NSDate does not have a time zone. It's a single time reference that's valid anywhere. It's basically just an object wrapper for NSTimeInterval-- all it stores is the number of seconds since a reference date. Time zones only apply when converting to/from user-visible strings.
You can get a user-visible string from your date formatter as
NSString *localizedDateString = [dateFormatter stringFromDate:start];
But converting NSDate to a particular time zone is not a meaningful goal. There's no time zone on NSDate, so it's not a conversion that can be applied.
Date of new york :
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"America/New_York"];
[dateFormatter setTimeZone:timeZone];
NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];
NSLog(#"Date : %#", dateString);
Date of all zones :
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSArray *timeZoneNames = [NSTimeZone knownTimeZoneNames];
for (NSString *name in timeZoneNames) {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:name];
[dateFormatter setTimeZone:timeZone];
NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];
NSLog(#"timeZone abbreviation : %#\nName : \"%#\"\nDate : %#\n\n", [timeZone abbreviation], name, dateString);
}
See the stringFromDate Method in the dateformatter. You pass in an NSDateand it converts to the specified format.
UPDATE: Here is how you set the current time zone [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/Reference/Reference.html
I am taking an NSDate, and pulling just a 2-digit number, representing the day of the month into an NSString. One of the dates in question is:
2013-11-30 00:00:00 +0000
I use:
NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
[formatter2 setDateFormat:#"dd"];
NSString *datefromdate = [formatter2 stringFromDate:articleDate];
NSLog(#"Date%#", datefromdate);
[formatter2 release];
but the log comes back
29
You are probably in a negative time zone i.e. GMT minus something. This is why 2013-11-30 00:00:00 +0000 GMT is on the 29th day when you log it. Set the formatter to GMT and you will be fine.
Set the timezone you want the time date formatter to use. NSDate is the first instant of 1 January 2001, GMT and thus has no timezone information in it.
So, this, according to Apple, is going to get complicated needing up to five classes: NSDateFormatter, NSDate, NSCalendar, NSTimeZone and finally NSDateComponents.
If all you want is the day you can use NSDateComponents.
Example:
NSString *dateString = #"2013-11-30 00:00:00 +0000";
NSDateFormatter *inDateFormatter = [[NSDateFormatter alloc] init];
[inDateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss Z"];
NSDate *date = [inDateFormatter dateFromString:dateString];
NSLog(#"dateFromString: %#", date);
NSTimeZone *timezone = [NSTimeZone timeZoneForSecondsFromGMT:0];
// Using date formatter, result is a string
NSDateFormatter *outDateFormatter = [NSDateFormatter new];
[outDateFormatter setTimeZone:timezone];
[outDateFormatter setDateFormat:#"dd"];
NSString *dayString = [outDateFormatter stringFromDate:date];
NSLog(#"date formatter day: %#", dayString);
// Using date components, result is an integer
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone: timezone];
NSDateComponents *dateComponents = [calendar components:NSDayCalendarUnit fromDate:date];
NSInteger day = [dateComponents day];
NSLog(#"date components day: %i", day);
NSLog output:
dateFromString: 2013-11-30 00:00:00 +0000
date formatter day: 30
date components day: 30