Objective-C的NSDate学习笔记

NSDate - 日期类

NSDate是Foundation框架中表示日期的类,用于保存时间值的一个OC类,NSDate对象封装了一个时间点,独立于任何特定的日历系统或时区。日期对象是不可变的,表示相对于绝对参考日期(2001 年 1 月 1 日 00:00:00 UTC(协调世界时))的不变时间间隔。

NSDate类提供了比较日期、计算两个日期之间的时间间隔以及从相对于另一个日期的时间间隔创建新日期的方法。 NSDate对象可以与NSDateFormatter对象结合使用来创建日期和时间的本地化表示,以及与NSCalendar对象一起执行日历算术。

NSDate常用属性
@property (readonly) NSTimeInterval timeIntervalSinceReferenceDate;

属性描述NSDate对象与系统的绝对参考日期(2001年1月1日00:00:00 UTC(协调世界时) )之间的时间间隔。如果日期对象早于系统的绝对参考日期则此属性的值为负数。

NSDate常用函数
- (instancetype)init NS_DESIGNATED_INITIALIZER;

函数描述返回一个初始化为当前日期和时间的日期对象。此方法是NSDate的指定初始化程序。

返回值 :初始化为当前日期和时间的NSDate对象。

- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;

函数描述返回相对于系统的绝对参考日期(2001年1月1日00:00:00 UTC(协调世界时) )经过给定秒数初始化的日期对象。此方法是NSDate类的指定初始化程序,主要声明用于NSDate的子类。 当你继承NSDate来创建一个具体的日期类时,必须重写这个方法。

参数 :

ti :添加到系统的绝对参考日期(2001年1月1日00:00:00 UTC(协调世界时) )的秒数。 负值表示调用方将早于绝对参考日期。

返回值 :一个相对于系统的绝对参考日期经过指定秒数为单位初始化的NSDate对象。

NSDate (NSExtendedDate) - 延期日期分类对NSDate的扩展

NSDate (NSExtendedDate)常用属性
@property (readonly) NSTimeInterval timeIntervalSinceNow;

属性描述NSDate对象与当前日期和时间之间的间隔。如果NSDate对象早于当前日期和时间,则此属性的值为负。

@property (readonly) NSTimeInterval timeIntervalSince1970;

属性描述NSDate对象与 1970年1月1日00:00:00 UTC 之间的时间间隔。如果NSDate对象早于 1970年1月1日00:00:00 UTC,则此属性的值为负数。

@property (readonly, copy) NSString *description;

属性描述NSDate对象的字符串表示形式。该表示形式仅对调试有用。

@property (class, readonly) NSTimeInterval timeIntervalSinceReferenceDate;

属性描述系统的绝对参考日期(2001年1月1日00:00:00 UTC(协调世界时) )与当前日期和时间之间的间隔。此方法是NSDate的原始方法,如果继承NSDate,必须用自己的实现覆盖这个方法。

NSDate (NSExtendedDate)常用函数
- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate;

函数描述:返回调用方与另一个给定日期之间的间隔。

参数 :

anotherDate :与调用方进行比较的日期。 必须传递一个非nil的NSDate对象。

返回值 :调用方与anotherDate参数之间的间隔。 如果调用方早于另一个日期,则返回值为负。 如果anotherDate为nil,则结果未定义。

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDate *date = [NSDate date];
    NSDate *date2 = [NSDate dateWithTimeIntervalSinceNow:10];
    NSLog(@"%f",[date2 timeIntervalSinceDate:date]);
}

打印如下:

截屏2019-11-12下午10.34.37.png
- (instancetype)dateByAddingTimeInterval:(NSTimeInterval)ti API_AVAILABLE(macos(10.6), ios(2.0), watchos(2.0), tvos(9.0));

函数描述:返回一个新的NSDate对象,该对象设置为相对于调用方经过了给定秒数。

参数 :

ti:添加到调用方的秒数。 使用负值表示秒数,会使返回的对象指定为调用方之前的日期。

返回值 :一个新的NSDate对象,相对于调用方经过了ti秒。 返回的日期可能具有与调用方不同的表示。

- (NSDate *)earlierDate:(NSDate *)anotherDate;

函数描述:返回调用方和anotherDate参数日期两者相比中的较早者。

参数 :

anotherDate:与调用方进行比较的日期对象。

返回值:调用方和anotherDate参数日期两者中的较早日期,使用timeIntervalSinceDate:确定。如果调用方和anotherDate参数日期代表相同的日期,则返回调用方。

- (NSDate *)laterDate:(NSDate *)anotherDate;

函数描述:返回调用方和anotherDate参数日期两者相比中的较晚者。

参数 :

anotherDate:与调用方进行比较的日期对象。

返回值:调用方和anotherDate参数日期两者中的较晚日期,使用timeIntervalSinceDate:确定。如果调用方和anotherDate参数日期代表相同的日期,则返回调用方。

- (NSComparisonResult)compare:(NSDate *)other;

函数描述指示调用方和other参数日期的时间顺序。此方法检测日期之间的亚秒级差异,如果要比较粒度较小的日期,请使用 timeIntervalSinceDate: 比较两个日期。

参数 :

other:与调用方进行比较的日期。该值不能为nil,如果值为nil 则行为未定义。

返回值 :调用方和anotherDate完全相等,返回NSOrderedSame;调用方在时间上晚于other参数日期,返回NSOrderedDescending;调用方在时间上早于other参数日期,返回NSOrderedAscending。

- (int)compareOneDay:(NSDate *)oneDay withAnotherDay:(NSDate *)anotherDay{
    
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    
    [dateFormatter setDateFormat:@"dd-MM-yyyy"];
    
    NSString *oneDayStr = [dateFormatter stringFromDate:oneDay];
    
    NSString *anotherDayStr = [dateFormatter stringFromDate:anotherDay];
    
    NSDate *dateA = [dateFormatter dateFromString:oneDayStr];
    
    NSDate *dateB = [dateFormatter dateFromString:anotherDayStr];
    
    NSComparisonResult result = [dateA compare:dateB];
    
    if (result == NSOrderedDescending) {
        //NSLog(@"oneDay比 anotherDay时间晚");
        return 1;
    }
    else if (result == NSOrderedAscending){
        //NSLog(@"oneDay比 anotherDay时间早");
        return -1;
    }
    //NSLog(@"两者时间是同一个时间");
    return 0;
}

- (BOOL)isEqualToDate:(NSDate *)otherDate;

函数描述返回一个布尔值,指示otherDate参数日期对象是否是与调用方完全相等的日期。此方法检测日期之间的亚秒级差异,如果要比较粒度较小的日期,请使用 timeIntervalSinceDate: 比较两个日期。

参数 :

otherDate :与调用方比较的日期。

返回值:如果otherDate参数是一个NSDate对象并且完全等于调用方,则为 YES,否则为 NO。

- (NSString *)descriptionWithLocale:(nullable id)locale;

函数描述:使用给定的语言环境返回日期的字符串表示形式。

参数 :

locale:一个NSLocale对象。

返回值 :调用方的字符串表示,使用给定的语言环境,或者如果语言环境参数为 nil,则采用国际格式 YYYY-MM-DD HH:MM:SS ±HHMM,其中 ±HHMM 表示时区偏移量,以小时和分钟为单位 UTC(协调世界时)时间(例如,“2001-03-24 10:45:32 +0600”)。

NSDate (NSDateCreation) - 日期创建分类对NSDate的扩展

NSDate (NSDateCreation)常用属性
@property (class, readonly, copy) NSDate *distantFuture;

属性描述表示遥远未来日期的NSDate对象(以世纪为单位)。可以使用distinctFuture属性返回的对象作为某个函数的日期参数来无限期地等待事件发。

@property (class, readonly, copy) NSDate *distantPast;

属性描述:表示遥远过去日期的NSDate对象(以世纪为单位)。

@property (class, readonly, copy) NSDate *now API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));

属性描述截至访问时的当前日期和时间。这相当于用 [[NSDate alloc] init] 初始化一个新实例。

NSDate (NSDateCreation)常用函数
+ (instancetype)date;

函数描述:创建并返回已经设置为当前日期和时间的新NSDate对象,以UTC(协调世界时)为时区,此方法使用类的默认初始化方法 init。

返回值 :设置为当前日期和时间的新NSDate对象。

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDate *date = [NSDate date];
    NSLog(@"%@",date);
}

当前NSDate对象打印的时间:

截屏2019-11-12下午10.16.12.png

当前NSDate对象的实际时间:

截屏2019-11-12下午10.17.27.png
+ (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;

函数描述:创建并返回一个NSDate对象,该对象设置为距当前日期和时间经过了的给定秒数。

参数 :

secs : 从当前日期和时间到新日期经过的秒数,使用负值会指定当前日期之前的日期。

返回值 :从当前日期和时间设置为经过seconds秒后的NSDate对象。

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDate *date = [NSDate date];
    NSDate *date2 = [NSDate dateWithTimeIntervalSinceNow:10];
    NSLog(@"%@-%@",date,date2);
}

两者之间相差10秒:

截屏2019-11-12下午10.23.55.png
+ (instancetype)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti;

函数描述:创建并返回一个NSDate对象,该对象设置为从系统的绝对参考日期(2001年1月1日00:00:00 UTC(协调世界时) )开始的经过给定秒数后的时间和日期。

参数 :

ti:要从系统的绝对参考日期(2001年1月1日00:00:00 UTC(协调世界时) )开始经过的秒数。使用负值会指定参考日期之前之前的日期和时间。

返回值 :从系统的绝对参考日期经过ti秒后的NSDate对象。

+ (instancetype)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;

函数描述:创建并返回一个NSDate对象,该对象设置为从1970 年1月1日 00:00:00 UTC 开始的经过给定秒数后的时间和日期。

参数 :

secs:要从1970 年1月1日 00:00:00 UTC 开始的经过的秒数。使用负值会指定参考日期之前之前的日期和时间。

返回值 :从1970 年1月1日 00:00:00 UTC 经过secs秒后的NSDate对象。

//  根据时间间隔和指定的date,获得对应的时间
+ (instancetype)dateWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;

函数描述:创建并返回一个NSDate对象,该对象的日期和时间设置为从指定NSDate参数对象开始的日期和时间经过给定的秒数。

参数 :

secsToBeAdded :要从NSDate参数对象开始的经过的秒数。使用负值会指定NSDate参数对象日期之前之前的日期和时间。

date :NSDate参数对象。

返回值 :从NSDate参数对象开始的经过secsToBeAdded秒后的NSDate对象。

//平年365天一年有8760小时,525600分钟,31536000秒。闰年366天一年有8784小时,527040分钟,31622400秒
- (void)viewDidLoad {
    [super viewDidLoad];
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:0];
    NSDate *date2 = [NSDate dateWithTimeInterval:31536000 sinceDate:date];
    NSLog(@"%@",date2);
}

相差一年:

截屏2019-11-12下午10.41.58.png

NSDateFormatter - NSDate与NSDate格式化文本表示之间相互转换的对象

派生自NSFormatter,是在NSDate和NSDate的格式化文本表示之间进行转换的对象。NSDateFormatter的实例可以创建NSDate对象的字符串表示,也可以将日期和时间的文本表示转换为 NSDate 对象。对于用户可见的日期和时间表示,NSDateFormatter提供了各种本地化预设和配置选项,对于日期和时间的固定格式表示,也可以指定自定义格式字符串。

NSDateFormatter常用属性
@property (null_resettable, copy) NSString *dateFormat;

属性描述调用方要使用的以字符串表示日期的字符串格式,应该只在使用固定格式表示时设置此属性。

@property NSDateFormatterStyle dateStyle;

属性描述:调用方的日期格式。

@property NSDateFormatterStyle timeStyle;

属性描述:调用方的时间格式。

  • NSDateFormatterStyle枚举的日期格式如下:
typedef NS_ENUM(NSUInteger, NSDateFormatterStyle) {    // date and time format styles
    //没有指定的格式
    NSDateFormatterNoStyle = kCFDateFormatterNoStyle,
    //指定一个简短的样式,通常只有数字,例如日期“11/23/37”,时间“3:30”。
    NSDateFormatterShortStyle = kCFDateFormatterShortStyle,
    //指定中等样式,通常使用缩写文本,例如日期“Nov 23, 1937”,时间“3:30:32
    NSDateFormatterMediumStyle = kCFDateFormatterMediumStyle,
    //指定长样式,通常带有全文,例如日期“November 23, 1937”,时间“3:30:32 GMT+8”。
    NSDateFormatterLongStyle = kCFDateFormatterLongStyle,
    //指定具有完整详细信息的完整样式,例如日期“Wednesday, September 7, 2022“,
    //例如时间“16:03:04 China Standard Time“。
    NSDateFormatterFullStyle = kCFDateFormatterFullStyle
};
@property (null_resettable, copy) NSLocale *locale;

属性描述:调用方的区域设置。

@property (null_resettable, copy) NSTimeZone *timeZone;

属性描述调用方的时区,如果未指定,则使用系统时区。

@property (null_resettable, copy) NSCalendar *calendar;

属性描述调用方的日历,如果未指定,则使用当前用户的逻辑日历。

NSDateFormatter常用函数
- (NSString *)stringFromDate:(NSDate *)date;

函数描述返回指定日期的字符串表示形式,系统使用调用方的当前设置来格式化该日期。

参数 :

date : 要格式化的NSDate日期对象。

返回值 : NSDate日期的字符串表示形式。

- (nullable NSDate *)dateFromString:(NSString *)string;

函数描述返回指定字符串的NSDate日期表示形式,系统使用调用方的当前设置格式解释该字符串。

参数 :

string :要解析的字符串。

返回值:字符串日期的NSDate表示形式,如果该函数无法解析字符串,则返回nil。

//将当前时间的时间戳转为字符串
NSString *timeStr = [self timestampToString:[NSDate date].timeIntervalSince1970];
NSLog(@"%@",timeStr);

- (NSString *)timestampToString:(NSTimeInterval)time {
    //得到Date类型的时间,这个时间是1970-1-1 00:00:00经过你时间戳的秒数之后的时间
    NSDate * detaildate = [NSDate dateWithTimeIntervalSince1970:time];
    //实例化NSDateFormatter对象
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    //设定时间格式,这里可以设置成自己需要的格式
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    //转换成字符串
    NSString * currentDateStr = [dateFormatter stringFromDate:detaildate];
    //返回字符串
    return currentDateStr;
}
屏幕快照 2019-01-30 下午11.29.51.png

NSString *dateStr = @"2019-11-11 10:56:30";
NSDate *date= [self nsstringConversionNSDate:dateStr];
NSLog(@"%@",date);

///字符串转Date时间
- (NSDate *)nsstringConversionNSDate:(NSString *)dateStr {
    //实例化NSDateFormatter对象
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    //设定时间格式,这里可以设置成自己需要的格式
    [dateFormatter setDateFormat:@"YYYY-MM-dd hh:mm:ss"];
    //转换成NSDate
    NSDate *date = [dateFormatter dateFromString:dateStr];
    //返回NSDate对象
    return date;
}

结果在使用时要注意时间差:

截屏2019-11-12下午11.00.17.png

注:YYYY-MM-dd 和yyyy-MM-dd区别:

YYYY 是按照周来计算时间,一周从周日开始,周六结束,例如2019年12月29号周日,以这个时间计算的方式,一年当中的时间,不足一周的(年末那一周),就要计算到下一年中去,就变成了2020年了,而月份与天数保持的是正确的。

yyyy是按照天来计算的,今天是12月29号,也是2019年,符合中国人的计算方式,平常计算中还是最好使用yyyy-MM-dd。

- (void)viewDidLoad {
    [super viewDidLoad];
    //2019-08-20 21:14:40 的时间戳
    NSInteger timeStamp1 = 1566306880;
    //2019-08-12 21:14:40 的时间戳
    NSInteger timeStamp2 = 1565615680;
    //时间差
    NSString *timeDifference = [NSString stringWithFormat:@"%ld",timeStamp1 - timeStamp2];
    NSLog(@"%@",[NSString stringWithFormat:@"%@",[self getOvertime:timeDifference]]);
}

//将时间戳(秒)转换xx天xx小时xx分xx秒形式的字符串
- (NSString*)getOvertime:(NSString*)mStr
{
    long msec = [mStr longLongValue];
    
    if (msec <= 0)
    {
        return @"";
    }
    
    NSInteger days = (int)(msec / (3600 * 24));
    NSInteger hours = (int)((msec - days * 24 * 3600) / 3600);
    NSInteger minutes = (int)(msec - days * 24 * 3600 - hours * 3600) /60;
    NSInteger seconds = (int)msec - days * 24 * 3600 - hours * 3600 - minutes * 60;
    
    
    NSString *timeStr = @"";
    NSString *daysStr = @"";
    NSString *hoursStr = @"";
    NSString *minutesStr = @"";
    NSString *secondsStr = @"";
    
    if (days >= 0)
    {
        daysStr = [NSString stringWithFormat:@"%ld天",(long)days];
    }
    
    if (hours >= 0)
    {
        hoursStr = [NSString stringWithFormat:@"%ld小时",(long)hours];
    }
    
    if (minutes >= 0)
    {
        minutesStr = [NSString stringWithFormat:@"%ld分",(long)minutes];
    }
    
    if (seconds >= 0)
    {
        secondsStr = [NSString stringWithFormat:@"%ld秒",(long)seconds];
    }
    timeStr = [NSString stringWithFormat:@"%@%@%@%@",daysStr,hoursStr,minutesStr,secondsStr];
    
    return timeStr;
}

运行结果:

屏幕快照 2019-08-12 下午11.19.05.png

NSTimeZone - 时区

NSTimeZone派生自NSObject,有关与特定地缘政治区域相关的标准时间约定的信息。时区代表地缘政治区域的标准时间策略。时区具有诸如“America/Los_Angeles”之类的标识符,也可以通过缩写来识别,例如太平洋标准时间的 PST。可以使用 initWithName: 通过 ID 创建时区对象或使用 timeZoneWithAbbreviation: 通过缩写创建时区对象。

时区数据库条目是ID ,而不是名称。例如太平洋夏令时间,它的ID是“America/Los_Angeles”,它的名称是“Pacific Daylight time”。尽管许多 NSTimeZone 符号包含“name”一词,但它们实际上指的是 ID。

时区还可以表示与格林威治标准时间 (GMT) 的时间偏移(正或负)。例如太平洋标准时间的时间偏移比格林威治标准时间 (GMT-8) 晚 8 小时。您可以使用timeZoneForSecondsFromGMT: 创建具有时间偏移的时区对象。

通常使用系统时区,而不是按标识符或偏移量创建时区类属性systemTimeZone返回系统当前使用的时区(如果已知),一旦访问该属性,该值就会被缓存,并且在调用resetSystemTimeZone方法之前不会反映任何系统时区更改。 类属性localTimeZone返回一个自动更新代理对象,该对象始终返回系统使用的当前时区。还可以设置类属性defaultTimeZone,以使应用程序像在与系统不同的时区一样运行。

NSTimeZone常用属性
@property (readonly, copy) NSString *name;

属性描述 :标识调用方的地缘政治区域ID。

@property (readonly, copy) NSData *data;

属性描述 :存储调用方使用的信息的数据,此数据视为不透明对象。

NSTimeZone常用函数
- (NSInteger)secondsFromGMTForDate:(NSDate *)aDate;

函数描述返回调用方给定日期与格林威治标准时间之间相差的秒数。如果时区在一年中的不同时间点改变其与GMT(格林威治标准时间)的偏移量,则可能导致获取的秒差有差异如美国时区随夏令时而改变。

参数 :

aDate : 调用方给定的日期。

返回值 :调用方给定的日期与格林威治标准时间之间的秒差。

- (nullable NSString *)abbreviationForDate:(NSDate *)aDate;

函数描述返回给定日期基于调用方的缩写。需要注意的是不同日期的缩写可能不同,例如在夏令时期间,美国/东部时区的缩写为“EDT”,在其他时候它的缩写则是“EST”。

参数 :

aDate:基于调用方缩写的日期。

返回值:aDate参数基于调用方的缩写。

- (BOOL)isDaylightSavingTimeForDate:(NSDate *)aDate;

函数描述:指示调用方是否在给定日期使用夏令时。

参数 :

aDate:调用方给定日期。

返回值 :如果调用方在aDate参数给定日期使用夏令时,则为 YES,否则为 NO。

- (NSTimeInterval)daylightSavingTimeOffsetForDate:(NSDate *)aDate API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

函数描述:返回给定日期的夏令时偏移量。

参数 :

aDate :调用方给定日期。

返回值 :调用方在aDate参数给定日期使用夏令时的夏令时偏移量。

- (nullable NSDate *)nextDaylightSavingTimeTransitionAfterDate:(NSDate *)aDate API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

函数描述:返回给定日期之后的下一个夏令时转换日期。

参数 :调用方给定日期。

返回值 :aDate参数之后的下一个夏令时转换日期。根据调用方的时区,此方法可能会返回时区与GMT(格林威治标准时间)的偏移量的变化,如果接收方的时区在参数aDate时不遵守夏令时,则返回 nil。

NSTimeZone (NSExtendedTimeZone) - 分类对时区的扩展

NSTimeZone (NSExtendedTimeZone)常用属性
@property (class, readonly, copy) NSTimeZone *systemTimeZone;

属性描述系统当前使用的时区。如果无法确定当前系统时区,则使用GMT(格林威治标准时间)时区。如果访问过类属性systemTimeZone后,则其值由应用程序缓存,如果用户在随后更改系统时区而systemTimeZone属性没有反映新的时区时,需要调用resetSystemTimeZone方法来清除缓存值,然后再次访问systemTimeZone属性时,它会返回当前系统时区,应用程序缓存再次缓存该值。

@property (class, copy) NSTimeZone *defaultTimeZone;

属性描述当前应用(App)的默认时区。如果未设置defaultTimeZone时区,则使用当前系统时区, 如果无法确定当前系统时区,则使用 GMT 时区(相当于返回systemTimeZone)。设置defaultTimeZone属性会清除之前设置的任何值,设置过defaultTimeZone属性后更新系统时区不会defaultTimeZone属性设置的时区进行更改,对应用程序使用defaultTimeZone时区进行日期和时间操作,这样设置可以将应用程序像在不同的系统时区以defaultTimeZone设置的时区为准运行。

例如可以在UIApplicationDelegate的didFinishLaunchingWithOptions函数中通过[NSTimeZone setDefaultTimeZone:[[NSTimeZone alloc]initWithName:@"Asia/Shanghai"]]为应用程序指定一个默认时区,之后通过NSTimeZone.defaultTimeZone始终获取该时区。

@property (class, readonly, copy) NSTimeZone *localTimeZone;

属性描述跟踪当前系统时区的对象。如果想要一个始终反映当前系统时区的对象,需要使用此属性。与类属性systemTimeZone的区别在于类属性systemTimeZone的值可能会被缓存,需要调用resetSystemTimeZone方法手动清除它。iOS 11及更高版本中,类属性 localTimeZone反映当前系统时区,而在iOS11之前的高版本中它反映的是类属性defaultTimeZone的时区。可以通过NSSystemTimeZoneDidChangeNotification通知监听时区的更改。

@property (readonly) NSInteger secondsFromGMT;

属性描述:较为常用的属性,返回与GMT(格林威治标准时间)之间的时间偏移量,单位为秒,可以使用 [NSTimeZone localTimeZone].secondsFromGMT 直接获取当前使用时区与GMT时间的偏移量,从而计算当前时区的正确时间。

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDate *date = [NSDate date];
    NSDate *date2 = [self handlingTimeZoneDifferences:[NSDate date]];
    NSLog(@"%@ —— %@",date,date2);
}

///处理时区差
- (NSDate *)handlingTimeZoneDifferences:(NSDate *)date{
    //获取系统时区
    NSTimeZone *timeZone = [NSTimeZone localTimeZone];
    //取系统时区与格林威治标准时间的秒差
    NSInteger interval = [timeZone secondsFromGMTForDate:date];
    //经过给定描述的新NSDate对象
    NSDate *localeDate = [date dateByAddingTimeInterval:interval];
    //返回新NSDate对象
    return localeDate;
}

获取当前的时间date与处理过时区的当前时间date2:

截屏2019-11-13下午9.46.58.png
@property (readonly, copy) NSString *description;

属性描述时区的文本描述,包括名称、缩写、与GMT(格林威治标准时间)的偏移量以及夏令时当前是否有效。

NSTimeZone (NSExtendedTimeZone)常用函数
- (BOOL)isEqualToTimeZone:(NSTimeZone *)aTimeZone;

函数描述:指示调用方是否与指定时区具有相同的名称和数据。

参数 :

aTimeZone : 与调用方比较的时区。

返回值:如果参数aTimeZone和调用方具有相同的名称和数据,则为 YES,否则为 NO。

- (nullable NSString *)localizedName:(NSTimeZoneNameStyle)style locale:(nullable NSLocale *)locale API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

函数描述:返回时区的本地化名称。

参数 :

style:时区的本地化名称格式样式。

locale:要为其格式化名称的区域设置。

返回值:调用方本地化的时区名称。

  • NSTimeZoneNameStyle枚举的时区名称格式:
typedef NS_ENUM(NSInteger, NSTimeZoneNameStyle) {
        //指定标准名称样式。例如“China Standard Time”
    NSTimeZoneNameStyleStandard,        // Central Standard Time
        //指定短名称样式。例如“GMT+8”
    NSTimeZoneNameStyleShortStandard,   // CST
        //指定夏令时名称样式。例如“China Daylight Time”
    NSTimeZoneNameStyleDaylightSaving,  // Central Daylight Time
        //指定一个简短的夏令时名称样式。例如“GMT+8”
    NSTimeZoneNameStyleShortDaylightSaving, // CDT
        //指定通用名称样式。例如“China Standard Time”
    NSTimeZoneNameStyleGeneric,     // Central Time
        //指定通用时区名称。例如“China mainland Time”
    NSTimeZoneNameStyleShortGeneric     // CT
};

NSTimeZone (NSTimeZoneCreation) - 分类对时区对象创建的扩展

NSTimeZone (NSTimeZoneCreation)常用函数
+ (nullable instancetype)timeZoneWithName:(NSString *)tzName;

函数描述:返回由给定时区的ID标识的时区对象。

参数 :

tzName :时区的ID。

返回值 :信息目录中名称与时区ID匹配的时区, 如果名称不匹配,则返回 nil。

+ (instancetype)timeZoneForSecondsFromGMT:(NSInteger)seconds;

函数描述返回从GMT(格林威治标准时间)偏移给定秒数的时区对象。新时区的名称是GMT +/- 偏移量,以小时和分钟为单位。 使用此方法创建的时区永远不会有夏令时,并且无论日期如何,偏移量都是恒定的。

参数 :

seconds : 新时区与GMT(格林威治标准时间)偏移的秒数。

返回值 :从GMT(格林威治标准时间)偏移给定秒数的时区对象。

- (nullable instancetype)initWithName:(NSString *)tzName;

函数描述:返回由给定时区的ID标识的时区对象。

参数 :

tzName :时区的ID,为该参数提供 nil 会引发无效参数异常。

返回值 :信息目录中名称与时区ID匹配的时区, 如果名称不匹配,则返回 nil。

+ (nullable instancetype)timeZoneWithAbbreviation:(NSString *)abbreviation;

函数描述返回由给定缩写标识的时区对象。通常不鼓励使用缩写,除非是“GMT”等独特的实例。 时区缩写没有标准化,因此给定的缩写可能有多种含义,例如“EST”指的是美国和澳大利亚的东部时间。

参数 :

abbreviation : 时区的缩写。

返回值 :由缩写标识的时区对象,通过使用缩写字典将缩写解析为名称,然后返回该名称来确定的时区。 如果缩写不匹配,则返回nil。

NSCalendar - 日历类

NSCalendar 派生自NSObject,定义日历单位(如纪元、年份和工作日)与绝对时间点之间关系的对象,提供计算和比较日期的功能

NSCalendar 对象封装了有关计算时间系统的信息,其中定义了一年的开始、长度和划分。 它们提供有关日历的信息并支持日历计算。

NSCalendar常用属性
@property (class, readonly, copy) NSCalendar *currentCalendar;

属性描述返回的日历是由当前用户所选系统区域设置的设置组成的,这些设置覆盖了用户在系统首选项中指定的任何自定义设置。从该日历获得的设置不会随着系统首选项的更改而更改

- (void)viewDidLoad {
    [super viewDidLoad];
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents  *components  =  [calendar components:NSCalendarUnitMinute | NSCalendarUnitMonth | NSCalendarUnitHour | NSCalendarUnitDay fromDate:[NSDate date]];
    NSLog(@"%ld月%ld日%ld时%ld分" ,(long)components.month,(long)components.day,(long)components.hour,(long)components.minute);   
}

打印结果:

截屏2019-11-13下午10.36.01.png
@property NSUInteger firstWeekday;

属性描述调用方第一个工作日的索引。西方工作日的第一天是周日,而我们是周一。

NSCalendar常用函数
- (nullable id)initWithCalendarIdentifier:(NSCalendarIdentifier)ident NS_DESIGNATED_INITIALIZER;

函数描述 : 根据给定的标识符初始化日历。

参数 :

ident : 日历的标识符。

返回值 : 初始化的日历,如果标识符未知(例如,它是无法识别的字符串或当前版本的操作系统不支持该日历),则为nil。

//一些日历标识符:
//欧洲、西半球和其他地方的通用日历(公历)
FOUNDATION_EXPORT NSCalendarIdentifier const NSCalendarIdentifierGregorian  API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); 
//中国日历的标识(农历)
FOUNDATION_EXPORT NSCalendarIdentifier const NSCalendarIdentifierChinese  API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
//佛教日历的标识符。
FOUNDATION_EXPORT NSCalendarIdentifier const NSCalendarIdentifierBuddhist            API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
//伊斯兰日历
FOUNDATION_EXPORT NSCalendarIdentifier const NSCalendarIdentifierIslamic             API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

- (void)viewDidLoad {
    [super viewDidLoad];
    NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierChinese];
    NSDateComponents  *components  =  [calendar components:NSCalendarUnitMinute | NSCalendarUnitMonth | NSCalendarUnitHour | NSCalendarUnitDay fromDate:[NSDate date]];
    NSLog(@"%ld月%ld日%ld时%ld分" ,(long)components.month,(long)components.day,(long)components.hour,(long)components.minute);
}

初始化中国日历(农历)打印结果:

截屏2019-11-13下午10.50.36.png
- (NSUInteger)ordinalityOfUnit:(NSCalendarUnit)smaller inUnit:(NSCalendarUnit)larger forDate:(NSDate *)date;

函数描述 : 在给定的绝对时间内,返回指定的较大日历单位(例如一周)内较小日历单位(例如一天)的序号。

参数 :

smaller:较小的日历单位。

larger: 较大的日历单位。

date: 执行计算的绝对时间

- (void)viewDidLoad {
    [super viewDidLoad];
   NSCalendar *calendar1 = [NSCalendar currentCalendar];
    NSInteger days = [calendar1 ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitYear forDate:[NSDate date]];
    NSLog(@"今天是今年的第%ld天",(long)days);    
}

打印结果:

截屏2019-11-13下午11.19.25.png
- (nullable NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSCalendarOptions)opts;

函数描述返回一个日期,该日期表示通过将给定组件添加到给定日期而计算出的绝对时间。有些操作可能是不明确的,并且计算的行为是特定于日历的,但是通常组件是按照指定的顺序添加的。请注意,有些计算可能需要相对较长的时间。

函数 :

comps : 要添加到日期的组件。

date : 添加补偿的日期。

opts : 计算选项。

返回值 : 一个新的NSDate对象,表示通过使用opts指定的选项将comps指定的日历组件添加到date来计算的绝对时间。如果日期超出接收器的定义范围或无法执行计算,则返回nil。

- (nullable NSDate *)dateFromComponents:(NSDateComponents *)comps;

函数描述返回一个日期,表示从给定组件计算的绝对时间。当提供的组件不足以完全指定绝对时间时,日历将使用其选择的默认值。当存在不一致的信息时,日历可能会忽略一些组件参数,或者方法可能返回nil。注意,有些计算可能需要相对较长的时间来执行。

参数 :

comps : 从中计算返回日期的组件。

返回值 : 一个新的NSDate对象,表示从comps计算的绝对时间。如果接收器无法将comps中给定的组件转换为NSDate对象,则返回nil。

- (NSInteger)component:(NSCalendarUnit)unit fromDate:(NSDate *)date API_AVAILABLE(macos(10.9), ios(8.0), watchos(2.0), tvos(9.0));

函数描述 :从给定日期返回指定的日期组件的 NSInteger 值。

参数 :

unit :要返回值的组件。

date :执行计算的日期。

返回值 :日期组件的 NSInteger 值。

- (NSDateComponents *)components:(NSCalendarUnit)unitFlags fromDate:(NSDate *)date;

函数描述 : 返回表示给定日期的日期组件。注:有些计算可能会花费相当长的时间。

参数 :

unitFlags : 要将数据分解成的组件。

date : 执行计算的日期。

返回值 : 一个NSDateComponents对象,包含分解为unitFlags指定的组件的日期。如果日期超出接收器的定义范围或无法执行计算,则返回nil。

  • NSCalendarUnit提供的组件标识符类型:
typedef NS_OPTIONS(NSUInteger, NSCalendarUnit) {
        //纪元单元的标识符,对应的值是一个 NSInteger。
        NSCalendarUnitEra                = kCFCalendarUnitEra,
        //年单位的标识符,对应的值是一个 NSInteger。
        NSCalendarUnitYear               = kCFCalendarUnitYear,
        //月份单位的标识符,对应的值是一个 NSInteger。
        NSCalendarUnitMonth              = kCFCalendarUnitMonth,
        //天单位的标识符,对应的值是一个 NSInteger。
        NSCalendarUnitDay                = kCFCalendarUnitDay,
        //小时单位的标识符,对应的值是一个 NSInteger。
        NSCalendarUnitHour               = kCFCalendarUnitHour,
        //分钟单位的标识符,对应的值是一个 NSInteger。
        NSCalendarUnitMinute             = kCFCalendarUnitMinute,
        //秒单位的标识符,对应的值是双精度浮点数。
        NSCalendarUnitSecond             = kCFCalendarUnitSecond,
        //工作日单位的标识符,对应的值是一个 NSInteger。
        NSCalendarUnitWeekday            = kCFCalendarUnitWeekday,
        //有序工作日单位的标识符,对应的值是一个 NSInteger,例如“本月的第二个星期二”。
        NSCalendarUnitWeekdayOrdinal     = kCFCalendarUnitWeekdayOrdinal,
        //日历季度的标识符,对应的值是一个 NSInteger。不推荐使用。
        NSCalendarUnitQuarter            API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = kCFCalendarUnitQuarter,
        //日历月份单位中的第几周的标识符。
        NSCalendarUnitWeekOfMonth        API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)) = kCFCalendarUnitWeekOfMonth,
        //日历年份单位中的第几周的标识符。
        NSCalendarUnitWeekOfYear         API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)) = kCFCalendarUnitWeekOfYear,
        //跨年周(即这一周可能跨年了)属于哪一年的标识符。
        NSCalendarUnitYearForWeekOfYear  API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)) = kCFCalendarUnitYearForWeekOfYear,
        //纳秒单位的标识符。
        NSCalendarUnitNanosecond         API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)) = (1 << 15),
        //日期组件对象的日历的标识符,对应的值是一个NSCalendar。
        NSCalendarUnitCalendar           API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = (1 << 20),
        //日期组件对象的时区标识符,对应的值为NSTimeZone。
        NSCalendarUnitTimeZone           API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = (1 << 21),
};

NSDateComponents - 日期与时间组件

NSDateComponents派生自NSObject,是一个日期信息的容器,以日历系统和时区中要计算的单位(如年、月、日、小时和分钟)来指定日期或时间的对象,NSDateComponents对象本身没有意义,而是需要知道它是根据哪种日历解释的,并且需要知道这些值是单位的绝对值还是单位的数量。

    //初始化日期与时间组件
    NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
    //设置天
    dateComponents.day = 4;
    //设置月
    dateComponents.month = 5;
    //设置年
    dateComponents.year = 2017;
    //以公历标识符初始化日历
    NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    //以公历从给定组件计算绝对时间
    NSDate *date = [gregorianCalendar dateFromComponents:dateComponents];
    //从给定日期返回指定的周几的 NSInteger 值
    NSInteger weekday = [gregorianCalendar component:NSCalendarUnitWeekday fromDate:date];
    //5对应于公历的星期四
    NSLog(@"%ld", (long)weekday);
NSDateComponents常用属性
//纪元
@property NSInteger era;
//年
@property NSInteger year;
//月
@property NSInteger month;
//日
@property NSInteger day;
//小时
@property NSInteger hour;
//分钟
@property NSInteger minute;
//秒
@property NSInteger second;
//纳秒
@property NSInteger nanosecond API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
//周几(在公历中1为周日、2为周一、3为周二 、4为周三、5为周四、6为周五、7为周六)
@property NSInteger weekday;
//表示工作日在下一个较大的日历单位(如月份)中的位置。例如,2是一个月的第二个星期五的工作日序数单位。
@property NSInteger weekdayOrdinal;
//季度
@property NSInteger quarter API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
//月份的周数。
@property NSInteger weekOfMonth API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
//年的周数
@property NSInteger weekOfYear API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
//年份的周编号
@property NSInteger yearForWeekOfYear API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
@property (getter=isLeapMonth) BOOL leapMonth API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0));

属性描述一个布尔值,指示该月是否为闰月(每逢闰年所加的一个月叫闰月)。如果该月是闰月,则为 YES,否则为 NO。

NSDateComponents常用函数
- (void)setValue:(NSInteger)value forComponent:(NSCalendarUnit)unit API_AVAILABLE(macos(10.9), ios(8.0), watchos(2.0), tvos(9.0));

函数描述 :为给定的日历组件单位设置一个值(为NSCalendarUnit值设置组件值)。

参数 :

value : 为单位组件设置的值。

unit :要为其设置值的日历单位。 不要传递 NSCalendarUnitCalendar 或 NSCalendarUnitTimeZone。

- (NSInteger)valueForComponent:(NSCalendarUnit)unit API_AVAILABLE(macos(10.9), ios(8.0), watchos(2.0), tvos(9.0));

函数描述 :返回给定日历组件单位的值(检索NSCalendarUnit枚举组件的值)。

参数 :

unit :要检索其值的日历组件单位。 不要传递 NSCalendarUnitCalendar 或 NSCalendarUnitTimeZone。

返回值 :给定日历单位组件的值。

- (BOOL)isValidDateInCalendar:(NSCalendar *)calendar API_AVAILABLE(macos(10.9), ios(8.0), watchos(2.0), tvos(9.0));

函数描述 :返回一个布尔值,指示当前组件组合表示的日期在指定日历中是否存在。

参数 :

calendar : 在计算中使用的日历。

返回值 :如果与调用方的值对应的日期有效并且存在于给定日历中,则为 YES,否则为 NO。

NSDateComponents日期与时间组件使用的例子

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDate *date = [NSDate date];
    NSDate *newDate = [self PlusOneMonth:date];
    NSLog(@"%@",newDate);
    
}

///为Date时间加上一个月
- (NSDate *)PlusOneMonth:(NSDate *)currentDate{
    //以公历标识初始化日历对象
    NSCalendar *calender2 = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    //设置第一个工作日的索引
    [calender2 setFirstWeekday:2];// 国外是从周日开始算的,我们是周一,所以写了2
    //初始化日期组件对象
    NSDateComponents *adcomps = [[NSDateComponents alloc]init];
    //设置补偿年
    [adcomps setYear:0];
    //设置补偿月
    [adcomps setMonth:+1];
    //设置补偿天
    [adcomps setDay:0];
    //将日期组件加到给定日期
    NSDate *newdate = [calender2 dateByAddingComponents:adcomps toDate:currentDate options:0];
    //返回新的NSDate对象
    return newdate;
}

打印结果:


截屏2019-11-13下午11.30.41.png

- (nullable NSDate *)fs_firstDayOfMonth:(NSDate *)month
{
    if (!month) return nil;
    //初始化日期组件(组件为年、月、日、小时)
    NSDateComponents *components = [self components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:month];
    //设置补偿天数
    components.day = 1;
    //从给定组件计算的绝对时间
    return [self dateFromComponents:components];
}

- (nullable NSDate *)fs_lastDayOfMonth:(NSDate *)month
{
    month = [NSDate date];
    if (!month) return nil;
    //初始化日期组件(组件为年、月、日、小时)
    NSDateComponents *components = [self components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:month];
    //设置补偿月数
    components.month++;
    //设置补偿天数
    components.day = 0;
    //从给定组件计算的绝对时间
    return [self dateFromComponents:components];
}

FSCalendar封装的日历插件调用的小示例

//
//  SignInController.h

#import 

@interface SignInController : UIViewController

@end

//
//  SignInController.m


#import "SignInController.h"
#import "SignInView.h"

@interface SignInController ()

@end

@implementation SignInController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self createUI];
}

///初始化签到视图
- (void)createUI{
    
    ///滚动视图
    UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    scrollView.contentSize = CGSizeMake(SCREEN_WIDTH, 690 + HEAD_BAR_HEIGHT);
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.directionalLockEnabled = YES;
    scrollView.bounces = NO;
    scrollView.alwaysBounceVertical = YES;
    scrollView.alwaysBounceHorizontal = NO;
    [self.view addSubview:scrollView];
    
    /**
     签到页面,690为YSCSignInView内部控件总高度,用来设置滚动范围,分别为头部包装视图200,日历视图300,底部包装视图150,底部包装视图与日历视图与视图最底侧间距和40。
     */
    SignInView *signInView = [[SignInView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 690 + HEAD_BAR_HEIGHT)];
    [scrollView addSubview:signInView];
   
    
}

@end

//
//  SignInView.h

#import 
#import "FSCalendar.h"//日历类


@interface SignInView : UIView


@end
//
//  SignInView.m
//  YiShopCustomer

#import "SignInView.h"
#import "FSCalendarDynamicHeader.h"

static NSInteger const YSCCommonPadding = 10;
static NSInteger const YSCCommonTopBottomPadding = 15;

@interface SignInView()

@property (nonatomic, strong) UIImageView *headerImageView;//头部背景图片视图
@property (nonatomic, strong) UIButton *signInButton;//签到按钮
@property (nonatomic, strong) UILabel *continuoussSignInLabel;//连续签到天数标签
@property (nonatomic, strong) UILabel *cumulativeSignInLabel;//累计签到天数标签
@property (nonatomic, strong) UILabel *everydaySignInRewardLabel;//每日签到奖励标签
@property (nonatomic, strong) FSCalendar *calendarView;//签到日历视图
@property (nonatomic, strong) UILabel *designatedDateSignInRewardLabel;//指定日期签到奖励标签
@property (nonatomic, strong) NSMutableArray *calendarImageDataSource;//日历显示图片的数据源
@property (nonatomic, strong) NSMutableArray *calendarAlreadyDataSource;//日历已经签到的数据源

@end

@implementation SignInView

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = HEXCOLOR(0x4c1130);
        [self addShowImageDaysData];
        [self createUI];
    }
    return self;
}

- (void)createUI{
    
    ///头部背景图片视图
    self.headerImageView = [[UIImageView alloc]initWithFrame:CGRectZero];
    [self.headerImageView setImage:[UIImage imageNamed:@"bg_img_0"]];
    [self addSubview:self.headerImageView];
    [self.headerImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.right.equalTo(self);
        make.height.mas_equalTo(294.5);
    }];
    
    ///头部包装视图
    UIView *headerwWrapingView = [[UIView alloc]initWithFrame:CGRectZero];
    headerwWrapingView.backgroundColor = [UIColor clearColor];
    [self addSubview:headerwWrapingView];
    [headerwWrapingView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.mas_top);
        make.left.equalTo(self.mas_left).offset(36);
        make.right.equalTo(self.mas_right).offset(-36);
        make.height.mas_equalTo(200);
    }];
    
    ///签到按钮
    self.signInButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.signInButton setImage:[UIImage imageNamed:@"btn_sign_in_now"] forState:UIControlStateNormal];
    self.signInButton.adjustsImageWhenHighlighted = NO;
    [self.signInButton addTarget:self action:@selector(signInButtonClick) forControlEvents:UIControlEventTouchUpInside];
    [headerwWrapingView addSubview:self.signInButton];
    [self setupHeartbeatAnimationInView:self.signInButton];
    [self.signInButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(headerwWrapingView);
        make.size.mas_equalTo(CGSizeMake(86, 86));
    }];
    
    ///每日签到奖励标签
    self.everydaySignInRewardLabel = [[UILabel alloc]initWithFrame:CGRectZero];
    self.everydaySignInRewardLabel.font = [UIFont systemFontOfSize:14];
    self.everydaySignInRewardLabel.textColor = [UIColor whiteColor];
    self.everydaySignInRewardLabel.textAlignment = NSTextAlignmentCenter;
    self.everydaySignInRewardLabel.text = @"每日签到送3积分";
    [headerwWrapingView addSubview:self.everydaySignInRewardLabel];
    [self.everydaySignInRewardLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.signInButton.mas_bottom).offset(YSCCommonPadding * 2);
        make.left.equalTo(headerwWrapingView.mas_left).offset(YSCCommonPadding);
        make.right.equalTo(headerwWrapingView.mas_right).offset(- YSCCommonPadding);
    }];
    
    ///连续签到图片视图
    UIImageView *continuoussSignInView = [[UIImageView alloc]initWithFrame:CGRectZero];
    [continuoussSignInView setImage:[UIImage imageNamed:@"continuity-pic"]];
    [headerwWrapingView addSubview:continuoussSignInView];
    [continuoussSignInView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(headerwWrapingView);
        make.left.equalTo(headerwWrapingView.mas_left);
        make.size.mas_equalTo(CGSizeMake(72, 86));
    }];
    
    ///连续签到天数标签
    self.continuoussSignInLabel = [[UILabel alloc]initWithFrame:CGRectZero];
    self.continuoussSignInLabel.textAlignment = NSTextAlignmentCenter;
    self.continuoussSignInLabel.font = [UIFont systemFontOfSize:13];
    self.continuoussSignInLabel.attributedText = [self changeSignInLabelText:@"5天"];
    [headerwWrapingView addSubview:self.continuoussSignInLabel];
    [self.continuoussSignInLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(continuoussSignInView);
        make.bottom.equalTo(continuoussSignInView.mas_bottom).offset(-YSCCommonTopBottomPadding);
        make.left.right.equalTo(continuoussSignInView);
    }];
    
    ///累计签到图片视图
    UIImageView *cumulativeSignInView = [[UIImageView alloc]initWithFrame:CGRectZero];
    [cumulativeSignInView setImage:[UIImage imageNamed:@"cumulativec-pic"]];
    [headerwWrapingView addSubview:cumulativeSignInView];
    [cumulativeSignInView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(headerwWrapingView);
        make.right.equalTo(headerwWrapingView.mas_right);
        make.size.mas_equalTo(CGSizeMake(72, 86));
    }];
    
    ///累计签到天数标签
    self.cumulativeSignInLabel = [[UILabel alloc]initWithFrame:CGRectZero];
    self.cumulativeSignInLabel.textAlignment = NSTextAlignmentCenter;
    self.cumulativeSignInLabel.font = [UIFont systemFontOfSize:13];
    self.cumulativeSignInLabel.attributedText = [self changeSignInLabelText:@"5天"];
    [headerwWrapingView addSubview:self.cumulativeSignInLabel];
    [self.cumulativeSignInLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(cumulativeSignInView);
        make.bottom.equalTo(cumulativeSignInView.mas_bottom).offset(-YSCCommonTopBottomPadding);
        make.left.right.equalTo(cumulativeSignInView);
    }];
    
    ///签到日历视图
    self.calendarView = [[FSCalendar alloc] initWithFrame:CGRectMake(20, 200, SCREEN_WIDTH - 40, 300)];
    self.calendarView.backgroundColor = [UIColor whiteColor];
    //日历语言为中文
    self.calendarView.locale = [NSLocale localeWithLocaleIdentifier:@"zh-CN"];
    //允许多选,可以选中多个日期
    self.calendarView.allowsMultipleSelection = YES;
    //如果值为1,那么周日就在第一列,如果为2,周日就在最后一列
    self.calendarView.firstWeekday = 1;
    //周一\二\三...或者头部的2017年11月的显示方式
    self.calendarView.appearance.caseOptions = FSCalendarCaseOptionsWeekdayUsesSingleUpperCase;
    //设置头部年月的显示格式
    self.calendarView.appearance.headerDateFormat = @"MM月yyyy年";
    //设置头部年月的显示颜色
    self.calendarView.appearance.headerTitleColor = [UIColor  blackColor];
    //设置工作日的显示颜色
    self.calendarView.appearance.weekdayTextColor = [UIColor greenColor];
    //今天背景填充色
    self.calendarView.appearance.todayColor = HEXCOLOR(0Xf56456);
    //图片偏移量
    self.calendarView.appearance.imageOffset = CGPointMake(0, -10);
    //今天的副标题文本颜色
    self.calendarView.appearance.subtitleTodayColor = HEXCOLOR(0Xf56456);
    //隐藏日历的所有占位符
    self.calendarView.placeholderType = FSCalendarPlaceholderTypeNone;
    //日期是否可以选择
    self.calendarView.allowsSelection = NO;
    //上月与下月标签静止时的透明度
    self.calendarView.appearance.headerMinimumDissolvedAlpha = 0;
    //隐藏底部分割线,需要引入FSCalendarDynamicHeader.h文件
    self.calendarView.bottomBorder.hidden = YES;
    self.calendarView.dataSource = self;
    self.calendarView.delegate = self;
    self.calendarView.layer.cornerRadius = 10.0;
    [self addShadowToView:self.calendarView withColor:[UIColor blackColor]];
    [self addSubview:self.calendarView];
    
    ///创建点击跳转显示上一月的Button
    UIButton *previousButton = [UIButton buttonWithType:UIButtonTypeCustom];
    previousButton.titleLabel.font = [UIFont systemFontOfSize:15];
    [previousButton setImage:[UIImage imageNamed:@"btn_back_dark"] forState:UIControlStateNormal];
    [previousButton addTarget:self action:@selector(previousClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.calendarView addSubview:previousButton];
    [previousButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.calendarView.mas_top).offset(15);
        make.centerX.equalTo(self.calendarView.mas_centerX).offset(-60);
        make.size.mas_equalTo(CGSizeMake(15, 15));
    }];
    
    ///创建点击跳转显示下一月的Button
    UIButton *nextButton = [UIButton buttonWithType:UIButtonTypeCustom];
    nextButton.titleLabel.font = [UIFont systemFontOfSize:15];
    [nextButton setImage:[UIImage imageNamed:@"btn_back_dark"] forState:UIControlStateNormal];
    nextButton.imageView.transform = CGAffineTransformMakeRotation(M_PI);
    [nextButton addTarget:self action:@selector(nextClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.calendarView addSubview:nextButton];
    [nextButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.calendarView.mas_top).offset(15);
        make.centerX.equalTo(self.calendarView.mas_centerX).offset(60);
        make.size.mas_equalTo(CGSizeMake(15, 15));
    }];
    
    ///底部图片视图
    UIImageView *footerView = [[UIImageView alloc]initWithFrame:CGRectZero];
    footerView.backgroundColor = [UIColor redColor];
    footerView.contentMode = UIViewContentModeScaleAspectFit;
    [footerView setImage:[UIImage imageNamed:@"mew_baseline"]];
    footerView.layer.cornerRadius = 10.0;
    [self addShadowToView:footerView withColor:[UIColor blackColor]];
    [self addSubview:footerView];
    [footerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(self.calendarView);
        make.top.equalTo(self.calendarView.mas_bottom).offset(YSCCommonPadding * 2).priorityHigh();
        make.height.mas_equalTo(150);
        make.bottom.equalTo(self.mas_bottom).offset(- YSCCommonPadding * 2).priorityLow();
    }];
    
}

///添加显示图片的天数数据
- (void)addShowImageDaysData{
    
    [self.calendarImageDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-11"]]];
    [self.calendarImageDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-17"]]];
    [self.calendarImageDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-19"]]];
    [self.calendarImageDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-25"]]];
    
}

///添加显示选中的天数数据
- (void)addShowSelectDaysData{
    
    [self.calendarAlreadyDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-15"]]];
    [self.calendarAlreadyDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-21"]]];
    [self.calendarAlreadyDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-23"]]];
    [self.calendarAlreadyDataSource addObject:[self handlingTimeZoneDifferences:[self nsstringConversionNSDate:@"2019-11-29"]]];
}

///更改签到天数的颜色与字体大小
- (NSMutableAttributedString *)changeSignInLabelText:(NSString *)labelText{
    
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:labelText];
    [attributedString addAttributes:@{
                                      NSForegroundColorAttributeName:[UIColor redColor],
                                      NSFontAttributeName:[UIFont systemFontOfSize:17],
                                      NSKernAttributeName:@(3),
                                      } range:NSMakeRange(0, labelText.length - 1)];
    return attributedString;
}

/// 添加四边阴影效果
- (void)addShadowToView:(UIView *)theView withColor:(UIColor *)theColor {
    // 阴影颜色
    theView.layer.shadowColor = theColor.CGColor;
    // 阴影偏移,默认(0, -3)
    theView.layer.shadowOffset = CGSizeMake(0,0);
    // 阴影透明度,默认0
    theView.layer.shadowOpacity = 0.15;
    // 阴影半径,默认3
    theView.layer.shadowRadius = 3;
}

///设置签到按钮动画
-(void)setupHeartbeatAnimationInView:(UIView *)view{
    // 设定为缩放
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    // 动画选项设定
    animation.duration = 0.4; // 动画持续时间
    animation.repeatCount = HUGE_VALF; // 重复次数(HUGE_VALF为无限重复)
    animation.autoreverses = YES; // 动画结束时执行逆动画
    // 缩放倍数
    animation.fromValue = [NSNumber numberWithFloat:1.0]; // 开始时的倍率
    animation.toValue = [NSNumber numberWithFloat:1.1]; // 结束时的倍率
    animation.removedOnCompletion = NO;
    // 添加动画
    [view .layer addAnimation:animation forKey:@"scale-layer"];
}

///处理签到按钮点击的方法
- (void)signInButtonClick{
    
    [self.signInButton setImage:[UIImage imageNamed:@"btn_sign_in_success"] forState:UIControlStateNormal];
    [self.signInButton.layer removeAnimationForKey:@"scale-layer"];
    self.signInButton.userInteractionEnabled = NO;
    [self addShowSelectDaysData];
    [self.calendarView reloadData];
    
}

///字符串转Date时间
- (NSDate *)nsstringConversionNSDate:(NSString *)dateStr {
    
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"YYYY-MM-dd"];
    NSDate *datestr = [dateFormatter dateFromString:dateStr];
    return datestr;
    
}

///懒加载日历显示图片的数据源
- (NSMutableArray *)calendarImageDataSource{
    
    if(_calendarImageDataSource == nil){
        
        _calendarImageDataSource = [[NSMutableArray alloc]init];
       
    }
    return _calendarImageDataSource;
    
}

///懒加载日历显示选中的数据源
- (NSMutableArray *)calendarAlreadyDataSource{
    
    if(_calendarAlreadyDataSource == nil){
        
        _calendarAlreadyDataSource = [[NSMutableArray alloc]init];
       
    }
    return _calendarAlreadyDataSource;
    
}

///Date数据源是否包含指定的Date
- (BOOL)didDataDource:(NSMutableArray *)dataSource contains:(NSDate *)date{
    
    //处理时区差
    NSDate *localeDate = [self handlingTimeZoneDifferences:date];
    //如果数据源包含date,这一天要显示小礼盒
    if([dataSource containsObject:localeDate]){
        //如果今天和小礼盒要显示的天数相等,显示小礼盒,隐藏填充色
        if([self compareOneDay:localeDate withAnotherDay:[self handlingTimeZoneDifferences:[NSDate date]]]){
            self.calendarView.appearance.todayColor = [UIColor clearColor];
        }
        return YES;
        
    }
    return NO;
}

///处理时区差
- (NSDate *)handlingTimeZoneDifferences:(NSDate *)date{
    
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    NSInteger interval = [zone secondsFromGMTForDate:date];
    NSDate *localeDate = [date dateByAddingTimeInterval:interval];
    return localeDate;
}

///忽略时分秒进行Date比较,相等返回yes
- (BOOL)compareOneDay:(NSDate *)oneDay withAnotherDay:(NSDate *)anotherDay{
    
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd"];
    NSString *oneDayStr = [dateFormatter stringFromDate:oneDay];
    NSString *anotherDayStr = [dateFormatter stringFromDate:anotherDay];
    if([oneDayStr isEqualToString:anotherDayStr]){
        
        return YES;
        
    }else{
        
        return NO;
        
    }
    
}

///上一月按钮点击事件
- (void)previousClicked:(id)sender {
    
    NSDate *currentMonth = self.calendarView.currentPage;
    NSCalendar *gregorian = [NSCalendar currentCalendar];
    NSDate *previousMonth = [gregorian dateByAddingUnit:NSCalendarUnitMonth value:-1 toDate:currentMonth options:0];
    [self.calendarView setCurrentPage:previousMonth animated:YES];
}

///下一月按钮点击事件
- (void)nextClicked:(id)sender {
    
    NSDate *currentMonth = self.calendarView.currentPage;
    NSCalendar *gregorian = [NSCalendar currentCalendar];
    NSDate *nextMonth = [gregorian dateByAddingUnit:NSCalendarUnitMonth value:1 toDate:currentMonth options:0];
    [self.calendarView setCurrentPage:nextMonth animated:YES];
}

///按大小缩放图片
- (UIImage *)scaleToSize:(UIImage *)img size:(CGSize)size{
    
    UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]);
    [img drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}


#pragma make -- FSCalendarDataSource
// 向数据源查询特定日期的图像。
- (nullable UIImage *)calendar:(FSCalendar *)calendar imageForDate:(NSDate *)date{
    
    if([self didDataDource:self.calendarImageDataSource contains:date]){
        //缩放一下图片
        UIImage *image = [self scaleToSize:[UIImage imageNamed:@"ic_gift"] size:CGSizeMake(20, 20)];
        return image;
        
    }
    return nil;
    
}

//向代理询问未选定状态下的具体日期的填充颜色。
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance fillDefaultColorForDate:(NSDate *)date{
    
    if([self didDataDource:self.calendarAlreadyDataSource contains:date]){
        return HEXCOLOR(0Xf56456);
    }
    return [UIColor clearColor];
}

//向代理询问未选定状态下的日期文本颜色。
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance titleDefaultColorForDate:(NSDate *)date{
    
    if([self didDataDource:self.calendarAlreadyDataSource contains:date]){
        return [UIColor whiteColor];
    }
    return [UIColor blackColor];
}

//在日期文本下向数据源请求特定日期的副标题
- (nullable NSString *)calendar:(FSCalendar *)calendar subtitleForDate:(NSDate *)date{
    
    if ([self.calendarView.today isEqualToDate:date]){
    
        return @"今天";
        
    }
    
    return nil;
    
}

//向代理询为特定日期的日期文本提供偏移量。
- (CGPoint)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance titleOffsetForDate:(NSDate *)date{
    
    if ([self.calendarView.today isEqualToDate:date]){
        
        return CGPointMake(0, 7);
        
    }
    return CGPointMake(0,0);

}

//向代理询问特定日期副标题的偏移量。
- (CGPoint)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance subtitleOffsetForDate:(NSDate *)date{
    
    if ([self.calendarView.today isEqualToDate:date]){
        
        return CGPointMake(0, 18);
        
    }
    return CGPointMake(0, 0);
    
}


@end

//
//  Macro.h
// 宏定义

#ifndef Macro_h
#define Macro_h

// MARK:- 系统尺寸宏定义
#define SCREEN_MAX_LENGTH (MAX(SCREEN_WIDTH, SCREEN_HEIGHT))
#define SCREEN_MIN_LENGTH (MIN(SCREEN_WIDTH, SCREEN_HEIGHT))

// MARK:- 手机型号
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4_OR_LESS (IS_IPHONE && SCREEN_MAX_LENGTH < 568.0)
#define IS_IPHONE_5 (IS_IPHONE && SCREEN_MAX_LENGTH == 568.0)
#define IS_IPHONE_6 (IS_IPHONE && SCREEN_MAX_LENGTH == 667.0)
#define IS_IPHONE_6P (IS_IPHONE && SCREEN_MAX_LENGTH == 736.0)
#define IS_IPHONE_6_OR_6P (IS_IPHONE_6 || IS_IPHONE_6P)
#define IS_IPHONE_X (IS_IPHONE && SCREEN_MAX_LENGTH >= 812)

//屏幕的宽
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
//屏幕的高
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
//导航栏的高
#define NAVIGATIONBAR_HEIGHT self.navigationController.navigationBar.frame.size.height
//导航栏的高
#define HEAD_BAR_HEIGHT (IS_IPHONE_X?88:64)
//状态栏的高
#define STATUSBARHEIGHT [UIApplication sharedApplication].statusBarFrame.size.height
//获取视图的宽度
#define WIDTH(view) view.frame.size.width
//字符串不为空
#define IS_NOT_EMPTY(string) (string !=nil && [string isKindOfClass:[NSString class]] && ![string isEqualToString:@""] && ![string isKindOfClass:[NSNull class]] && ![string isEqualToString:@""])
//数组不为空
#define ARRAY_IS_NOT_EMPTY(array) (array && [array isKindOfClass:[NSArray class]] && [array count])
//设置RGBA颜色
#define RGBA(r, g, b, a) [UIColor colorWithRed:r / 255.0 green:g / 255.0 blue:b / 255.0 alpha:a]
//十六进制颜色
#define HEXCOLOR(c)                                  \
[UIColor colorWithRed:((c >> 16) & 0xFF) / 255.0 \
green:((c >> 8) & 0xFF) / 255.0  \
blue:(c & 0xFF) / 255.0         \
alpha:1.0]

//是否为iphone_X
#define DEVICE_IS_IPHONE_X ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125.0, 2436.0), [[UIScreen mainScreen] currentMode].size) : NO)

#endif /* Macro_h */

运行结果:

Jietu20191115-225750-HD.gif

你可能感兴趣的:(Objective-C的NSDate学习笔记)