I'm facing some problems to get the weekOfYear for a valid DateComponents value. This code
let cal = Calendar.current
let dc = DateComponents(calendar:cal, year: 2023, month: 1, day:12)
let woy = dc.weekOfYear
print("Week of year: \(woy)")
generates
Week of year: nil
as an output.
I've expected 2...
DateComponents is a simple type. It is merely a collection of date components. It does not do date computations like calculating the week of year. That's the job of Calendar. You did not give it a week of year when you initialise the date components, so you get nil when you ask for it.
You can ask the calendar to make a Date out of the DateComponents you have, and ask it what the week of year is:
let cal = Calendar.current
let dc = DateComponents(calendar:cal, year: 2023, month: 1, day:12)
if let date = cal.date(from: dc) {
// prints 2 as expected
print(cal.component(.weekOfYear, from: date))
}
Related
Is there a way to get the exact number of weeks in a given year in Swift (e.g. 52 or 53)?
In my region (week starts with Monday, min 4 days in first week), we get 52 weeks (e.g. 2019, 2021-2025) or 53 weeks (e.g. 2020, 2026). That's my basic for the background of my question.
You can get number of weeks by combining Calendar and DateComponents like this:
let year = 2023
let calendar = Calendar.current
let dateComponents = DateComponents(calendar: calendar, year: year)
let date = calendar.date(from: dateComponents)!
let range = calendar.range(of: .weekOfYear, in: .year, for: date)!
let numberOfWeeks = range.count
After creating DateComponents() and fetching the Calendar.current and the Date(), I altered some date components and created a new date variable based on the altered components. The value of this variable displays correctly in the sidebar on Xcode playground but when I print this value it shows the wrong date. The same happens in the app code too (i.e. not just in the Playground).
// Initial set-up
var dateComponents = DateComponents()
let date = Date()
let calendar = Calendar.current
// Getting some components
var month = calendar.component(.month, from: date)
var year = calendar.component(.year, from: date)
var day = calendar.component(.day, from: date)
// Some sample code
if day > 15 {
day = 1
if month != 12 {
month += 1
} else {
month == 1
}
} else {
day = 15
}
// Create date from components
// Generic
dateComponents.hour = 00
dateComponents.minute = 00
// Specific
dateComponents.day = day
dateComponents.month = month
dateComponents.year = year
// Finalise the next resetDate
let resetDate = calendar.date(from: dateComponents)
resetDate // Displays correct value in the side bar
print(resetDate!) // Prints wrong value
The expected result of course is that the value of resetDate in both the last two lines should agree. On 31 August 2019 this is what I get in the sidebar:
"Sep 1, 2019 at 12:00 AM"
versus what the print statement displays:
2019-08-31 18:30:00 +0000
Output is correct. Please note that print date object direct alway received result on timezone +0 UTC. In your brain, just plus with your timezone is time in your timezone.
I found that DateFormatter date(from:) method can't parse a couple of specific dates. Method returns nil for the 1st april of 1981-1984 years. Is it a bug of Foundation? What can we do to perform parsing of such dates?
Xcode 8.0, iOS SDK 10.0. Here is a screenshot of a short playground example:
This problem occurs if daylight saving time starts exactly on
midnight, as it was the case in Moscow in the years 1981–1984 (see for example Clock Changes in Moscow, Russia (Moskva)).
This was also observed in
Why does NSDateFormatter return nil date for these 4 time zones? and
Why NSDateFormatter is returning null for a 19/10/2014 in a Brazilian time zone?
For example, at midnight of April 1st 1984, the clocks were adjusted one hour forward, which means that the date "1984-04-01 00:00"
does not exist in that timezone:
let dFmt = DateFormatter()
dFmt.dateFormat = "yyyy-MM-dd"
dFmt.timeZone = TimeZone(identifier: "Europe/Moscow")
print(dFmt.date(from: "1984-04-01")) // nil
As a solution, you can tell the date formatter to be "lenient":
dFmt.isLenient = true
and then it will return the first valid date on that day:
dFmt.isLenient = true
if let date = dFmt.date(from: "1984-04-01") {
dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dFmt.string(from: date))
}
// 1984-04-01 01:00:00
A different solution
was given by rob mayoff, which is to make the date formatter use noon instead of midnight as the
default date. Here is a translation of rob's code from Objective-C to Swift:
let noon = DateComponents(calendar: dFmt.calendar, timeZone: dFmt.timeZone,
year: 2001, month: 1, day: 1, hour: 12, minute: 0, second: 0)
dFmt.defaultDate = noon.date
if let date = dFmt.date(from: "1984-04-01") {
dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dFmt.string(from: date))
}
// 1984-04-01 12:00:00
I have a question regarding the Calendar class in Swift 3:
Basically, I wanted to get the Date of the next Sunday. However, the following code gives me the wrong output:
let cal = Calendar.current //gregorian
let today = Date(timeIntervalSinceNow: 0)
let date_c = DateComponents(calendar: cal, weekday: 1)
let today_weekday = cal.component(Calendar.Component.weekday, from: today)
let next_sunday = cal.nextDate(after: today, matching: date_c, matchingPolicy: Calendar.MatchingPolicy.nextTime)!
print(today_weekday) //prints out: 1 (today is Sunday, that's true)
print(next_sunday) //prints out: 2017-10-28 which is a Saturday
Please note that I tested the code yesterday, that's why I wrote "today is Sunday".
I was expecting the second print output to be the 2017-10-29 (next Sunday) however, it is giving me Saturday. What am I doing wrong?
Dates are always printed as if they are in the GMT timezone.
Suppose your time zone is UTC+1, then next Sunday will be 29-10-2017 00:00:00 in your time zone. However, this Date instance is expressed as 28-10-2017 23:00:00 in GMT.
So nextDate does return the correct date, it's just not formatted in your time zone. To fix this, just use a DateFormatter:
let formatter = DateFormatter()
formatter.dateStyle = .short
print(formatter.string(from: next_sunday))
This question already has answers here:
first and last day of the current month in swift
(11 answers)
Closed 6 years ago.
I have spent almost a week with unsuccessful tries. How do I get the last date of the current month. For example if it is February in non-leap year then the last date of the month must be 28. How do get the '28'. Another example: If it is January then the last date must be '31'
There are many ways, here are two of them:
Get next 1. and subtract one day
func lastDayOfMonth1() -> Date
{
let calendar = Calendar.current
let components = DateComponents(day:1)
let startOfNextMonth = calendar.nextDate(after:Date(), matching: components, matchingPolicy: .nextTime)!
return calendar.date(byAdding:.day, value: -1, to: startOfNextMonth)!
}
print(lastDayOfMonth1())
Use range(of:in:for:) to get the last day from the range and set the components accordingly:
func lastDayOfMonth2() -> Date
{
let calendar = Calendar.current
let now = Date()
var components = calendar.dateComponents([.year, .month, .day], from: now)
let range = calendar.range(of: .day, in: .month, for: now)!
components.day = range.upperBound - 1
return calendar.date(from: components)!
}
print(lastDayOfMonth2())