I have a date in string format, "yyyy-MM-dd" and would like to return an array of the difference in dates in that same format from today.
For example, the given date is "2019-06-29", and today's date is 2019-06-25. The returned array would contain: ["2019-06-25", "2019-06-26", "2019-06-27", "2019-06-28", "2019-06-29"].
The method I am trying to write needs to also work cross-months/years. Is something like this possible in Swift?
What I have tried: calculating the difference in dates numerically (difference of days) and adding a day to the given date until it reaches today's date. This is what brought on the issue of exceeding 30/31 days and not moving to the next months/exceeding 2019-12-31 and not moving to 2020. Surely there is a simpler concise way to achieve this result without having to write that date logic manually?
extension Formatter {
static let date: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.calendar = Calendar(identifier: .iso8601)
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter
}()
}
extension Date {
var noon: Date {
return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
}
func dates(for date: String) -> [String] {
// For calendrical calculations you should use noon time
// So lets get endDate's noon time
guard let endDate = Formatter.date.date(from: date)?.noon else { return [] }
// then lets get today's noon time
var date = Date().noon
var dates: [String] = []
// while date is less than or equal to endDate
while date <= endDate {
// add the formatted date to the array
dates.append( Formatter.date.string(from: date))
// increment the date by one day
date = Calendar.current.date(byAdding: .day, value: 1, to: date)!
}
return dates
}
dates(for: "2019-06-29") // ["2019-06-25", "2019-06-26", "2019-06-27", "2019-06-28", "2019-06-29"]
Related
I'm using this extension to convert a string containing date to Date() object:
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/YYYY"
let date = dateFormatter.date(from: self)
return date
}
}
the result always containing a time in it. I'm curious where is the time coming from, why it is not all zero?
print("11/12/2021".toDate())
result is ->
2020-12-19 21:00:00 +0000
In the time that I run the code, it is showing 21:00:00, so why it is 21? I believe It is not related to my time because I run it at different times.
A Date object indicates an instant in time anywhere on the planet, independent of time zone.
A DateFormatter can convert a String to a Date (or a Date to a String, but ignore that for now). When it converts a String to a Date, it may make assumptions about the time of day if that is not included in the String. I believe it assumes that the time is midnight (00:00:00) in the date formatter's time zone. (And by the way, midnight is the starting point of a day, so midnight is zero hours/minutes/seconds into the day. Essentially midnight IS zeros for time.)
So when you call your String extension to convert "11/12/2021" to a Date, the extension creates a DateFormatter which defaults to the device time zone. It creates a Date assuming Midnight in the local time zone.
When you print that date, it gets displayed in GMT.
It looks like your format string has problems though. You're getting the wrong year and month. I think you must be using the wrong month or day string in your formatter. (I always have to look those up when I use them.)
Edit:
You likely want a format string of "MM-dd-yyyy"
(2-digit month, 2-digit day of month, and 4-digit year.)
Lower-case "m" or "mm" is minutes. Upper-case "Y" is for "week of year" based calendars, which you probably don't want.
Try this code:
func toDate() -> Date?{
let dateFormatter = DateFormatter()
let posixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM-dd-yyyy"
dateFormatter.locale = posixLocale
let date = dateFormatter.date(from: self)
return date
}
}
And to use it:
let dateString = "12/11/2021"
let date = dateString.toDate()
print(date)
if let date = date {
let convertedDateString = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .medium)
print(convertedDateString)
} else {
print("Can't convert \(dateString) to a date")
}
That displays "Dec 11, 2021 at 12:00:00 AM" in my US locale (US Date formatting.) Note that since I use the DateFormatter class method localizedString(from:dateStyle:timeStyle:) I see midnight as the displayed time (The time you get from a DateFormatter when you don't specify a time, but displayed in the local time zone.)
The answer is:
when we are converting a string to a Date Object the important part is the time zone that we are converting it to.
for example, if you convert your string date to a UTC time zone when you want to bring it back you have to set the time zone of the date to UTC.
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(identifier: "UTC")
so this is the reason why when we are printing the Date() object it is deferred from our string date.
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
let date = dateFormatter.date(from: self)
return date
}
}
extension Date {
func toString() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
return dateFormatter.string(from: self)
}
}
let stringDate = "01-12-2021"
let date = "01-12-2021".toDate()
let convertBack = date?.toString()
print("(\(stringDate)) -> (\(date!)) -> (\(convertBack!))")
and the result is:
(01-12-2021) -> (2021-01-11 21:01:00 +0000) -> (01-12-2021)
so at the end when we convert back the Date object it will be the same. because that 2 dateFormatter in the extensions are using the default time zone. and if you want to specify a specific time zone you have to declare it in converting from and to string together.
I need to compare two Date object to get the day difference between them, for example: 10/10 compares with today 10/7 will be 3, but the Date object returned to me from server is not aligned with the current time. There will be a few minutes difference which results in 10/10 being 2 days ahead of 10/7 because of the delay
I found a line of code that can give me a Date object of the current time, but I want to convert an existing Date object from somewhere else, how do I do it?
let today = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date())!
e.g. 2020-10-08 16:08:57.259580+0000 I want it to be 2020-10-08 00:00:00 +0000 something like this
Don’t use midnight. Just parse your date string first. For calendrical calculations you should always use noon. First create a custom date format to parse your date string properly.
extension Formatter {
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = .init(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSSSSxx"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
}
Then create a helper to convert your date to noon time and another one to calculate the days between two dates and set them to noon:
extension Date {
var noon: Date {
Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
func days(from date: Date) -> Int {
Calendar.current.dateComponents([.day], from: date.noon, to: noon).day!
}
var daysFromToday: Int { days(from: Date()) }
}
Playground testing:
let dateString = "2020-10-08 16:08:57.259580+0000"
let now = Date() // "Oct 8, 2020 at 5:56 AM"
print(Formatter.iso8601.string(from: now)) // "2020-10-08 08:56:46.179000+0000\n"
if let date = Formatter.iso8601.date(from: dateString) { // "Oct 8, 2020 at 1:08 PM"
let days = Date().days(from: date) // 0
}
let dateString = "2020-10-10 16:08:57.259580+0000"
if let date = Formatter.iso8601.date(from: dateString) {
let days = date.daysFromToday // 2
}
via dateformatter, how can I write a function to know if a specific date has passed or is today?
example: March 8, 2020
Date()
if Date() >= 28March2020 {
return true
} else {
return false
}
thanks
You can do:
if Date() >= Calendar.current.dateWith(year: 2020, month: 3, day: 28) ?? Date.distantFuture {
return true
} else {
return false
}
where dateWith(year:month:day:) is defined as:
extension Calendar {
func dateWith(year: Int, month: Int, day: Int) -> Date? {
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
return date(from: dateComponents)
}
}
This method basically returns the Date with the specified year, month, and day, with the hour, minute, and second components all being 0, that is, start of the specified day. In other words, I am checking whether the instant now is after the start of the day 2020-03-28.
The easiest way and most flexible is to convert it to seconds and then compare.
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
let today = Date().timeIntervalSince1970
let date1 = formatter.date(from: "2022-10-01")!.timeIntervalSince1970
let date2 = formatter.date(from: "2022-10-02")!.timeIntervalSince1970
if today >= date1 && today <= date2 {
}
I am trying to convert fajerTime to NSDate. When I compile the project the dateValue is nil. Any idea how to fix this issue?
if prayerCommingFromAdan.id == 0 && prayerCommingFromAdan.ringToneId != 0{
// NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name:"NotificationIdentifier", object: nil)
let fajerTime = "\(prayer0.time[0...1]):\(prayer0.time[3...4])" as String
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
// convert string into date
let dateValue = dateFormatter.dateFromString(fajerTime) as NSDate!
print(dateValue)
var dateComparisionResult:NSComparisonResult = NSDate().compare(dateValue)
if dateComparisionResult == NSComparisonResult.OrderedDescending {
addNotificationAlarm(year, month: month, day: day, hour: prayer0.time[0...1], minutes: prayer0.time[3...4], soundId: prayerCommingFromAdan.ringToneId, notificationBody: "It is al fajr adan")
}
The problem seems be the format of fajerTime. It looks like fajerTime is a time string, e.g. 12:34, whereas the date formatter is configured to accept string containing a month, day and year, e.g. 24-07-2016.
You need to format fajerTime to include the year, month and day, as well as the time. Also configure the date formatter to accept the full date and time.
Assuming prayer0 is an array, you will also need to combine the elements into a string, using joinWithSeparator.
e.g.
let hours = prayer0.time[0...1].joinWithSeparator("")
let minutes = prayer0.time[3...4].joinWithSeparator("")
let fajerTime = "\(month)-\(day)-\(year) \(hours):\(minutes)"
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy hh:mm"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
// convert string into date
let dateValue = dateFormatter.dateFromString(fajerTime) as NSDate!
Please follow example (Swift 3):
let dateStr = "2016-01-15 20:10:01 +0000"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let myDate = dateFormatter.date(from: dateStr)
Keep in mind:
Date format e.g. "yyyy-MM-dd HH:mm:ss Z" must match the date string pattern
In your case, your date string contains only time, yet, your date
formatter contains only date
When using dateFormatter.date() there is no need to cast it to Date as it returns a Date:
Helpful website for date formats:
http://nsdateformatter.com/
I'm getting information from an API and one of the fields is a DateTime in this format:
2014-12-12T14:44:18.973
I would like to remove the "T" and anything after it so in the end, I get:
2014-12-12
Any ideas or leads on how to do this with Swift? Unfortunately, I can't just create an NSDate out of directly as it throws an Exception.
In the end, I want to have something like 12-12-2014 in a string format.
Thanks!
Fortunately, You CAN just create an NSDate out of it directly as follow:
extension String {
func toDateFormattedWith(format:String)-> NSDate {
let formatter = NSDateFormatter()
// formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) // you can set GMT time
formatter.timeZone = NSTimeZone.localTimeZone() // or as local time
formatter.dateFormat = format
return formatter.dateFromString(self)!
}
}
There is many ways to pick just the date from that string. I would use the following approach:
let myDate = "2014-12-12T14:44:18.973".componentsSeparatedByString("T")[0].toDateFormattedWith("yyyy-MM-dd") // "Dec 12, 2014, 12:00 AM"
You can also create an extension to extract that info from the Date object:
public extension NSDate {
var day: Int { return NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitDay, fromDate: self).day }
var month: Int { return NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitMonth, fromDate: self).month }
var year: Int { return NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitYear, fromDate: self).year }
var dateAt12am: NSDate {
return NSCalendar.currentCalendar().dateWithEra(1, year: year, month: month, day: day, hour: 0, minute: 0, second: 0, nanosecond: 0)!
}
}
let myDate1 = "2014-12-12T14:44:18.973".toDateFormattedWith("yyyy-MM-dd\'T\'HH:mm:ss.SSS").dateAt12am
println(myDate1) // "2014-12-12 02:00:00 +0000" (12am local time)
If you need reference you can use this:
This actually isn't too bad. What you're looking for is rangeOfString() to find out where "T" is. If you call .startIndex on that, you can find out where the first occurrence of that "T" is.
Once you have that, you can just create a substring from the beginning of the string to that point like this:
let date: String = dateTime.substringWithRange(Range<String.Index>(start: dateTime.startIndex, end: dateTime.rangeOfString("T")!.startIndex))
If you wanted to take that and turn it into an NSDate, you could use an NSDateFormatter:
let formatter: NSDateFormatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let newDate: NSDate = formatter.dateFromString(date)! // "Dec 12, 2014, 12:00 AM"
If you don't want that time piece at the end and just want to output the date in a different style than that returned by the API, you could add this:
formatter.dateStyle = .ShortStyle // Change this to modify the style of the date that is returned in the next line
let formattedDate: String = formatter.stringFromDate(newDate) // "12/12/14"