I have a date that I need to split in some components. for example
let components = NSCalendarUnit.CalendarUnitDay | NSCalendarUnit.CalendarUnitHour
let date = calendar.components(components, fromDate: aDate, toDate: NSDate(), options: nil)
var dateToPrint = "\(date.day) days \(date.hour) hours"
dateToPrint will be the number of days and hours from aDate to now. But if i want the number of weeks instead of days
let components = NSCalendarUnit.CalendarUnitWeek | NSCalendarUnit.CalendarUnitHour
let date = calendar.components(components, fromDate: aDate, toDate: NSDate(), options: nil)
var dateToPrint = "\(date.week) weeks \(date.hour) hours"
date.week does not exist. So how I could resolve this?
Xcode 8.3.2 • Swift 3.1
extension Date {
func xDays(_ x: Int) -> Date {
return Calendar.current.date(byAdding: .day, value: x, to: self)!
}
func xWeeks(_ x: Int) -> Date {
return Calendar.current.date(byAdding: .weekOfYear, value: x, to: self)!
}
var weeksHoursFromToday: DateComponents {
return Calendar.current.dateComponents( [.weekOfYear, .hour], from: self, to: Date())
}
var relativeDateString: String {
var result = ""
if let weeks = weeksHoursFromToday.weekOfYear,
let hours = weeksHoursFromToday.hour,
weeks > 0 {
result += "\(weeks) week"
if weeks > 1 { result += "s" }
if hours > 0 { result += " and " }
}
if let hours = weeksHoursFromToday.hour, hours > 0 {
result += "\(hours) hour"
if hours > 1 { result += "s" }
}
return result
}
}
let today = Date() // "May 1, 2017, 9:29 PM"
let yesterday = Date().xDays(-1) // "Apr 30, 2017, 9:29 PM"
let twoWeeksAgo = Date().xWeeks(-2) // "Apr 17, 2017, 9:29 PM"
let anotherDate = DateComponents(calendar: .current, year: 2013, month: 12, day: 4).date! // "Dec 4, 2013, 12:00 AM"
let anotherDate2 = DateComponents(calendar: .current, year: 2012, month: 12, day: 3).date! // "Dec 3, 2012, 12:00 AM"
yesterday.relativeDateString // "24 hours"
twoWeeksAgo.relativeDateString // "2 weeks"
anotherDate.relativeDateString // "177 weeks and 141 hours"
anotherDate2.relativeDateString // "230 weeks and 21 hours"
yesterday.relativeDateString // "24 hours"
twoWeeksAgo.relativeDateString // "2 weeks"
anotherDate.relativeDateString // "177 weeks and 141 hours"
anotherDate2.relativeDateString // "230 weeks and 21 hours"
I liked the API in Leo's solution and used it. However I ran into problem when I ran it (Jun 13, 2015 9:33 AM PST). The symptoms were:
When the date is in the future, xFromToday function were returning results for -(t-delta) (for example for 1 month in the future the XFromToday functions would return (0, -4, -29, -719, -43199) for x=(month,week,days, hrs, mins) . The relativeDates string would return "1 weeks from today"
When the date is in the past, the results would be for -t, except for relative date string. For example, for one month in the past, I would get (1, 4, 31, 744, 44640). Relative date string was: "4 weeks and 744 hrs"
I cannot paste the test output due to confidentiality, but the code with NSDate.test() is pasted. It also has few other stuff borrowed from another post (quoted in code) and some formatting stuff I wrote.
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 _________________________")
}
}
Here is the "fix" I did. I have added a santizedDates() function that returns "from" and "to" dates , and a sign multiplier (+1/-1). A 1-minute offset is added to date span if the comparison was in the future . It works for the test case. Also produces correct and human readable output from relativeDateString(capitalizeFirst:Bool=false) such as: "today, 1 hour 1 minute in the past" . I have posted a question as to why this behavior happens in the first place here:
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 _________________________")
}
}
You're going to need to make another variable called "calendar" (NSCalendar object) and using the init "currentCalendar" method. Once you have the calendar object defined, call the method under calendar rangeOfUnit:NSWeekCalendarUnit inUnit:NSMonthCalendarUnit forDate:date. Then make a NSInteger called amountOfWeeks equal to the range the above function returns. The length of the range the function returns will be the number of weeks in that month.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With