Getting the day of year is straightforward, e.g.
func dayOfYear(inputDate:NSDate) -> (Int) {
let cal = NSCalendar.currentCalendar()
let returnDay = cal.ordinalityOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitYear, forDate: inputDate)
return returnDay
}
But how do you do the reverse? It would obviously return just the day/month. I can easily write a tedious routine back-calculating but is there a smart way?
Yes, NSCalendar provides a way to coalesce calendar components into a single date object. Take a look at this example I wrote in a Playground:
import UIKit
import Foundation
let inputDate: NSDate = NSDate()
let calendar = NSCalendar.currentCalendar()
let day = calendar.ordinalityOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitYear, forDate: inputDate)
let components = NSDateComponents()
components.day = day
let date = calendar.dateFromComponents(components)
According to the documentation,
When there are insufficient components provided to completely specify an absolute time, a calendar uses default values of its choice. When there is inconsistent information, a calendar may ignore some of the components parameters or the method may return nil. Unnecessary components are ignored (for example, Day takes precedence over Weekday and Weekday ordinals).
Furthermore,
Note that some computations can take a relatively long time to perform.
See the NSCalendar Class Reference for more information.
func dayOfYear(inputDate: NSDate) -> Int {
return NSCalendar.currentCalendar().ordinalityOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitYear, forDate: inputDate)
}
func dateFromDayOfYear(day: Int) -> NSDate {
return NSCalendar.currentCalendar().dateWithEra(1, year: NSCalendar.currentCalendar().component(.CalendarUnitYear, fromDate: NSDate()), month: 1, day: day, hour: 0, minute: 0, second: 0, nanosecond: 0)!
}
let numberOfDays = dayOfYear(NSDate()) // 195
dateFromDayOfYear(numberOfDays) // "Jul 14, 2015, 12:00 AM"
Related
I’m trying to create a Date extension static function that accepts two parameters: (month: Int, year: Int) and returns -> Date.
The month that is returned would be its .startOfMonth:
var startOfMonth: Date {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.year, .month], from: self)
return calendar.date(from: components)!
}
I know how to retrieve a month in the future by adding n-months to the current:
func addMonths(_ numMonths: Int) -> Date {
let cal = NSCalendar.current
return cal.date(byAdding: .month, value: numMonths, to: self)!
}
And I suppose this could be used in a roundabout way to determine what I’m looking for, by first determining how many months from the current is the month I’m interested in getting a Date returned for. But I would much prefer if I could retrieve it without needing to go through this step, especially if there’s a possibility that I’m not actually sure if the month is in fact in the future.
You can use DateComponents initializer to compose a new date just passing the month and year components. Just make sure to pass a calendar as well otherwise the result would be nil. Maybe something like:
extension Date {
static func startOfMonth(for month: Int, of year: Int, using calendar: Calendar = .current) -> Date? {
DateComponents(calendar: calendar, year: year, month: month).date
}
}
Date.startOfMonth(for: 2, of: 2021) // "Feb 1, 2021 at 12:00 AM"
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
}
I've looked around, and haven't found what I need.
Here's what I need:
In Swift, I want to create a Date (or NSDate) object that represents a day of the week, and a specific time during that weekday. I don't care about years and months.
This is because I have a system for recurring weekly events (meetings on specific weekdays, at specific times, like "Every Monday at 8PM").
Here's the code I have so far (that doesn't work):
/* ################################################################## */
/**
:returns: a Date object, with the weekday and time of the meeting.
*/
var startTimeAndDay: Date! {
get {
var ret: Date! = nil
if let time = self["start_time"] {
let timeComponents = time.components(separatedBy: ":")
let myCalendar:Calendar = Calendar.init(identifier: Calendar.Identifier.gregorian)
// Create our answer from the components of the result.
let myComponents: DateComponents = DateComponents(calendar: myCalendar, timeZone: nil, era: nil, year: nil, month: nil, day: nil, hour: Int(timeComponents[0])!, minute: Int(timeComponents[1])!, second: nil, nanosecond: nil, weekday: self.weekdayIndex, weekdayOrdinal: nil, quarter: nil, weekOfMonth: nil, weekOfYear: nil, yearForWeekOfYear: nil)
ret = myCalendar.date(from: myComponents)
}
return ret
}
}
Lots of ways to PARSE a date into this, but I want to CREATE a Date object to be parsed later.
Any assistance would be appreciated.
(NS)Date represents an absolute point in time and knows nothing about weekdays, hours, calendar, timezones etc. Internally it is represented
as the number of seconds since the "reference date" Jan 1, 2001, GMT.
If you are working with EventKit then EKRecurrenceRule might be
better suited. It is a class used to describe the recurrence pattern for a recurring event.
Alternatively, store the event just as a DateComponentsValue, and
compute a concrete Date when necessary.
Example: A meeting every Monday at 8 PM:
let meetingEvent = DateComponents(hour: 20, weekday: 2)
When is the next meeting?
let now = Date()
let cal = Calendar.current
if let nextMeeting = cal.nextDate(after: now, matching: meetingEvent, matchingPolicy: .strict) {
print("now:", DateFormatter.localizedString(from: now, dateStyle: .short, timeStyle: .short))
print("next meeting:", DateFormatter.localizedString(from: nextMeeting, dateStyle: .short, timeStyle: .short))
}
Output:
now: 21.11.16, 20:20
next meeting: 28.11.16, 20:00
I'm trying to display the time of something being open based on what day it is. Something like this:
Opening Hours
**Monday: 8:00-17:00**
Tuesday: 8:00-17:00
Wednesday: 8:00-17:00
Thursday: 8:00-17:00
Friday: 8:00-17:00
Saturday: 8:00-13:00
Sunday: closed
Or simply display
Monday: 8:00-17:00
My assumption would be to use switch statements, but what would I need to do to find out what day it is?
Another solution could be:
import Foundation
let today = NSDate()
let dateFormatter = NSDateFormatter()
let currentDay = NSCalendar.currentCalendar().component(.Weekday, fromDate:today);
dateFormatter.dateFormat = "EEEE"
let dayOfWeekString = dateFormatter.stringFromDate(today)
switch currentDay
{
case 2,3,4,5:
print("\(dayOfWeekString): 8:00 - 17:00")
case 6:
print("\(dayOfWeekString): 8:00 - 13:00")
default:
print("\(dayOfWeekString): closed")
}
You can use component(_:fromDate:) to get the week day from the current date. That would look like:
let currentDay = NSCalendar.currentCalendar().component(.Weekday, fromDate:NSDate());
Based on the value you get for currentDay, you can provide the correct opening hours.
You can make use of NSCalendar to get the .Weekday unit as an integer (Sunday through Saturday as 1 ... 7 for the Gregorian calendar).
Given you know the day of the week represented as an Int, rather than using a switch statement, you could use a [Int: String] dictionary for the different opening hours.
let calendar = NSCalendar.currentCalendar()
let today = calendar.component(.Weekday, fromDate: NSDate())
// Gregorian calendar: sunday = 0, monday = 1, ...
let openingHours: [Int: String] = [1: "Sunday: closed", 2: "Monday: 8:00-17:00", 3: "Tuesday: 8:00-17:00"] // ...
print("Opening hours:\n\(openingHours[today] ?? "")")
/* Opening hours:
Monday: 8:00-17:00 */
Another alternative is to create a computed property extension to NSDate() that returns the current weekday as a String
extension NSDate {
var weekday : String {
let formatter = NSDateFormatter()
formatter.dateFormat = "EEEE"
return formatter.stringFromDate(self)
}
}
This can be readily used with a [String: String] dictionary for holding the set of weekday : opening hours:
/* example usage */
let openingHours: [String: String] =
["Sunday": "closed",
"Monday": "8:00-17:00",
"Tuesday": "8:00-17:00"] // ...
let today = NSDate().weekday
print("Opening hours:\n\(today): \(openingHours[today] ?? "")")
/* Opening hours:
Monday: 8:00-17:00 */
Rather than going with switch statements I would prefer a more generic solution. This is also a nice demonstration of leveraging tuples and type aliases for enhancing code expressiveness and readability.
typealias Time = (start: Int, end: Int)
// starting with Sunday
let openTimes: [Time] = [(0,0), (9,17), (9,17), (9,17), (9,17), (9,17), (9,12)]
let flags : NSCalendarUnit = [.Hour, .Weekday]
func isOpenAtTime(date: NSDate) -> Bool {
let time = NSCalendar.currentCalendar().components(flags, fromDate: date)
let openingHours = openTimes[time.weekday - 1]
let hour = time.hour
return hour >= openingHours.start && hour <= openingHours.end
}
You might want to handle a few edge cases as well, but you get the idea.
You could make this work with more granular time by using minutes instead of hours.
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"