Date Formatter isn't showing correct day of the month when using format "DD" - swift

I am trying to show today's date as March 26 but it is showing as "March 85" when I use this code.
let formatter = DateFormatter()
formatter.dateFormat = "MMMM DD"
let defaultTimeZoneStr = formatter.string(from: Date())

The problem is that you are using the wrong date format. D is for "day of year". The correct symbol for "day of month" is lowercased d Thats why you are getting 85instead of 26.
Another point you should consider is to set your locale fixed to "en_US_POSIX" if you don't want your date string to reflect the users settings and locale.
Note that you should use Swift native type Date instead of NSDate.
If your intent is to display it respecting the user locale and settings you should use date formatter dateStyle (short, medium, long or full) How do I get the current Date in short format in Swift
If you need a localized date format limited to
month and day only, you can use DateFormatter method dateFormat from template:
class func dateFormat(fromTemplate tmplate: String, options opts: Int, locale: Locale?) -> String?
let df = DateFormatter()
df.dateFormat = DateFormatter.dateFormat(fromTemplate: "MMMMdd", options: 0, locale: .current)
df.string(from: Date()) // "March 27"

Related

How to parse date in "2022-03-04T10:30:00-08:00" format correctly in swift?

I have date coming from API shown below. These dates are for different countries.
dateTime = "2022-03-04T14:30:00-08:00"
I need to convert this use it both as Date and as String. But I do not know if date and time I am converting are correct. I am using following code:
To Convert String from API to Date:
extension String {
var CommonDateFormat: Date? {
get {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
return dateFormatter.date(from: self)
}
}
}
To Convert Date to String:
extension Date {
func getDateAccoringTo(format: DateFormat ) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format.rawValue
dateFormatter.timeZone = .current
dateFormatter.timeZone = TimeZone.init(identifier: "UTC")
return dateFormatter.string(from: self)
}
}
enum DateFormat: String {
case ddmmyyyy = "dd/MM/yyyy"
case mmddyyyy = "MM/dd/yyyy"
case mmmd_yyyy = "MMM d, yyyy"
case llll_yyyy = "LLLL ,yyyy"
case TIME = "HH:mm:ss"
case day = "dd"
}
When I try to get day from date it always gives next day date. For example if date = "2022-03-04T14:30:00-08:00" then if I try to get day using code below
date?.getDateAccoringTo(format: .day)
This returns 5 not 4
date?.getDateAccoringTo(format: .TIME)
This returns time 00:00:00
Am I missing something important which is leading to these values?
Also, if I am missing something in my question kindly let me know so that I can improve it.
The given string
let dateTime = "2022-03-04T14:30:00-08:00"`
is a standard ISO8601 formatted date string. It can be converted to Date with
let formatter = ISO8601DateFormatter()
let date = formatter.date(from: dateTime)!
At this specific point in time it is
14:30 on Friday, March 4 in Denver, CO, USA
22:30 on Friday, March 4 in London, UK
06:30 on Saturday, March 5 in Tokyo, Japan
Now let's see how Xcode displays dates.
print displays Date instances always in UTC indicated by +0000 which is the London time zone unless you print(date.description(with: .current), this displays the date in the local time zone.
In a Xcode Playground the result area displays Date instances in the local time zone except in print lines.
Last point to consider is that DateFormatter converts Date to String in the local time zone if no time zone is specified.
Keeping this behavior in mind you get the next day if you convert the date to string with DateFormatter but without specifying the time zone and your local time zone is greater than or equal to +01:30.
And you get the time 00:00 if you convert the date to string with DateFormatter but without specifying the time zone and your local time zone is exactly +01:30 which is a pretty unusual time zone by the way.

TimeZone Date Formatting Issue

I'm trying to get a date from a string with multiple time zones, It works without any problems if the API returns zones with abbreviations like CST or UTC but it fails if it returns EET
let timeString = "17:32 (EET)"
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm (zzz)"
let time = formatter.date(from: timeString) // return nil
Any idea what is the issue might be?!
Probably because there is more than one timezone that matches the timezone abbreviation or the date formatter's default date (January 1st) doesn't match the daylight savings of the timezone abbreviation used. Not all countries uses daylight savings time as well it might change at any time. Check this link. This will probably happen for all non US timezones abbreviation as well. For example CST it is used for "China Standard Time" and "Chicago Standard Time" as well. You can workaround that issue setting your date formatter property isLenient to true. Note that this will result in a date of January 1st 2000 with a probably incorrect timezone offset. If you have control of your string input you should use the timezone identifiers instead of its abbreviations to avoid ambiguity. You should also set your date formatter's locale to "en_US_POSIX" when parsing fixed date format to avoid date formatter's reflecting the users device locale and settings:
let timeString = "17:32 (EET)"
let formatter = DateFormatter()
formatter.locale = .init(identifier: "en_US_POSIX")
formatter.dateFormat = "HH:mm (zzz)"
formatter.isLenient = true
let date = formatter.date(from: timeString) // "Jan 1, 2000 at 1:32 PM" "BRT Brazilian Standard Time"
So you should use "GMT+2" or "GMT-3" to avoid ambiguity or as I have already suggested use its identifiers "HH:mm (VV)" i.e. "Europe/Athens" or "America/Sao_Paulo"

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
}

Using DateFormatter to return iOS lock screen date style

There seem to be five date styles for the DateFormatter class: none, short, medium, long, and full. However, none of these seem to return the lock screen date style, which is as follows:
Tuesday, 6 June
Using DateFormatter's .long style returns the year as well:
Tuesday, 6 June 2017
Additionally, this lock screen date style will change with the current localization/regional settings.
Is there a way to return the date, à la iOS lock screen date style (along with localizational changes)?
You can get the localized format for any combination of date components:
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.setLocalizedDateFormatFromTemplate("EEEE MMMM d")
print(dateFormatter.string(from: date)) // Friday, 4 August
In Spanish:
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "es")
dateFormatter.setLocalizedDateFormatFromTemplate("EEEE MMMM d")
print(dateFormatter.string(from: date)) // viernes, 4 de agosto
Note how the order of components has been automatically changed and correct separators inserted.
You can use the custom date format "EEEE, d MMMM"
Swift 3
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, d MMMM"
dateFormatter.string(from: date)
// Friday, 4 August