我喜欢 Leo 解决方案中的 API 并使用了它。但是,我在运行它时遇到了问题(太平洋标准时间 2015 年 6 月 13 日上午 9:33)。症状是:
当日期在未来时,xFromToday 函数正在返回
-(t-delta) 的结果(例如未来 1 个月
XFromToday 函数将返回 (0, -4, -29, -719,
-43199) 对于 x=(month,week,days, hrs, mins) 。 relativeDates 字符串将返回“从今天起 1 周”
当日期是过去时,结果将为 -t,除了
对于相对日期字符串。例如,在过去的一个月里,我
将得到 (1, 4, 31, 744, 44640)。相对日期字符串为:“4 周
和 744 小时”
由于机密性,我无法粘贴测试输出,但粘贴了带有 NSDate.test() 的代码。它也有一些从另一篇文章中借来的其他东西(在代码中引用)和我写的一些格式化的东西。
import Foundation
// https://stackoverflow.com/questions/27339072/working-with-nsdate-components-in-swift
// **** Use with caution may not do what you expect. See the stackoverflow post above. *******
public extension NSDate {
func xDays(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: x, toDate: self, options: nil)!
}
func xWeeks(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitWeekOfYear, value: x, toDate: self, options: nil)!
}
func xMonths(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMonth, value: x, toDate: self, options: nil)!
}
func xMins(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMinute, value: x, toDate: self, options: nil)!
}
func xHours(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitHour, value: x, toDate: self, options: nil)!
}
var hoursFromToday: Int{
return NSCalendar.currentCalendar().components(.CalendarUnitHour, fromDate: self, toDate: NSDate(), options: nil).hour
}
var weeksFromToday: Int{
return NSCalendar.currentCalendar().components(.CalendarUnitWeekOfYear, fromDate: self, toDate: NSDate(), options: nil).weekOfYear
}
var daysFromToday: Int{
return NSCalendar.currentCalendar().components(.CalendarUnitDay, fromDate: self, toDate: NSDate(), options: nil).day
}
var monthsFromToday: Int{
return NSCalendar.currentCalendar().components(.CalendarUnitMonth, fromDate: self, toDate: NSDate(), options: nil).month
}
var minsFromToday: Int{
return NSCalendar.currentCalendar().components(.CalendarUnitMinute, fromDate: self, toDate: NSDate(), options: nil).minute
}
var relativeDateString: String {
if weeksFromToday > 0 { return weeksFromToday > 1 ? "\(weeksFromToday) weeks and \(hoursFromToday) hours" : "\(weeksFromToday) week and \(hoursFromToday) hours" }
if hoursFromToday > 0 { return hoursFromToday > 1 ? "\(hoursFromToday) hours" : "\(hoursFromToday) hour" }
return ""
}
//Date Comparisions
//https://stackoverflow.com/questions/26198526/nsdate-comparison-using-swift
func isGreaterThanDate(dateToCompare : NSDate) -> Bool
{
//Declare Variables
var isGreater = false
//Compare Values
if self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
{
isGreater = true
}
//Return Result
return isGreater
}
func isLessThanDate(dateToCompare : NSDate) -> Bool
{
//Declare Variables
var isLess = false
//Compare Values
if self.compare(dateToCompare) == NSComparisonResult.OrderedAscending
{
isLess = true
}
//Return Result
return isLess
}
// Date printing converstions
var dayMonthYear: String {
let dateMonthYearFormatter: NSDateFormatter = NSDateFormatter()
let currentLocale: NSLocale = NSLocale.currentLocale()
let dateMonthYearFormatString: NSString! = NSDateFormatter.dateFormatFromTemplate("EdMMMyyyy",options: 0, locale: currentLocale)
dateMonthYearFormatter.dateFormat = dateMonthYearFormatString as! String
return dateMonthYearFormatter.stringFromDate(self)
}
var timeDayMonthYear: String {
let dateMonthYearFormatter: NSDateFormatter = NSDateFormatter()
let currentLocale: NSLocale = NSLocale.currentLocale()
let dateMonthYearFormatString: NSString! = NSDateFormatter.dateFormatFromTemplate("EdMMMyyyy' ' HH':'mm",options: 0, locale: currentLocale)
dateMonthYearFormatter.dateFormat = dateMonthYearFormatString as! String
return dateMonthYearFormatter.stringFromDate(self)
}
var hourMin: String {
let hourMinFormatter = NSDateFormatter();
hourMinFormatter.dateFormat = "HH:mm"
return hourMinFormatter.stringFromDate(self)
}
static func rfc3339DateFormatter() -> NSDateFormatter {
let rfc3339DateFormatterRet = NSDateFormatter()
let enUSPOSIXLocale: NSLocale = NSLocale(localeIdentifier: "en_US_POSIX")
rfc3339DateFormatterRet.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
rfc3339DateFormatterRet.locale = enUSPOSIXLocale
rfc3339DateFormatterRet.timeZone = NSTimeZone(forSecondsFromGMT: 0)
return rfc3339DateFormatterRet
}
var rfcString: String {
return NSDate.rfc3339DateFormatter().stringFromDate(self)
}
func rfcDate(rfcString: String) -> NSDate {
return NSDate.rfc3339DateFormatter().dateFromString(rfcString)!
}
func changeDate(toDate: NSDate) -> NSDate {
let rfcToDate = toDate.rfcString
let rfcSelf = self.rfcString
let toDateArray : [String] = rfcToDate.componentsSeparatedByString("T")
let selfArray : [String] = rfcSelf.componentsSeparatedByString("T")
return rfcDate(toDateArray[0]+"T"+selfArray[1])
}
static func test(months: Int = 0, weeks: Int = 0, days: Int = 0, hrs: Int = 0, mins: Int = 0) {
NSLog("****************** Start Testing of NSDate **************************")
NSLog("Inputs: months:\(months) weeks:\(weeks) days:\(days) hrs: \(hrs) mins: \(mins)")
var today = NSDate()
NSLog("Today is: \(today.timeDayMonthYear)")
var monthsFromToday = today.xMonths(months)
NSLog("** \(months) months from today: \(monthsFromToday.timeDayMonthYear)")
NSLog("monthsFromToday returns: \(monthsFromToday.monthsFromToday)");
NSLog("weeksFromToday returns: \(monthsFromToday.weeksFromToday)");
NSLog("daysFromToday returns: \(monthsFromToday.daysFromToday)");
NSLog("hoursFromToday returns: \(monthsFromToday.hoursFromToday)");
NSLog("minsFromToday returns: \(monthsFromToday.minsFromToday)");
NSLog("relativeDateString returns: \(monthsFromToday.relativeDateString)")
var weeksFromToday = today.xWeeks(weeks)
NSLog("** \(weeks) weeks from today: \(weeksFromToday.timeDayMonthYear)")
NSLog("weeksFromToday returns: \(weeksFromToday.weeksFromToday)");
NSLog("relativeDateString returns: \(weeksFromToday.relativeDateString)")
var daysFromToday = today.xDays(days)
NSLog("** \(days) days from today: \(daysFromToday.timeDayMonthYear)")
NSLog("daysFromToday returns: \(daysFromToday.daysFromToday)");
NSLog("relativeDateString returns: \(daysFromToday.relativeDateString)")
var hrsFromToday = today.xHours(hrs)
NSLog("** \(hrs) hours from today: \(hrsFromToday.timeDayMonthYear)")
NSLog("hoursFromToday returns: \(hrsFromToday.hoursFromToday)");
NSLog("relativeDateString returns: \(hrsFromToday.relativeDateString)")
var minsFromToday = today.xMins(mins)
NSLog("** \(mins) minutes from today: \(minsFromToday.timeDayMonthYear)")
NSLog("minsFromToday returns: \(minsFromToday.minsFromToday)");
NSLog("relativeDateString returns: \(minsFromToday.relativeDateString)")
NSLog("__________________ End Testing of NSDate _________________________")
}
}
这是我所做的“修复”。我添加了一个 santizedDates() 函数,它返回 "from" 和 "to" 日期,以及一个符号乘数 (+1/-1)。如果比较是在 future 中,则在 date span 中添加 1 分钟的偏移量。它适用于测试用例。还从 relativeDateString(capitalizeFirst:Bool=false) 生成正确且人类可读的输出,例如: "today, 1 hour 1 minute in the past" 。我已经发布了一个关于为什么首先在这里发生这种行为的问题:
NSCalendar.components().minute returning inconsistent values
import Foundation
// https://stackoverflow.com/questions/27339072/working-with-nsdate-components-in-swift
public extension NSDate {
func xDays(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: x, toDate: self, options: nil)!
}
func xWeeks(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitWeekOfYear, value: x, toDate: self, options: nil)!
}
func xMonths(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMonth, value: x, toDate: self, options: nil)!
}
func xMins(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMinute, value: x, toDate: self, options: nil)!
}
func xHours(x:Int) -> NSDate {
return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitHour, value: x, toDate: self, options: nil)!
}
var hoursFromToday: Int{
var fromDate: NSDate = self
var toDate: NSDate = NSDate()
var sign: Int = -1
(fromDate,toDate, sign) = self.sanitizedDates()
return (sign * NSCalendar.currentCalendar().components(.CalendarUnitHour, fromDate: fromDate, toDate: toDate, options: nil).hour)
}
var weeksFromToday: Int{
var fromDate: NSDate = self
var toDate: NSDate = NSDate()
var sign: Int = -1
(fromDate,toDate,sign) = self.sanitizedDates()
return (sign * NSCalendar.currentCalendar().components(.CalendarUnitWeekOfYear, fromDate: fromDate, toDate: toDate, options: nil).weekOfYear)
}
var daysFromToday: Int{
var fromDate: NSDate = self
var toDate: NSDate = NSDate()
var sign: Int = -1
(fromDate,toDate, sign) = self.sanitizedDates()
return (sign * NSCalendar.currentCalendar().components(.CalendarUnitDay, fromDate: fromDate, toDate: toDate, options: nil).day)
}
var monthsFromToday: Int{
var fromDate: NSDate = self
var toDate: NSDate = NSDate()
var sign: Int = -1
(fromDate,toDate, sign) = self.sanitizedDates()
return (sign * NSCalendar.currentCalendar().components(.CalendarUnitMonth, fromDate: fromDate, toDate: toDate, options: nil).month)
}
var minsFromToday: Int{
var fromDate: NSDate = self
var toDate: NSDate = NSDate()
var sign: Int = -1
var offset: Int = 0
(fromDate,toDate,sign) = self.sanitizedDates()
return ( sign * NSCalendar.currentCalendar().components(.CalendarUnitMinute, fromDate: fromDate, toDate: toDate, options: nil).minute)
}
func relativeDateString(capitalizeFirst:Bool = false) -> String {
let days: Int = daysFromToday
let mins: Int = minsFromToday % 60
let tense: String = (minsFromToday > 0) ? " in the future" : " in the past"
let hrs: Int = hoursFromToday % 24
var retString = (capitalizeFirst) ? "Now" : "now"
if(minsFromToday != 0) {
if(days == 0) {
retString = (capitalizeFirst) ? "Today" : "today"
retString = (mins != 0 || hrs != 0) ? retString+"," : retString
}
else {
let absDays = abs(days)
retString = "\(absDays)"
retString += (absDays > 1) ? " days" : " day"
}
if(hrs != 0) {
let absHrs = abs(hrs)
retString += " \(absHrs)"
retString += (absHrs > 1) ? " hours" : " hour"
}
if(mins != 0) {
let absMins = abs(mins)
retString += " \(absMins)"
retString += (absMins > 1) ? " minutes" : " minute"
}
retString += tense
}
return retString
}
//Date Comparisons
//https://stackoverflow.com/questions/26198526/nsdate-comparison-using-swift
func isGreaterThanDate(dateToCompare : NSDate) -> Bool
{
//Declare Variables
var isGreater = false
//Compare Values
if self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
{
isGreater = true
}
//Return Result
return isGreater
}
func isLessThanDate(dateToCompare : NSDate) -> Bool
{
//Declare Variables
var isLess = false
//Compare Values
if self.compare(dateToCompare) == NSComparisonResult.OrderedAscending
{
isLess = true
}
//Return Result
return isLess
}
// Date printing converstions
var dayMonthYear: String {
let dateMonthYearFormatter: NSDateFormatter = NSDateFormatter()
let currentLocale: NSLocale = NSLocale.currentLocale()
let dateMonthYearFormatString: NSString! = NSDateFormatter.dateFormatFromTemplate("EdMMMyyyy",options: 0, locale: currentLocale)
dateMonthYearFormatter.dateFormat = dateMonthYearFormatString as! String
return dateMonthYearFormatter.stringFromDate(self)
}
var timeDayMonthYear: String {
let dateMonthYearFormatter: NSDateFormatter = NSDateFormatter()
let currentLocale: NSLocale = NSLocale.currentLocale()
let dateMonthYearFormatString: NSString! = NSDateFormatter.dateFormatFromTemplate("EdMMMyyyy' ' HH':'mm",options: 0, locale: currentLocale)
dateMonthYearFormatter.dateFormat = dateMonthYearFormatString as! String
return dateMonthYearFormatter.stringFromDate(self)
}
var hourMin: String {
let hourMinFormatter = NSDateFormatter();
hourMinFormatter.dateFormat = "HH:mm"
return hourMinFormatter.stringFromDate(self)
}
static func rfc3339DateFormatter() -> NSDateFormatter {
let rfc3339DateFormatterRet = NSDateFormatter()
let enUSPOSIXLocale: NSLocale = NSLocale(localeIdentifier: "en_US_POSIX")
rfc3339DateFormatterRet.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
rfc3339DateFormatterRet.locale = enUSPOSIXLocale
rfc3339DateFormatterRet.timeZone = NSTimeZone(forSecondsFromGMT: 0)
return rfc3339DateFormatterRet
}
var rfcString: String {
return NSDate.rfc3339DateFormatter().stringFromDate(self)
}
func rfcDate(rfcString: String) -> NSDate {
return NSDate.rfc3339DateFormatter().dateFromString(rfcString)!
}
func changeDate(toDate: NSDate) -> NSDate {
let rfcToDate = toDate.rfcString
let rfcSelf = self.rfcString
let toDateArray : [String] = rfcToDate.componentsSeparatedByString("T")
let selfArray : [String] = rfcSelf.componentsSeparatedByString("T")
return rfcDate(toDateArray[0]+"T"+selfArray[1])
}
private func sanitizedDates() -> (fromDate: NSDate, toDate: NSDate, sign: Int ) {
var toDate: NSDate = self
var fromDate: NSDate = NSDate()
var sign: Int = 1
// For toDates in the past, results are reasonable, except for sign.
//In future dates, we to flip dates to make them past dates and add 1 minute for unknown reason.
if(toDate.isGreaterThanDate(fromDate)) {
// NSLog("****** Flipping dates ********")
toDate = fromDate.xMins(-1) // In this case, the results are consistently shorter by a minute
fromDate = self
sign = -1
}
return (fromDate,toDate,sign)
}
static func test(months: Int = 0, weeks: Int = 0, days: Int = 0, hrs: Int = 0, mins: Int = 0) {
NSLog("****************** Start Testing of NSDate **************************")
NSLog("Inputs: months:\(months) weeks:\(weeks) days:\(days) hrs: \(hrs) mins: \(mins)")
var today = NSDate()
NSLog("Today is: \(today.timeDayMonthYear)")
var monthsFromToday = today.xMonths(months)
NSLog("** \(months) months from today: \(monthsFromToday.timeDayMonthYear)")
NSLog("monthsFromToday returns: \(monthsFromToday.monthsFromToday)");
NSLog("weeksFromToday returns: \(monthsFromToday.weeksFromToday)");
NSLog("daysFromToday returns: \(monthsFromToday.daysFromToday)");
NSLog("hoursFromToday returns: \(monthsFromToday.hoursFromToday)");
NSLog("minsFromToday returns: \(monthsFromToday.minsFromToday)");
NSLog("relativeDateString returns: \(monthsFromToday.relativeDateString())")
var weeksFromToday = today.xWeeks(weeks)
NSLog("** \(weeks) weeks from today: \(weeksFromToday.timeDayMonthYear)")
NSLog("weeksFromToday returns: \(weeksFromToday.weeksFromToday)");
NSLog("relativeDateString returns: \(weeksFromToday.relativeDateString(capitalizeFirst:true))")
NSLog("minsFromToday returns: \(weeksFromToday.minsFromToday)");
NSLog("monthsFromToday returns: \(weeksFromToday.monthsFromToday)");
NSLog("weeksFromToday returns: \(weeksFromToday.weeksFromToday)");
NSLog("daysFromToday returns: \(weeksFromToday.daysFromToday)");
NSLog("hoursFromToday returns: \(weeksFromToday.hoursFromToday)");
NSLog("minsFromToday returns: \(weeksFromToday.minsFromToday)");
var daysFromToday = today.xDays(days)
NSLog("** \(days) days from today: \(daysFromToday.timeDayMonthYear)")
NSLog("daysFromToday returns: \(daysFromToday.daysFromToday)");
NSLog("relativeDateString returns: \(daysFromToday.relativeDateString())")
NSLog("minsFromToday returns: \(daysFromToday.minsFromToday)");
NSLog("monthsFromToday returns: \(daysFromToday.monthsFromToday)");
NSLog("weeksFromToday returns: \(daysFromToday.weeksFromToday)");
NSLog("daysFromToday returns: \(daysFromToday.daysFromToday)");
NSLog("hoursFromToday returns: \(daysFromToday.hoursFromToday)");
NSLog("minsFromToday returns: \(daysFromToday.minsFromToday)");
var hrsFromToday = today.xHours(hrs)
NSLog("** \(hrs) hours from today: \(hrsFromToday.timeDayMonthYear)")
NSLog("hoursFromToday returns: \(hrsFromToday.hoursFromToday)");
NSLog("minsFromToday returns: \(hrsFromToday.minsFromToday)");
NSLog("monthsFromToday returns: \(hrsFromToday.monthsFromToday)");
NSLog("weeksFromToday returns: \(hrsFromToday.weeksFromToday)");
NSLog("daysFromToday returns: \(hrsFromToday.daysFromToday)");
NSLog("hoursFromToday returns: \(hrsFromToday.hoursFromToday)");
NSLog("minsFromToday returns: \(hrsFromToday.minsFromToday)");
NSLog("relativeDateString returns: \(hrsFromToday.relativeDateString(capitalizeFirst:true))")
var minsFromToday = today.xMins(mins)
NSLog("** \(mins) minutes from today: \(minsFromToday.timeDayMonthYear)")
NSLog("minsFromToday returns: \(minsFromToday.minsFromToday)");
NSLog("monthsFromToday returns: \(minsFromToday.monthsFromToday)");
NSLog("weeksFromToday returns: \(minsFromToday.weeksFromToday)");
NSLog("daysFromToday returns: \(minsFromToday.daysFromToday)");
NSLog("hoursFromToday returns: \(minsFromToday.hoursFromToday)");
NSLog("minsFromToday returns: \(minsFromToday.minsFromToday)");
NSLog("relativeDateString returns: \(minsFromToday.relativeDateString())")
NSLog("__________________ End Testing of NSDate _________________________")
}
}