doesRelativeDateFormatting not working for future date in swift - swift

/// A date formatter for displaying the complete relative date plus the time plus day, no seconds.
///
/// Examples: Today at 2:07 PM; Yesterday at 2:07 PM; Monday, Oct 7, 2018 at 5:05 AM
///
static let relativeFullDateShortFormatterWithDay: DateFormatter = {
let formatter = DateFormatter()
formatter.timeStyle = .short
formatter.dateStyle = .medium
formatter.doesRelativeDateFormatting = true
formatter.timeZone = TimeZone.current
formatter.locale = Locale.autoupdatingCurrent
return formatter
}()
From the server, I m receiving the expiry date. If the expiry date is of today, I want to show Today and if the expiry date is within week, I want to show weekday name.
I m setting doesRelativeDateFormatting to true for this. From the server I m getting expiry date of Sep 4 which is on Friday. When I convert Date to a string using relativeFullDateShortFormatterWithDay it is returning Sep 4, 2020 instead of Friday.
is there any more setting i need to set in formatter? For past dates, this is working fine.

Given the formatter in the question and a special weekday formatter
let weekDayFormatter = DateFormatter()
weekDayFormatter.dateFormat = "EEEE HH:mm"
(if the locale is always the same the format could be changed to "EEEE 'at' HH:mm" for English)
We can then use a function that formats a date by first checking if the fist formatter returns something like "Today" and then returns that and otherwise checks if the given date is within the next week and then returns a weekday
func format(date: Date) -> String {
formatter.doesRelativeDateFormatting = true
let relative = formatter.string(from: date)
formatter.doesRelativeDateFormatting = false
let absolute = formatter.string(from: date)
if relative != absolute {
return relative
}
let calendar = Calendar.current
let startDate = calendar.startOfDay(for: Date())
guard let endDate = calendar.date(byAdding: .day, value: 7, to: startDate) else {
return formatter.string(from: date)
}
if date > startDate && date < endDate {
return weekDayFormatter.string(from: date)
} else {
return formatter.string(from: date)
}
}
Example
print(format(date: Date(timeIntervalSinceNow: 60 * 60 * 24 * 1)))
print(format(date: Date(timeIntervalSinceNow: 60 * 60 * 24 * 4)))
print(format(date: Date(timeIntervalSinceNow: 60 * 60 * 24 * 12)))
Tomorrow at 17:07
Saturday 17:07
13 Sep 2020 at 17:07

Related

Swift date components incorrect return of date day number

I need to obtain a date from some variable values
So I specify year, month and day and I need a Date as return
Doing the following works except for the day because it return the day input - 1
let todayDate: Date = Calendar.current.startOfDay(for: Date.from(year: 2022, month: 09, day: 05)!)
print("today date = \(todayDate)")
extension Date {
static func from(year: Int, month: Int, day: Int) -> Date? {
let calendar = Calendar(identifier: .gregorian)
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
return calendar.date(from: dateComponents) ?? nil
}
}
And the output is
today date = 2022-09-04 22:00:00 +0000
Date and time can be a bit tricky. The Date struct stores a point in time relative to GMT. If you print it it will show exactly that.
Solution:
DonĀ“t use print, use a proper Dateformatter. To illustrate what I mean use this in a playground:
let date = Calendar.current.startOfDay(for: Date())
print(date)
//2022-09-03 22:00:00 +0000
// when it is 4.th of september 00:00 in my timezone (+- Daylight saving) it is this time in GMT
let formatter = DateFormatter()
formatter.dateFormat = "dd MM yyyy HH:mm:ss"
print(formatter.string(from: date))
//04 09 2022 00:00:00
// this is the time in my timezone
So the issue here is not that it has the wrong time, it is just not presented in the correct time zone.

how to get time difference form formatted time string values

Hi how to get time difference for two sting value
startTime = "09:00 AM"
EndTime = "05:30 PM"
func timeDifferenceBetweenTwoTime(startTime: String, endTime:String) ->CGFloat{
let start = startTime
let end = endTime
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "hh:mm a"
if let startDate = dateFormatter.date(from: start),
let endDate = dateFormatter.date(from: end) {
let hours: CGFloat = CGFloat(Calendar.current.dateComponents([.hour], from: startDate, to: endDate < startDate ? Calendar.current.date(byAdding: .day, value: 1, to: endDate) ?? endDate : endDate).hour ?? 00 )
return hours
}
return 00.00
}
The expected result is 08.50, But for me its giving 8.0
You just need to get the minutes instead of hours and divide it by 60. Btw you should also set your dateFormatter's default date to today:
func timeDifferenceBetweenTwoTime(startTime: String, endTime: String) -> CGFloat {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.defaultDate = Calendar(identifier: .iso8601).startOfDay(for: Date())
dateFormatter.dateFormat = "hh:mm a"
if let startDate = dateFormatter.date(from: startTime),
var endDate = dateFormatter.date(from: endTime) {
if startDate > endDate {
endDate = Calendar.current.date(byAdding: .day, value: 1, to: endDate)!
}
let minute = Calendar.current.dateComponents([.minute], from: startDate, to: endDate).minute!
return CGFloat(minute) / 60
}
return 0
}
let startTime = "09:00 AM"
let endTime = "05:30 PM"
timeDifferenceBetweenTwoTime(startTime: startTime, endTime: endTime) // 8.5
You asked for the difference in hours. The Calendar function dateComponents(_:from:to:) will give you the number of whole hours between your dates if that's what you ask it for. By not also asking for the number of minutes, that gets truncated.
If you want hours and whole minutes you could ask for hours and minutes and do some math to combine them, or just ask for minutes as Leo suggests and divide minutes by 60.
You could also use endDate.timeIntervalSinceReferenceDate-startDate.timeIntervalSinceReferenceDate)/3600.0. That will give you a Double representing the exact number of hours between the two dates, including seconds and fractions of a second. (Not relevant given that your source Dates only specify hours and minutes, but there are other cases where that might be useful.)

GMT to local time conversion ERROR with swift UIKIT

Does anyone have an answer to why I am getting -8 hours instead of -7 (my timezone) hours difference upon converting time from GMT to local i using the formula below:
print("TIMEZONE IN HOURS: \(timeZone/3600)")
let interval = Date(timeIntervalSinceReferenceDate: 93866.4142533315)
print("GMT TIME: \(interval)")
let intervalDateComponents = calendar.dateComponents([.hour, .minute, .second], from: interval)
let mHour = intervalDateComponents.hour ?? 0
let mMinute = intervalDateComponents.minute ?? 0
print("INTERVAL DATE COMPONENT: \(intervalDateComponents)")
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let intervalDateString = formatter.string(from: interval)
print("DATE STRING FORMAT: \(intervalDateString)")
prints out the following: (please pay attention to date)
TIMEZONE IN HOURS: -7
GMT TIME: 2001-01-02 02:04:26 +0000 (January 2)
INTERVAL DATE COMPONENT: hour: 18 minute: 4 second: 26 isLeapMonth: false
DATE STRING FORMAT: 2001-01-01 18:04:26 (January 1)
interval HOURS: 18
edit/update:
let mDateString = formatter.string(from: interval)
print("DATE STRING FORMAT: (mDateString)")
let mdate = formatter.date(from: mDateString)
print("MDATE: (mdate)")
let mDateComponents = calendar.dateComponents([.hour, .minute, .second], from: mdate!)
print("M DATE COMPONENT: (mDateComponents)")
prints out:
DATE STRING FORMAT: 2001-01-01 19:01:27 -0700
MDATE: Optional(2001-01-02 02:01:27 +0000)
M DATE COMPONENT: hour: 18 minute: 1 second: 27 isLeapMonth: false
If you notice above in the code my intention is to extract the local time component to trigger alarm at a later time daily. So I fed the string that shows
2001-01-01 19:01:27 -0700
I converted the string to mdate in order to get time components that I need to use for the alarm to work on time so I got (hour: 18) rather than hour: 19 which I used to generate the mdate string.
The problem there is that you are getting the timezone offset for today. You need to get the timezone offset for the same date you are using:
let timeZone = TimeZone.current.secondsFromGMT(for: interval)
print("TIMEZONE IN HOURS: \(timeZone/3600)")

Swift date manipulation - strange month return

I'm trying to do some date manipulation with Swift and I'm getting an unexpected result. The webservice will pass in a string date, and then I want to get that month and the previous month. I'm using this code (with input grab and such removed):
import Foundation
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "America/New_York")
formatter.dateFormat = "yyyy-MM-dd"
let date = formatter.date(from: "2018-12-01")!
let prev = Calendar.current.date(byAdding: .month, value: -1, to: date)!
formatter.string(from: date)
formatter.string(from: prev)
So I've got a valid date, and then I subtract a month from it. The first formatted date shows my expected 2018-12-01 but then on the second line, instead of saying 2018-11-01 it says 2018-10-31.
I'm in PST, which is of course 3 hours ahead of EST. If I add 3 hours I'd get the expected strings. However, since both the input and the output strings were done with a formatter using the timezone, why don't I get the expected output?
The problem is that Calendar.current is in a different timezone (for you) than the formatter.
So date is December 1, 2018 at midnight New York time. But that is November 30, 2018 at 9pm local time (PST) for you.
When you subtract one month it is done in local time (Calendar.current) so you get October 30, 2018 at 9pm. Then you format that date to New York time and it results in October 31, 2018 at midnight.
To get the proper results you want a Calendar in the same timezone as the formatter:
var cal = Calendar(identifier: Calendar.current.identifier)
cal.timeZone = formatter.timeZone
let prev = cal.date(byAdding: .month, value: -1, to: date)!
This will give the expected result.

Convert any time to GMT +3 in Swift

I would like to convert any time, UTC,, GMT+2 .. etc , anything to be only GMT +3
I tried this code but no success
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
formatter.timeZone = TimeZone.current
let currentdate = formatter.string(from: date)
print("currentdate \(currentdate)")
let gmt = DateFormatter()
gmt.timeZone = TimeZone(secondsFromGMT: 3600*3)
gmt.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let gmtDate = gmt.date(from: currentdate)!
print("gmtDate -> \(gmtDate)")
I tried abbreviations for the time zone, same result the date comes out to be only GMT.
Any ideas?
Your code has a lot of issues. First, there is no reason to go from Date to String and back to Date. Second, if you are converting a String to a Date, and the String contains its own timezone information, then setting the formatter's timeZone is pointless. The timezone in the string will be used when calculating the associated Date. There are only two cases where setting a date formatter's timezone makes sense:
When parsing a date/time string that does not contain any timezone information. The formatter's timezone will then be used to interpret the string.
When converting a Date to a String. The formatter's timezone will be used when generating the resulting string from the date.
If you simply want to show any Date as a String in a specific timezone then all you need is:
let date = Date() // some date
print("Original Date (in GMT): \(date)")
// Setup a formatter with a date and time style
let formatter = DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 3600 * 3) // the desired timezone
formatter.dateStyle = .long
formatter.timeStyle = .medium
let string = formatter.string(from: date)
print("GMT+3 style result: \(string)")
// For comparison, set the formatter to a specific format
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let string2 = formatter.string(from: date)
print("GMT+3 format result: \(string2)")
Output (for the en_US locale):
Original Date (in GMT): 2017-10-28 20:53:59 +0000
GMT+3 style result: October 28, 2017 at 11:53:59 PM
GMT+3 format result: 2017-10-28 23:53:59 +0300
There is no need to convert any time. Simply create a String from a Date to get the desired output.
Note that, by default, a DateFormatter shows its result in local time. Set the formatter's timeZone if you want the result in some other specific timezone.
Also note that printing a Date object always shows the date in UTC time (+0000). Many people get confused by this and think they are getting the wrong date when they are not.
Swift 5
private func convertDate(string:String) -> String {
let dateFormatter = DateFormatter()
// format in which the date and time comes from the server
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
// set the time zone, the time is stored on the server
dateFormatter.timeZone = TimeZone(abbreviation: "GMT+3")
// convert to Date
let date = dateFormatter.date(from: string)
// now set our local time zone
dateFormatter.timeZone = TimeZone.current
// time format we need
dateFormatter.dateFormat = "dd.MM.yyyy HH:mm"
// convert the Date obtained above into text format
let displayString = dateFormatter.string(from: date!)
// local time is ready)))
return displayString
}