DateFormatter.localizedString is broken for some of the dates - ios

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

Related

FSCalendar select day selects previous day at 23:00

I'm using FSCalendar in a Swift app, and when user selects a day, I'm printing the selected day, and the it prints the previous day, at 23:00. I'm not sure why and how can I solve this. I'm in spain. Maybe it's related with where you are and your local hour?
This is how I'm printing the selected day:
extension CalendarDataViewViewController: FSCalendarDataSource {
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd hh:mm:ss"
let now = df.string(from: date)
logger.debug("Date: \(date)")
}
}
And this is what it's printed when I select 18 march:
21:01:24.646 💚 DEBUG CalendarDataViewViewController.calendar():258 - Date: 2021-03-17 23:00:00 +0000
Your code creates a date formatter, converts the returned date to a date string with that formatter, and then ignores that and simply prints the date, which is being displayed in UTC. (Note the output Date: 2021-03-17 23:00:00 +0000)
Change your log command to read:
logger.debug("Date: \(now)")
And by the way, the variable name now is a terrible choice for holding a user-selected date that is not the current date.
I'd suggest renaming the returned date parameter selectedDate and the String output of the formatter as selectedDateString
Edit:
Consider this code:
import Foundation
func dateStringFromDate(_ inputDate: Date) -> String {
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd hh:mm:ss a"
let dateString = df.string(from: inputDate)
return dateString
}
func isoDateStringFromDate(_ inputDate: Date) -> String {
let df = ISO8601DateFormatter()
df.formatOptions = .withInternetDateTime
df.timeZone = TimeZone.current //Force the formatter to express the time in the current time zone, including offset
let dateString = df.string(from: inputDate)
return dateString
}
let now = Date()
print("Current timezone = \(TimeZone.current)")
print("now in 'raw' format = \(now)")
let localizedDateString = DateFormatter.localizedString(from: now,
dateStyle: .medium,
timeStyle: .medium)
print("localizedString for the current date = \(localizedDateString)")
print("dateStringFromDate = \(dateStringFromDate(now))")
print("isoDateStringFromDate = \(isoDateStringFromDate(now))")
Right now, at about 9:16 PM EDT on Thursday March 18th, that logs the following:
Current timezone = America/New_York (current)
now in 'raw' format = 2021-03-19 01:16:52 +0000
localizedString for the current date = Mar 18, 2021 at 9:16:52 PM
dateStringFromDate = 2021-03-18 09:16:52 PM
isoDateStringFromDate = 2021-03-18T21:16:52-04:00
The 'raw' date format is in GMT, with an offset value of 0. In that form, in GMT, the calendar date is already March 19th. (Because GMT is 4 hours ahead of EDT)
The class function NSDateFormatter.localizedString(from:dateStyle:timeStyle) displays a date in the current time zone and using the device's locale settings. The dateStyle and timeStyle parameters give you the option to choose whether or not, and in what format (short, medium, or long) to display the date or time.
An ISO8601DateFormatter displays the date following the conventions in the ISO8601 standard. The isoDateStringFromDate(:) function above uses the .withInternetDateTime option to express the date in the ISO8601 "internet date and time" format. I forced that date to be in the local time zone, so it displays the date with a -4 hour offset from GMT (since it is EDT, eastern daylight savings time where I live.)
The function dateStringFromDate(_:) is a slight variation on your function. It returns a date string in the current time zone, using 12 hour times and an AM/PM string.

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"

HKActivitySummary dateComponents a day behind

For some strange reason when executing a HKActivitySummaryQuery the returned date component for each summary is a day behind.
The query returns data from the correct date but the dateComponents date of the data is behind by a day. I've tried setting the timezone and locale but results remain the same.
Summary Model
struct ActivitySummary {
init?(_ summary: HKActivitySummary) {
var calendar = Calendar.current
calendar.timeZone = TimeZone.current
guard let date = summary.dateComponents(for: calendar).date else { return nil }
print("ORIGINAL: ", date.description(with: Locale.current))
//Expected: Tuesday, January 30, 2018 at 7:00:00 PM Eastern Standard Time
//Results: Monday, January 29, 2018 at 7:00:00 PM Eastern Standard Time
let other = calendar.dateComponents( [ .year, .month, .day ], from: date)
print("START OF DAY: ", date.startOfDay.description(with: Locale.current))
//Expected: Tuesday, January 30, 2018 at 12:00:00 AM Eastern Standard Time
//Results: Monday, January 29, 2018 at 12:00:00 AM Eastern Standard Time
}
}
HKAcitivitySummaryQuery
func summaryQuery(){
let predicate = HKQuery.predicate(forActivitySummariesBetweenStart: fromDate.components(), end: toDate!.components())
let query = HKActivitySummaryQuery(predicate: predicate) { (query, summaries, error) in
guard let summaries = summaries, summaries.count > 0 else {
return
}
//
var activitySummaries: [ActivitySummary] = []
activitySummaries = summaries.compactMap({
ActivitySummary($0)
})
}
}
Maybe the calendar you're working with is wrong. set your calendar like this :
let calendar = Calendar.current
Try manually setting the time zone for the DateComponents before converting them into a Date:
let calendar = Calendar.current
var dateComponents = summary.dateComponents(in: calendar)
dateComponents.timeZone = calendar.timeZone
let date = dateComponents.date!
The DateComponents associated with each HKActivitySummary do not include a time zone. The resulting Date may be slightly off from what you’re expecting because it’s not in the correct time zone (Swift defaults to UTC if a time zone is not specified). Manually specifying the time zone should resolve this issue.

Convert datestring to Swift 3 NSDate [duplicate]

This question already has an answer here:
How to format DateFormatter(NSDateFormattter) for Date String
(1 answer)
Closed 5 years ago.
I having a date string:
Wed Mar 25 2017 05:30:00 GMT+0530 (IST)
I want to convert this string to the Date object in Swift 3.
I have referred to Link1, Link 2 and Link 3 for generating the format for this date string but could not get the correct format.
1) If you have string which contains timezone names like IST or GMT differences, then you can use NSDateDetector as explained in this SO post answer:
extension String {
var nsString: NSString { return self as NSString }
var length: Int { return nsString.length }
var nsRange: NSRange { return NSRange(location: 0, length: length) }
var detectDates: [Date]? {
return try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
.matches(in: self, range: nsRange)
.flatMap{$0.date}
}
}
//your example here
let dateString = "Wed Mar 25 2017 05:30:00 GMT+0530 (IST)"
if let dateDetected = dateString.detectDates?.first {
let date = dateDetected//Mar 25, 2017, 5:30 AM
print(dateDetected)//2017-03-25 00:00:00 +0000 - this is GMT time
}
Mar 25, 2017, 5:30 AM //date converted to local time zone
2017-03-25 00:00:00 +0000 //printed value of GMT time
2) Or if you some how able to remove GMT and IST reference from your string then try this:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE, MMM d yyyy HH:mm:ss Z"
let date = dateFormatter.date(from: "Wed Mar 25 2017 05:30:00 +0530")!
print(date)
It will give you
Mar 25, 2017, 5:30 AM //date converted to local time zone
2017-03-25 00:00:00 +0000 //printed value of GMT time
var dateString = "Wed, 25 Mar 2017 05:30:00 +0000"
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E, d MMM yyyy HH:mm:ss Z"
var dateFromString = dateFormatter.date(from: dateString)
print(dateFromString)

How to get Current EST time IOS Swift

I need to compare two dates in my app and i have a EST date to compare with current date , but let today = NSDate() returns the date in UTC , how can i get current EST time or convert to EST ?
The NSDate store the time in absolute moment i.e. No matter in what time zone your date was created when you use compare method of NSDate it will respect the time zone (because time zone was never stored). You can think of NSDate as epoch time which is always calculated from 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970
I don't know about the implementation of NSDate may be it store the date in UTC internally.
It's our implementation (NSDateFormatter) that changes it's format to our requirement.
So whether you use compare or NSCalendar regardless how you created your date they will compare you date
e.g. My local time zone is IST and it's 2016-10-21 12:10:00 here right now
In EST it's 2016-10-21 02:40:00,
In PST it's 2016-10-20 23:40:00 right now
So this code
let today = NSDate()
print("\(today)")
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.timeZone = TimeZone(abbreviation: "EST")
let estDate = formatter.date(from: "2016-10-21 02:40:00")
print("\(estDate!)")
formatter.timeZone = TimeZone(abbreviation: "PST")
let pstDate = formatter.date(from: "2016-10-20 23:40:00")
print("\(pstDate!)")
formatter.timeZone = TimeZone(abbreviation: "IST")
let istDate = formatter.date(from: "2016-10-21 12:10:00")
print("\(istDate!)")
Will print the same time because it's the same moment everywhere
2016-10-21 06:40:00 +0000
2016-10-21 06:40:00 +0000
2016-10-21 06:40:00 +0000
2016-10-21 06:40:00 +0000
Try This-
import UIKit
let date = Date();
let formatter = DateFormatter();
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
let defaultTimeZoneStr = formatter.string(from: date)
formatter.timeZone = TimeZone(abbreviation: "UTC")
let utcTimeZoneStr = formatter.string(from: date)
If you need to compare two dates, you can use the method below
// Date comparision to compare current date and end date.
var dateComparisionResult:NSComparisonResult = NSDate().compare(endDate)
if dateComparisionResult == NSComparisonResult.OrderedAscending
{
// Current date is smaller than end date.
}
else if dateComparisionResult == NSComparisonResult.OrderedDescending
{
// Current date is greater than end date.
}
else if dateComparisionResult == NSComparisonResult.OrderedSame
{
// Current date and end date are same.
}
If you want to know all time zones abbreviations available you can do like this:
var timeZoneAbbreviationsType: [String:String] { return TimeZone.abbreviationDictionary }
timeZoneAbbreviationsType // ["CEST": "Europe/Paris", "WEST": "Europe/Lisbon", "CDT": "America/Chicago", "EET": "Europe/Istanbul", "BRST": "America/Sao_Paulo", "EEST": "Europe/Istanbul", "CET": "Europe/Paris", "MSD": "Europe/Moscow", "MST": "America/Denver", "KST": "Asia/Seoul", "PET": "America/Lima", "NZDT": "Pacific/Auckland", "CLT": "America/Santiago", "HST": "Pacific/Honolulu", "MDT": "America/Denver", "NZST": "Pacific/Auckland", "COT": "America/Bogota", "CST": "America/Chicago", "SGT": "Asia/Singapore", "CAT": "Africa/Harare", "BRT": "America/Sao_Paulo", "WET": "Europe/Lisbon", "IST": "Asia/Calcutta", "HKT": "Asia/Hong_Kong", "GST": "Asia/Dubai", "EDT": "America/New_York", "WIT": "Asia/Jakarta", "UTC": "UTC", "JST": "Asia/Tokyo", "IRST": "Asia/Tehran", "PHT": "Asia/Manila", "AKDT": "America/Juneau", "BST": "Europe/London", "PST": "America/Los_Angeles", "ART": "America/Argentina/Buenos_Aires", "PDT": "America/Los_Angeles", "WAT": "Africa/Lagos", "EST": "America/New_York", "BDT": "Asia/Dhaka", "CLST": "America/Santiago", "AKST": "America/Juneau", "ADT": "America/Halifax", "AST": "America/Halifax", "PKT": "Asia/Karachi", "GMT": "GMT", "ICT": "Asia/Bangkok", "MSK": "Europe/Moscow", "EAT": "Africa/Addis_Ababa"]
The time zone problem is because when you print Date() the value you get is of UTC zone. (GMT + 00:00).
Now to convert this into your local date do the following steps.
Know the difference in time between your zone and the UTC time. If you are not sure, just google it and you'll get the time difference.
Eg: I am from India and the time difference is 5 hrs 30 mins.
IST is GMT + 05:30.
Add that time interval to the standard UTC date.
extension Date {
static func getCurrentDate() -> Date
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
let curr = Date().addingTimeInterval(TimeInterval((5*3600) + (30*60)))
return curr
}
}
Here, 5 * 3600 is for the 5 hrs, and 30*60 is for the 30 min difference.
So, I am basically converting the 5hrs 30 mins in seconds and adding that time interval.
It worked for me. Hope you find this useful too!

Resources