Get Date from week number in Swift 3 - swift

I have myYear and myWeek strings comes from apis , I want to convert them to date string like YYYY-mm-dd how can I do it in swift 3 ? my strings under below.
let myYear = "2017"
let myWeek = "8"

You just have to convert the strings into Ints, then generate a DateComponents object from them, get a Date from the DateComponents through a Calendar and finally use a DateFormatter to get the expected String representation of the date.
Bear in mind that the Date object will represent the first second of the week of that year and hence the String representation will correspond to the first day of that week.
let yearString = "2017"
let weekOfYearString = "8"
guard let year = Int(yearString), let weekOfYear = Int(weekOfYearString) else {return}
let components = DateComponents(weekOfYear: weekOfYear, yearForWeekOfYear: year)
guard let date = Calendar.current.date(from: components) else {return}
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd"
let outputDate = df.string(from: date) //2017-02-19

Related

DateIntervalFormatter: string of format "m:ss"

I'd like to get a string of format "m:ss" out of two dates.
E.g.: "0:27" for 27 seconds difference and "1:30" for 90 seconds difference between dates.
Here's the code I'm using:
import Foundation
let formatter = DateIntervalFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .none
formatter.dateTemplate = "m:ss"
let startDate = Date()
let endDate = Date(timeInterval: 1, since: startDate)
let outputString = formatter.string(from: startDate, to: endDate)
print(outputString) //16:12 – 16:13 ???
// This is correct, but it doesn't actually calculate the interval.
But I'm getting just two dates printed out with a dash.
How can I actually make the DateIntervalFormatter to calculate the difference as I want?
The code is almost 1:1 sample from the Apple documentation but with the custom dateTemplate: https://developer.apple.com/documentation/foundation/dateintervalformatter
It seems that you actually want DateComponentsFormatter
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.minute, .second]
formatter.zeroFormattingBehavior = .pad
let startDate = Date()
let endDate = Date(timeInterval: 129, since: startDate)
let outputString = formatter.string(from: startDate, to: endDate)!
print(outputString)
to remove the leading zero if the minutes are < 10 you could use Regular Expression
print(outputString.replacingOccurrences(of: "^0(\\d)", with: "$1", options: .regularExpression))
I created this solution which doesn't involve the DateIntervalFormatter:
import Foundation
let minutes = 2
let seconds = 9
let formatted = String(format: "%01d:%02d", minutes, seconds)
print(formatted) // 2:09
Looks like what DateIntervalFormatter does is just applying a standard Date->String conversion to both of the dates and adds a dash between them.

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
}

Swift get the remaining seconds of time on target string date

The current date time today was May 9, 2020 10:03 PM, and I have a target string date with the value of 2020-05-09 22:07:30 with the format of yyyy-MM-dd HH:mm:ss.
How can I get the remaining date from that 2 date and print the value with the string of 04:30 as the range of those 2 dates are 4 minutes and 30 seconds
What I can only do is convert the milliseconds to time format like
func msToTime(ms: Int) {
let seconds = ms % 60
let minutes = ms / 60000
return String(format: "%0.2d:%0.2d",minutes,seconds)
}
Output 04:30
But I don't know how to get the range of milliseconds from today's date time to target's date time.
Or if there's any other easier way to do it?
You can use Calendar and DateComponents to easily calculate differences between dates in whatever units you desire. For example, this gets the difference in minutes and seconds:
let dateformatter = DateFormatter()
dateformatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateformatter.date(from: "2020-05-09 22:07:30")!
let now = Date()
let components = Calendar.current.dateComponents([.minute, .second], from: now, to: date)
print("difference: \(components.minute!):\(components.second!)")
A straightforward way, with no calculation of any kind needed:
let d1 = Date()
let s = "2020-05-09 22:07:30"
let f = DateFormatter()
f.dateFormat = "yyyy-MM-dd HH:mm:ss"
f.timeZone = TimeZone.current // or whatever
if let d2 = f.date(from: s) {
let f = DateComponentsFormatter()
f.unitsStyle = .positional
let result = f.string(from: d1, to: d2)
}
If you don't like the resulting string format of result, you can eliminate pieces of it. — However, note that this works only because no full days are involved. It isn't clear what the range of possible inputs might be or what the desired output would be if the second date were three months into the future, for example.

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

Swift: get the offset value from a date object

as simple as it sounds, but it is hard to find my exact question in google.
I'm trying to ignore the UTC printed out value. I receive multiple dates, this one here is just an example: (it could be +0900, -0200, etc...)
"2017-05-01T12:30:00-0700"
once I apply it to a value using these lines:
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
if let result = formatter.date(from: time) {print result}
the value of the dateTimeResult prints:
2017-05-01 19:30:00 UTC
Using swift date objects, how do I slice out the part "-0700", multiply the -7 or +7 (this example is negative) by minutes by seconds. I'll save that total as int in DB (I need it for categorizing the different timezones later). Then applying that total to the incoming date input using this line:
let output = Calendar.current.date(byAdding: .second, value: totalSecs, to: result)
The goal is to end up with this date:
"2017-05-01 12:30:00"
I already have a solution using string manipulation, but I don't think that is the ideal solution. If it must be done by string, how do you do it?
If I understand you correctly you want only the date and time portion ignoring always the time zone information.
In this case strip the time zone from the date string with regular expression
let dateString = "2017-05-01T12:30:00-0700"
let dateStringIgnoringTimeZone = dateString.replacingOccurrences(of: "[+-]\\d{4}", with: "", options: .regularExpression)
print(dateStringIgnoringTimeZone) // "2017-05-01T12:30:00"
let dateFormatter = DateFormatter()
dateFormatter.calendar = Calendar(identifier: .iso8601)
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let date = dateFormatter.date(from: dateStringIgnoringTimeZone)!
I think you should keep the date as it is and then just use DateFormatter to display the time at that timezone
let time = "2017-05-01T12:30:00-0700"
let dateFormatter = DateFormatter()
dateFormatter.calendar = Calendar(identifier: .iso8601)
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
if let result = dateFormatter.date(from: time) {
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dateFormatter.string(from: result)) // "2017-05-01 16:30:00 (corresponding time at my location GMT-3)
// to display it at -0700 just set the formatter timaZone
dateFormatter.timeZone = TimeZone(secondsFromGMT: -3600 * 7)
print(dateFormatter.string(from: result)) // "2017-05-01 12:30:00\n"
}
To get the timezone offset from the string:
let hours = Int(time.suffix(5).prefix(3)) ?? 0
let minutes = Int(time.suffix(2)) ?? 0
let offset = hours * 3600 + minutes * 60
print(offset) // -25200