iOS 关于Date、DateFormatter、时区和Cale
1. Swift 实现 Date 的日常用法
// 获取当前的时间戳10位
class func getCurrentTimeInTerval() -> TimeInterval {
return Date().timeIntervalSince1970
}
// 获取当前的日期
class func getCurrentDate() -> String {
let now = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter.string(from: now)
}
// 获取明天年月日
class func getTomorrowDate() -> String {
let current = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy年MM月dd日"
let interval: TimeInterval = TimeInterval(24*60*60)
let tomorrow = current.addingTimeInterval(interval)
return formatter.string(from: tomorrow)
}
// 时间戳转换为Date类型
class func dateFromTimeStamp(timeStamp:String) ->Date {
let interval:TimeInterval = TimeInterval.init(timeStamp)!
return Date(timeIntervalSince1970: interval)
}
// 时间格式转换成时间戳
class func dateStrToTimeInterval(dateStr: String) -> Int {
let dateformatter = DateFormatter()
dateformatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss"
let date = dateformatter.date(from: dateStr)
let dateTimeInterval:TimeInterval = date!.timeIntervalSince1970
return Int(dateTimeInterval)
}
// 时间格式转换为Date类型
class func timeStrToDate(timeStr: String) -> Date {
let dateformatter = DateFormatter()
dateformatter.dateFormat = "yyyy年MM月dd日"
return dateformatter.date(from: timeStr)!
} // 注:print(dateformatter.date(from: time)!)不要这样打印去看date ,看到的跟你输入的不一样但是它并不是错误的。可以采用下面的方式打印查看 print(dateformatter.string(from: date))
// 获取当前年份和月份和日
class func getCurrentYearMonthDay() -> (Int, Int, Int) {
let calendar = Calendar.current
let dateComponets = calendar.dateComponents([Calendar.Component.year,Calendar.Component.month,Calendar.Component.day], from: Date())
return (dateComponets.year!, dateComponets.month!, dateComponets.day!)
}
// 获取当前月的总天数
class func getCurentMonthDays() -> Int {
let calendar = Calendar.current
let range = calendar.range(of: Calendar.Component.day, in: Calendar.Component.month, for: Date())
return range!.count
}
// 根据date获取是周几
func getWeedayFromeDate(date: Date) -> String {
let calendar = Calendar.current
let dateComponets = calendar.dateComponents([Calendar.Component.year,Calendar.Component.month,Calendar.Component.weekday,Calendar.Component.day], from: date)
//获取到今天是周几 1(星期天) 2(星期一) 3(星期二) 4(星期三) 5(星期四) 6(星期五) 7(星期六)
let weekDay = dateComponets.weekday
switch weekDay {
case 1:
return "星期天"
case 2:
return "星期一"
case 3:
return "星期二"
case 4:
return "星期三"
case 5:
return "星期四"
case 6:
return "星期五"
case 7:
return "星期六"
default:
return ""
}
}
// 根据时间戳与当前时间的比较(类似朋友圈的时间显示)
class func compareCurrntTime(timeStamp:String) -> String {
//计算出时间戳距离现在时间的一个秒数(..s)
let interval:TimeInterval=TimeInterval(timeStamp)!
let date = Date (timeIntervalSince1970: interval)
var timeInterval = date.timeIntervalSinceNow
//得到的是一个负值 (加' - ' 得正以便后面计算)
timeInterval = -timeInterval
//根据时间差 做所对应的文字描述 (作为返回文字描述)
var result:String
//一分钟以内
if interval < 60{
result = "刚刚"
return result
}
else if Int(timeInterval/60) < 60{
//一小时以内
result = String.init(format:"%@分钟前",String(Int(timeInterval/60)))
return result
} else if Int((timeInterval/60)/60) < 24{
//一天以内
result = String.init(format:"%@小时前",String(Int((timeInterval/60)/60)))
return result
}
else {
//超过一天的
let dateformatter = DateFormatter()
//自定义日期格式
dateformatter.dateFormat="yyyy年MM月dd日 HH:mm"
result = dateformatter.string(from: date as Date)
return result
}
}
// 得到本每周 开始 和 结束的时间
class func getCurrentWeekDays() -> (String, String) {
let now = Date()
let calendar = Calendar.current
let dateComponets = calendar.dateComponents([Calendar.Component.year,Calendar.Component.month,Calendar.Component.weekday,Calendar.Component.day], from: now)
//获取到今天是周几 1(星期天) 2(星期一) 3(星期二) 4(星期三) 5(星期四) 6(星期五) 7(星期六)
let weekDay = dateComponets.weekday
// 得到几号
let day = dateComponets.day
// 计算当前日期和这周的星期一和星期天差的天数
var firstDiff = 0
var lastDiff = 0
if weekDay == 1 {
firstDiff = 1
lastDiff = 0
}
else {
firstDiff = calendar.firstWeekday - weekDay!+1;
lastDiff = 9 - weekDay!-1;
}
var firstDayComp = calendar.dateComponents([Calendar.Component.year,Calendar.Component.month,Calendar.Component.day], from: now)
firstDayComp.day = day! + firstDiff
let firstDayOfWeek = calendar.date(from: firstDayComp)
var lastDayComp = calendar.dateComponents([Calendar.Component.year,Calendar.Component.month,Calendar.Component.day], from: now)
lastDayComp.day = day! + lastDiff
let lastDayOfWeek = calendar.date(from: lastDayComp)
let formater = DateFormatter()
formater.dateFormat="yyyy年MM月dd日"
let startW = formater.string(from: firstDayOfWeek!)
let endW = formater.string(from: lastDayOfWeek!)
return (startW, endW)
}
// 检测日期
class func checkDate(str: String) -> String {
let format = DateFormatter()
if let date = format.date(from: str) {
if Calendar.current.isDateInToday(date) {
return "日期是今天"
} else if Calendar.current.isDateInYesterday(date) {
return "日期是昨天"
} else if Calendar.current.isDateInTomorrow(date) {
return "日期是明天"
} else if Calendar.current.isDateInWeekend(date) {
return "日期是周末"
} else if Calendar.current.isDate(date, inSameDayAs: Date()) {
return "日期是今天,也就是传入的和今天的是相同的日期"
} else {
return ""
}
}
return ""
}
2. Swift中使用DateFormatter
iOS中DateFormatter可以把时间转换成我们想要的时间格式:
//日期转字符串
let currentDate = Date.init()
let dateFormatter = DateFormatter.init()
dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
let currentTime: String = dateFormatter.string(from: currentDate)
//字符串转日期
let createTime = "2018-11-30 13:40:54"
let dateFormatter = DateFormatter.init()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date: Date = dateFormatter.date(from: createTime)
2.1 时间格式化过程中的符号
-
分割符:
: — 时间分隔符
/ — 日期分隔符 -
纪元的显示:
G:显示AD,也就是公元 -
年的显示:
yy:年的后面2位数字
yyyy:显示完整的年 -
月的显示:
M:显示成1~12,1位数或2位数
MM:显示成01~12,不足2位数会补0
MMM:英文月份的缩写,例如:Jan
MMMM:英文月份完整显示,例如:January -
日的显示:
d:显示成1~31,1位数或2位数
dd:显示成01~31,不足2位数会补0 -
星期的显示:
EEE:星期的英文缩写,如Sun
EEEE:星期的英文完整显示,如,Sunday -
上/下午的显示:
aa:显示AM或PM -
小時的显示:
H:显示成0~23,1位数或2位数(24小时制
HH:显示成00~23,不足2位数会补0(24小时制)
K:显示成0~12,1位数或2位数(12小時制)
KK:显示成0~12,不足2位数会补0(12小时制) -
分的显示:
m:显示0~59,1位数或2位数
mm:显示00~59,不足2位数会补0 -
秒的显示:
s:显示0~59,1位数或2位数
ss:显示00~59,不足2位数会补0
S: 毫秒的显示 -
时区的显示:
z / zz /zzz :PDT
zzzz:Pacific Daylight Time
Z / ZZ / ZZZ :-0800
ZZZZ:GMT -08:00
v:PT
vvvv:Pacific Time
2.2 时间格式化过程中的locale
最近在工作中,需要将后台返回的时间字符串做格式化操作,期望格式是要展示成
09 Nov 2018 14:56:47,后台返回的时间字符串格式是20181119175515,所以程序中代码自然就写成如下格式:
NSString* createTime =@"20181119175515";
NSDateFormatter* dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"yyyyMMddHHmmss"];
NSDate*date =[dateFormat dateFromString:createTime];
[dateFormat setDateFormat:@"dd MMM yyyy HH:mm:ss"];
NSString*currentDateStr = [dateFormat stringFromDate:date];
但是输出的是:19 11月 2018 17:55:15
如果更改手机系统语言为English,时间格式化输出的就是19 Nov 2018 17:55:15
这时,NSDateFormatter中的locale属性就可以发挥作用了,NSLocale类是将与国家和语言相关的信息进行简单的组合,包括货币、语言、国家等的信息。将下面这段代码添加到dateFormat初始化之后,发现无论在什么系统语言下,时间格式化之后都是19 Nov 2018 17:55:15
//根据需要设置国家代码
NSLocale *usLocale=[[NSLocale alloc]initWithLocaleIdentifier:@"en_US"];
dateFormat.locale=usLocale;
NSLocale的常用方法:
//当前用户设置的本地化对象
[NSLocale currentLocale]
//获取系统所有本地化标识符数组列表
[NSLocale availableLocaleIdentifiers] ;
//获取所有已知合法的国家代码数组列表
[NSLocale ISOCountryCodes] ;
//获取所有已知合法的ISO货币代码数组列表
[NSLocale ISOCurrencyCodes] ;
//获取所有已知合法的ISO语言代码数组列表
[NSLocale ISOLanguageCodes] ;
//获取当前系统设置语言的标识符
[[NSLocale currentLocale] localeIdentifier];
//常量
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleIdentifier;// 当前系统设置语言的标识符
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleLanguageCode;// 语言代码
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleCountryCode;// 国家代码
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleScriptCode;// 脚本代码
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleVariantCode;// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleExemplarCharacterSet;// NSCharacterSet
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleCalendar;// 当地日历
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleCollationIdentifier;// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleUsesMetricSystem;// NSNumber boolean
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleMeasurementSystem;// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleDecimalSeparator;// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleGroupingSeparator;// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleCurrencySymbol;// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleCurrencyCode;// 货币代码
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleCollatorIdentifierNS_AVAILABLE(10_6,4_0);// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleQuotationBeginDelimiterKey NS_AVAILABLE(10_6,4_0);// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleQuotationEndDelimiterKey NS_AVAILABLE(10_6,4_0);// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleAlternateQuotationBeginDelimiterKey NS_AVAILABLE(10_6,4_0);// NSString
FOUNDATION_EXPORT NSLocaleKeyconst NSLocaleAlternateQuotationEndDelimiterKey NS_AVAILABLE(10_6,4_0);// NSString
//获取当前语言的排版方向和字符方向
[NSLocale lineDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode];
[NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode] ;
//获取用户的语言偏好设置列表,该列表对应于IOS中Setting>General>Language弹出的面板中的语言列表。
[NSLocale preferredLanguages];
//监听用户本地化设置的消息
[[NSNotification CenterdefaultCenter] addObserver:self selector:@selector(localChangedHandler:) name:NSCurrentLocaleDidChangeNotificationobject:nil];
//以本地化方式获取国际化信息的显示名称
NSLocale* curLocal = [[NSLocale alloc]initWithLocaleIdentifier:@"zh-Hans"] ;
NSLog(@"%@",[curLocal displayNameForKey:NSLocaleIdentifiervalue:@"fr_FR"] );//法文(法国)
curLocal= [[NSLocale alloc]initWithLocaleIdentifier:@"zh-Hant"] ;
NSLog(@"%@",[curLocal displayNameForKey:NSLocaleIdentifiervalue:@"fr_FR"]);//法文(法國)
3. 时区的处理 (dateFormatter.timeZone)
格林威治时间(Greenwich Mean Time,GMT),是指英国伦敦南郊格林威治的天文台所在地的标准时间。1884年,国际子午线会议通过表决,以通过格林威治的天文台的经线为本初子午线。子午线就是经线,本初子午线就是零度经线。这就是为什么24个时区都以格林威治的区时为基础进行加减的原因。
格林尼治平均时间的正午是指当平太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时基于天文观测本身的缺陷,已经被原子钟报时的协调世界时(UTC)所取代。
一般使用GMT+8表示中国的时间,是因为中国位于东八区,时间上比格林威治时间快8个小时。
区分:CST、CET、UTC、GMT、DST、Unix时间戳
UTC:UTC指的是Coordinated Universal Time- 世界协调时间(又称世界标准时间、世界统一时间),是经过平均太阳时(以格林威治时间GMT为准)、地轴运动修正后的新时标以及以「秒」为单位的国际原子时所综合精算而成的时间,计算过程相当严谨精密,因此若以「世界标准时间」的角度来说,UTC比GMT来得更加精准。其误差值必须保持在0.9秒以内,若大于0.9秒则由位于巴黎的国际地球自转事务中央局发布闰秒,使UTC与地球自转周期一致。所以基本上UTC的本质强调的是比GMT更为精确的世界时间标准。
GMT:Greenwich Mean Time 格林尼治平均时,UTC和GMT都与英国伦敦的本地时相同,所以程序中UTC与GMT没什么不同。只是说GMT可能就精确性来说不如UTC。
Unix时间戳:在计算机中看到的UTC时间都是从(1970年01月01日 0:00:00)开始计算秒数的。所看到的UTC时间那就是从1970年这个时间点起到具体时间共有多少秒。 这个时间戳可以唯一地标识某一刻的时间,即不论时区、当地时间是多少,时间戳都一致。它的提出主要是为用户提供一份电子证据, 以证明用户的某些数据的产生时间。
CST:CST却同时可以代表如下 4 个不同的时区:
Central Standard Time (USA) UT-6:00(美国中部时间)
Central Standard Time (Australia) UT+9:30(澳大利亚中部时间)
China Standard Time UT+8:00(中国标准时间)
Cuba Standard Time UT-4:00(古巴标准时间)
CET:Central European Time,欧洲中部时间,它是比世界标准时间UTC早一个小时的时区名称之一,它被大部分欧洲国家和部分北非国家采用。冬季时间为UTC+1,夏季欧洲夏令时为UTC+2。
DST:Daylight Saving Time(夏日节约时),其实就是前面提到的夏令时。
这么来看,它们的关系就很明确了,一般认为:
UTC = GMT
CET = UTC/GMT+1
CST = CET+9 = UTC/GMT+8 = UTC/GMT-6 = UTC/GMT-4
4. 时间的比较
let date1 = Date()
let date2 = Date()
// date1 < date2 升序排列
if date1?.compare(date2!) == .orderedAscending {
print("<")
}
//date1 = date2 相等
if date1?.compare(date2!) == .orderedSame {
print(" = ")
}
//降序排列的 date1 > date2 降序排列
if date1?.compare(date2!) == .orderedDescending {
print("<")
}
5. iOS中的Calendar
用打印台来输出日历:
在显示日历的时候,我们只需要传入参数年月即可,加上日期可以定位到当天在日历上的显示。日历的显示排版一般是7x5或7x4,每一行从周日开始,周六结束,当我们要显示某个月份的时候,我们必需获取到下面这些值:
1.当月第一天是周几;
2.当月总天数
有了这两条数据我们就可以显示出一个粗糙的日历了,但是有一点,假如当月第一天不是周一,如上图,那么前面几天的日期如何显示呢?所以我们会需要多一条数据:上个月份的总天数,通过简单的加减就能显示了,下面开始通过代码了解如何制作一个日历。
//根据date获取日
- (NSInteger)convertDateToDay:(NSDate *)date {
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitDay) fromDate:date];
return [components day];
}
//根据date获取月
- (NSInteger)convertDateToMonth:(NSDate *)date {
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitMonth) fromDate:date];
return [components month];
}
//根据date获取年
- (NSInteger)convertDateToYear:(NSDate *)date {
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear) fromDate:date];
return [components year];
}
//根据date获取当月周几 (美国时间周日-周六为 1-7,改为0-6方便计算)
- (NSInteger)convertDateToWeekDay:(NSDate *)date {
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond|NSCalendarUnitWeekday fromDate:date];
NSInteger weekDay = [components weekday] - 1;
weekDay = MAX(weekDay, 0);
return weekDay;
}
//根据date获取当月周几
- (NSInteger)convertDateToFirstWeekDay:(NSDate *)date {
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setFirstWeekday:1];//1.Sun. 2.Mon. 3.Thes. 4.Wed. 5.Thur. 6.Fri. 7.Sat.
NSDateComponents *comp = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
[comp setDay:1];
NSDate *firstDayOfMonthDate = [calendar dateFromComponents:comp];
NSUInteger firstWeekday = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:firstDayOfMonthDate];
return firstWeekday - 1; //美国时间周日为星期的第一天,所以周日-周六为1-7,改为0-6方便计算
}
//根据date获取当月总天数
- (NSInteger)convertDateToTotalDays:(NSDate *)date {
NSRange daysInOfMonth = [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
return daysInOfMonth.length;
}
在获取周几的时候要注意一点,美国时间是星期日作为一周的第一天,所以通过函数回调的值,周日-周六是为1-7的,为了方便计算,我进行了-1操作。
接下来是获取上个月份的数据:
//根据date获取偏移指定天数的date
- (NSDate *)getDateFrom:(NSDate *)date offsetDays:(NSInteger)offsetDays {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *lastMonthComps = [[NSDateComponents alloc] init];
[lastMonthComps setDay:offsetDays]; //year = 1表示1年后的时间 year = -1为1年前的日期,month day 类推
NSDate *newdate = [calendar dateByAddingComponents:lastMonthComps toDate:date options:0];
return newdate;
}
//根据date获取偏移指定月数的date
- (NSDate *)getDateFrom:(NSDate *)date offsetMonths:(NSInteger)offsetMonths {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *lastMonthComps = [[NSDateComponents alloc] init];
[lastMonthComps setMonth:offsetMonths]; //year = 1表示1年后的时间 year = -1为1年前的日期,month day 类推
NSDate *newdate = [calendar dateByAddingComponents:lastMonthComps toDate:date options:0];
return newdate;
}
//根据date获取偏移指定年数的date
- (NSDate *)getDateFrom:(NSDate *)date offsetYears:(NSInteger)offsetYears {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *lastMonthComps = [[NSDateComponents alloc] init];
[lastMonthComps setYear:offsetYears]; //year = 1表示1年后的时间 year = -1为1年前的日期,month day 类推
NSDate *newdate = [calendar dateByAddingComponents:lastMonthComps toDate:date options:0];
return newdate;
}
有了这几个函数,就能具备制作一个日历的数据支持了,接下来的代码可以自行复制输出:
- (void)test {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
for (int i = 1; i <= 12; i++) {
NSString *string = [NSString stringWithFormat:@"2018-%02d-01",i];
NSDate *date = [dateFormatter dateFromString:string];
[self logCalendarWith:date];
}
}
- (void)logCalendarWith:(NSDate *)date {
NSInteger year = [self convertDateToYear:date];
NSInteger month = [self convertDateToMonth:date];
NSInteger day = [self convertDateToDay:date];
NSInteger firstWeekDay = [self convertDateToFirstWeekDay:date];
NSInteger totalDays = [self convertDateToTotalDays:date];
printf("第%ld月\n",month);
NSInteger line = totalDays <= 28 ? 4 : 5;
NSInteger column = 7;
NSInteger available = 1; //超过总天数后为下月
NSInteger nextMonthDay = 1; //下月天数开始计数
NSDate *lastMonthDate = [self getDateFrom:date offsetMonths:-1]; //上月月数
NSInteger lastMonthTotalDays = [self convertDateToTotalDays:lastMonthDate]; //上月天数计数
for (int i = 0; i < line; i++) {
for (int j = 0; j < column; j++) {
if (available > totalDays) {
printf("\t%ld ",nextMonthDay++);
continue;
}
if (i == 0 && j < firstWeekDay) {
NSInteger lastMonthDay = lastMonthTotalDays - firstWeekDay + j + 1; //j从0开始,所以这里+1
printf("\t%ld ",lastMonthDay);
}else {
printf("\t%ld",available++);
}
}
printf("\n");
}
printf("\n");
printf("\n");
}
想要测试年份、月份、日的准确性的话,就在test函数里面自行修改就好了,如果没有问题,打印台就有下面的显示:
参考文章:
https://www.jianshu.com/p/e008341fbda1
https://www.jianshu.com/p/b7d0023c0bdf