iOS时间问题
在iOS开发中,经常会遇到各种各样的时间问题,8小时时差,时间戳,求时间间隔,农历等等。解决办法网上比比皆是,但大多零零散散,很多资料并没有说明其中问题。这里集中总结一下,以便于以后查阅和供大家参考。有我自己的理解,错漏之处请大家吐槽。
NSDate的8小时问题
NSDate转字符串时间
初始化一个NSDate时间[NSDate date],获取的是零时区的时间(格林尼治的时间: 年-月-日 时:分:秒: +时区),而北京时间是东八区时间,因为时区不同,所以打印的时间相差了8小时。此刻表示的时间是一样的。
NSDate *date = [NSDate date];
NSLog(@"date时间 = %@", date); NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
NSString *dateStr = [formatter stringFromDate:date];
NSLog(@"字符串时间 = %@", dateStr);
打印结果:
2016-12-07 10:44:24.470 timeTest[32743:2995134] date时间 = 2016-12-07 02:44:24 +0000
2016-12-07 10:44:24.471 timeTest[32743:2995134] 字符串时间 = 2016-12-07 10:44:24 +0800
打印结果前面的时间是北京时间:2016-12-07 10:44:24.470。而date打印出来的时间显示少了8小时,因为它表示的是零时区(+0000)时间02:44:24。此刻对应东八区的北京时间就是10:44:24。只是时区不同,表示的时间点是一样的。好比1公斤和2斤,重量是一样的。[NSDate date]获取的时间单位是零时区(+0000),我们所要的北京时间的单位是东八区(+0800)。
系统会默认[NSDate date]获取的时间为零时区时间,而经过NSDateFormatter转化为字符串时间就是当前所在时区的准确时间,并没有8小时误差。
转字符串时间的时区设定
上文中NSDate时间转为字符串时间并没有设置NSDateFormatter的timeZone。不设置会默认使用当前所在的时区,与设置系统时区formatter.timeZone = [NSTimeZone systemTimeZone]的效果是一样的。
也可以设置时区,获取指定时区的字符串时间
NSDate *date = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
formatter.timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];//东八区时间
NSString *dateStr = [formatter stringFromDate:date];
NSLog(@"字符串时间 = %@", dateStr);
这时获取的时间就是东八区时间,哪怕手机拿到零时区的格林尼治,获取的也是东八区的时间,因为这里指定时区了。也有如下时区指定:
formatter.timeZone = [NSTimeZone timeZoneWithName:@"Asia/Tokyo"];//东九区时间
formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];//零区时间
通过下面方法可得到系统支持的时区对应的字符串常量:
NSArray *zones = [NSTimeZone knownTimeZoneNames];
for (NSString *zone in zones) {
NSLog(@"时区名 = %@", zone);
}
时区对照表
字符串时间转NSDate
字符串时间转为NSDate时间也会有时区问题。也会遇到有所谓的8小时误差,其实就是时区不同。比如下面的例子:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
NSDate *newDate = [formatter dateFromString:@"2016-12-07 14:06:24 +0800"];
NSLog(@"newDate = %@", newDate);
打印结果:
2016-12-07 14:12:17.468 timeTest[34279:3155380] newDate = 2016-12-07 06:06:24 +0000
NSDateFormatter的指定格式是:@"yyyy-MM-dd HH:mm:ss Z"。这里面的Z指的是时区。要转化的字符串时间格式必须和这个格式匹配,上面给定的字符串时间是:@"2016-12-07 14:06:24 +0800",是一个东八区时间,转化为NSDate后是零区时间2016-12-07 06:06:24 +0000,字面显示上少了8小时,其实时间一样。
其实如果上面给定的字符串时间为@"2016-12-07 14:06:24 +0000",转化出来的NSDate时间会完全一样,因为字符串时间为零时区时间,不存在时区误差。大家可以试一下。
当不指定字符串时间的时区时,即没有后面的+0800,同时要把NSDateFormatter时间格式里的Z去掉,保证格式匹配。系统会认为字符串时间是系统所在时区的时间,转化为NSDate时间是零时区时间。
同样,也可以使用formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];这种方式指定字符串时间的时区,和用Z对应+0000是一样的。
NSDate转当前时区的NSDate时间
因为[NSDate date]得出的时间是零时区时间,当我们要获取当前所在时区的NSDate时间时,通常会用以下方法:
NSDate *date = [NSDate date];
NSTimeZone *zone = [NSTimeZone systemTimeZone];
NSInteger interval = [zone secondsFromGMTForDate:date];
NSDate *localDate = [date dateByAddingTimeInterval:interval];
NSLog(@"localDate = %@",localDate);
打印结果:
2016-12-07 14:49:03.777 timeTest[34519:3183548] localDate = 2016-12-07 14:49:03 +0000
上面代码中zone是当前时区,interval是当前时区和零时区时间的差值,最后结果localDate是零时区时间date加上这个差值interval,得到当前时区的NSDate时间。更有甚者,在开发中直接加8*60*60或28800这样的值,因为相差8小时嘛。这样在东八区没问题,在其他时区时间就错了。
其实这种做法是不科学的,因为得到的最终时间还是零时区时间,时间后面明显是+0000,在使用中一般不显示时区,所以认为当做当前时区的时间使用也未尝不可。此为大坑!
坑1:这时如果转为字符串时间,又会增加8小时。因为做时间转换的时候,系统会认为这个NSDate是零时区,得到的字符串时间是东八区的。
解决办法是:将错就错,字符串时间也设置为零时区的字符串时间。从深坑跌入更深的坑!
NSDate *date = [NSDate date];
NSTimeZone *zone = [NSTimeZone systemTimeZone];
NSInteger interval = [zone secondsFromGMTForDate:date];
NSDate *localDate = [date dateByAddingTimeInterval:interval]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSString *dateStr = [formatter stringFromDate:localDate];
NSLog(@"字符串时间 = %@", dateStr);
这里的@"UTC"是指世界标准时间,也是现在用的时间标准,东八区比这个时间也是快8小时,这里填@"GMT"也是可以的。
坑2:在与后台交互时,有时需要+0000时区,这时只能手动拼接字符串更改这个时区字段,改为正确的时区。
所以,在开发中尽量不要这么做,当时间要求显示、存储或与后台交互的时候,使用字符串时间!不要使用转化的NSDate。
时间换算,时间戳的概念
当前时间转时间戳
时间戳是指1970年1月1日0时0分0秒到当前时间的秒数。注意:这里的当前时间是指零时区的NSDate时间。
NSDate *date = [NSDate date];
NSTimeInterval timeIn = [date timeIntervalSince1970];
NSLog(@"时间戳 = %.0f", timeIn);
打印结果:
2016-12-07 15:41:04.000 timeTest[34994:3232390] 时间戳 = 1481096464
时间戳转当前时间
NSDate *date = [NSDate date];
NSTimeInterval timeIn = [date timeIntervalSince1970];
NSDate *newDate = [NSDate dateWithTimeIntervalSince1970:timeIn];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
NSString *newTime = [dateFormatter stringFromDate:newDate];
NSLog(@"初始化时间 = %@,时间戳=%.0f,时间戳转为NSDate时间 = %@,转为字符串时间 = %@", date, timeIn, newDate, newTime);
打印结果:
2016-12-07 16:11:56.146 timeTest[35186:3253589] 初始化时间 = 2016-12-07 08:11:56 +0000,时间戳=1481098316,时间戳转为NSDate时间 = 2016-12-07 08:11:56 +0000,转为字符串时间 = 2016-12-07 16:11:56 +0800
注意时间戳使用的NSDate时间是当前零时区的时间!当前零时区时间!当前零时区时间!重要的事情说三遍!不要进行NSDate转当前时区的NSDate时间,再转时间戳。下面是验证:
NSDate *date = [NSDate date];
NSLog(@"系统零时区NSDate时间 = %@", date);
NSTimeInterval timeIn = [date timeIntervalSince1970];
NSLog(@"系统零时区NSDate时间转化为时间戳 = %.0f", timeIn);
NSTimeZone *zone = [NSTimeZone systemTimeZone];
NSInteger interval = [zone secondsFromGMTForDate:date];
NSDate *localDate = [date dateByAddingTimeInterval:interval];
NSLog(@"转化为本地NSDate时间 = %@", localDate);
NSTimeInterval timeIn2 = [localDate timeIntervalSince1970];
NSLog(@"本地NSDate时间转化为时间戳 = %.0f", timeIn2);
NSDate *detaildate = [NSDate dateWithTimeIntervalSince1970:timeIn];
NSDate *detaildate2 = [NSDate dateWithTimeIntervalSince1970:timeIn2];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
NSString *newTime = [dateFormatter stringFromDate:detaildate];
NSString *newTime2 = [dateFormatter stringFromDate:detaildate2];
NSLog(@"最终转为字符串时间1 = %@, 时间2 = %@", newTime, newTime2);
打印结果:
2016-12-07 16:13:57.834 timeTest[35211:3255842] 系统零时区NSDate时间 = 2016-12-07 08:13:57 +0000
2016-12-07 16:13:57.834 timeTest[35211:3255842] 系统零时区NSDate时间转化为时间戳 = 1481098438
2016-12-07 16:13:57.835 timeTest[35211:3255842] 转化为本地NSDate时间 = 2016-12-07 16:13:57 +0000
2016-12-07 16:13:57.835 timeTest[35211:3255842] 本地NSDate时间转化为时间戳 = 1481127238
2016-12-07 16:13:57.836 timeTest[35211:3255842] 最终转为字符串时间1 = 2016-12-07 16:13:57 +0800, 时间2 = 2016-12-08 00:13:57 +0800
问题解释详见上文的NSDate转当前时区的NSDate时间。
时间操作与比较
时间初始化和比较方法
几个时间初始化方法:
//初始化当前时间,返回零时区时间
NSDate *date = [NSDate date];
//以当前时间为准,正数超前指定秒数,负数延后指定秒数
NSDate *laterDate = [NSDate dateWithTimeIntervalSinceNow:60];
//以2001-01-01 00:00:00 +0000为基准,正数超前指定秒数,负数延后指定秒数
NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:60];
//以1970-01-01 00:00:00 +0000为基准,正数超前指定秒数,负数延后指定秒数
NSDate *newDate1 = [NSDate dateWithTimeIntervalSince1970:60];
//实例方法,以指定时间为基准,正数超前指定秒数,负数延后指定秒数
NSDate *newDate2 = [date dateByAddingTimeInterval:60];
//很久以后的某一天
NSDate *newDate3 = [NSDate distantFuture];
//很久以前的某一天
NSDate *newDate4 = [NSDate distantPast];
几个时间比较方法:
//比较两个时间是否相等
- (BOOL)isEqualToDate:(NSDate *)otherDate;
//两个时间比较,返回较早时间
- (NSDate *)earlierDate:(NSDate *)anotherDate;
//两个时间比较,返回较晚时间
- (NSDate *)laterDate:(NSDate *)anotherDate;
//两个时间比较,返回枚举类型
- (NSComparisonResult)compare:(NSDate *)other;
几个计算时间间隔的方法:
//返回实例时间与refDate时间间隔秒数
- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)refDate;
//返回实例时间与当前时间间隔秒数
- (NSTimeInterval)timeIntervalSinceNow;
//返回实例时间的时间戳
- (NSTimeInterval)timeIntervalSince1970;
//返回实例时间和2001-01-01 00:00:00 +0000的间隔秒数
- (NSTimeInterval)timeIntervalSinceReferenceDate;
//返回当前时间和2001-01-01 00:00:00 +0000的间隔秒数
+ (NSTimeInterval)timeIntervalSinceReferenceDate;
获取年月日时分秒周时区
OC里的时间坑太多,根本没办法像其他语言那样直接time.year就能获取年份。要想获取NSDate的年月日需要使用日历对象NSCalendar。
NSDate *date = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *dateComps = [cal components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond|NSCalendarUnitWeekday|NSCalendarUnitWeekOfMonth|NSCalendarUnitWeekOfYear|NSCalendarUnitTimeZone fromDate:date];
NSLog(@"时间 = %@", date);
NSLog(@"年=%ld,月=%ld,日=%ld,时=%ld,分=%ld,秒=%ld,周=%ld,本月第%ld周,本年第%ld周,时区=%@", dateComps.year, dateComps.month, dateComps.day, dateComps.hour, dateComps.minute, dateComps.second, dateComps.weekday, dateComps.weekOfMonth, dateComps.weekOfYear, dateComps.timeZone.name);
打印结果:
2016-12-07 17:20:41.639 timeTest[35734:3311752] 时间 = 2016-12-07 09:20:41 +0000
2016-12-07 17:20:41.640 timeTest[35734:3311752] 年=2016,月=12,日=7,时=17,分=20,秒=41,周=4,本月第2周,本年第50周,时区=Asia/Shanghai
NSDateComponents创建方法中添加的枚举NSCalendarUnit,是后面要获取的年月日时分秒必须对应添加的。比如要获取年dateComps.year,就需要添加枚举NSCalendarUnitYear。
可以看到,[NSDate date]时间可以使用NSCalendar直接获取当前时区的时分秒,打印的时和时区即可看出。这是[NSCalendar currentCalendar]日历对象初始化的原因,也可以用[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]指定Identifier的方式初始化阳历日历。可以试试指定Identifier为NSCalendarIdentifierChinese,打印的是中国农历。
dateComps.weekOfMonth是今天属于本月的第几周。
dateComps.weekOfYear是今天属于本年的第几周。
dateComps.weekday是星期,这个和日常使用有些不同。上述程序打印的是周=4,但2016-12-07是周三。这里weekday的对应关系是:周日-1,周一-2,周二-3,周三-4,周四-5,周五-6,周六-7。毕竟国外惯例周日是每周的第一天。
农历
获取农历的工具方法,可根据需求添加农历节日和二十四节气
+ (NSString *)LunarForSolarYear:(int)wCurYear Month:(int)wCurMonth Day:(int)wCurDay
{
//农历日期名
NSArray *cDayName = [NSArray arrayWithObjects:@"*",@"初一",@"初二",@"初三",@"初四",@"初五",@"初六",@"初七",@"初八",@"初九",@"初十",@"十一",@"十二",@"十三",@"十四",@"十五",@"十六",@"十七",@"十八",@"十九",@"二十",@"廿一",@"廿二",@"廿三",@"廿四",@"廿五",@"廿六",@"廿七",@"廿八",@"廿九",@"三十",nil];
//农历月份名
NSArray *cMonName = [NSArray arrayWithObjects:@"*",@"正月",@"二月",@"三月",@"四月",@"五月",@"六月",@"七月",@"八月",@"九月",@"十月",@"冬月",@"腊月",nil];
//公历每月前面的天数
const int wMonthAdd[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
//农历数据
const int wNongliData[100] = {2635,333387,1701,1748,267701,694,2391,133423,1175,396438
,3402,3749,331177,1453,694,201326,2350,465197,3221,3402
,400202,2901,1386,267611,605,2349,137515,2709,464533,1738
,2901,330421,1242,2651,199255,1323,529706,3733,1706,398762
,2741,1206,267438,2647,1318,204070,3477,461653,1386,2413
,330077,1197,2637,268877,3365,531109,2900,2922,398042,2395
,1179,267415,2635,661067,1701,1748,398772,2742,2391,330031
,1175,1611,200010,3749,527717,1452,2742,332397,2350,3222
,268949,3402,3493,133973,1386,464219,605,2349,334123,2709
,2890,267946,2773,592565,1210,2651,395863,1323,2707,265877};
static int nTheDate,nIsEnd,m,k,n,i,nBit;
//计算到初始时间1921年2月8日的天数:1921-2-8(正月初一)
nTheDate = (wCurYear - 1921) * 365 + (wCurYear - 1921) / 4 + wCurDay + wMonthAdd[wCurMonth - 1] - 38;
if((!(wCurYear % 4)) && (wCurMonth > 2))
nTheDate = nTheDate + 1;
//计算农历天干、地支、月、日
nIsEnd = 0;
m = 0;
while(nIsEnd != 1) {
if(wNongliData[m] < 4095)
k = 11;
else
k = 12;
n = k;
while(n>=0) {
//获取wNongliData(m)的第n个二进制位的值
iOS时间问题的更多相关文章
- iOS 时间的处理
做App避免不了要和时间打交道,关于时间的处理,里面有不少门道,远不是一行API调用,获取当前系统时间这么简单.我们需要了解与时间相关的各种API之间的差别,再因场景而异去设计相应的机制. 时间的形式 ...
- iOS时间那点事儿–NSTimeZone
NSTimeZone **时区是一个地理名字,是为了克服各个地区或国家之间在使用时间上的混乱. 基本概念: GMT 0:00 格林威治标准时间; UTC +00:00 校准的全球时间; CCD +08 ...
- iOS 时间处理(转)
NSDate NSDate对象用来表示一个具体的时间点. NSDate是一个类簇,我们所使用的NSDate对象,都是NSDate的私有子类的实体. NSDate存储的是GMT时间,使用的时候会根据 当 ...
- IOS时间格式转换
在开发iOS程序时,有时候需要将时间格式调整成自己希望的格式,这个时候我们可以用NSDateFormatter类来处理. 例如:如何将格式为“12-May-14 05.08.02.000000 PM” ...
- 【safari挖的那些坑】iOS safari 浏览器 时间乱码(ios时间显示NaN) 问题解决
通常 iOS下时间错误表现形式 问题一: 这个界面运用了大量的日期类型的计算,当我们用JavaScript实例化一个日期对象时,我们可以这样用: var date =new Date(); 上面这段代 ...
- [置顶] ios 时间定时器 NSTimer应用demo
原创文章,转载请注明出处:http://blog.csdn.net/donny_zhang/article/details/9251917 demo功能:ios NSTimer应用demo .ipho ...
- IOS 时间字符串转换时间戳失败问题
链接:https://pan.baidu.com/s/1nw6VWoD 密码:1peh 有时候获取到的时间带有毫秒数或者是(2018-2-6 11:11:11)格式的(别说你没遇到过,也别什么都让后台 ...
- iOS 时间校准解决方案
背景 在 iOS 开发中,凡是用到系统时间的,都要考虑一个问题:对时.有些业务是无需对时,或可以以用户时间为准的,比如动画用到的时间.一些日程类应用等.但电商相关的业务大都不能直接使用设备上的时间,而 ...
- IOS safari 浏览器 时间乱码(ios时间显示NaN) 问题解决
问题一: 项目中遇到一个关于日期时间在ios中乱码在安卓中安然无恙的问题,焦躁了半天 问题如上图,通过用户选择的时间和当天的天数相加然后在ios上就是乱码 这个界面运用了日期类型的计算,当我们用Jav ...
随机推荐
- JDStatusBarNotification和一些宏定义
// // AddTopicViewController.m // vMeet2 // // Created by 张源海 on 16/6/30. // Copyright © 2016年 h ...
- linux6.*无法正常启动has进程解决方案
1. 数据库安装成功之后:发现ohasd进程存在异常:[root@oracle234 ~]# su - grid[grid@oracle234 ~]$ ps -ef| grep ohasdgrid ...
- 阐述ArrayList、Vector、LinkedList的存储性能和特性。
答:ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快 ...
- java.sql.SQLException: 关闭的 Resultset: next
根据异常信息判断是数据库查询出来的结果集被关闭了,所以就了next 我的代码是一个Impl方法(假设为A方法)中调用另一个Impl方法(假设为B方法),我在执行完B方法后,调用了一下关闭数据库连接的方 ...
- JavaScript打印正倒直线
做了一个作业,用JavaScript打印正倒直线,突然觉得自己还是逻辑有待加强训练啊 document.write("<h3>打印倒正金字塔直线</h3>" ...
- [mysql]throw exception
CREATE PROCEDURE pro_throwException (errorCode char(5), errorMessage text) BEGIN SIGNAL SQLSTATE err ...
- [Linux] vimdiff 快速比较和合并少量文件
纯文本文件比较和合并工具一直是软件开发过程中比较重要的组成部分,vimdiff 能够在比较出来的多处差异之间快速定位,很容易的进行文件合并操作.在需要快速比较和合并少量文件的时候,vimdiff是很好 ...
- DOM树操作
DOM 操作 访问与树关系(节点) 绘制 DOM 树: childNodes, attributes 从一个中心元素访问其所有的直系亲属元素 访问父节点: parentNode 访问上一个兄弟节点: ...
- 蚁群算法简介(part2: 蚁群算法之构造路径)
蚁群算法主要可以分为以下几个步骤:首先,蚁群中的每只蚂蚁都根据地面上信息素浓度的大小找出一条从原点通向终点的遍历所有城市一次的路径(构造路径):然后每只蚂蚁沿着自己刚刚找到的路径回溯,在路径经过的各个 ...
- 微信小程序-表单
wxml <view> 按钮: <button size="{{buttom.size}}" type="{{buttom.type}}" p ...