日历(一)原理

日历的实现原理

这一篇只说明日历实现的算法原理

首先我们想一下日历的样子 。在每一个月中都是28~31天 。每个月都是7列 。分为 周1~周日 。那么按照一个月的第一天是最后一列来看 ,这个月有31天 。那么这个月需要的矩阵块要 7X6个小格子来盛放。
现在有了日历展示的容器,剩下的就是每个格子需要展示什么内容了。
其实我们只需要关注每个月的第一天在这个月中的位置,然后从1~31(需要看各个月不通)来顺序填写到容器中就可以了 。其他格子可以空或者我们放上上个月(下个月)的数据就可以了 。


下边来解决我们的核心算法

怎么得到某天是星期几
先介绍一下在数学上最著名的计算公式
w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1

公式中的符号含义如下,w:星期;c:世纪-1;y:年(两位数);m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算);d:日;[ ]代表取整,即只要整数部分。(C是世纪数减一,y是年份后两位,M是月份,d是日数。1月和2月要按上一年的13月和 14月来算,这时C和y均按上一年取值。)

对于编程来说,我们不需要这么多变量,这么复杂的公式 。我们只需要知道元年的起点是周一就可以了 。
一周固定是七天 。现在问题变成需要计算的时间距离原点时间的天数 然后和一周的 七天 去余数 。就得到了周几 。(0 就是周日)


我们知道,公历的平年是365天,闰年是366天。置闰的方法是能被4整除的年份在2月加一天,但能被100整除的不闰,能被400整除的又闰。因此,像1600、2000、2400年都是闰年,而1700、1800、1900、2100年都是平年。公元前1年,按公历也是闰年。

因此,对于从公元前1年(或公元0年)12月31日到某一日子的年份Y之间的所有整年中的闰年数,就等于
[(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400],

第一项表示需要加上被4整除的年份数,第二项表示需要去掉被100整除的年份数,第三项表示需要再加上被400整除的年份数。之所以Y要减一,这
样,我们就得到了第一个计算某一天是星期几的公式:

W = (Y-1)*365 + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D

其中D是这个日子在这一年中的累积天数。算出来的W就是公元前1年(或公元0年)12月31日到这一天之间的间隔日数。把W用7除,余数是几,这一天就是星期几。

对于一年中的一个时间是这年中的第几天的计算

这个就需要了解iOS的一个类 。NSCalendar
- (NSUInteger)ordinalityOfUnit: (NSCalendarUnit)smaller inUnit:(NSCalendarUnit)larger forDate:(NSDate *)date;
我们大致可以理解为:某个时间点所在的“小单元”,在“大单元”中的位置(从1开始)

NSInteger aa = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitYear forDate:[NSDate date]];

这样就计算出了这[NSDate date]这个时间下,是这中的第几

以上就解决了我们的对于日历的算法需求。

只需要输入需要计算的时间NSDate以及转化的四位年份就可以顺利的填满我们准备好的格子容器 。布局一个月的日历。

以下是基本的代码实现

- (NSInteger )weakDayForFirstDayOfMonth
{
    NSInteger numOfThisYear = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitYear forDate:[self firstWeekDayInMonth]];
    
    NSInteger year = [self componentsOfDay:[self firstWeekDayInMonth]].year;
    NSInteger W = (year-1)*365 + ((year-1)/4) - ((year-1)/100) + ((year-1)/400) + numOfThisYear;
    return  W % 7;
}

//生成当前月的1号的时间 。
-(NSDate *)firstWeekDayInMonth {
    
    NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT+0800"]];
     NSDateComponents *comps = [gregorian components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
    // 设置成每个月的1号 。
    [comps setDay:1];
    NSDate *newDate = [gregorian dateFromComponents:comps];
    
    // 消除中国时区的影响
    return [newDate dateByAddingTimeInterval:8*60*60];
    
}

在这里有个疑问 。我设置timeZone时区 ,并没有影响calendar的输出 。利用formatter是可以正确打印当前时区的时间字符串 ,timeZone也能影响这个打印 。但是calendar并不行 。
取每个月的一月的作用是:我们的日历不是做一个月的,也需要上一个月,下一个月的嘛,所以为了避免当前时间是30(31)有可能下个月没有这天的情况,统一取第一天

//生成当前时间的component   后续可以获得 时间组成 。
- (NSDateComponents *)componentsOfDay:(NSDate *)date
{
     NSDateComponents *dateComponents = nil;
     NSDate *previousDate = nil;
     NSCalendar *greCalendar;
    if (!greCalendar) {
        greCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    }
    
    if (!previousDate || ![previousDate isEqualToDate:date]) {
        previousDate = date;
        dateComponents = [greCalendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit | NSWeekCalendarUnit | NSWeekOfMonthCalendarUnit | NSWeekOfYearCalendarUnit| NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:date];
    }
    
    return dateComponents;
}

最后的这个是获取时间的组成 。也就是一个date的零件

可以作为NSDate的分类使用。
日历资料参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341

推荐阅读更多精彩内容