Is there a way to detect which millennium a NSDate is in? - ios

I have an NSDate from a string of 01/01/14. Unfortunately the strings are saved with two digits so I have no idea if the year is from 1900's or 2000's. But I want create some intelligent code to guess what millennium and century the full year is. I know for a fact (based on the application I'm creating) that the year can't be below 1900 in 99% of cases.
I have converted the date to now show four digits on but it's now being displayed as 01/01/0014.
Is there a way to detect the millennium of the NSDate as 0 and make my changes accordingly?
What I really want to do is something like this (pseudo code):
if (millennium == 0 && decade < 15 && decade > 99)
{
millennium = 2;
century = 0;
// change the date to 2000's here
}
else if (millennium == 00)
{
millennium = 1;
century = 9;
// change the date to 1900's here
}

For starters your pseudocode isn't quite right (the first if statement is an impossible condition as decade can't be less than 15 and more than 99 at the same time).
I have created a basic solution using NSDateComponents and made up my own conditions for when to change the year which you can easily change.
NSDate *date; // this is your date
NSDateComponents* components = [[NSCalendar currentCalendar] components: NSMonthCalendarUnit | NSDateCalendarUnit | NSYearCalendarUnit fromDate:date];
if([components year] < 15) // 2000 to 2015
{
[components setYear:[components year] + 2000];
}
else if([components year] < 100) // 1915 to 1999
{
[components setYear:[components year] + 1900];
}
date = [NSCalendar currentCalendar] dateFromComponents:components];

there is an oldschool stuff (from the 80s) here to convert a year in 2 digits to year in 4 digits.
int finalYear = (19 + (yearIn2Digits < 50)) * 100 + yearIn2Digits;
then you can use that finalYear value to build up the proper NSDate object.
the covered interval is always 100 years and it is up you where you'd like to draw the line. the original idea worked between 1950 and 2049 – as you see in the formula, – but you can refine it for covering better your actual bounds of years.

I wouldn't recommend hardcoding this logic.
what if you want to convert a date which is 1908 , or 2015.
A smarter way to do this is as follows :
-(BOOL)isInCurrentDecade:(NSDate*)date{
// I don't recollect the api to extract year from NSDate
NSInteger tmpYear = [date year];
NSInteger currentYear = [[NSDate date] year];
return tmpYear > currentYear%100
}
What I am doing here is that I compare 87 , 04 , 99 with current year digit's 14. and if it's between 00 and now then its current decade. This code is more robust than your's because it's relative comparison with current date
o/p of the code is as follow as of year 2014 :
87 - > NO // in 90's
99 -> NO
04 - > YES // in 2000's
12 - > YES
Edge case occurs when your date includes 1/1/14 if you want it to be filtered in current decade replace the '>' with ' >=" .

Use NSDateFormatter and its property "twoDigitStartDate". You set this to the earliest date that you want to be able to enter with a two digit year. No complicated logic needed, and easy to update. Now data entry is one thing, but storing a date "with a two digit year", that's criminal.
And it handles things like "I want to be able to store anything starting with the start of the tax year 1985 in two digits, that is April 1st 1985". Then April 1st 85 will be in 1985, while March 31st 85 will be in 2085. With no effort.

Related

How to offset NSDate with UTC timezone offset without hardcoded manual calculation

Imagine the current local time being 15:11 UTC. I retrieve a data set from the server showing the opening closing time of a business displayed like so:
{
close = {
day = 3;
time = 0200;
};
open = {
day = 2;
time = 1700;
};
I also receive a utc-offset property exposed like so: "utc_offset" = "-420”; which I imagine is a minute offset giving an hour offset of 7 hours which seems right considering the timezone I'm in is UTC and the business location's opening hours information I'm receiving is for a business in Los Angeles who are 7 hours behind.
How do I use this property to then be able to do any time calculations on it
I want to determine whether the current local time falls between the open and close time that bit I have figured out but the calculations come out wrong considering the time comparison is done in the local timezone when it needs to be offset before calculating against that time range.
I'm trying to avoid doing things like
Psuedocode:
NSDate.date hour componenent + (UTC_offset / 60 = -7 hours)
Update:
Here's how I'm currently checking if the business is open right now
if currentArmyTime.compare(String(openInfo.time)) != .OrderedAscending && currentArmyTime.compare(String(closeInfo.time)) != .OrderedDescending {
//The business is open right now, though this will not take into consideration the business's time zone offset.
}
Is it easier to offset the current time?
Before you can use the 'open' and 'close' times in date operations you need to create an NSDate from a calendar that has been set to the time zone for those times. Here's an example:
// Create calendar for the time zone
NSInteger timeOffsetInSeconds = -420 * 60;
NSTimeZone *tz = [NSTimeZone timeZoneForSecondsFromGMT:timeOffsetInSeconds];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
calendar.timeZone = tz;
// Create an NSDate from your source data
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = 1;
comps.month = 1;
comps.year = 2016;
comps.hour = 8;
comps.minute = 0;
NSDate *openTime = [calendar dateFromComponents:comps];
// 'openTime' can now be to compared with local time.
NSLog(#"openTime = %#", openTime); // Result is openTime = 2016-01-01 15:00:00 +0000
You should put the above code into a method that takes in the raw time and the time offset to apply.

(Obj-c) Identify morning, afternoon or night from a date string

I'm working in Objective-C. I have this three values in string, which they determine the part of the day
NSString *morning = #"7:01";
NSString *afternoon = #"12:01"
NSString *night = #"19:01"
And my original date is this, also in String
NSString *currentTime = #"Sat, 17 Oct 2015 9:58 am CDT"
I need to identify if the current date is morning, afternoon or night according with the current date as String. Anyone have a solution?
Your question has received a negative score (-4 at the time of writing) as people obviously feel you haven't shown the effort SO expects. However your question hides a gotcha and something which Apple recently made harder, which makes it interesting.
Your sample time is:
NSString *currentTime = #"Sat, 17 Oct 2015 9:58 am CDT"
which would seem to be "morning". However this is exactly the same time as:
Sat, 17 Oct 2015 2:58 pm GMT
which would seem to be "afternoon". Both these times are:
Sat, 17 Oct 2015 14:58 UTC
Why is this an issue?
An NSDate is a point in time without any associated time zone. The class NSDateFormatter, and associated methods on NSDate itself, will parse a date-time string and produce the absolute UTC point in time the string represents. Any time zone in the string, such as CDT in the example, is allowed for in determining the absolute time point but is not directly represented in the NSDate value that results.
When the NSCalendar class is used to break out the parts of a date it does so in relation to a time zone, which defaults to the system time zone.
What all this adds up to is if your app is running, say, on a computer in the UK and you follow the suggestion in the comments:
parse the time (using NSDateFormatter)
break out the hour & min to get an NSDateComponents value (using NSCalendar); and
compare the hour & min to your boundaries then your sample time will be reported as "afternoon"
Not good :-(
What you need is to parse the date-time (getting a standard UTC time point) and the time zone, you can then pass that time zone to NSCalendar and the rest is easy.
Apple makes it harder
Prior to OS X 10.9 & iOS 7 the NSDateFormatter class returned an NSCalendarDate date value, that type was a subclass of NSDate and also stored an NSTimeZone value. So parsing your sample returned both the time point "Sat, 17 Oct 2015 14:58 UTC" and the time zone "UTC-5". With that information NSCalendar could be used to break out the hour & min and correctly determine the time is "morning".
NSCalendarDate is now deprecated, and while it is still possible to use it this could change at any moment. Apple do not yet appear to have provide an alternative "parse date and time zone" method.
Parsing both the date and time zone offset
From the simple observation that if you parse "Sat, 17 Oct 2015 9:58 am CDT" ignoring the time zone and treating it as UTC the result is an absolute time point which differs by 5 hours, the time zone offset for CDT, from the one obtained if the string is parsed taking the time zone into account you have a method to obtain the time zone - parse the string twice, once ignoring the time zone, and determine the difference.
This may not be the best algorithm, but it does work... (you may insert here warnings about premature optimisation!)
So here goes (minimal comments, look the methods up in the documentation, error checking etc. - treat as an outline only):
- (BOOL) dateAndZoneFromString:(NSString *)timeString // the date-time string
dateFormat:(NSString *)dateFormat // the format of the date-time, should contain a time zone format at the end
parsedDate:(NSDate **)date // NSDate representing the absolute time point
parsedZone:(NSTimeZone **)zone // NSTimeZone representing the time zone of the original string
error:(NSError **)error
{
NSDateFormatter *df = [NSDateFormatter new];
// parse timeString taking time zone into account
df.dateFormat = dateFormat;
NSDate *absDate = [df dateFromString:timeString];
// parse timeString ignoring the time zone by removing the format specifier from dateFormat
df.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"UTC"];
df.dateFormat = [dateFormat stringByReplacingOccurrencesOfString:#" *[zZvV]+$" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, dateFormat.length)];
NSDate *zonelessDate;
NSRange range = NSMakeRange(0, timeString.length);
if ( [df getObjectValue:&zonelessDate forString:timeString range:&range error:error] )
{
// parse successful, calculate the difference and construct an NSTimeZone value
NSTimeInterval offset = [zonelessDate timeIntervalSinceDate:absDate];
NSTimeZone *timezone = [NSTimeZone timeZoneForSecondsFromGMT:offset];
*date = absDate;
*zone = timezone;
return YES;
}
else
return NO;
}
If you pass #"Sat, 17 Oct 2015 9:58 am CDT" and for format #"E, d MMM y h:m a z" to this method it will return the time point "Sat, 17 Oct 2015 14:58 UTC" as an NSDate and the time zone "UTC-5" as an NSTimeZone.
If you pass #"Sat, 17 Oct 2015 2:58 pm GMT" then it will return the same absolute time point and a time zone of "UTC+0".
For this point you can use these values with NSCalendar, NSDateComponents, and simple comparisons to determine morning/afternoon/night.
HTH
-(void)ShowTimeMessage
{
// For calculating the current date
NSDate *date = [NSDate date];
// Make Date Formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh a EEEE"];
// hh for hour mm for minutes and a will show you AM or PM
NSString *str = [dateFormatter stringFromDate:date];
// NSLog(#"%#", str);
// Sperate str by space i.e. you will get time and AM/PM at index 0 and 1 respectively
NSArray *array = [str componentsSeparatedByString:#" "];
// Now you can check it by 12. If < 12 means Its morning > 12 means its evening or night
NSString *message;
NSString *timeInHour;
NSString *am_pm;
NSString *DayOfWeek;
if (array.count>2)
{
// am pm case
timeInHour = array[0];
am_pm = array[1];
DayOfWeek = array[2];
}
else if (array.count>1)
{
// 24 hours case
timeInHour = array[0];
DayOfWeek = array[1];
}
if (am_pm)
{
if ([timeInHour integerValue]>=4 && [timeInHour integerValue]<=9 && [am_pm isEqualToString:#"AM"])
{
message = [NSString stringWithFormat:#"Morning"];
}
else if (([timeInHour integerValue]>=10 && [timeInHour integerValue]!=12 && [am_pm isEqualToString:#"AM"]) || (([timeInHour integerValue]<4 || [timeInHour integerValue]==12) && [am_pm isEqualToString:#"PM"]))
{
message = [NSString stringWithFormat:#"Afternoon"];
}
else if ([timeInHour integerValue]>=4 && [timeInHour integerValue]<=9 && [am_pm isEqualToString:#"PM"])
{
message = [NSString stringWithFormat:#"Evening"];
}
else if (([timeInHour integerValue]>=10 && [timeInHour integerValue]!=12 && [am_pm isEqualToString:#"PM"]) || (([timeInHour integerValue]<4 || [timeInHour integerValue]==12) && [am_pm isEqualToString:#"AM"]))
{
message = [NSString stringWithFormat:#"Night"];
}
}
else
{
if ([timeInHour integerValue]>=4 && [timeInHour integerValue]<10)
{
message = [NSString stringWithFormat:#"Morning"];
}
else if ([timeInHour integerValue]>=10 && [timeInHour integerValue]<16)
{
message = [NSString stringWithFormat:#"Afternoon"];
}
else if ([timeInHour integerValue]>=16 && [timeInHour integerValue]<22)
{
message = [NSString stringWithFormat:#"Evening"];
}
else
{
message = [NSString stringWithFormat:#"Night"];
}
}
if (DayOfWeek)
{
_timeLbl.text=[NSString stringWithFormat:#"%# %#",DayOfWeek,message];
}
}

Get NSDate from start of 7 days ago

I'm trying to do some simple 7 day history plotting. Given the current NSDate.date, I want to get the NSDate that corresponds to the start of day, 7 days ago. So basically 7 days prior to 0.00 this morning.
What I've tried, is the following:
// decompose the current date, do I need more component fields?
NSDateComponents *comps = [NSCalendar.currentCalendar components: NSDayCalendarUnit fromDate: NSDate.date];
NSLog(#"components: %#", comps);
// Back day up 7 days. Will this wrap appropriate across month/year boundaries?
comps.day -= 7;
NSDate *origin = comps.date;
NSLog(#"new date: %#", origin);
What I assumed was that by just specifying NSDayCalendarUnit, the other things would be defaults (like start of day, etc). Unfortunately, origin ends up as (null). What is the correct way to do this?
To construct a new date you should know not only a day but also a month and a year. So you should add NSYearCalendarUnit | NSMonthCalendarUnit. Also you should setup calendar and probably timezone properties of NSDateComponents's instance:
NSDateComponents *comps = [NSCalendar.currentCalendar components: NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate: NSDate.date];
comps.calendar = NSCalendar.currentCalendar;
comps.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
NSLog(#"components: %#", comps);
// Back day up 7 days. Will this wrap appropriate across month/year boundaries?
comps.day -= 7;
NSDate *origin = comps.date;
NSLog(#"new date: %#", origin);

Getting date from weekday

I'm doing this app where the user inputs some days (Using UITableView, monday - sunday).
I then need the app to figure out which dates this matches with. Say it's the user sits on sunday the 15th and chooses monday and tuesday. The app will figure out the dates are monday 16th and tuesday 17th.
How would one go about that using NSDate and such? I know how to find a weekday using the date, but I want the opposite.
Of course it has to be the closest days, like not finding monday the 23rd, but finding 16th.
Hope that makes sense. :-)
A direct method, without using a loop:
NSUInteger targetWeekday = ...; // 1 = Sunday, 2 = Monday, ...
// Date components for today:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *comp = [cal components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit
fromDate:now];
// Adjust components for target weekday:
if (targetWeekday >= comp.weekday) {
comp.day += (targetWeekday - comp.weekday);
} else {
comp.day += (targetWeekday + 7 - comp.weekday); // Assuming 7 days per week.
}
comp.weekday = targetWeekday;
// And back to NSDate:
NSDate *targetDate = [cal dateFromComponents:comp];
Remark:
if (targetWeekday >= comp.weekday) {
comp.day += (targetWeekday - comp.weekday);
} else {
comp.day += (targetWeekday + 7 - comp.weekday); // Assuming 7 days per week.
}
can be replaced by the shorter, equivalent code
comp.day += (targetWeekday + 7 - comp.weekday) % 7;
You can do it by following a simple procedure:
Start with an NSDate that represents today
Get the day of the week from it (here is how it is done)
If the day of the week matches what's in the selected UITableViewCell, you are done.
Otherwise, add one day to NSDate (here is how it is done), and go back to step 2.

Validation of date and month

i have a current date using NSDate which is ma start date....and i add 4 more days to the current date where i get ma endDate..
NSString *StrtDate= [dateFormatter stringFromDate:[NSDate date]];
int daysToAdd = 4;
NSDate *newDate1 = [[NSDate date] dateByAddingTimeInterval:60*60*24*daysToAdd];
NSString *StopDate= [dateFormatter stringFromDate:newDate1];
suppose the current date is todays date..thats 28th of jan the end date becomes 32nd of Jan which is invalid rite?
how do u validate the date??
Have you run the code and checked whether the resultant date is indeed 32nd January?
The framework is intelligent enough to understand valid dates. You will get correct date.
You don't need to worry about this as the date framework knows how many days are in each month.

Resources