#ios #swift #charts #nsdateformatter
#iOS #swift #Диаграммы #nsdateformatter
Вопрос:
Мне нужно отобразить текущий месяц / первый день недели на оси X графика. Кто-нибудь может мне помочь с этим? Как я могу получить первый день текущего месяца за все недели.
Ответ №1:
Вы можете создать DateComponents
с помощью компонентов:
year: someYear,
month: someMonth,
weekday: someCalendar.firstWeekday,
weekOfMonth: n
используйте Calendar.date(from:)
, чтобы получить Date
соответствующие этим компонентам. Теперь вам просто нужно изменить n
значение от 1 до 5 и поместить каждый результат в массив.
var firstsOfWeek = [Date]()
let year = 2020
let month = 10
let calendar = Calendar.current
for n in 1...5 {
let dc = DateComponents(
year: year,
month: month,
weekday: calendar.firstWeekday,
weekOfMonth: n
)
firstsOfWeek.append(calendar.date(from: dc)!)
}
Однако date(from:)
вернет дату, которой нет, someMonth
если первая неделя someMonth
находится внутри только частично someMonth
. Например, (при условии, что неделя начинается в воскресенье) первая неделя октября 2020 года начинается 27 сентября и date(from:)
даст вам эту дату, если вы спросите, какой дате соответствует weekOfMonth: 1
.
Если вам не нужна эта дата. просто добавьте флажок, чтобы включать только начало месяца, если это также начало недели, и измените диапазон цикла for на 2...5
:
let firstDayOfMonth = calendar.date(from: DateComponents(year: year, month: month))!
if calendar.component(.weekday, from: firstDayOfMonth) == calendar.firstWeekday {
firstsOfWeek.append(firstDayOfMonth)
}
for n in 2...5 {
...
Но обратите внимание, что если вы сделаете это, результирующий массив иногда будет содержать 4 элемента, а иногда и 5 элементов.
Ответ №2:
Вы можете получить .yearForWeekOfYear для текущей даты, получить диапазон weekOfYear в месяце для этой даты и вернуть все даты в том же месяце для диапазона:
extension Date {
func month(using calendar: Calendar = .current) -> Int {
calendar.component(.month, from: self)
}
func firstWeekdaysInMonth(using calendar: Calendar = .current) -> [Date] {
var components = calendar.dateComponents([.calendar, .yearForWeekOfYear], from: self)
return calendar.range(of: .weekOfYear, in: .month, for: self)?.compactMap {
components.weekOfYear = $0
return components.date?.month(using: calendar) == month(using: calendar) ? components.date : nil
} ?? []
}
}
Другой вариант — получить начало месяца для этой конкретной даты, перечислить даты после этой даты, которая соответствует первому дню недели календаря, и остановить, если результирующая дата не совпадает с месяцем этой даты:
extension Date {
func firstWeekdaysInMonth(using calendar: Calendar = .current) -> [Date] {
let year = calendar.component(.year, from: self)
let month = calendar.component(.month, from: self)
let start = DateComponents(calendar: calendar, year: year, month: month).date!
let matching = DateComponents(weekday: calendar.firstWeekday)
var dates: [Date] = []
calendar.enumerateDates(startingAfter: start, matching: matching, matchingPolicy: .strict) { date, _, stop in
guard let date = date, calendar.component(.month, from: date) == month else {
stop = true
return
}
dates.append(date)
}
return dates
}
}
let dates = Date().firstWeekdaysInMonth() // "Oct 4, 2020 at 12:00 AM", "Oct 11, 2020 at 12:00 AM", "Oct 18, 2020 at 12:00 AM", "Oct 25, 2020 at 12:00 AM"]
let august = DateComponents(calendar: .current, year: 2020, month: 8, day: 10).date!
august.firstWeekdaysInMonth() // ["Aug 2, 2020 at 12:00 AM", "Aug 9, 2020 at 12:00 AM", "Aug 16, 2020 at 12:00 AM", "Aug 23, 2020 at 12:00 AM", "Aug 30, 2020 at 12:00 AM"]