Using DateFormatter to parse an esoteric date string - swift

I would like to use Foundation's DateFormatter to parse a datestring of the rather weird format /Date(1488335133000+0100)/ (representing 2017-03-01 03:25:33). As far as I can tell this describes the date as milliseconds since 1970 with a timezone of GMT+1 specified as well.
I can't however find a way to specify a format string for milliseconds or seconds as a unix timestamp. Is this even possible?
In case that's not possible, what would be the best option for parsing this date correctly? I'm currently resorting to picking apart the string until I have the milliseconds and timezone, dividing the milliseconds by 1000 and creating a new date object via Date(timeIntervalSince1970: seconds). Not quite sure how the timezone is supposed to play into this though.

DateFormatter can't handle this. Use NSRegularExpression to pick apart the components:
let str = "/Date(1488335133000+0100)/"
let regex = try! NSRegularExpression(pattern: "/Date\\((\\d+)(\\+|-)(\\d{2})(\\d{2})\\)/", options: [])
if let match = regex.firstMatch(in: str, options: [], range: NSMakeRange(0, str.characters.count)) {
let nsstr = str as NSString
let millisecond = Double(nsstr.substring(with: match.rangeAt(1)))!
let sign = nsstr.substring(with: match.rangeAt(2))
let hour = Double(nsstr.substring(with: match.rangeAt(3)))!
let minute = Double(nsstr.substring(with: match.rangeAt(4)))!
let offset = (sign == "+" ? 1 : -1) * (hour * 3600.0 + minute * 60.0)
let date = Date(timeIntervalSince1970: millisecond / 1000 + offset)
print(date)
}

Related

Convert current time to local time in date format(Without converting it to string) in swift

I want to convert current time(in UTC) to my local time zone. But I don't want it in string I want it in date format itself.
I have written following code:
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
Now after that, mostly people have done
let dateString = dateFormatter.string(from: now)
Its okay, I am getting correct time in my local timezone but in string format. I want it in date format.
Again, if I do
let currentDateInLocal = dateFormatter.date(from: dateString) //converting string to date
I am getting the date in UTC format. How I can get the date in local timezone in date format?
You said "I want to convert current time(in UTC) to my local time zone. But I don't want it in string I want it in date format itself."
A Date object does not have a time zone. A Date records an instant in time, anywhere in the world. Imagine there is a bright flash in the sky all over the world at the same instant. What time did that happen? It depends on the time zone you are in. However, a Date object could capture that instant. You'd then convert that Date to a specific time zone if you wanted to describe the time of day the event occurred.
So your question doesn't really make sense.
I suggest using the DateFormatter class method localizedString(from:dateStyle:timeStyle:) to display a date in your local time zone:
e.g.
print(DateFormatter.localizedString(
from: Date(),
dateStyle: .medium,
timeStyle: .medium))
That lets you view a Date object in your local time zone without needing to create a DateFormatter.
I know you said you don't want it in a string format, however you can simply convert it over after to a date object.
you're welcome to use this, i created this function based on my own string format, change that to whatever you need and boom. enjoy
//this function will allow you to easily convert your date string into readable current context
func convertDateToNow(createdAt:String) -> String{
//create a date to represent the seconds since the start of the internet
let currentDate = Date().timeIntervalSince1970
//create a dateformatter
let dateFormatter = DateFormatter()
//use the locale on the device to set the the related time
dateFormatter.locale = Locale.current
//set the formate of which our date will be coming in at
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
//create a created at date for the post based off its createdAt date and get the seconds since the start of the internet
let createdDate = dateFormatter.date(from: "\(createdAt)")?.timeIntervalSince1970
//now we are left with two values depending on age of post either a few thousands off or it could be years in seconds. so we minus the current date on the users phone seconds from the created date seconds and voilla
let secondsDifference = currentDate - createdDate!
//use our specially created function to convert the seconds difference into time values
let (d,h,m,s) = secondsToHoursMinutesSeconds(seconds: Int(secondsDifference))
//test the values
// print("d:",d)
// print("h:",h)
// print("m:",m)
// print("s:",s)
//
//this will hold the final output of the string
var finalDateLabel:String!
//set the datelabel on the cell accordingly
//create arithmetic for the date features
if d != 0 {
//check to see if the label is a day old
if d == 1 {
finalDateLabel = "\(d) day ago"
}
}else if h != 0{
//set the date label
finalDateLabel = "\(h) hour ago"
if h == 1 {
finalDateLabel = "\(h) hour ago"
}else if h >= 2 {
finalDateLabel = "\(h) hours ago"
}
}else if m != 0{
//set the date label
finalDateLabel = "\(m) minutes ago"
if m == 1 {
finalDateLabel = "\(m) minute ago"
}
}else if s != 0{
//set the date label
finalDateLabel = "\(s) seconds ago"
if s == 1 {
finalDateLabel = "\(s) second ago"
}
}
return finalDateLabel
}
//to help convert the story
func secondsToHoursMinutesSeconds (seconds : Int) -> (Int ,Int, Int, Int) {
return (seconds / 86400, seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}

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.

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.

How to display only the hours when minutes are zero with date formatter?

Am currently using the format MM-dd-yyyy HH:mm to display the time stamp. Is it possible to drop the minutes if the time does not have any minute component ?
Like, instead of 3:00 PM is it possible to display only 3 PM ?
EDIT:
func formatDate(_ dateFormat: String, timeInterval: TimeInterval) -> String {
let date = Date(timeIntervalSince1970: timeInterval)
let formatter = DateFormatter()
formatter.dateFormat = dateFormat
return formatter.string(from: date)
}
let formattedDate = formatDate("MM-dd-yyyy HH:mm", timeInterval: 1520422200)
print(formattedDate)
Option 1
Replace any with zero minutes on return string.
return formatter.string(from: date).replacingOccurrences(of: ":00", with: "")
Option 2
Determine if there are minutes, and adjust date format.
if (Int(timeInterval) % 3600 == 0) {
let newFormat = dateFormat.replacingOccurrences(of: ":mm", with: "")
// ...
}
You could use DateComponents to determine what the .minute component is. If it's non-zero set the date format to include minutes, and otherwise set it to leave it out.
Note if this is for a user-facing string, use setLocalizedDateFormatFromTemplate and for hours use "j" so you can respect a user's setting for whether to display 24 hour time.
Note it is probably strange for most users to see 24 hour time without the minutes (e.g. "9pm" vs "21", the latter doesn't look right and probably would be better with ":00").

Convert Double to NSDate time, for querying on Parse

I have two Doubles, and need to query for NSDate's with a timeinterval that matches the two doubles.. How can I convert a Double to a NSDate with time matching my Double? :) If it's possible at all..
Edit: Sorry, I was in a bit of a hurry - my apologies! The Doubles I have, represent a time of the day, like 8.00 or 17.00 .. So I need to convert that Double, to a time in an NSDate, If that makes sense :)
Simple use timeIntervalSince1970 and pass double value as parameter.
let date = NSDate(timeIntervalSince1970: doubleValue)
I found the solution with a bit of hints from your answers. Here's what I ended up with and it works perfectly:
let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
let secondsSince1970ToYesterdayAt23 = newDate.timeIntervalSince1970
let secondsInOneHour = 3600.0
let secondsSince1970FromMidnight = secondsSince1970ToYesterdayAt23 + secondsInOneHour
let secondsFromMidnightToStartTime = (secondsInOneHour * (slider?.lowerValue)!) + secondsSince1970FromMidnight
let dateWithStartTime = NSDate(timeIntervalSince1970: secondsFromMidnightToStartTime)
let secondsFromMidnightToEndTime = (secondsInOneHour * (slider?.upperValue)!) + secondsSince1970FromMidnight
let dateWithEndTime = NSDate(timeIntervalSince1970: secondsFromMidnightToEndTime)
print("Start \(dateWithStartTime)") // Returning 2017-03-18 08:00:00 +0000
print("End: \(dateWithEndTime)") // Returning 2017-03-18 17:00:00 +0000