从小时返回打开,关闭或关闭的功能 [英] Function to return open, closed or closing from Hours of day

查看:313
本文介绍了从小时返回打开,关闭或关闭的功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆工作时间,我想确定商店是开放的,关闭的还是在30,29,28,27 ...分钟内,我在Xcode / Objectic-C中这样做。现在我必须这样做,让我们说50个不同的营业时间。我已经做了一个这样做的功能,但它并不是非常有效率,涉及很多if-else语句。这是一个操作时间示例

 周一至周四
上午7:30 - 午夜
星期五
7:30 am - 10:00 pm
星期六
9:00 am - 10:00 pm
星期日
上午9:00 - 午夜

这里是我的功能,如何处理它

 <$ c $ (NSInteger)endHe getEndHour:(NSInteger)endHour getEndMin(NSInteger)endMin {(NSInteger)endMin {(NSInteger)endMin getEndDe(NSInteger) 

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:@ 关闭%@ min,countdownNumber];
else text = @已关闭;

正如你所看到的那样,一周中的所有日子和每一天的时间都需要很多if语句是非常糟糕的。只是为了这个例子,它需要8个if-else语句(非常时间消费)



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



我做了一些研究,找不到任何有当某事物关闭并且有效时,倒计时。



下面是该示例的完整的if-else语句,如果您需要或希望看到 https://gist.github.com/spennyf/b0b18e31c3e9deaa0455





编辑



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

  @interface HomeTableViewController(){

ShopHours WeekSchedule [];
}

@end

但这让我编译我在评论中谈到的错误,我该如何设置这个变量,这样可以在这个.m文件中使用?现在我只是传递它作为一个额外的栏杆的功能应该是罚款。 :)



如果该地方在确定开放后30分钟内关闭,那么最好的方法是设置if语句?如果某个地方在一天/半夜的部分地区开放,您可以添加设置。



感谢您的帮助:)

解决方案

将您的时间存储为C结构会创建一些内存管理头痛。我会避免,如果你可以(我认为你可以)。相反,我建议创建一些新课程来帮助我们在这里。



首先,我们应该将这些时间看成一周内的名义时间。通过这些名义时代,我们可以明确地摆脱DST的担忧,称星期天凌晨1点30分是指我们上午1:30上午的第一个瞬间,无论什么DST转变可能会发生另外1:30a 。这就是商店通常工作的方式,但是在考虑时间功能时要精确一些很重要。



一个星期内的名义时间这个伟大的事情是,我们可以开始从星期日的午夜计算分钟,我们知道本周将会有60 * 24 * 7(10,080)分钟。我们只是在这里关心几分钟,所以我们只需要追踪0到10,079之间的数字。但是,我们必须记住,这些数字需要进行模数运算(它们绕过)。问星期二是星期三还是之后是没有意义的。星期二是星期三之前的。但是询问星期二(星期二)是星期一(作为起点)和周三(作为终点)之间是否有意义是有意义的。我们可以确定如果从周二向前走,我们将在星期三遇到星期三之前遇到。如果是这样,那就在之间。这就是你必须考虑模块化的时间。



好的,太多的理论。我们来看一些代码(所有的代码都是 here )。首先,我们想要一个 WeekTime 在一周内表示一段时间:

  typedef枚举{
星期日,
星期一,
星期二,
星期三,
星期四,
星期五,
星期六
工作日

@interface WeekTime:NSObject
@property(nonatomic,readonly)NSInteger minutesSinceStartOfWeek;

- (instancetype)initWithWeekday:(工作日)工作日小时:(NSInteger)小时分:(NSInteger)分钟;
@end

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



现在我们想考虑名义周的范围。我们将把这些开放的范围,包括他们的开始时间,而不是他们的结束时间。如果一个地方以9p关闭,您想要以9p返回Closed,而不是在1m关闭。

  @interface WeekTimeRange:NSObject 
@property(nonatomic,readonly)WeekTime * start;
@property(nonatomic,readonly)WeekTime * end;

- (instancetype)initWithStart:(WeekTime *)start end:(WeekTime *)end;
- (BOOL)包含:(WeekTime *)时间;
@end

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

   - (BOOL)包含:(WeekTime *)time {
NSInteger queryMinutes = time星期六
NSInteger startMinutes = self.start.minutesSinceStartOfWeek;
NSInteger endMinutes = self.end.minutesSinceStartOfWeek;

if(startMinutes< endMinutes){
return(startMinutes< = queryMinutes&&
queryMinutes< endMinutes);
} else {
return(queryMinutes< endMinutes ||
queryMinutes> = startMinutes);
}
}

(在我的例子中,我选择了范围,其中 start == end 是未定义的,如果要分配一些含义,意思是所有时间或无时间,那么您可能需要调整这一点,要注意所有的时间,这真的意味着商店关闭并立即重新打开,所以你会得到关闭...消息,这就是为什么我只是 start = = / code>未定义。)



好的,最后我们想要一些助手:

  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:@关闭%ld分钟,(long)minutesLeft];
} else {
return @Open;
}
} else {
return @已关闭;
}
}

这些不需要太多的解释。让我们在实践中看到它。你似乎已经知道如何把日期变成他们的组件,所以我要直接创建一个 WeekTime ,而不用担心从 NSDate

  NSArray * shopHours = @ [WTMakeRange(星期一,7,30, 0,0),
WTMakeRange(星期二,7,30,星期三,0,0),
WTMakeRange(星期三,7,30,星期四,0,0),
WTMakeRange ,7,30,星期五,0,0),
WTMakeRange(星期五,7,30,星期五,22,0),
WTMakeRange(星期六,9,0,星期六,22,0)
WTMakeRange(星期日,9,0,星期一,2,0)
];

WeekTime * mondayElevenPM = [[WeekTime alloc] initWithWeekday:Monday hour:23 minutes:00];
WeekTimeRange * openRange = WTFindOpenRangeIncluding(shopHours,mondayElevenPM);
NSString * result = WTStatus(openRange,mondayElevenPM);
NSLog(@%@,result);

我没有尝试过大量的测试用例,但是我的临时测试似乎工作正常。所有代码都在 gist 中。一个我不测试的情况是你的范围是否重叠。上述算法不关心范围是否有序,但如果范围重叠,您可能会收到错误的关闭...消息。解决(通过抛出错误或合并范围)应该是一个很容易的增强。


I have a bunch of hours of operation I am I want to determine whether the store is open, closed or is closing in 30,29,28,27... minutes I am doing this in Xcode/ Objectic-C. Now I have to do this for lets say 50 different hours of operation. I have made a function that does this but it is not very efficient and involves a lot of if-else statements. Here is a sample hours of operation

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

And here is my function and how I handle it

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

}

Now I pass in the day, from 0-6 (0 being monday) and then the time in 24-hour time. And then use it like this:

    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";

As you can see to do this for all the days of the week and hours on each day it requires a lot of if statements and is very bad. Just for this one example it requires 8 if-else statements (VERY TIME CONSUMING)

Now the basis of this question is how can I make this much more efficient/ what is a better way to do this, while sill being able to have the countdown for the last 30 minutes?

I have done some research and can't find anything that has a countdown to when something is closing and is efficient.

Here is the full if-else statements for the example if you need it or want to see https://gist.github.com/spennyf/b0b18e31c3e9deaa0455

Thanks for the help and/or advice in advance :)

EDIT

This is at the top of my .m file I have this

@interface HomeTableViewController () {

    ShopHours WeekSchedule[];
}

@end

But this gives me the compile error I talked about in the comments, how can I set this variable so it can be used throughout this one .m file? Right now I am just passing it is as an extra parapet for the function which should be fine. :)

And what would be the best way to set up the if statement for if the place is closing in 30 minutes once I have determined that it is open? And could you add the set up for if a place is open for parts of a day/ over midnight.

Thanks for all your help :)

解决方案

Storing your times as C structures creates a number of memory management headaches. I'd avoid that if you can (and I think you can). Instead, I recommend creating some new classes to help us out here.

First, we should think about these times as "nominal times within a week." By making these nominal times, we can explicitly get rid of DST concerns by saying that "1:30am on Sunday" means "the first instant that we would call 1:30am, no matter what DST transitions might happen to create another 1:30a." That's how shops usually work anyway, but it's important to be precise when thinking about time functions.

The great thing about "nominal times within a week" is that we can start counting minutes from Sunday at midnight, and we know that there will be exactly 60*24*7 (10,080) minutes in the week. We only really care here about minutes, so we just need to keep track of a number between 0 and 10,079. But we must remember that these numbers are subject to modular arithmetic (they "wrap around"). It is meaningless to ask whether Tuesday is before or after Wednesday. Tuesday is before and after Wednesday. But it's meaningful to ask whether Tuesday is between Monday (as a starting point) and Wednesday (as an ending point). We can determine if moving forward from Tuesday we will encounter Wednesday before we encounter Monday. If that's true, it's between. That's how you have to think about modular time.

OK, way too much theory. Let's look at some code (all the code is here). First we'd like a WeekTime to represent some time within a week:

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

The impl of this should be obvious so I won't paste it here.

Now we want to think about ranges of nominal week times. We'll make these open ranges, so they include their start time but not their end time. If a place closes at 9p, you want to return "Closed" at 9p, not "Closing in 1m."

@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

Most of this should be obvious, but contains: has one little trick to it. Since we're in modular arithmetic, it's legal for start to be "greater" than end (remembering that "greater" doesn't mean anything). That's easy to deal with:

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

(In my example I've chosen to make the range where start==end be undefined. If you want to assign some meaning to that, either meaning "all times" or "no times," then you may need to tweak this. Be careful of "all times," though. That really would mean the shop closes and immediately reopens, so you'll get "closing in..." messages. That's why I just made start==end be undefined.)

OK, finally we'll want a few helpers:

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";
    }
}

These shouldn't require much explanation. Let's see it in practice. You already seem to know how to turn dates into their components, so I'm going to just create a WeekTime directly and not worry about converting it from an NSDate.

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

I haven't tried tons of test cases, but my ad hoc tests seem to work. All the code is in the gist. One case I don't test for is whether your ranges overlap. The above algorithm doesn't care if the ranges are in order, but you may get incorrect "closing in..." messages if the ranges overlap. Resolving that (either by throwing an error, or by merging ranges) should be an pretty easy enhancement.

这篇关于从小时返回打开,关闭或关闭的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆