函数返回打开,closures或closures从小时

我有一堆小时的操作我想确定商店是否打开,closures或closures在30,29,28,27 …分钟我在Xcode / Objectic-C做这个。 现在我必须这样做,让我们说50个不同的操作时间。 我已经做了一个这样做的function,但它不是很有效,并涉及到很多if-else语句。 这是一个示例小时的操作

Monday - Thursday 7:30am - Midnight Friday 7:30am - 10:00pm Saturday 9:00am - 10:00pm Sunday 9:00am - Midnight 

这是我的function和我如何处理它

 -(BOOL) dateAndTime:(NSDate*)date getStartDay:(NSInteger)startDay getStartHour:(NSInteger)startHour getStartMin:(NSInteger)startMin getEndDay:(NSInteger)endDay getEndHour:(NSInteger)endHour getEndMin:(NSInteger)endMin{ NSCalendar *calendar = [NSCalendar currentCalendar]; const NSCalendarUnit units = NSWeekdayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit; NSDateComponents *comps = [calendar components:units fromDate:date]; if (comps.weekday == 1) { comps.weekday = 7; } else comps.weekday = comps.weekday - 2; NSDate *startOfToday; [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&startOfToday interval:NULL forDate:date]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; NSTimeZone *timeZone = [NSTimeZone localTimeZone]; [dateFormatter setDateFormat:@"HH:mm"]; [dateFormatter setTimeZone:timeZone]; NSString *dateString = [dateFormatter stringFromDate:date]; NSDate *startDate = [dateFormatter dateFromString:[NSString stringWithFormat:@"%ld:%ld", (long)startHour, (long)startMin]]; NSDate *endDate = [dateFormatter dateFromString:[NSString stringWithFormat:@"%ld:%ld", (long)endHour, (long)endMin]]; NSString *startDateString = [dateFormatter stringFromDate:startDate]; NSString *endDateString = [dateFormatter stringFromDate:endDate]; if ([startDateString compare:dateString] == NSOrderedAscending && [dateString compare:endDateString] == NSOrderedAscending && startDay <= comps.weekday && comps.weekday <= endDay) { return YES; } else return NO; } 

现在我从0-6(0是星期一)过去,然后是24小时。 然后像这样使用它:

  if ([self dateAndTime:date getStartDay:0 getStartHour:7 getStartMin:30 getEndDay:3 getEndHour:23 getEndMin:30] == YES) text = @"Open"; else if ([self dateAndTime:date getStartDay:0 getStartHour:23 getStartMin:30 getEndDay:3 getEndHour:24 getEndMin:0] == YES) text = [NSString stringWithFormat:@"Closes in %@ min", countdownNumber]; else text = @"Closed"; 

正如你所看到的,在一周中的所有日子和每个小时都要这么做,这需要大量的if语句,而且非常糟糕。 就这个例子而言,它需要8个if-else语句(非常耗时)

现在这个问题的基础是如何使这个更有效率/更好的方式来做到这一点,而基层能够在最后30分钟倒计时?

我已经做了一些研究,找不到什么东西倒闭了,什么东西都closures了,效率很高。

这里是完整的if-else语句的例子,如果你需要它或想看到https://gist.github.com/spennyf/b0b18e31c3e9deaa0455

感谢您的帮助和/或build议提前:)

编辑

这是我的.m文件的顶部我有这个

 @interface HomeTableViewController () { ShopHours WeekSchedule[]; } @end 

但是,这给了我在评论中谈到的编译错误,我怎样才能设置这个variables,以便在整个这个.m文件中使用? 现在我只是通过它作为一个额外的栏杆function,应该是罚款。 🙂

一旦我确定它是开放的,如果这个地方在30分钟内closures,那么设置if语句的最好方法是什么? 如果一个地方在一天中的某个时间/午夜之间开放,你可以添加这个设置吗?

感谢你的帮助 :)

将时间存储为C结构会造成一些内存pipe理难题。 如果可以的话,我会避免的(我想你也可以)。 相反,我build议创build一些新的类来帮助我们。

首先,我们应该把这些时间看作“一个星期内的标称时间”。 通过使这些标称时间,我们可以明确地摆脱DST的担心,说“星期天上午1点30分”是指“第一个时刻,我们会打电话给凌晨1:30,无论DST转换可能会发生什么创造另一个1:30a “。 商店通常都是这样工作的,但是在思考时间函数的时候精确到位是很重要的。

关于“一星期内的名义时间”的好处是,我们可以从星期天的午夜开始计算分钟数,而且我们知道在这个星期的时间恰好是60 * 24 * 7(10,080)分钟。 我们在这里只关心几分钟,所以我们只需要跟踪0到10,079之间的数字。 但是,我们必须记住,这些数字是模块化算术(他们“环绕”)。 问周二是周三之前还是之后是没有意义的。 周二是周三前后。 但是询问星期二是星期一(作为起点)和星期三(作为终点) 之间是有意义的。 我们可以确定,如果从周二前进,我们将在周三遇到星期三之前遇到。 如果那是真的,那就在两者之间。 这就是你必须考虑模块化的时间。

好的,理论太多了。 让我们看看一些代码(所有的代码都在这里 )。 首先,我们希望一周时间来表示一周内的某个时间:

 typedef enum { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday } Weekday; @interface WeekTime : NSObject @property (nonatomic, readonly) NSInteger minutesSinceStartOfWeek; - (instancetype)initWithWeekday:(Weekday)weekday hour:(NSInteger)hour minute:(NSInteger)minute; @end 

这个impl应该是显而易见的,所以我不会把它粘贴在这里。

现在我们想要考虑名义上一周的时间范围。 我们将这些开放范围,所以他们包括他们的开始时间,但不是他们的结束时间。 如果某个地方以9pclosures,则要以9p返回“Closed”,而不是“1mclosures”。

 @interface WeekTimeRange : NSObject @property (nonatomic, readonly) WeekTime *start; @property (nonatomic, readonly) WeekTime *end; - (instancetype)initWithStart:(WeekTime *)start end:(WeekTime *)end; - (BOOL)contains:(WeekTime *)time; @end 

大部分这应该是显而易见的,但contains:有一个小技巧。 由于我们是模块化算术,因此startend “更大”是合法的(记住“更大”并不意味着什么)。 这很容易处理:

 - (BOOL)contains:(WeekTime *)time { NSInteger queryMinutes = time.minutesSinceStartOfWeek; NSInteger startMinutes = self.start.minutesSinceStartOfWeek; NSInteger endMinutes = self.end.minutesSinceStartOfWeek; if (startMinutes < endMinutes) { return (startMinutes <= queryMinutes && queryMinutes < endMinutes); } else { return (queryMinutes < endMinutes || queryMinutes >= startMinutes); } } 

(在我的示例中,我已经select将start==end的范围设置为undefined,如果要为其指定一些含义,无论是“所有时间”还是“没有时间”,那么您可能需要对其进行调整。但是要小心“所有的时间”,这意味着商店closures并立即重新开放,所以你会得到“closures…”的消息,这就是为什么我只是start==end未定义。

好的,最后我们需要一些帮手:

 WeekTimeRange *WTMakeRange(Weekday startDay, NSInteger startHour, NSInteger startMinute, Weekday endDay, NSInteger endHour, NSInteger endMinute) { return [[WeekTimeRange alloc] initWithStart:[[WeekTime alloc] initWithWeekday:startDay hour:startHour minute:startMinute] end:[[WeekTime alloc] initWithWeekday:endDay hour:endHour minute:endMinute]]; } WeekTimeRange *WTFindOpenRangeIncluding(NSArray *ranges, WeekTime *time) { for (WeekTimeRange *range in ranges) { if ([range contains:time]) { return range; } } return nil; } NSString *WTStatus(WeekTimeRange *range, WeekTime *requestedTime) { if (range != nil) { NSInteger minutesLeft = range.end.minutesSinceStartOfWeek - requestedTime.minutesSinceStartOfWeek; if (minutesLeft < 0) { minutesLeft += [WeekTime maxMinute]; } if (minutesLeft <= 30) { return [NSString stringWithFormat:@"Closing in %ld minutes", (long)minutesLeft]; } else { return @"Open"; } } else { return @"Closed"; } } 

这些不需要太多的解释。 我们在实践中看到它。 您似乎已经知道如何将date转换为其组件,所以我将直接创build一个WeekTime ,而不用担心从NSDate转换date。

  NSArray *shopHours = @[WTMakeRange(Monday, 7, 30, Tuesday, 0, 0), WTMakeRange(Tuesday, 7, 30, Wednesday, 0, 0), WTMakeRange(Wednesday, 7, 30, Thursday, 0, 0), WTMakeRange(Thursday, 7, 30, Friday, 0, 0), WTMakeRange(Friday, 7, 30, Friday, 22, 0), WTMakeRange(Saturday, 9, 0, Saturday, 22, 0), WTMakeRange(Sunday, 9, 0, Monday, 2, 0) ]; WeekTime *mondayElevenPM = [[WeekTime alloc] initWithWeekday:Monday hour:23 minute:00]; WeekTimeRange *openRange = WTFindOpenRangeIncluding(shopHours, mondayElevenPM); NSString *result = WTStatus(openRange, mondayElevenPM); NSLog(@"%@", result); 

我还没有尝试过大量的testing用例,但是我的临时testing似乎正常。 所有的代码都是要点 。 我不testing的一个案例是你的范围是否重叠。 上述algorithm不关心范围是否正确,但如果范围重叠,则可能会得到不正确的“正在closures…”消息。 解决这个问题(通过抛出一个错误,或通过合并范围)应该是一个非常简单的增强。

尝试从不同的方向处理问题。 以下面的内容为出发点,不是所有的解释 – 如果你说不知道什么是字典 ,那么你应该研究它。

看问题

你有一个开门和关门时间表,检查你的店是否开放应该是这张表的查找 – 就像你在“现实生活中”一样。 要知道商店是否开放,你需要知道星期几 – 告诉你哪一行表格需要咨询,以及时间 – 你在表格的那一行中比较两次。

要在程序中表示一个表格,通常使用一个数组来表示两个关联的时间 – 例如打开和closures时间 – 可以使用logging对象字典等。

你怎么能代表一天的时间? 一般的时间和date计算是复杂的,但在工作日之后,所有你需要知道的是当天的时间,假设你不担心闰秒(你不是),你可以假设有24小时60分钟每天一个,所以你可以将时间存储为自午夜以来的分钟数 – 给你一个单一的数字。 如果您使用自午夜以来的分钟数来确定商店是打开还是closures,则可以避免复杂的date比较。

一些代码

由于你的代码已经显示你可以使用NSCalendar获取任何NSDate的周末,小时和分钟。

如何获得自午夜以来的小时和分钟,这是简单的算术,但你会想要做几次,所以也许一个简单的macros转换时间在几小时和几分钟:

 #define TO_MINUTES(hour, min) (hour * 60 + min) 

如何表示开放时间表?

那么你可以使用一个由星期NSArray索引的NSArray ,其中每个元素是一个包含两个键/值对的NSDictionary – 用于打开和closures时间。 然而,你的时间只是整数,自午夜以来的分钟数,并将整数存储在一个NSDictionary需要包装为一个NSNumber对象。 (如果这样做没有意义的话,那就去做一些研究吧!)

另一种方法是为你的表使用一个C风格的数组和结构 – 这将会很好地工作,因为你只是存储整数。

这是一个C结构定义来表示开始和结束时间:

 typedef struct { NSInteger openTime; NSInteger closeTime; } ShopHours; 

用上面这个和上面的macros你可以很容易地定义一个代表工作时间的常量数组:

 ShopHours WeekSchedule[] = { {0, 0}, // index 0 - ignore { TO_MINUTES(9, 0), TO_MINUTES(24, 0) }, // index 1 - Sunday { TO_MINUTES(7, 30), TO_MINUTES(24, 0) }, // index 2 - Monday ... { TO_MINUTES(9, 0), TO_MINUTES(22, 0) }, // index 7 - Saturday }; 

在实际的代码中,你可能会从数据文件中读取这个数据,但是上面的全局数组现在就可以完成了。

(请注意,索引0被忽略 – NSDateComponents数字从星期天开始的天数为1,而数组(C风格和NSArray )从0开始索引,只是忽略第零个元素,避免了代码中的第一个元素。

你已经有了将NSDate分解成NSDateComponents代码,使用它你可以很容易地从午夜开始获得星期几和分钟数:

 NSInteger weekday = comps.weekday; NSInteger minutes = TO_MINUTES(comps.hour, comps.minute); 

使用weekdayWeekSchedule表编制索引,并将minutes与两个条目进行比较,您就完成了工作,例如,店铺打开:

 if (minutes >= WeekSchedule[weekday].openTime && minutes <= WeekSchedule[weekday].closeTime) { // shop is open... } else { // shop is closed... } 

你可以把上面的方法换成一个给定date的方法,告诉你商店的状态:

 - (NSString *) shopState:(NSDate *)dateAndTime { // break out the weekday, hours and minutes... // your code from the question NSInteger weekday = comps.weekday; NSInteger minutes = TO_MINUTES(comps.hour, comps.minute); if (minutes >= WeekSchedule[weekday].openTime && minutes <= WeekSchedule[weekday].closeTime) { // shop is open... // determine if its closing within 30 mins and return an appropriate string } else { // shop is closed... return @"Closed"; } } 

正如你将会看到这个解决scheme比你所采用的方法短得多。

HTH

附录 – 细化

正如罗布·纳皮尔(Rob Napier)在评论中指出的那样,如果不是很明显的话,上述的解决scheme大纲就是这样,并且省略了诸如午夜开放的商店之类的情况。 以下是您可能需要考虑的一些事情:

  1. 商店每天开放超过一个时间段:一些商店在午餐时间closures,餐馆可能在午餐和晚上开放等等。为了解决这个问题,你需要每天打开/closures时间列表,而不是仅仅一对。 一旦你确定了工作日testing是迭代这样一个列表的问题。

  2. 午夜时分开放的商店:这只是(1)的特例,想一想…

  3. 时区:在问题中的代码和此答案中的代码中,假定开/关时间和testing时间全部来自同一时区。 如果你想支持一个在加拿大的人来决定在德国的一家商店是否现在开放并且可以打电话,你需要考虑到时差。

  4. 夏令时:这是Rob在评论中提到的angular落案例。 发生DST更改时,可能会跳过或重复一小时。 如果您支持那个小时开放的商店,这只是一个问题 – 在整个变更期间开放的商店不需要特殊的处理。 NSCalendar将会从您testing的时间中提供正确的小时/分钟,您需要处理打开/closures时间的任何调整。 例如,考虑一家商店在凌晨2点关门,DST的换货时间从凌晨2点跳到凌晨1点,店铺是在凌晨1:30开的吗? 是的,这是第一次,但第二次呢? 决定这是一个超越时间计算的问题。

你需要决定是否以及如何解决这些问题。

一些更多的提示

好吧,那么圣诞节(以你喜欢的方式 – 吃减速的大脑,送礼的时间等等)

我看到你在另外一个问题上已经问过如何dynamic地创build表格,而不是使用静态的表格,所以你已经覆盖了这个表格。

我们来考虑一下午夜的开放时间:

  1. 数组的数组可以工作,但你可以保持一整个星期的开放时间数组。 例如,更改TO_MINUTESmacros以获取date编号,并将所有时间存储为星期天0000h以来的分钟数。 现在,不是索引数组来查找迭代或search的那一天 – 数组是有序的,所以如果您愿意,可以进行二分search,但是一个简单的迭代可能足够快(一周内有多less个开/关周期? )

  2. 通过将closures时间设置为第二天(1)涵盖周六至周日之外的所有事情,包括在30分钟内closures计算。

  3. 为了处理星期六 – >太阳,首先将时间分成星期六晚上和星期日早上的部分。 将它们添加到您的arrays中,它们将是第一个(星期天早上)和最后一个(星期六晚上晚上)的条目。 现在,当您确定closures时间的分钟时,如果closures时间是星期六午夜(例如TO_MINUTES(7, 24, 0) ),则检查第一个条目的打开时间是否为星期日0000hrs,如果是,则要调整closures时间做30分钟的检查(加上第一期的长度)。

这将处理多个时期,并在午夜开放。 它不处理夏令时,购物假期等 – 你需要决定要处理多less。 对于DST,使用NSTimeZone来查明时间和时间的变化(它不一定是1小时),以找出“重复”和“失踪”时间 – 但记住这只是你的店铺的一个问题,实际上打开/closures在那个时候。

这是新的一年;-)

认真罗布决定给几乎完整的代码,但使用Objective-C对象和一些方法,所以我想我会添加我的代码进行比较,因为它引发了有趣的问题。

首先要注意的是相似性,在algorithm上这两个解决scheme是紧密的 – 环绕处理方式不同,但任一方法都可以做到这一点,所以不重要。

不同之处在于数据结构的select – 您应该使用C结构和数组来处理这些简单或Objective-C对象吗? 框架本身有很多结构types – 例如NSRect – 在Objective-C代码中使用它们没有任何问题。 这个select不是黑白的,有一个灰色的地方可能是合适的,这个问题可能在这个灰色地带。 所以这里有多个开放时间/天解决scheme:

 // convenience macro // day 1 = Sunday, ... 7 = Saturday #define TO_MINUTES(day, hour, min) ((day * 24 + hour) * 60 + min) #define WEEK_START TO_MINUTES(1, 0, 0) #define WEEK_FINISH TO_MINUTES(7, 24, 0) typedef struct { NSInteger openTime; NSInteger closeTime; } ShopHours; // Opening hours ShopHours WeekSchedule[] = { { TO_MINUTES(1, 0, 0), TO_MINUTES(1, 0, 15) }, // Sat night special, part of Sat 11:30pm - Sun 0:15am { TO_MINUTES(1, 9, 0), TO_MINUTES(1, 24, 0) }, // Sun 9am - Midnight { TO_MINUTES(2, 7, 30), TO_MINUTES(2, 24, 0) }, // Mon 7:30am - Midnight { TO_MINUTES(3, 7, 30), TO_MINUTES(3, 24, 0) }, { TO_MINUTES(4, 7, 30), TO_MINUTES(5, 2, 0) }, // Midweek madness, Wed 7:30am - Thursday 2am { TO_MINUTES(5, 7, 30), TO_MINUTES(5, 24, 0) }, { TO_MINUTES(6, 7, 30), TO_MINUTES(6, 22, 0) }, // Fri 7:30am - 10pm { TO_MINUTES(7, 9, 0), TO_MINUTES(7, 22, 0) }, // Sat 9am - 10pm { TO_MINUTES(7, 23, 30),TO_MINUTES(7, 24, 0) }, // Sat night special, part of Sat 11:30pm - Sun 0:15am }; - (NSString *) shopState:(NSDate *)dateAndTime { NSCalendar *calendar = [NSCalendar currentCalendar]; const NSCalendarUnit units = NSWeekdayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit; NSDateComponents *comps = [calendar components:units fromDate:dateAndTime]; NSInteger minutes = TO_MINUTES(comps.weekday, comps.hour, comps.minute); NSLog(@"%ld (%ld, %ld, %ld)", minutes, comps.weekday, comps.hour, comps.minute); unsigned periods = sizeof(WeekSchedule)/sizeof(ShopHours); for (unsigned ix = 0; ix < periods; ix++) { if (minutes >= WeekSchedule[ix].openTime) { if (minutes < WeekSchedule[ix].closeTime) { // shop is open, how long till close time? NSInteger closeTime = WeekSchedule[ix].closeTime; // handle Sat -> Sun wraparound if (closeTime == WEEK_FINISH && WeekSchedule[0].openTime == WEEK_START) closeTime += WeekSchedule[0].closeTime - WEEK_START; NSInteger closingIn = closeTime - minutes; if (closingIn <= 30) return [NSString stringWithFormat:@"Closes in %ld min", closingIn]; else return @"Open"; } } else // minutes < WeekSchedule[ix].openTime break; } return @"Closed"; } 

DST问题

我首先认为这不是一个问题,罗布在评论中提出了这个问题,现在他认为这不是问题,但这不是 – 虽然它有点学术性。

如果您不使用NSDate来表示查询的时间,这是一个非问题,Rob的解决scheme将采用该路线。

原来的问题和上面的代码确实使用了NSDate并将它的工作日,小时和分钟作为NSDateComponents分解。 考虑到由于DST变化而凌晨2点变为凌晨1点的情况,并且商店通常在凌晨1点30分closures。 如果你在NSDate之前从一个NSDate值开始,并且每次增加10min,直到你从components:fromDate:获得一个小时值components:fromDate:大于2,你会看到如下的值:00 components:fromDate: ,…,01:50,01:00,01:10,01:50,02:00,02:10 testing每一个这样的时间将报告商店closures30分钟后,第一个01:30通过,然后将重新开放30分钟,直到下一个通过!

这个问题只发生在你开始使用NSDate ,如果你简单地把工作日/小时/分作为你的input,那么它就不会发生。 无论哪种方法(结构体或对象)都可以以任何方式运行,您只需决定是否是您想要解决的问题。

而不是使用所有这些参数的方法dateAndTime创build一个自定义的date对象,并设置在该对象上创build的属性,如下所示:

 @interface MyDateModelObject : NSObject @property (nonatomic, strong) NSDate *startDay; @property (nonatomic, assign) NSInteger startHour; And so on... 

将dateAndTime方法转换为:

 - (BOOL)dateAndTime:(MyDateModelObject *)myDateModelObject 

现在进入dateAndTime的代码。 这种方法做得太多了。 为了遵循单一职责原则,我将NSDateFormatter和date比较代码等代码移植到自己的方法中,形成一个实用方法。

有了这个说法,我会更进一步,将dateAndTime重命名为以下内​​容,并在其中添加您的要点代码:

 - (NSString *)retrieveStoreState:(MyDateModelObject *)date 

现在在你的代码中,你可以创build一个模型对象,并设置它的属性,将它传递给retrieveStoreState,并使它返回基于你的要点逻辑的状态。

希望这会给你一个很好的方向。如果你遵循SRP,你应该能够清理你的代码,使你的要点变得更加可读。