Having trouble with NSCalendar get date components - ios

So I have an NSCalendar and do some tests:
First, change system time zone to America/Los_Angeles (GMT-8), giving a unix time stamp 1455552000000, convert it to NSDate: by
[NSDate dateWithTimeIntervalSince1970:1455552000000 / 1000];
It returns 2016-02-15 16:00:00 +0000
And then use the calendar to generate the date components:
+(void)applyCalendarToCurrentTimeZone:(NSCalendar *)calendar {
if (![calendar.timeZone isEqualToTimeZone:[NSTimeZone localTimeZone]]) {
calendar.timeZone = [NSTimeZone localTimeZone];
}
}
+ (NSDateComponents *)getDateComponentsFromDate:(NSDate *)date {
NSDateComponents *components;
static NSCalendar *ISO8601 = nil;
static NSCalendarUnit unit;
if (!ISO8601) {
ISO8601 = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierISO8601];
ISO8601.firstWeekday = 2; // Sunday = 1, Saturday = 7
ISO8601.minimumDaysInFirstWeek = 7;
unit = NSCalendarUnitYear|NSCalendarUnitYearForWeekOfYear|NSCalendarUnitQuarter|NSCalendarUnitWeekOfYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond;
}
[ChartModel applyCalendarToCurrentTimeZone:ISO8601];
components = [ISO8601 components:unit fromDate:date];
return components;
}
But while debugging, I found problem,
At first, everything looks fine, the date components are correct, the Day value is 15 in GMT-8 time zone.
(lldb) po ISO8601.timeZone
America/Los_Angeles (GMT-8) offset -28800
(lldb) po date
2016-01-18 16:00:00 +0000
(lldb) po ISO8601.timeZone
America/Los_Angeles (GMT-8) offset -28800
(lldb) po date
2016-02-15 16:00:00 +0000
(lldb) po [ISO8601 components:unit fromDate:date];
<NSDateComponents: 0x13e4ae530>
Calendar Year: 2016
Month: 2
Leap month: no
Day: 15
Hour: 8
Minute: 0
Second: 0
Quarter: 0
Year for Week of Year: 2016
Week of Year: 7
(lldb)
Without restarting app, change system time zone to auto, since I am in Beijing, it is GMT+8, and refresh data, the date components looks fine too, the Day value is 16 in Shanghai time zone.
(lldb) po ISO8601.timeZone
Local Time Zone (Asia/Shanghai (GMT+8) offset 28800)
(lldb) po date
2016-02-15 16:00:00 +0000
(lldb) po [ISO8601 components:unit fromDate:date];
<NSDateComponents: 0x13ec15150>
Calendar Year: 2016
Month: 2
Leap month: no
Day: 16
Hour: 0
Minute: 0
Second: 0
Quarter: 0
Year for Week of Year: 2016
Week of Year: 7
(lldb)
Again, without restarting app, change back system time zone to GMT-8, now weird thing happens, the Day value is 16 in GMT-8 time zone, which is not corret! What did I miss? Please help!
(lldb) po ISO8601.timeZone
Local Time Zone (America/Los_Angeles (GMT-8) offset -28800)
(lldb) po date
2016-02-15 16:00:00 +0000
(lldb) po [ISO8601 components:unit fromDate:date];
<NSDateComponents: 0x13f888260>
Calendar Year: 2016
Month: 2
Leap month: no
Day: 16
Hour: 0
Minute: 0
Second: 0
Quarter: 0
Year for Week of Year: 2016
Week of Year: 7
(lldb)

Related

In Rails, how to convert time zone into an integer

I have time and time zone, what I need is I just want to convert like this Wed, 11 Dec 2019 19:00:00 +0530 and then I need to convert like this 1576071000.
So for I tried like this
time = "19"
hour = "00"
time_zone = "IST"
e = DateTime.now.change({hour: time, minute: hour})
I get the exact output, but I need to convert with timezone. that means something like this
DateTime.now.change({hour: time, minute: hour}).time_zone('IST')
credit to #engineersmnky
hour = 19
minute = 0
time_zone = 'Chennai'
e = Time.zone.now.change({hour: hour, minute: minute}).in_time_zone(time_zone).to_i
You need to convert it to UTC:
DateTime.now.change({hour: time, minute: hour}).time_zone('IST').utc
Or if you are in Rails 4 or above you can use in_time_zone:
DateTime.now.change({hour: time, minute: hour}).in_time_zone('IST').utc

Get current month date in date components

I am getting NSDateComponents of date 2018-06-01 00:00:00 +0000 like this
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth|NSCalendarUnitYear |NSCalendarUnitHour | NSCalendarUnitMinute fromDate:date];
[components setTimeZone:[NSTimeZone timeZoneWithAbbreviation: #"UTC"]];
NSInteger month =[components month];
When I print the value of components , I get this value.
TimeZone: GMT (GMT) offset 0
Calendar Year: 2018
Month: 5
Leap month: no
Day: 31
Hour: 21
Minute: 0
My expected out put should be
TimeZone: GMT (GMT) offset 0
Calendar Year: 2018
Month: 6
Leap month: no
Day: 1
Hour: 0
Minute: 0
How can I get the value of month correctly?
When you use NSCalendar components:fromDate: the results are based on the calendar's current timezone which defaults to the user's local timezone.
Your attempt to set the resulting components' timeZone doesn't alter the current components. That would only affect how the components would be interpreted if you used the components to create a new NSDate.
Assuming your goal is to get the components of date in UTC time and not in local time, then you need to set the calendar's timeZone before getting the components from date.
NSDate *date = // your date
NSCalendar *calendar = NSCalendar.currentCalendar;
calendar.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"UTC"];
NSDateComponents *components = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour | NSCalendarUnitMinute fromDate:date];
NSLog(#"UTC Components: %#", components);
But keep in mind that you must understand which timezone you really want here. Be sure you really want the UTC timezone. Don't use UTC just because it matches the output of NSLog(#"Date: %#", date);. That log shows the date in UTC time.
How do you create your initial date object? When I try that setup everything works as expected:
NSString *dateString = #"2018-06-01 00:00:00 +0000";
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ss Z";
dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"];
dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
NSDate *date = [dateFormatter dateFromString:dateString];
NSCalendar *calendar = NSCalendar.currentCalendar;
NSDateComponents *components = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour | NSCalendarUnitMinute fromDate:date];
NSLog(#"Before: %#", components);
/*
Before: <NSDateComponents: 0x604000158e10>
Calendar Year: 2018
Month: 6
Leap month: no
Day: 1
Hour: 2
Minute: 0
*/
components.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"UTC"];
NSLog(#"After: %#", components);
/*
After: <NSDateComponents: 0x604000158e10>
TimeZone: GMT (GMT) offset 0
Calendar Year: 2018
Month: 6
Leap month: no
Day: 1
Hour: 2
Minute: 0
*/
You need to get the last month date first.
extension Date {
//it will give the date of last month
var previousMonthDate: Date {
return Calendar.current.date(byAdding: .month, value: -1, to: self)!
}
}
Get the date components from date
let dt = Date()
let components = Calendar.current.dateComponents([.year, .month, .day], from: dt.previousMonthDate)
print(components.day ?? 0)
print(components.month ?? 0)
print(components.year ?? 0)

Swift - Timezone off by one hour / secondsFromGMT incorrect

This should be a really simple question but I just can't seem to wrap my head around it.
Given my timezone is EDT (GMT-4), why does 04:00 in GMT turn into 23:00 and not 00:00?
// The offset is -4 hours
let offsetFromGMT = Calendar.current.timeZone.secondsFromGMT() / 60 / 60
// 2017-03-12 04:00
var destinationComponents = DateComponents()
destinationComponents.timeZone = TimeZone(secondsFromGMT: 0)
destinationComponents.year = 2017
destinationComponents.month = 03
destinationComponents.day = 12
destinationComponents.hour = -offsetFromGMT // 4 hours
// Why is this 2017-03-11 23:00 and not 2017-03-12 00:00?
let date = Calendar.current.date(from: destinationComponents)!
// Outputs 23
Calendar.current.dateComponents([.hour], from: date).hour
Calendar.current.timeZone.secondsFromGMT()
is the current GMT offset for your time zone. In your case that
is 4 hours, because the current time zone in New York is EDT = GMT-4,
with daylight saving time active.
So your destinationComponents and date are four o'clock in
the morning Greenwich time:
2017-03-12 04:00:00 +0000
At that point, the time zone in New York was EST = GMT-5, and
daylight saving time not active. Therefore that date is 2017-03-11 23:00 in your local time zone.
I would proceed differently, avoiding "secondsFromGMT".
Example: "2017-03-12 00:00:00" New York time is "2017-03-12 05:00:00" GMT.
var srcComponents = DateComponents()
srcComponents.timeZone = TimeZone(identifier: "America/New_York")!
srcComponents.year = 2017
srcComponents.month = 3
srcComponents.day = 12
srcComponents.hour = 0
srcComponents.minute = 0
let date = Calendar.current.date(from: srcComponents)!
print(date) // 2017-03-12 05:00:00 +0000

How to get year month day from NSDate for the current timezone

I am current at timezone UTC-05:00. When I call the function NSDate(timeIntervalSince1970:0), it returns back "Dec 31, 1969, 7:00 PM"
let date = NSDate.init(timeIntervalSince1970: 0) // "Dec 31, 1969, 7:00 PM"
print(date) // "1970-01-01 00:00:00 +0000\n"
I read about this How to get NSDate day, month and year in integer format? But the problem is that with the following, I always get 1969-12-31 because of the 5 hour time difference.
let calendar = NSCalendar.currentCalendar()
calendar.getEra(&era, year:&year, month:&month, day:&day, fromDate: date)
year // 1969
month // 12
day // 31
var hour = 0, minute = 0, second = 0
calendar.getHour(&hour, minute: &minute, second: &second, nanosecond: nil, fromDate: date)
hour // 19
minute // 0
second // 0
Is there a way to get the current year, month, day values and etc. in the current timezone. What I am looking for here is:
year // 1970
month // 01
day // 01
The timeIntervalSince1970 initializer gives you (as documented) an NSDate which is some number of seconds since Jan 1 1970 at 00:00:00 in UTC, not in your local time zone. Those results you're getting are correct, because they're showing your local time zone's offset from that time. You're passing in 0, so you're getting Jan 1 1970 at 00:00:00 UTC, and then NSCalendar is giving you the equivalent date and time in your local time zone.
If you want to get Jan 1 1970 at 00:00:00 in your local time zone, you need to request that date specifically from NSCalendar:
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone.localTimeZone()
let date = calendar.dateWithEra(1, year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0, nanosecond: 0)
calendar.getEra(&era, year:&year, month:&month, day:&day, fromDate: date!)
year // 1970
month // 1
day // 1
This is not a 0 offset for timeIntervalSince1970. If you check, you'll see that the result corresponds to your time zone's offset from UTC:
date?.timeIntervalSince1970 // 25200, for me
This will return the current date ex. 02/26/2016
// Format date so we may read it normally
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd/M/yyyy"
let currentDate = String(dateFormatter.stringFromDate(NSDate()))
Swift 3.0
NSDateFormatter > DateFormatter && NSDate > Date
let df = DateFormatter()
df.timeZone = .current
df.dateStyle = .medium
df.timeStyle = .short
df.dateFormat = "dd/M/yyyy"
let currentDate = df.string(from: Date())

Converting Date+Time Formats

I need to convert the following format into a new format using NSDateFormatter.
'Fri, 25 Mar 2011 10:16:00 -0700'.
I tried using "aaa, dd bbb YYYY HH:MM:SS ZHHMM" as format, but it doesn't work; it gives me a date way in the past.
I also need to convert it into the Eastern Time Zone when creating a new date.
The code I used is the following one:
NSDateFormatter *newDateFormatter = [[NSDateFormatter alloc] init];
[newDateFormatter setDateFormat:#"dd MMM YYYY HH:mm:SS"];
Let's break this string down into the various portions:
Fri, 25 Mar 2011 10:16:00 -0700 becomes:
Fri: abbreviated day of the week
25: potentially zero-padded day of the month
Mar: abbreviated month
2011: year
10: potentially zero-padded hour (in 24-hour format because there is no AM/PM)
16: potentially zero-padded minute
00: zero-padded second
-0700: time zone offset
Now let's look at the date format patterns that NSDateFormatter supports:
abbreviated day of the week: EEE
zero-padded day of the month: dd
abbreviated month: MMM
year: y
zero-padded hour (24-hour): HH
zero-padded minute: mm
zero-padded second: ss
time zone offset: ZZZ
Thus, your format string should be: #"EEE, dd MMM y HH:mm:ss ZZZ".

Resources