Get current month date in date components - ios

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)

Related

Is it NSCalendar bug?

Why are the last two lines of the output the same?
Use NSCalendar to calculate the diff between startTime and endTime, find that the diff between #"2008-02-28 00:00:00" and #"2022-02-28 00:00:00" and the diff between #"2008-02-29 00:00:00" and #"2022-02-28 00:00:00" are the same. It looks like a bug of NSCalendar, maybe about leapMonth?
code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self printDiffBetweenStartTime:#"2008-02-27 00:00:00" endTime:#"2022-02-28 00:00:00"];
[self printDiffBetweenStartTime:#"2008-02-28 00:00:00" endTime:#"2022-02-28 00:00:00"];
[self printDiffBetweenStartTime:#"2008-02-29 00:00:00" endTime:#"2022-02-28 00:00:00"];
}
- (void)printDiffBetweenStartTime:(NSString *)startTime endTime:(NSString *)endTime
{
static NSDateFormatter *dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ss";
dateFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
});
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startDate = [dateFormatter dateFromString:startTime];
NSDate *endDate = [dateFormatter dateFromString:endTime];
NSCalendarUnit unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [calendar components:unitFlags fromDate:startDate toDate:endDate options:0];
NSLog(#"\"%#\" to \"%#\" : %# year %# month %# day %# hour %# minute %# second", startTime, endTime, #(components.year), #(components.month), #(components.day), #(components.hour), #(components.minute), #(components.second));
}
output:
"2008-02-27 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 1 day 0 hour 0 minute 0 second
"2008-02-28 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 0 day 0 hour 0 minute 0 second
"2008-02-29 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 0 day 0 hour 0 minute 0 second
This is expected. There are many ways to do these period calculations, and the one that NSCalendar uses turns out to not be the one you expected.
The documentation briefly describes what it does:
Some operations can be ambiguous, and the behavior of the computation is calendar-specific, but generally larger components will be computed before smaller components; for example, in the Gregorian calendar a result might be 1 month and 5 days instead of, for example, 0 months and 35 days.
What this means is that it will compute how many years are in between the two dates first, then months, then days, and so on. "Years" is the biggest component you requested.
And NSCalendar finds that adding 14 years to 2008-02-28 makes exactly 2022-02-28. Adding 14 years to 2008-02-29 is also exactly 2022-02-28, because 2022 is not a leap year. Note that "adding a year" does not mean the same as "adding 12 months" or "adding 365 days".
For a difference to appear in this case, you need to compute the days first. One period has 5114 days, and the other has 5113.
A few more examples:
If you instead compute the year, month, day period between 2008-02-28 and 2022-02-01, and the period between 2008-02-29 and 2022-02-01. You wouldn't see a difference, both are 13 years, 11 months, and 4 days. This is because adding 13 years to both 2008-02-29 and 2008-02-28 gets you to 2021-02-28, then adding 11 months is 2022-01-28. 4 days after that is 2022-02-01.
However, if you only compute months and days, the period between 2008-02-28 and 2022-02-01, and the period between 2008-02-29 and 2022-02-01 are different.
The period between 2008-02-28 and 2022-02-01 is 167 months and 4 days. Adding 167 months to 2008-02-28 is 2022-01-28. 4 days after that is 2022-02-01.
The period between 2008-02-29 and 2022-02-01 is 167 months and 3 days. Adding 167 months to 2008-02-29 is 2022-01-29. 3 days after that is 2022-02-01.
Period calculations are weird, aren't they! But they are consistent in a unique way.

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())

Having trouble with NSCalendar get date components

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)

(Objective C) NSDate is nil to convert a date in String

I have this string date:
Wed, 14 Oct 2015 9:58 am CDT
And I'm try to convert this String to NSData with this code
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"EE, d LLL YYYY HH:mm a Z"];
NSDate *date = [dateFormat dateFromString:#"Wed, 14 Oct 2015 9:58 am CDT"];
But date, which is a NSDate variable is always nil. I have tried to change my DateFormat with different syntax but I have the same result.
The issue is you are using capital Z instead of lowercase z for the time zone abbreviation. I also suggest reducing EEE to one E, changing LLL to MMM, using yyyy for year and h instead of HH for the hour.
"E, d MMM yyyy h:mm a z"
As a quick hint to date formatting issues, try converting a date to a string first and comparing the output.

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