Swift Date Formatter setting an strange day (86) [duplicate] - swift

This question already has answers here:
Swift - Date formatting (DD vs dd)
(3 answers)
Difference between 'YYYY' and 'yyyy' in NSDateFormatter
(4 answers)
Closed 11 months ago.
Building a MacOS app.
Not sure where this is going off the rails. I am trying to get the start and end of the week (Sunday/Saturday) and am using this function:
func getStartAndEndOfWeek(_ dateToCheck:Date) {
//vars
var sundayDiff = 0
var saturdayDiff = 0
//set up a date formatter
var formatter:DateFormatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "YYYY-MM-DD"
//get day of this startDate
let calendar = Calendar.current
let dayComponents = calendar.dateComponents([.weekday], from: Date())
if let dayOfWeek = dayComponents.weekday {
sundayDiff = dayOfWeek - 1
saturdayDiff = 7 - dayOfWeek
}
//get sunday as date
let sunday:Date = Calendar.current.date(byAdding: .day, value: -1 * sundayDiff, to: dateToCheck)!
let saturday:Date = Calendar.current.date(byAdding: .day, value: saturdayDiff, to: dateToCheck)!
let strSunday = formatter.string(from: sunday)
}
printing sunday gives me the correct date (current date is Wednesday, March 30, 2022):
2022-03-27 15:36:45 +0000
converting sunday to a string gives me a day of 86
2022-03-86
What am I doing wrong?

The correct syntax is:
formatter.dateFormat = "yyyy-MM-dd"
Lower or upper case matters here.

The issue there is that D uppercased means the day of the year not the day of the month. Note also that Y uppercased is for yearForWeekOfYear.
If you need further reference you can check this

Related

Swift - Problem converting year + weekOfYear components to Date [duplicate]

When I run this code:
let calendar = Calendar.current
var dateComponents = DateComponents()
dateComponents.weekday = calendar.firstWeekday
dateComponents.weekOfYear = 2
dateComponents.year = 2017
let startOfWeek = calendar.date(from: dateComponents)
let endOfWeek = calendar.date(byAdding: .day, value: 6, to: startOfWeek!)
let formatter = DateFormatter()
formatter.dateStyle = .short
print(formatter.string(from: startOfWeek!))
print(formatter.string(from: endOfWeek!))
It prints this:
1/8/17
1/14/17
When I change the code to this:
dateComponents.weekOfYear = 1
dateComponents.year = 2017
It prints this:
12/31/17
1/6/18
Why is it 12/31/17?
When I use .full style to print the dates, I get Sunday, December 31, 2017 for the first date, but it's obviously wrong because December 31 is a Thursday.
If you want to get the correct date, use yearForWeekOfYear instead of year. Docs:
You can use the yearForWeekOfYear property with the weekOfYear and weekday properties to get the date corresponding to a particular weekday of a given week of a year. For example, the 6th day of the 53rd week of the year 2005 (ISO 2005-W53-6) corresponds to Sat 1 January 2005 on the Gregorian calendar.
Alternative, you can be a little naughty and not listen to the docs and use weekOfYear = 54:
let calendar = Calendar.current
var dateComponents = DateComponents()
dateComponents.weekday = calendar.firstWeekday
dateComponents.weekOfYear = 54
dateComponents.year = 2017
let startOfWeek = calendar.date(from: dateComponents)
let endOfWeek = calendar.date(byAdding: .day, value: 6, to: startOfWeek!)
let formatter = DateFormatter()
formatter.dateStyle = .short
print(formatter.string(from: startOfWeek!))
print(formatter.string(from: endOfWeek!))
This prints:
1/1/17
1/7/17
which is coincidentally, the correct dates.

Is there a more efficient way to combine a date and time in swift 5? [duplicate]

This question already has an answer here:
How to combine two strings (date & time) into a new date in Swift 3
(1 answer)
Closed 2 years ago.
I have function which receives 2 strings the first is a date "y-M-d" and the second a time "HH:mm". I then combine them using the following code.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "y-M-d"
let date = dateFormatter.date(from: dateStr)!
dateFormatter.dateFormat = "HH:mm"
let time = dateFormatter.date(from: timeStr)!
let calendar = Calendar.init(identifier: .iso8601)
let components = NSDateComponents()
components.day = calendar.component(.day, from: date) //split from date above
components.month = calendar.component(.month, from: date)
components.year = calendar.component(.year, from: date)
components.hour = calendar.component(.hour, from: time) //split from time above
components.minute = calendar.component(.minute, from: time)
let newDate = calendar.date(from: components as DateComponents)
The code all works fine and is doing what I want it to. However, I was wondering if anyone can suggest a slicker way of doing it, using less lines of code?
You can join the date & time strings and parse them in one go:
let dateStr = "2020-03-12"
let timeStr = "15:35"
let df = DateFormatter()
df.dateFormat = "y-M-d HH:mm"
let date = df.date(from: dateStr + " " + timeStr)
// prints: 2020-03-12 13:35:00 +0000 (my machine is GMT+2)
Edit: As Leo Dabus said in the comments, a more appropriate format for the provided strings should be yyyy-MM-dd HH:mm (just kept the provided format from the question). The spirit of the answer was not to propose a format but to provide a way to avoid parsing date/time separately.
Simply get a combined String using date and time. Then use that String to get the Date instance from it.
func getDate(d: String, t: String) -> Date? {
let str = d + t
let formatter = DateFormatter()
formatter.dateFormat = "y-M-dHH:mm"
let date = formatter.date(from: str)
return date
}

Get percentage between two dates as a float in Swift

So I'm currently trying to add a feature that given two dates, will tell me how far through I am.
TLDR:
So, let's say I have a date that is July 1st, 2017 20:00:00 and another that is July 2nd, 2017 22:00:00 and today is July 2nd, 08:00:00, then I will get that I am 46.17% of the way through.
The way I tried to do this is using a simple formula:
progress = (current time - start time) / (end time - start time)
but when put into code, I can subtract two dates, and get the DateComponent difference, but I cannot divide two DateComponent's. Here is my code set up, with an extension t
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let startDate = formatter.date(from: "2017-07-01 20:00:00")
let endDate = formatter.date(from: "2017-07-02 22:00:00")
let currentDate = formatter.date(from: "2017-07-01 08:00:00")
let progress = (currentDate - startDate) / (endDate - startDate)
extension Date {
static func - (date1: Date, date2: Date) -> DateComponents {
let calender:Calendar = Calendar.current
return calender.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date1, to: date2)
}
}
There has to be a way around dividing two dates. I can't think of it though, everything I find online (using different languages) has required division between two dates.
I tried to convert everything to seconds and just divide that, but I didn't know what to do with the seconds to convert them back to a DateComponent because there might be a 5000 second difference. Any help appreciated!
You just need to turn the dates into numbers, because then you can add and subtract them.
You can use the timeIntervalSince1970 to turn the date into numbers, then you can use your formula:
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let startDate = formatter.date(from: "2017-07-01 20:00:00")!.timeIntervalSince1970
let endDate = formatter.date(from: "2017-07-02 22:00:00")!.timeIntervalSince1970
let currentDate = formatter.date(from: "2017-07-02 08:00:00")!.timeIntervalSince1970
let percentage = (currentDate - startDate) / (endDate - startDate)
Alternatively, use timeIntervalSince(_:):
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let startDate = formatter.date(from: "2017-07-01 20:00:00")!
let duration = formatter.date(from: "2017-07-02 22:00:00")!.timeIntervalSince(startDate)
let elapsed = formatter.date(from: "2017-07-02 08:00:00")!.timeIntervalSince(startDate)
let percentage = elapsed / duration
I think this way is better because you get less maths :).
What you are looking for is Date().timeIntervalSinceReferenceDate:
let currentInterval = currentDate.timeIntervalSinceReferenceDate
let startInterval = startDate.timeIntervalSinceReferenceDate
let endInterval = endDate.timeIntervalSinceReferenceDate
let progress = ((currentInterval - startInterval) / (endInterval - startInterval)) * 100
Here is apples documentation on it. Basically it returns the amount of seconds that have passed since the reference date of January 1, 2001 UTC.
Do the exact same calculation, just convert startdate, enddate, and currentdate into milliseconds using yourDate.timeIntervalSince1970
That should give you a decimal between 0 and 1, and just multiply by 100 for the percentage

How to get the last date of the month [duplicate]

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())

NSDate startOfDay is 4AM?

I'm trying to write a loop for every 10 minutes of a given 24 hour day, starting at midnight and ending at ten minutes before midnight. So I tried this...
let x = Calendar.current.component(.year, from: Date())
let dateFormatter = DateFormatter()
dateFormatter.dateFormat="dd-MM-yyyy"
let june = dateFormatter.date(from: "21-06-" + String(x))
The result for june is "2017-06-21 04:00:00 UTC". Now technically this is correct, my local day will be 4 AM UTZ, but the code I'm passing this into, from the Astronomical Almanac, already handles local/global conversion.
So then I tried this:
var UTZCal = Calendar.current
UTZCal.timeZone = TimeZone(abbreviation: "GMT")!
let x = UTZCal.component(.year, from: Date())
let dateFormatter = DateFormatter()
dateFormatter.dateFormat="dd-MM-yyyy"
dateFormatter.calendar = UTZCal
let june = dateFormatter.date(from: "21-06-" + String(x))
This produced the exact same result. What am I missing?
It seems that the date formatter does not use the timezone of the
assigned calendar, and adding
dateFormatter.timeZone = UTZCal.timeZone
to your code makes it produce the expected result. But note that you
can simplify the calculation to
var utzCal = Calendar(identifier: .gregorian)
utzCal.timeZone = TimeZone(secondsFromGMT: 0)!
let year = utzCal.component(.year, from: Date())
let june = DateComponents(calendar: utzCal, year: year, month: 6, day: 21).date!
print(june) // 2017-06-21 00:00:00 +0000