Swift DateFormatter converting string day/times incorrectly - ios

I'm trying to convert time strings that look like the following into Swift Dates:
Sun 11:00a PDT
Sun 7:00p PDT
Mon 11:00a PDT
Mon 7:00p PDT
Tue 11:00a PDT
Tue 7:00p PDT
Wed 10:00a PDT
Wed 7:00p PDT
etc
These represent opening and closing times for various businesses.
While the Date object conversion does succeed (meaning the returned optional is not nil,) the times are incorrect. Here is my code:
let dateFormatter = DateFormatter()
let shortDays = ["Sun-","Mon-","Tue-","Wed-","Thu-","Fri-","Sat-"]
dateFormatter.dateFormat = "E HH:mmaaaaa zzz"
var daysCounter = 0
for hours in hoursArray {
let dayHours = shortDays[daysCounter]+hours
//split hours
let splitHours = dayHours.split(separator: "-")
//print(String(splitHours[0]+" "+splitHours[1]+" "+timezone))
//print(String(splitHours[0]+" "+splitHours[2]+" "+timezone))
//create an opening time date optional
let openingOpt = dateFormatter.date(from: String(splitHours[0]+" "+splitHours[1]+" "+timezone))
//create a closing time date optional
let closingOpt = dateFormatter.date(from: String(splitHours[0]+" "+splitHours[2]+" "+timezone))
//unwrap those optionals
guard let opening = openingOpt else{
//deal with "closed"
print("Something happened with unwrapping opening time optional: \(String(describing: openingOpt))")
return
}
guard let closing = closingOpt else{
//deal with "closed"
print("Something happened with unwrapping closing time optional: \(String(describing: closingOpt))")
return
}
self.fullHours.append(opening)
self.fullHours.append(closing)
daysCounter += 1
}
for hours in fullHours{
print(hours)
}
However, when I print the converted dates using the for loop above, I get incorrect results. Some of the results are fine; for example, the closing times of 7pm seem to convert properly. To the left are the printed Date objects, to the right are the original inputs in parenthesis:
2000-01-02 07:00:00 +0000 (Sun 11:00a PDT)
2000-01-02 19:00:00 +0000 (Sun 7:00p PDT)
2000-01-03 07:00:00 +0000 (Mon 11:00a PDT)
2000-01-03 19:00:00 +0000 (Mon 7:00p PDT)
2000-01-04 07:00:00 +0000 (Tue 11:00a PDT)
2000-01-04 19:00:00 +0000 (Tue 7:00p PDT)
2000-01-05 07:00:00 +0000 (Wed 10:00a PDT)
2000-01-05 19:00:00 +0000 (Wed 7:00p PDT)
2000-01-06 07:00:00 +0000 (Thu 11:00a PDT)
2000-01-06 19:00:00 +0000 (Thu 7:00p PDT)
2000-01-07 07:00:00 +0000 (Fri 11:00a PDT)
2000-01-07 19:00:00 +0000 (Fri 7:00p PDT)
12000-01-01 07:00:00 +0000 (Sat 11:00a PDT)
2000-01-01 19:00:00 +0000 (Sat 7:00p PDT)
In particular, why is Saturday being converted to the year 12000? Why does Wednesday at 10am still show at 7am UTC? I'm confused and lost, and could not find a similar question via search. Any help would be appreciated, thank you!
Note: I am using Xcode 13.0 Beta 5, developing for iOS 15 beta 5.
Edit: here is the source for the date-time formatting: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
Edit2: Also, I discovered that any time appended with "a" converts to 7am, and any time appended with "p" converts to 7pm. So... ??

The answer was that my format was off. It should have been h:mm, not HH:mm. Thanks to Sweeper for the detail-oriented answer!

Related

Parse Javascript new Date().toString() in Swift

I need to parse javascript dates in swift. Since dates are already stored in some database I cannot change the format of them. I just need to parse them into correct Dates in swift.
Below are examples results of javascript's toString() function. It depends on Locale/Language
// js
new Date().toString()
'Tue Jun 01 2021 14:11:27 GMT+0900 (JST)'
'Tue Jun 01 2021 14:03:45 GMT+0900 (Japan Standard Time)'
'Mon May 31 2021 17:38:31 GMT+0800 (中国標準時)'
'Mon May 31 2021 19:25:37 GMT+0930 (オーストラリア中部標準時)'
How can I parse this in Swift DateFormatter?
I have tried this:
// swift
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z (zzz)"
Date-> String conversion looks correct but String -> Date does not work
// swift
dateFormatter.string(from: Date())
> "Tue Jun 01 2021 14:11:27 GMT+0900 (JST)"
dateFormatter.date(from: "Tue Jun 01 2021 14:11:27 GMT+0900 (JST)")
> nil
Any help is highly appreciated.
As mentioned in the comments by #Sweeper - The timezone name part is implementation dependent - which can be confirmed from the docs
Optionally, a timezone name consisting of:
space
Left bracket, i.e. "("
An implementation dependent string representation of the timezone, which might be an abbreviation or full name (there is no standard for names or abbreviations of timezones), e.g. "Line Islands Time" or "LINT"
Right bracket, i.e. ")"
So we need to - remove the part within parentheses at the end and parse the rest - as mentioned by #Joakim Danielson
Taking this into account, we can do it like this -
extension String {
static private var jsDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z"
return formatter
}()
func parsedDate() -> Date? {
let input = self.replacingOccurrences(of: #"\(.*\)$"#, with: "", options: .regularExpression)
return String.jsDateFormatter.date(from: input)
}
}
Tests
func testJSDateParsing() {
[
"Tue Jun 01 2021 14:11:27 GMT+0900 (JST)",
"Tue Jun 01 2021 14:03:45 GMT+0900 (Japan Standard Time)",
"Mon May 31 2021 17:38:31 GMT+0800 (中国標準時)",
"Mon May 31 2021 19:25:37 GMT+0930 (オーストラリア中部標準時)",
].forEach({
if let date = $0.parsedDate() {
print("Parsed Date : \(date) for input : \($0)")
}
else {
print("Failed to parse date for : \($0)")
}
})
}
Output
Parsed Date : 2021-06-01 05:11:27 +0000 for input : Tue Jun 01 2021 14:11:27 GMT+0900 (JST)
Parsed Date : 2021-06-01 05:03:45 +0000 for input : Tue Jun 01 2021 14:03:45 GMT+0900 (Japan Standard Time)
Parsed Date : 2021-05-31 09:38:31 +0000 for input : Mon May 31 2021 17:38:31 GMT+0800 (中国標準時)
Parsed Date : 2021-05-31 09:55:37 +0000 for input : Mon May 31 2021 19:25:37 GMT+0930 (オーストラリア中部標準時)

Adding/Reducing day in the Date object returns wrong date when day light savings time ends

I'm using
Calendar.current.date(byAdding: .day, value: -1, to: somedate) ?? somedate
to reduce 1 day from some date. Since Daylight Saving Time Ended on 1'st Nov, 2020. When i'm trying to do this on 2nd Nov, 2020 0hr:0m:0s's date object, i expect it to return 1st Nov, 2020 0hr:0m:0s, but instead it is returning 31st Oct, 2020 23hr:0m:0s.
Is it something i'm doing wrong or is it some other issue?
How to reproduce:-
Create a date object using time stamp 1604275200. using Date(timeIntervalSince1970: 1604275200)
Change timezone of ur device to some place where daylight savings time is considered. i tried it in HST timezone
Try reducing the day using the above given method.
You'll see date returning as 31st Oct.
extension Date {
init(timeIntervalInMillis: Double) {
self.init(timeIntervalSince1970: timeIntervalInMillis / 1000)
}
func add(_ component: Calendar.Component, value: Int) -> Date {
return Calendar.current.date(byAdding: .day, value: value, to: self) ?? self
}
var noon: Date {
return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
}
print(Calendar.current.timeZone.identifier)
let date = Date(timeIntervalInMillis: 1604275200000)
print("Date is ",date)
print("Yesterday's date is ",date.add(.day, value: -1))
print("Noon time is ",date.noon)
print("Yesterday date from noon's date is ",date.noon.add(.day, value: -1))
Output:
America/Chicago
Date is 2020-11-02 00:00:00 +0000
Yesterday's date is 2020-10-31 23:00:00 +0000
Noon time is 2020-11-01 18:00:00 +0000
Yesterday date from noon's date is 2020-10-31 17:00:00 +0000
Any help would be appreciated.
edit/update:
There is nothing wrong with your code. Your issue is that you are printing the UTC date representation instead of using DateFormatter with the time zone set to chicago to show the resulting date at the desired timezone.
print("Date is ",date) // Date is 2020-11-02 00:00:00 +0000\n"
print("Yesterday's date is ",date.add(.day, value: -1)) // "Yesterday's date is 2020-10-31 23:00:00 +0000\n"
print("Noon time is ",date.noon) // "Noon time is 2020-11-01 18:00:00 +0000\n"
print("Yesterday date from noon's date is ",date.noon.add(.day, value: -1)) // "Yesterday date from noon's date is 2020-10-31 17:00:00 +0000\n"
let fmter = DateFormatter()
fmter.timeZone = TimeZone(identifier: "America/Chicago")!
fmter.dateStyle = .full
fmter.timeStyle = .full
print("Date is ", fmter.string(from: date)) // "Date is Sunday, 1 November 2020 18:00:00 Central Standard Time\n"
print("Yesterday's date is ", fmter.string(from: date.add(.day, value: -1))) // "Yesterday's date is Saturday, 31 October 2020 18:00:00 Central Daylight Time\n"
print("Noon time is ", fmter.string(from: date.noon)) // "Noon time is Sunday, 1 November 2020 12:00:00 Central Standard Time\n"
print("Yesterday date from noon's date is ",fmter.string(from: date.noon.add(.day, value: -1))) // "Yesterday date from noon's date is Saturday, 31 October 2020 12:00:00 Central Daylight Time\n"

Swift comparing date is not working and getting wrong time

Hi I have multiple dates which I am comparing with current date. But it is not working. I have done coding but nothing is working for me. I am using comparison same like this link.
Here is what I am doing
let todayDate = Date()
let date1FromServer = //its Date from server which is 2020-02-01 09:00:00 +0000
let date2FromServer = //its Date from server which is 2020-02-02 09:00:00 +0000
let date3FromServer = //its Date from server which is 2020-02-03 09:00:00 +0000
now here I am comparing All three date in simple if else
if(date1FromServer < todayDate){
//Do something for date 1
}else if (date2FromServer < todayDate){
//Do something for date 3
}else if (date3FromServer < todayDate){
// Do something for date 3
}
Case: Now I have date1 and Date 2 is working but date3 is not working. I am saying this because the date3 is exactly on 3 feb 2020
with 9:00 am which is smaller then today date which is 3 feb 2020 with
11:00 am , but it looks like that it is dealing like date 3 is equal
or greater then today date.
When I print date 3 it prints (2020-02-03 06:32:22 +0000) and here the time is wrong, as right now on my device it si 11:32 am but it showing 06: 32 . I think as I am just printing it in log so there could be problem of time zone, but on above code it is also not working.
Please let me know what is the problem here.
Note: I want to compare the Date and time as well.....
If your server date has GMT as time zone then you can adjust your local date to GMT by using offsets
let offsetDate = Date(timeIntervalSince1970:
todayDate.timeIntervalSince1970 + Double(TimeZone.current.secondsFromGMT(for: todayDate)))
and then use offsetDate instead when doing the comparison

DateFormatter.localizedString is broken for some of the dates

This is what I do when picker changes:
extension Date {
var fromCurrentToUTC: Date {
return addingTimeInterval(-TimeInterval(TimeZone.current.secondsFromGMT()))
}
}
var title = "--"
if let date = datePickerView.date?.fromCurrentToUTC {
title = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .none)
}
print("-----")
print(datePickerView.date!)
print(title)
print(TimeZone.current)
print(datePickerView.date!.timeIntervalSince1970)
dateTimeSegmentControl.setTitle(title, forSegmentAt: 0)
And this is how it looks for the dates:
Assuming. Everything is fine for the dates before 6th November, and everything is off after 6th November. Why?
update:
That critical date is different for every time zone I use. For example:
Warsaw (+0200) the date is 30 October
Chicago (-0500) the date is 6th November
The ordered prints:
-----
2017-11-04 00:00:00 +0000
4 Nov 2017
America/New_York (current)
1509753600.0
-----
2017-11-05 00:00:00 +0000
5 Nov 2017
America/New_York (current)
1509840000.0
-----
2017-11-06 00:00:00 +0000
5 Nov 2017
America/New_York (current)
1509926400.0
-----
2017-11-07 00:00:00 +0000
6 Nov 2017
America/New_York (current)
1510012800.0
In your function
extension Date {
var fromCurrentToUTC: Date {
return addingTimeInterval(-TimeInterval(TimeZone.current.secondsFromGMT()))
}
}
the GMT offset of the current date is subtracted, not
by the GMT offset of the date to be adjusted. Therefore you get
a wrong result if the date to be adjusted is in a DST period and the current date is not, or vice versa.
That can be fixed by using
extension Date {
var fromCurrentToUTC: Date {
return addingTimeInterval(-TimeInterval(TimeZone.current.secondsFromGMT(for: self)))
}
}
instead. However, a better solution would be to set the timezone
of the date formatter to UTC, instead of adjusting the date:
if let date = datePickerView.date {
let fmt = DateFormatter()
fmt.dateStyle = .medium
fmt.timeStyle = .none
fmt.timeZone = TimeZone(secondsFromGMT: 0)
let title = fmt.string(from: date)
print(title)
}

HttpDateParser problem

I having a problem when convert String to Date in Blackberry SDK. Please support for me.
This is my code:
String date = "Mon May 09 09:00:00 GMT 2011";
Date formatter = new Date();
formatter.setTime(HttpDateParser.parse(date));
SimpleDateFormat dateFormat = new SimpleDateFormat(formatStr);
string dateString = dateFormat.format(formatter);
purpsoe of the function is format date MMM dd, YYYY.
But after i run the function, it will return result that I unexpected.
Expected: May 09, 2011
UnExpected : Jan,01,1970.
I suspect the "Mon May 09 09:00:00 GMT 2011" is not a supported by HttpDateParser date format. Since the HttpDateParser.parse() does not throw an exception, I guess it simply return 0 in case of an unsupported format.

Resources