I have a an array of objects that contains a date value. I have calculated the date differences and returned the number of days left. Now I am trying to sort it so it appends based on the object with least number of days left.
I have been able to use this function:
func sortList() {
item.sort { (first: Item, second: Item) -> Bool in
return first.days() < second.days()
}
}
Which gives me this:
However as you can see the date which is equal to 0 is appended at the bottom.
This is how I am calculating the days difference:
func daysDiff(startDate: Date, endDate: Date) -> Int {
let calendar = Calendar.current
let date1 = calendar.startOfDay(for: startDate)
let date2 = calendar.startOfDay(for: endDate)
let a = calendar.dateComponents([.day], from: date1, to: date2)
return a.value(for: .day)!
}
And this is how I am formatting it:
func days() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "MM dd, yyyy"
let date = formatter.date(from: itemDate!)
let date1 = Date()
let date2 = date
let days = daysDiff(startDate: date1, endDate: date2!)
if days > 1 {
return "\(days) days left"
} else if days == 1 {
return "a day left"
} else if days == 0 {
return "Due today!"
} else if days < 0 {
return "Late"
} else {
return "\(days)"
}
}
I am not really sure why this issue is happening.
Your sort is based on the text from your days() function so you are sorting the data alphabetically based on your text. You should sort based on an actual integer value, not a string.
You should have two methods on your class.
days() which returns an Int instead of a String.
daysLabel which returns a String based on the result of days.
Use days when sorting by number. Use daysLabel when displaying an Item instance somewhere.
func days() -> Int {
let formatter = DateFormatter()
formatter.dateFormat = "MM dd, yyyy"
let date = formatter.date(from: itemDate!)
let date1 = Date()
let date2 = date
let days = daysDiff(startDate: date1, endDate: date2!)
return days
}
func daysLabel() -> String {
let days = days()
if days > 1 {
return "\(days) days left"
} else if days == 1 {
return "a day left"
} else if days == 0 {
return "Due today!"
} else if days < 0 {
return "Late"
} else {
return "\(days)"
}
}
Related
In my application i have an option to enter the data for every 15days.I have to maintain this for an current year.Please help me to figure out this problem.
For ex: [
"1-1-2018 to 15-1-2018", "16-1-2018 to 31-1-2018",
"1-2-2018 to 15-2-2018", "16-2-2018 to 28-2-2018",
"1-3-2018 to 15-3-2018", "16-3-2018 to 31-3-2018",
"1-4-2018 to 15-4-2018", "16-4-2018 to 30-4-2018",
"1-5-2018 to 15-5-2018", "16-5-2018 to 31-5-2018",
"1-6-2018 to 15-6-2018", "16-6-2018 to 30-6-2018",
"1-7-2018 to 15-7-2018", "16-7-2018 to 31-7-2018",
"1-8-2018 to 15-8-2018", "16-8-2018 to 31-8-2018",
"1-9-2018 to 15-9-2018", "16-9-2018 to 30-9-2018",
"1-10-2018 to 15-10-2018", "16-10-2018 to 31-10-2018",
"1-11-2018 to 15-11-2018", "16-11-2018 to 30-11-2018",
"1-12-2018 to 15-12-2018", "16-12-2018 to 31-12-2018"
]
From Calendar API you can get total number of days for any month in the given date and also first day of the month like below,
extension Calendar {
public func firstDayOfMonth(date: Date) -> Date {
let components = self.dateComponents([.year, .month], from: date)
return self.date(from: components) ?? date
}
public func numberOfDaysInMonthFor(date: Date) -> Int {
let range = self.range(of: .day, in: .month, for: date)
return range?.count ?? 0
}
public func lowerHalfOfMonthFor(date: Date) -> (Date, Date) {
let startDate = self.firstDayOfMonth(date: date)
let endDate = startDate.dateByAppending(day: 14)
return (startDate, endDate)
}
public func upperHalfOfMonthFor(date: Date) -> (Date, Date) {
let firstDayOfMonthDate = self.firstDayOfMonth(date: date)
let totalNoOfDaysInMonth = self.numberOfDaysInMonthFor(date: firstDayOfMonthDate)
let startDate = firstDayOfMonthDate.dateByAppending(day: 15)
let endDate = firstDayOfMonthDate.dateByAppending(day: totalNoOfDaysInMonth - 1)
return (startDate, endDate)
}
}
you can also extend Date to get new date by appending any number of days,
extension Date {
public func dateByAppending(day: Int) -> Date {
let newDate = Calendar.current.date(byAdding: .day, value: day, to: self)
return newDate ?? self
}
public func daysDifference(_ date: Date?) -> Int? {
guard let date = date else { return nil }
return Calendar.current.dateComponents([.day], from: self, to: date).day
}
With the mix of above helper methods, you should be able to achieve the required result like below,
let date = Date()
let lowerHalf = Calendar.current.lowerHalfOfMonthFor(date: date)
let uppperHalf = Calendar.current.upperHalfOfMonthFor(date: date)
Below code will calculate the 15th day if you give the input,
static func getFortnightly(selectedDate : String) -> String?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yy" //Your date format
if let dateSelected = dateFormatter.date(from: selectedDate) {//according to d
let newDate = Calendar.current.date(byAdding: .weekOfYear, value: 2, to: dateSelected)
let convertedDateToString = dateFormatter.string(from: newDate!)
return convertedDateToString
}
return nil
}
}
I have this code:
let miesiacOd : 2017
let rokOd : Int = 10
let dzienOd : Int = 1
let dataOd = String(format: "%02d-%02d-%02d", rokOd, miesiacOd, dzienOd)
let miesiacDo : Int = 2018
let rokDo : Int = 10
let dzienDo : Int = 1
let dataDo = String(format: "%02d-%02d-%02d", rokDo, miesiacDo, dzienDo)
let dateFormatter2 = DateFormatter()
dateFormatter2.dateFormat = "yyyy-MM-dd"
I'm trying to compare it, but I have error. When converting variables to dates:
let dataDo2 = dateFormatter2.date(from: dataDo)
let dataOd2 = dateFormatter2.date(from: dataOd)
I have the date and time as a result. For example: 2017-10-01 +000
Why is this happening and how to fix it?
Finally, I would like to check if the current date is within the above dates.
I'm trying to do it like this:
let sprawdzamDostepnoscDat = Date().isBetweeen(date: dataOd2!, andDate: dataDo2!)
extension Date {
func isBetweeen(date date1: Date, andDate date2: Date) -> Bool {
return date1.timeIntervalSince1970 < self.timeIntervalSince1970 && date2.timeIntervalSince1970 > self.timeIntervalSince1970
}
}
Will this solution be ok?
You don't need a formatter (string parser) to create Date:
var dateFromComponents = DateComponents()
dateFromComponents.year = 2017
dateFromComponents.month = 10
dateFromComponents.day = 1
let dateFrom = Calendar.current.date(from: dateFromComponents)
var dateToComponents = DateComponents()
dateToComponents.year = 2018
dateToComponents.month = 10
dateToComponents.day = 1
let dateTo = Calendar.current.date(from: dateToComponents)
Also note that Date is already comparable, therefore your inBetween function can be just:
extension Date {
func isBetweeen(date date1: Date, andDate date2: Date) -> Bool {
return date1 <= self && self <= date2
}
}
However, if you want to ignore time and just compare the days, you should use:
extension Date {
func isBetweeen(date date1: Date, andDate date2: Date) -> Bool {
return Calendar.current.compare(date1, to: self, toGranularity: .day) != .orderedDescending
&& Calendar.current.compare(self, to: date2, toGranularity: .day) != .orderedDescending
}
}
I have Date() properties. startingAt and endingAt. And an array of Date(), which are alreadyRegistred. I have to create an array of strings with dates between startingAt and endingAt. StartingAt and endingAt are included and the last requirement is to exclude alreadyRegistred dates.
Do you have some elegant idea, how to do it? Thanks for help!
Edit: Maximum number of dates in final array will be about 7 days.
Dont forget that a Date is basically just a timestamp, and that you can have access to the addingTimeInterval(_:) method.
Knowing that, is very easy to do some calculation between two dates.
I do not have the whole knowledge about your required business logic, but here is a naive implementation that generates Dates between two dates. I'm sure you can run it in a playground and explore a little bit.
import UIKit
func intervalDates(from startingDate:Date, to endDate:Date, with interval:TimeInterval) -> [Date] {
guard interval > 0 else { return [] }
var dates:[Date] = []
var currentDate = startingDate
while currentDate <= endDate {
currentDate = currentDate.addingTimeInterval(interval)
dates.append(currentDate)
}
return dates
}
let startingDate = Date() // now
let endDate = Date(timeIntervalSinceNow: 3600 * 24 * 7) // one week from now
let intervalBetweenDates:TimeInterval = 3600 * 3// three hours
let dates:[Date] = intervalDates(from: startingDate, to: endDate, with: intervalBetweenDates)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
let dateStrings = dates.map{dateFormatter.string(from: $0)}
print("NOW : \(startingDate)")
for (index, string) in dateStrings.enumerated() {
print("\(index) : \(string)")
}
print("END DATE : \(endDate)")
Try this and see:
// Start & End date string
let startingAt = "01/01/2018"
let endingAt = "08/03/2018"
// Sample date formatter
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
// start and end date object from string dates
var startDate = dateFormatter.date(from: startingAt) ?? Date()
let endDate = dateFormatter.date(from: endingAt) ?? Date()
// String date array, to be excluded
let alreadyRegistred = ["01/01/2018", "15/01/2018", "10/02/2018", "20/02/2018", "05/03/2018"]
// Actual operational logic
var dateRange: [String] = []
while startDate <= endDate {
let stringDate = dateFormatter.string(from: startDate)
startDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate) ?? Date()
if (alreadyRegistred.contains(stringDate)) {
continue
} else {
dateRange.append(stringDate)
}
}
print("Resulting Array - \(dateRange)")
Here is result:
Resulting Array - ["02/01/2018", "03/01/2018", "04/01/2018", "05/01/2018", "06/01/2018", "07/01/2018", "08/01/2018", "09/01/2018", "10/01/2018", "11/01/2018", "12/01/2018", "13/01/2018", "14/01/2018", "16/01/2018", "17/01/2018", "18/01/2018", "19/01/2018", "20/01/2018", "21/01/2018", "22/01/2018", "23/01/2018", "24/01/2018", "25/01/2018", "26/01/2018", "27/01/2018", "28/01/2018", "29/01/2018", "30/01/2018", "31/01/2018", "01/02/2018", "02/02/2018", "03/02/2018", "04/02/2018", "05/02/2018", "06/02/2018", "07/02/2018", "08/02/2018", "09/02/2018", "11/02/2018", "12/02/2018", "13/02/2018", "14/02/2018", "15/02/2018", "16/02/2018", "17/02/2018", "18/02/2018", "19/02/2018", "21/02/2018", "22/02/2018", "23/02/2018", "24/02/2018", "25/02/2018", "26/02/2018", "27/02/2018", "28/02/2018", "01/03/2018", "02/03/2018", "03/03/2018", "04/03/2018", "06/03/2018", "07/03/2018", "08/03/2018"]
let startDate = Date()
let endDate = Date().addingTimeInterval(24*60*60*10) // i did this to get the end date for now
var stringdateArray = [String]()
if let days = getNumberofDays(date1: startDate, date2: endDate) {
for i in 0...days-1 {
let date = startDate.addingTimeInterval(Double(i)*24*3600)
let stringDate = getStringDate(fromDate: date, havingFormat: "yyyy-MM-dd")
if !(alreadyRegisteredArray.contains(stringDate)) { // checking if already registered
stringdateArray.append(stringDate)
}
}
}
and our helper method
let dateFormatter = DateFormatter()
func getStringDate(fromDate: Date,havingFormat: String) -> String {
dateFormatter.dateFormat = havingFormat
dateFormatter.amSymbol = "AM"
dateFormatter.pmSymbol = "PM"
let date = dateFormatter.string(from: fromDate)
return date
}
func getNumberofDays(date1: Date, date2: Date) -> Int? {
let calendar = NSCalendar.current
let date1 = calendar.startOfDay(for: date1)
let date2 = calendar.startOfDay(for: date2)
let components = calendar.dateComponents([.day], from: date1, to: date2)
return components.day
}
I'm using an api that gives me the following time format:
"071953Z"
07 is the day
19 is the hours
53 are the minutes
Z is Zulu time
As you can see, there is no current month and year, i also want that to be added but i dont know how.
Could you help me to convert this to the current time? Thanks!
I think your best bet is to store the partial-datetime in a DateComponents. Then you can add a month and year to it and use Foundation's calendar API to do what you want:
extension DateComponents {
static func from(str: String) -> DateComponents? {
guard str.characters.count == 7 else {
return nil
}
var components = DateComponents()
components.timeZone = TimeZone(secondsFromGMT: 0)
components.calendar = Calendar(identifier: .gregorian)
let ranges = [0,2,4].map {
return str.index(str.startIndex, offsetBy: $0)..<str.index(str.startIndex, offsetBy: $0 + 2)
}
if let day = Int(str[ranges[0]]) {
components.day = day
} else {
return nil
}
if let hour = Int(str[ranges[1]]), hour < 24 {
components.hour = hour
} else {
return nil
}
if let minute = Int(str[ranges[2]]), minute < 60 {
components.minute = minute
} else {
return nil
}
return components
}
}
// This formatter is to convert the date to your local time. Configure to taste
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
if var components = DateComponents.from(str: "071953Z") {
components.year = 2017
components.month = 4
if let date = components.date {
print(formatter.string(from: date))
} else {
print("\(components) does not make a valid date")
}
}
I'm trying to get the weekday of a date (Wednesday, Tuesday) but I can't seem to find a good way to actually get the string. This is what I'm doing:
internal func fromToday(daysAgo: Date) -> String {
let calendar = NSCalendar.autoupdatingCurrent
let startOfNow = calendar.startOfDay(for: Date())
let startOfTimeStamp = calendar.startOfDay(for: daysAgo)
let numDaysDifference = abs(calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp).day!)
let daysAgoComponents = calendar.dateComponents([.weekday, .hour, .minute], from: daysAgo)
var hour = daysAgoComponents.hour!
var timeOfDay = "AM"
if hour >= 13 {
hour -= 12
timeOfDay = "PM"
}
var res = "\(hour):\(daysAgoComponents.minute!)\(timeOfDay)"
if calendar.isDateInYesterday(daysAgo) {
res = "Yesterday"
} else if numDaysDifference < 8 && numDaysDifference > 1 {
res = stringFromWeekday(day: daysAgoComponents.weekday!) // HERE THIS IS WHAT IM ASKING ABOUT IGNORE OTHER IFS
} else if numDaysDifference >= 8 {
res = stringFromDate(day: daysAgo)
}
return res
}
fileprivate func stringFromWeekday(day: Int) -> String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US")
if formatter.weekdaySymbols.count < day {
print("ConversationTVC - \(day) is not a valid day of the week")
}
return formatter.weekdaySymbols[day]
}
Specifically, the line:
else if numDaysDifference < 8 && numDaysDifference > 1
res = stringFromWeekday(day: daysAgoComponents.weekday!)`
I think the issue has to do with weekdaySymbols
How do I get the string representation of the day of the week X days ago given a Date?
Issue was I wasn't subtracting 1 from day, weekday returns 1-7 and weekday symbols is 0-6