尝试从不同的方向解决问题。以以下内容为起点,并非所有内容均已解释 - 如果您说不知道什么是字典那么你应该研究它。
审视问题
你有一个table开门和关门时间,检查您的商店是否开门应该查找此表 - 就像您在“现实生活”中一样。要知道商店是否营业,您需要知道工作日 - 它告诉您要咨询表中的哪一行,以及时间 - 您可以将其与表中该行的两个时间进行比较。
要表示程序中的表,通常使用array,要表示两个关联的时间 - 例如开盘时间和关盘时间 - 您可以使用record, object or 字典 etc.
你如何代表一天中的某个时间?一般的时间和日期计算很复杂,但是在工作日之后您需要知道的就是当天的时间,并且假设您不担心闰秒(您不担心),您可以假设有 24 小时 60 分钟一天中的每个时间,这样您就可以将时间存储为自午夜以来的分钟数 - 为您提供一个数字。如果您使用自午夜以来的分钟数来确定商店是开门还是关门,您可以避免复杂的日期比较。
一些代码
正如您的代码已经表明您可以使用NSCalendar
获取任意时间的星期几、小时和分钟NSDate
.
如何获取自午夜以来的小时和分钟到分钟,这是简单的算术,但您需要执行几次,所以也许一个简单的macro将时间转换为小时和分钟:
#define TO_MINUTES(hour, min) (hour * 60 + min)
开放时间表如何表示?
那么你可以使用NSArray
,按工作日索引,其中每个元素都是NSDictionary
包含两个键/值对 - 用于打开和关闭时间。但是,您的时间只是整数,即自午夜以来的分钟数,并将整数存储在NSDictionary
需要将它们包装为NSNumber
对象。 (如果这没有意义,那么是时候做一些研究了!)
另一种方法是为表使用 C 风格的数组和结构 - 这会很好地工作,因为您只存储整数。
下面是一个代表打开和关闭时间的 C 结构体定义:
typedef struct
{
NSInteger openTime;
NSInteger closeTime;
} ShopHours;
通过该宏和上面的宏,您可以轻松定义表示商店营业时间的常量数组:
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 开始索引,简单地忽略第 0 个元素就可以避免这样做- 1
在你的代码中。)
你已经有了可以破解的代码NSDate
into NSDateComponents
,使用它您可以轻松获取自午夜以来的工作日和分钟数:
NSInteger weekday = comps.weekday;
NSInteger minutes = TO_MINUTES(comps.hour, comps.minute);
use weekday
来索引WeekSchedule
表并比较minutes
到这两个条目就完成了,例如商店是否营业:
if (minutes >= WeekSchedule[weekday].openTime && minutes <= WeekSchedule[weekday].closeTime)
{
// shop is open...
}
else
{
// shop is closed...
}
您可以将上述内容包装到一个方法中,该方法给定日期告诉您商店的状态:
- (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";
}
}
正如您将看到的,这个解决方案比您采用的方法要短得多。
HTH
附录 - 改进
正如罗布·纳皮尔 (Rob Napier) 在评论中指出的那样,如果情况不明显,上述解决方案的概述就是这样,并忽略了商店在午夜营业等情况。以下是您可能需要考虑的一些事项:
商店每天营业超过一时段:有些商店在午餐时间关门,餐馆可能在午餐和晚上营业,等等。要处理这个问题,您需要每天的开放/关闭时间列表,而不仅仅是一对。一旦确定了工作日测试,就需要迭代这样的列表了。
午夜营业的商店:这只是(1)的一个特例,想一想......
时区:在问题中的代码和此答案中的代码中,假设打开/关闭时间和测试时间均来自同一时区。例如,如果您希望帮助加拿大的人确定德国的一家商店目前是否营业并且可以打电话,您需要考虑到时差。
夏令时:这是 Rob 在评论中提到的极端情况。当 DST 发生更改时,可能会跳过或重复一个小时。仅当您支持在该时间段内开门/关门的商店时,这才是一个问题 - 在整个更改期间开门的商店不需要特殊处理。NSCalendar
将给出从您测试时起的正确小时/分钟,您需要处理对打开/关闭时间的任何调整。例如,假设一家商店在凌晨 2 点关门,DST 更改会从凌晨 2 点跳回凌晨 1 点,该商店在凌晨 1:30 开门吗?第一次是这样,但是第二次呢?决定这个问题是一个超越时间计算的问题。
您需要决定是否以及如何解决这些问题。
更多提示
好吧,圣诞节到了(随你喜欢吧——吃得慢,大脑迟钝,送礼物的时间等等。;-))
我看到您在另一个问题中问过如何动态创建表而不是使用静态表,所以您已经涵盖了。
让我们考虑白天和午夜开放的多个开放时间:
数组的数组可以工作,但您也可以只保留一整周的开放时间数组。例如。改变TO_MINUTES
宏也采用天数并将所有时间存储为自周日 0000 点起的分钟数。现在,不再索引数组来查找迭代或搜索它的日期 - 数组是有序的,因此您可以根据需要进行二分搜索,但简单的迭代可能足够快(一周中有多少个打开/关闭周期? )
通过将关闭时间设置为第二天 (1) 涵盖了所有在午夜开放的情况but周六 -> 周日,包括 30 分钟内关闭的计算。
要处理周六 -> 周日,首先将时间段分为周六晚上和周日早上部分。将它们添加到您的数组中,它们将是第一个(周日清晨)和最后一个(周六深夜)条目。现在,当您确定距离关门时间还有多少分钟时,请检查关门时间是否为周六午夜(例如,周六午夜)。TO_MINUTES(7, 24, 0)
),如果是,请检查第一个条目的开放时间是否为星期日 0000 点,如果是,则要调整关闭时间以进行 30 分钟检查(添加第一个时段的长度)。
这将处理多个时段并在午夜开放。它不处理 DST、商店假期等 - 您需要决定处理多少。用于夏令时NSTimeZone
找出时间变化的时间和变化程度(并不总是 1 小时),找出“重复”和“缺失”时间 - 但请记住,这只是您的商店在这些时间段内实际开门/关门的问题。
新年到了;-)
说真的,Rob 决定提供几乎完整的代码,但使用 Objective-C 对象和许多方法,所以我想我应该添加我的代码进行比较,因为它提出了一个有趣的问题。
首先应该注意的是相似性,从算法上来说,这两种解决方案很接近 - 环绕的处理方式不同,但任何一种方法都可以以任何方式完成,因此这并不重要。
区别在于数据结构的选择 - 对于如此简单的东西,您应该使用 C 结构和数组还是 Objective-C 对象?框架本身有很多结构类型 - 例如NSRect
et al- 在 Objective-C 代码中使用它们没有任何问题。选择不是黑白的,存在一个灰色区域,其中任何一个都可能合适,并且这个问题可能属于该灰色区域。因此,这是每天多次开放的解决方案:
// 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";
}
夏令时问题
我一开始认为这不是问题,罗布在评论中提出了它,现在他认为这不是问题,但事实并非如此——尽管它可能有点学术性。
如果你不使用这不是问题NSDate
来表示被查询的时间,Rob 的解决方案就采用了这条路线。
原来的问题和上面的代码确实使用NSDate
并将工作日、小时和分钟分解为NSDateComponents
。考虑一下这样的情况:由于夏令时的变化,凌晨 2 点变成了凌晨 1 点,商店通常在凌晨 1:30 关门。如果您从NSDate
凌晨 1 点之前的值并递增,例如每次 10 分钟,直到您获得一个小时值components:fromDate:
大于 2,您将看到如下值:00:50、01:00、01:10、...、01:50、01:00、01:10、...、01:50、02:00、 02:10。每次测试都会报告商店在第一个 01:30 过去后关闭 30 分钟,然后它将重新开放 30 分钟,直到下一个 01:30 过去!
仅当您从以下位置开始时才会出现此问题NSDate
,如果您简单地将工作日/小时/分钟作为输入,则不会发生这种情况。任何一种方法(结构或对象)都可以以任何方式操作,您只需决定这是否是您希望解决的问题。