最近开发的产品中,有很多日历相关的需求,所以整理一下开发日历相关的东西。
1、时间戳转换
2、UI实现
3、第三方库推荐
4、值得注意的坑
1、时间戳转换
在开发日历相关功能的时候,后台与前端肯定是通过时间戳来通信,所以当我们遇到了有关日历的需求的时候,首先要准备一个工具类,把时间戳和时间信息来回转化。同时要注意:java的时间戳一般是毫秒级的,OC处理的时间戳是秒级的,所以后台传过来的时间戳要注意除1000。
调接口的时候,可以使用时间戳转换网站来确认两端参数或数值的准确。
下面是我使用的NSDate分类,基本可以满足所有的需求了。
NSDate+Extension.h:
#import <Foundation/Foundation.h>
@interface NSDate (Extensions)
/// 获取年
+ (NSInteger)year:(NSString *)dateStr;
/// 获取月
+ (NSInteger)month:(NSString *)dateStr;
/// 获取星期
+ (NSInteger)week:(NSString *)dateStr;
/// 获取星期 中文 日
+ (NSString *)getWeekFromDate:(NSDate *)date;
/// 获取星期 中文 周日
+ (NSString *)getChineseWeekFrom:(NSString *)dateStr;
/// 获取日
+ (NSInteger)day:(NSString *)dateStr;
/// 获取月共有多少天
+ (NSInteger)daysInMonth:(NSString *)dateStr;
/// 获取当前日期 2018-01-01
+ (NSString *)currentDay;
/// 获取当前小时 00:00
+ (NSString *)currentHour;
/// 获取下月最后一天
+ (NSString *)nextMonthLastDay;
/// 判断是否是今天
+ (BOOL)isToday:(NSString *)dateStr;
/// 判断是否是明天
+ (BOOL)isTomorrow:(NSString *)dateStr;
/// 判断是否是后天
+ (BOOL)isAfterTomorrow:(NSString *)dateStr;
/// 判断是否是过去的时间
+ (BOOL)isHistoryTime:(NSString *)dateStr;
/// 从时间戳获取具体时间 格式:6:00
+ (NSString *)hourStringWithInterval:(NSTimeInterval)timeInterval;
/// 从时间戳获取具体小时 格式:6
+ (NSString *)hourTagWithInterval:(NSTimeInterval)timeInterval;
/// 从毫秒级时间戳获取具体小时 格式:600
+ (NSString *)hourNumberWithInterval:(NSTimeInterval)timeInterval;
/// 从时间戳获取具体日期 格式:2018-03-05
+ (NSString *)timeStringWithInterval:(NSTimeInterval)timeInterval;
/// 从具体日期获取时间戳 毫秒
+ (NSTimeInterval)timeIntervalFromDateString:(NSString *)dateStr;
/// 获取当前天的后几天的星期
+ (NSString *)getWeekAfterDay:(NSInteger)day;
/// 获取当前天的后几天的日
+ (NSString *)getDayAfterDay:(NSInteger)day;
/// 获取当前月的后几月
+ (NSString *)getMonthAfterMonth:(NSInteger)Month;
@end
/*
G: 公元时代,例如AD公元
yy: 年的后2位
yyyy: 完整年
MM: 月,显示为1-12
MMM: 月,显示为英文月份简写,如 Jan
MMMM: 月,显示为英文月份全称,如 Janualy
dd: 日,2位数表示,如02
d: 日,1-2位显示,如 2
EEE: 简写星期几,如Sun
EEEE: 全写星期几,如Sunday
aa: 上下午,AM/PM
H: 时,24小时制,0-23
K:时,12小时制,0-11
m: 分,1-2位
mm: 分,2位
s: 秒,1-2位
ss: 秒,2位
S: 毫秒
Z:GMT
*/
NSDate+Extension.m:
#import "NSDate+Extensions.h"
@implementation NSDate (Extensions)
/// 获取年
+ (NSInteger)year:(NSString *)dateStr {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *startDate = [dateFormatter dateFromString:dateStr];
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:startDate];
return components.year;
}
/// 获取月
+ (NSInteger)month:(NSString *)dateStr {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *startDate = [dateFormatter dateFromString:dateStr];
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:startDate];
return components.month;
}
/// 获取星期
+ (NSInteger)week:(NSString *)dateStr {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *startDate = [dateFormatter dateFromString:dateStr];
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:startDate];
return components.weekday - 1;
}
/// 获取星期 中文
+ (NSString *)getWeekFromDate:(NSDate *)date {
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:date];
NSInteger week = components.weekday - 1;
NSDictionary *weekDic = @{@"0":@"日",@"1":@"一",@"2":@"二",@"3":@"三",@"4":@"四",@"5":@"五",@"6":@"六"};
NSString *key = [NSString stringWithFormat:@"%ld",(long)week];
return weekDic[key];
}
/// 获取星期中文
+ (NSString *)getChineseWeekFrom:(NSString *)dateStr {
NSDictionary *weekDic = @{@"0":@"周日",@"1":@"周一",@"2":@"周二",@"3":@"周三",@"4":@"周四",@"5":@"周五",@"6":@"周六"};
NSInteger week = [NSDate week:dateStr];
NSString *weekKey = [NSString stringWithFormat:@"%ld",(long)week];
return weekDic[weekKey];
}
/// 获取日
+ (NSInteger)day:(NSString *)dateStr {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *startDate = [dateFormatter dateFromString:dateStr];
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:startDate];
return components.day;
}
/// 获取月共有多少天
+ (NSInteger)daysInMonth:(NSString *)dateStr {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[NSDate timeIntervalFromDateString:dateStr] / 1000];
NSRange daysInLastMonth = [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
return daysInLastMonth.length;
}
//获取当前日期
+ (NSString *)currentDay {
NSDateFormatter *formater = [[ NSDateFormatter alloc] init];
NSDate *date = [NSDate date];
[formater setDateFormat:@"yyyy-MM-dd"];
NSString * time = [formater stringFromDate:date];
return time;
}
//获取当前小时
+ (NSString *)currentHour {
NSDateFormatter *formater = [[ NSDateFormatter alloc] init];
NSDate *curDate = [NSDate date];
[formater setDateFormat:@"H:mm"];
NSString * curTime = [formater stringFromDate:curDate];
return curTime;
}
//找到两个月后的第一天~ 然后通过减一天来找到下个月的最后一天,所以,下月最后一天
+ (NSString *)nextMonthLastDay {
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents *dateComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
//设置日为1号
dateComponents.day =1;
//设置月份为后延2个月
dateComponents.month +=2;
NSDate * endDayOfNextMonth = [calendar dateFromComponents:dateComponents];
//两个月后的1号往前推1天,即为下个月最后一天
endDayOfNextMonth = [endDayOfNextMonth dateByAddingTimeInterval:-1];
//格式化输出
NSDateFormatter *formater = [[ NSDateFormatter alloc] init];
[formater setDateFormat:@"yyyy-MM-dd"];
NSString * curTime = [formater stringFromDate:endDayOfNextMonth];
return curTime;
}
///判断是否是今天
+ (BOOL)isToday:(NSString *)dateStr {
BOOL isDay = NO;
NSString *day = [NSDate timeStringWithInterval:[NSDate date].timeIntervalSince1970];
if ([dateStr isEqualToString:day]) {
isDay = YES;
}
return isDay;
}
///判断是否是明天
+ (BOOL)isTomorrow:(NSString *)dateStr {
BOOL isDay = NO;
NSTimeInterval time = [NSDate date].timeIntervalSince1970 + 24 * 3600;
NSString *day = [NSDate timeStringWithInterval:time];
if ([dateStr isEqualToString:day]) {
isDay = YES;
}
return isDay;
}
///判断是否是后天
+ (BOOL)isAfterTomorrow:(NSString *)dateStr {
BOOL isDay = NO;
NSTimeInterval time = [NSDate date].timeIntervalSince1970 + 48 * 3600;
NSString *day = [NSDate timeStringWithInterval:time];
if ([dateStr isEqualToString:day]) {
isDay = YES;
}
return isDay;
}
/// 判断是否是过去的时间
+ (BOOL)isHistoryTime:(NSString *)dateStr {
BOOL activity = NO;
NSTimeInterval timeInterval = [NSDate timeIntervalFromDateString: dateStr];
NSTimeInterval currentInterval = [NSDate timeIntervalFromDateString:[NSDate currentDay]];
if (timeInterval < currentInterval) {
activity = YES;
}
return activity;
}
/// 从时间戳获取具体时间 格式:6:00
+ (NSString *)hourStringWithInterval:(NSTimeInterval)timeInterval {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"H:mm"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSString *dateString = [dateFormatter stringFromDate:date];
return dateString;
}
/// 从时间戳获取具体小时 格式:6
+ (NSString *)hourTagWithInterval:(NSTimeInterval)timeInterval {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"H"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSString *dateString = [dateFormatter stringFromDate:date];
return dateString;
}
/// 从毫秒级时间戳获取具体小时 格式:600
+ (NSString *)hourNumberWithInterval:(NSTimeInterval)timeInterval {
NSString *hourStr = [self hourStringWithInterval:timeInterval / 1000];
NSString *hourNumber = [hourStr stringByReplacingOccurrencesOfString:@":" withString:@""];
return hourNumber;
}
/// 从时间戳获取具体日期 格式:2018-03-05
+ (NSString *)timeStringWithInterval:(NSTimeInterval)timeInterval {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSString *dateString = [dateFormatter stringFromDate:date];
return dateString;
}
/// 根据具体日期获取时间戳(毫秒)
+ (NSTimeInterval)timeIntervalFromDateString:(NSString *)dateStr {
//要精确到毫秒2018-01-01 与 2018-01-01 00:00 都要转换成2018-01-01 00:00:00
if (dateStr.length == 10) {
dateStr = [dateStr stringByAppendingString:@" 00:00:00"];
} else if (dateStr.length == 16) {
dateStr = [dateStr stringByAppendingString:@":00"];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSDate *date = [dateFormatter dateFromString:dateStr];
NSTimeInterval interval = [date timeIntervalSince1970] * 1000;
return interval;
}
/// 获取当前天的后几天的星期
+ (NSString *)getWeekAfterDay:(NSInteger)day {
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:[NSDate date]];
NSInteger currentWeek = components.weekday - 1;
NSDictionary *weekDic = @{@"0":@"日",@"1":@"一",@"2":@"二",@"3":@"三",@"4":@"四",@"5":@"五",@"6":@"六"};
NSInteger week = currentWeek + day;
if (week >= 7) {
week -= 7;
}
NSString *key = [NSString stringWithFormat:@"%ld",(long)week];
return weekDic[key];
}
/// 获取当前天的后几天的日
+ (NSString *)getDayAfterDay:(NSInteger)day {
NSTimeInterval time = [NSDate date].timeIntervalSince1970 + 24 * 3600 * day;
NSString *date = [NSDate timeStringWithInterval:time];
NSInteger dayNum = [self day:date];
NSString *dayStr = [NSString stringWithFormat:@"%ld",(long)dayNum];
return dayStr;
}
/// 获取当前月的后几月
+ (NSString *)getMonthAfterMonth:(NSInteger)Month {
NSDate *currentDate = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM"];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *lastMonthComps = [[NSDateComponents alloc] init];
// [lastMonthComps setYear:1]; // year = 1表示1年后的时间 year = -1为1年前的日期,month day 类推
[lastMonthComps setMonth:Month];
NSDate *newdate = [calendar dateByAddingComponents:lastMonthComps toDate:currentDate options:0];
NSString *dateStr = [formatter stringFromDate:newdate];
return dateStr;
}
@end
2、UI实现
接下来讲下页面构成,其他的实现起来都差不多,主要是利用CollectionView
蓝色星期视图是个独立的View,红色是CollectionView,有自定义的组头UICollectionReusableView和cell
首先要设计个model,满足我们展示视图的需要,比如CalendarModel,这个model属性看情况自定义:
CalendarModel.h:
#import <Foundation/Foundation.h>
//哪一天
typedef NS_ENUM(NSInteger, DayType) {
Other = 0,
Today, /**< 今天 */
Tomorrow, /**< 明天 */
AfterTomorrow /**< 后天 */
};
@interface ReplaceCalendarModel : NSObject
@property (nonatomic, strong) NSString *time; /**< 时间戳 */
@property (nonatomic, strong) NSString *year;
@property (nonatomic, strong) NSString *month;
@property (nonatomic, strong) NSString *day;
@property (nonatomic, strong) NSString *week;
@property (nonatomic, assign) DayType dayType; /**< 是否是今天,明天,后天 */
@property (nonatomic, assign) BOOL isSelected; /**< 是否被选择 */
@end
组头UICollectionReusableView就一个Label就可以了,展示年月,cell就是两个Label,展示日和其他信息,然后根据model来展示组头和cell就可以了,这两个实现起来比较简单,比较复杂的是collectionView的数据源处理,接下来我主要记下这个
首先看页面要展示多少个月的日期,像这个页面展示的是当前月当前日到下一个月月底的时间,在initWithFrame
里面初始化CollectionView的数据源:
//今天的日期为开始和下个月最后一日为结束
self.dataSource = [NSMutableArray array];
//当前月
NSMutableArray *currentMonth = [self createMonthDataInday:[NSDate currentDay]];
//下个月
NSMutableArray *nextMonth = [self createMonthDataInday:[NSDate nextMonthLastDay]];
//初始化数据源的组
[self.dataSource addObject:currentMonth];
[self.dataSource addObject:nextMonth];
//根据某一日数据初始化该月数据源
- (NSMutableArray *)createMonthDataInday:(NSString *)dateStr {
NSMutableArray *monthData = [NSMutableArray array];
NSString *yearStr = [NSString stringWithFormat:@"%ld",[NSDate year:dateStr]];
NSString *monthStr = [NSString stringWithFormat:@"%ld",[NSDate month:dateStr]];
NSString *monthStr02 = [NSString stringWithFormat:@"%02ld",[NSDate month:dateStr]];
//第一天的星期
NSInteger firstDayWeek = [NSDate week:[NSString stringWithFormat:@"%@-%@-01",yearStr,monthStr]];
//如果是当前月,"第一天星期几"就以今天的星期为开始
if ([NSDate isToday:dateStr]) {
firstDayWeek = [NSDate week:dateStr];
}
//这个月的天数
NSInteger days = [NSDate daysInMonth:dateStr];
//每天的数字
for (int i = 0; i < 42; i++) {
CalendarModel *model = [[CalendarModel alloc] init];
model.year = yearStr;
model.month = monthStr;
model.dayType = Other;
//在第一天之前的格子为空
if (i < firstDayWeek) {
model.day = @"";
} else if ( i > days + firstDayWeek - 1) {
//最后一天之后的,不添加进数据源
continue;
} else {
//其他的都是有效数据
model.day = [NSString stringWithFormat:@"%ld", i - firstDayWeek + 1];
model.date = [NSString stringWithFormat:@"%@-%@-%@",yearStr,monthStr02,[NSString stringWithFormat:@"%02ld", i - firstDayWeek + 1]];
//如果是过去的时间,就跳出循环,不添加进数据源
if ([NSDate isHistoryTime:model.date]) {
continue;
}
//今天明天后天
if ([NSDate isToday:model.date]) {
model.dayType = Today;
model.isSelected = YES;
} else if ([NSDate isTomorrow:model.date]) {
model.dayType = Tomorrow;
} else if ([NSDate isAfterTomorrow:model.date]) {
model.dayType = AfterTomorrow;
}
}
[monthData addObject:model];
}
return monthData;
}