How to parse date string in PKT time zone in Swift? - swift

I'm having trouble parsing a date string in the PKT time zone.
Here is some code to paste in a playground:
import Foundation
let dateText = "Tue, 27 Jun 2017 04:00 AM PKT"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE, d MMM yyyy hh:mm a zzz"
if let checkDate = dateFormatter.date(from: dateText) {
let date = checkDate
print(date)
} else {
print("Could not parse")
}
If I change the time zone to "CDT" it works. However, I am trying to parse a date in Pakistan Standard Time (PKT) as that is the format I am parsing.
I found this handy tool and it says the format is invalid.
Using this table as a reference in formatting.
Documentation: https://developer.apple.com/documentation/foundation/nsdateformatter?language=objc
I see a references to similar questions How to get Current EST time IOS Swift

Related

Where the time coming from when converting date string without any time in to Date() in swift?

I'm using this extension to convert a string containing date to Date() object:
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/YYYY"
let date = dateFormatter.date(from: self)
return date
}
}
the result always containing a time in it. I'm curious where is the time coming from, why it is not all zero?
print("11/12/2021".toDate())
result is ->
2020-12-19 21:00:00 +0000
In the time that I run the code, it is showing 21:00:00, so why it is 21? I believe It is not related to my time because I run it at different times.
A Date object indicates an instant in time anywhere on the planet, independent of time zone.
A DateFormatter can convert a String to a Date (or a Date to a String, but ignore that for now). When it converts a String to a Date, it may make assumptions about the time of day if that is not included in the String. I believe it assumes that the time is midnight (00:00:00) in the date formatter's time zone. (And by the way, midnight is the starting point of a day, so midnight is zero hours/minutes/seconds into the day. Essentially midnight IS zeros for time.)
So when you call your String extension to convert "11/12/2021" to a Date, the extension creates a DateFormatter which defaults to the device time zone. It creates a Date assuming Midnight in the local time zone.
When you print that date, it gets displayed in GMT.
It looks like your format string has problems though. You're getting the wrong year and month. I think you must be using the wrong month or day string in your formatter. (I always have to look those up when I use them.)
Edit:
You likely want a format string of "MM-dd-yyyy"
(2-digit month, 2-digit day of month, and 4-digit year.)
Lower-case "m" or "mm" is minutes. Upper-case "Y" is for "week of year" based calendars, which you probably don't want.
Try this code:
func toDate() -> Date?{
let dateFormatter = DateFormatter()
let posixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM-dd-yyyy"
dateFormatter.locale = posixLocale
let date = dateFormatter.date(from: self)
return date
}
}
And to use it:
let dateString = "12/11/2021"
let date = dateString.toDate()
print(date)
if let date = date {
let convertedDateString = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .medium)
print(convertedDateString)
} else {
print("Can't convert \(dateString) to a date")
}
That displays "Dec 11, 2021 at 12:00:00 AM" in my US locale (US Date formatting.) Note that since I use the DateFormatter class method localizedString(from:dateStyle:timeStyle:) I see midnight as the displayed time (The time you get from a DateFormatter when you don't specify a time, but displayed in the local time zone.)
The answer is:
when we are converting a string to a Date Object the important part is the time zone that we are converting it to.
for example, if you convert your string date to a UTC time zone when you want to bring it back you have to set the time zone of the date to UTC.
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(identifier: "UTC")
so this is the reason why when we are printing the Date() object it is deferred from our string date.
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
let date = dateFormatter.date(from: self)
return date
}
}
extension Date {
func toString() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
return dateFormatter.string(from: self)
}
}
let stringDate = "01-12-2021"
let date = "01-12-2021".toDate()
let convertBack = date?.toString()
print("(\(stringDate)) -> (\(date!)) -> (\(convertBack!))")
and the result is:
(01-12-2021) -> (2021-01-11 21:01:00 +0000) -> (01-12-2021)
so at the end when we convert back the Date object it will be the same. because that 2 dateFormatter in the extensions are using the default time zone. and if you want to specify a specific time zone you have to declare it in converting from and to string together.

Assigning NSDateComponents to a label

I'm creating a mobile app that has a countdown to a specific date. I think I have the timer itself correct, but I'm struggling to get it into a format where I can assign it to my label. I'm getting an error "Cannot invoke initializer for type 'String' with an argument list of type '(NSDateComponents)'. This error is found at the line "var date = String(openingGavelDate)". The outlet for the label has been properly created in this file.
First step I took was creating the date variable and setting it equal to the converted value of my other variable. Second step involved trying to look through documentation but so far I haven't really found any substantial documentation that can help.
func createGavelTimer() {
let openingGavelDate = NSDateComponents()
openingGavelDate.year = 2019
openingGavelDate.month = 7
openingGavelDate.day = 16
openingGavelDate.hour = 14
openingGavelDate.minute = 00
openingGavelDate.timeZone = NSTimeZone(abbreviation: "CST")! as TimeZone
var date = String(openingGavelDate) //problem is here
countdownLabel.text = date
}
One of possible solutions:
let date = Calendar.current.date(from: openingGavelDate)
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short
dateFormatter.dateStyle = .medium
dateFormatter.doesRelativeDateFormatting = true
dateFormatter.timeZone = TimeZone(abbreviation: "CST")!
let yourString = dateFormatter.string(from: date)
Try converting the NSDateComponents object to a Date by using Calendar.date(from:), and then converting that to a String using a DateFormatter:
let gregorianCalendar = Calendar(identifier: .gregorian)
if let date = gregorianCalendar.date(from: openingGavelDate as DateComponents) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
countdownLabel.text = dateFormatter.string(from: date)
}
Also, as #Sh_Khan and #rmaddy have commented, you should be using DateComponents, TimeZone, etc. instead of their NS counterparts (unless you're using Swift 2 or lower).
Two things you need to do to form your date:
Set a calendar on the DateComponents instance.
Get your date by accessing the date property on your DateComponents instance.
Also, I'd recommend using time zone identifiers instead of abbreviation to specify a time zone; advantage is that identifiers will automatically apply special rules such as daylight savings as appropriate. (Below I've substituted the "America/Chicago" zone for UTC.)
Try this code in a playground:
var openingGavelDate = DateComponents()
let timeZone = TimeZone(identifier: "America/Chicago")!
openingGavelDate.year = 2019
openingGavelDate.month = 7
openingGavelDate.day = 16
openingGavelDate.hour = 14
openingGavelDate.minute = 00
openingGavelDate.calendar = Calendar.current
openingGavelDate.timeZone = timeZone
let date = openingGavelDate.date
print(date ?? "no date")
Output: 2019-07-16 19:00:00 +0000 (your date in GMT.)
This will get you a date, but notice that the Date class prints in GMT by default, because Date has no concept of timezone.
To print date in the timezone and format you want, use DateFormatter:
let f = DateFormatter()
f.dateFormat = "yyyy-MM-dd hh:mm a"
f.timeZone = TimeZone(identifier: "America/Chicago")!
print(f.string(from: date!))
Output: 2019-07-16 02:00 PM (your date & time, in CST and formatted for reading.)
DateFormatter allows you to either control the format yourself, or follow the user's system settings to determine what is in the final string. See the docs for DateFormatter to see how to get it into the format you want to display.

Converting string to date returning the day before

I have a date in a string with this format "2017-03-14" (yyyy-MM-dd) and i am trying to convert it into a string with this format "Tuesday, March 14, 2017".
This is my code:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = dateFormatter.date(from: "2017-03-14")
Now if i print the value of "date", i get this:
2017-03-13 22:00:00 +0000
This is just the day before. Why is that ?
EDIT:
I need to compare date before formatting it.
var newDateString : String = ""
let date2 = Date()
let comp = date2.compare(date!)
if comp.rawValue == 0 {
newDateString = "TONIGHT"
} else {
dateFormatter.dateFormat = "EEEE, MMMM d, yyyy"
newDateString = dateFormatter.string(from: date!)
}
Thanks
The desired Format should be:
EEEE, MMMM dd, yyyy
All you have to do is to add after your code the following code snippet:
dateFormatter.dateFormat = "EEEE, MMMM dd, yyyy"
let string = dateFormatter.string(from: date!) // "Tuesday, March 14, 2017"
Remark:
I'd like to suggest to do optional binding for declaring the date, as follows:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
if let date = dateFormatter.date(from: "2017-03-14") {
dateFormatter.dateFormat = "EEEE, MMMM dd, yyyy"
let string = dateFormatter.string(from: date) // "Tuesday, March 14, 2017"
}
Your confusion is based on a misunderstanding of what Time and Date are. Evidently, you are currently located in a time zone that is 2 hours ahead of UTC (Coordinated Universal Time), previously known as Greenwich Mean Time (GMT).
When you ask the OS for a Date object converted from "2017-03-14" you get a date/time reference of Midnight the morning of 2017-03-14 in your time zone which is, correctly, 10:00 pm (22:00) then night before in UTC.
When you ask the OS for a Date object for now with Date() you get a date/time reference of now in your time zone, which will be two hours earlier in UTC.
To accurately evaluate your date string to say "is now earlier than 'tonight of 2017-03-14'" you will probably want to convert from "2017-03-14 23:59" (or 11:59 pm, or perhaps prior to the start of tonight's event of 8:00 pm, etc).
This will do your original comparison, but would work better as a function (although I'm not sure how you want to use it)...
var newDateString : String = ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
// set tonightDate to 1 minute before midnight, tonight, in local time
if let tonightDate = dateFormatter.date(from: "2017-03-14 23:59") {
// set nowDate to current local time
let nowDate = Date()
let comp = nowDate.compare(tonightDate)
if comp.rawValue <= 0 {
newDateString = "TONIGHT"
} else {
dateFormatter.dateFormat = "EEEE, MMMM d, yyyy"
newDateString = dateFormatter.string(from: tonightDate)
}
}
print(newDateString)
It is calculated time based on UTC so you are getting day before.You can get proper format using below code:
func chageDateFormat(date:String) -> String{
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
guard let date = dateFormatter.dateFromString(date) else {
assert(false, "No date from string")
return ""
}
print(date)
dateFormatter.dateFormat = "EEEE, dd MMMM, yyyy"
let result = dateFormatter.stringFromDate(date)
return result
}
let resultFormateDate = chageDateFormat("2017-03-14")
For comparison you also need to convert Date() to proper format.Both date must be in same format.
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = NSDate()
let strdate = dateFormatter.stringFromDate(date)
let resultFormateDate2 = chageDateFormat(strdate)
Now you can compare two strings
if resultFormateDate == resultFormateDate2{
print("True")
}

NSDate which is returned is incorrect

I am build a function to get the date for the Chinese new year from the date which is passed from a datePicker. the convertDateFormater converts a string formatted date to a date object. the date which i get back is
Returned date = "03-Feb-2030" for the year "1970-05-22"
but if i run the current date with "NSDate() as Date" i get the right output. for the year 2016.
func convertDateFormater(date: String) -> Date
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"//this your string date format
dateFormatter.timeZone = NSTimeZone(name: "UTC") as TimeZone!
let date = dateFormatter.date(from: date)
return date!
}
var inputDate1 = convertDateFormater(date: "1970-05-22")
print(inputDate1)
let chineseCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.chinese)!
let formatter: DateFormatter = {
let fmt = DateFormatter()
fmt.dateStyle = .full
fmt.timeStyle = .full
fmt.dateFormat = "dd-MMM-yyyy"
return fmt
}()
var comps = chineseCalendar.components([.year], from: inputDate)
guard let newYear = chineseCalendar.date(from: comps) else { fatalError("no date for \(comps.year)")}
let getNewYear = formatter.string(from: newYear)
print("\(NSDate() as Date) : \(getNewYear) - \(newYear)")
In the Gregorian calendar, there are two eras: the current "Common Era" (abbreviated "CE" and also known as "Anno Domini" or "AD") and "Before Common Era" (abbreviated "BCE" and also known as "Before Christ" or "BC"). So a year number on the Gregorian calendar is ambiguous: "1970" by itself can mean either "1970 CE" or "1970 BCE". We usually assume CE when no era is specified.
I know almost nothing about the Chinese calendar, but I know it has many more eras than the Gregorian calendar, so a year number on the Chinese calendar is even more ambiguous. The code you posted doesn't do anything about eras. So I copied and pasted your code into a playground and made one change:
var comps = chineseCalendar.components([.era, .year], from: inputDate)
// ^^^^^^
The output:
1970-05-22 00:00:00 +0000
2016-11-30 01:40:12 +0000 : 06-Feb-1970 - 1970-02-06 06:00:00 +0000
Indeed, 1970-02-06 was the date of the Chinese new year in 1970 CE on the Gregorian calendar.

Swift date formatting

I'm trying to pull a date string from a button and format is as a date to be store in CoreData.
Here is my code:
let dateStr = setDateBTN.titleLabel?.text
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-YYYY"
let date:NSDate = dateFormatter.dateFromString(dateStr!)!
If I do a println on dateStr I get the following: 03-10-2015. Then if I immediately println on date I get: 2014-12-21 05:00:00 +0000.
Any ideas as to why the actual date is changing when I run it through the date formatter?
NSDateFormatter Class Reference : http://goo.gl/7fp9gl
Date Formatting Guide (Apple) : http://goo.gl/8zRTQl
A common mistake is to use YYYY. yyyy specifies the calendar year whereas YYYY specifies the year (of “Week of Year”), used in the ISO year-week calendar.
Your code should work, as you expect, like this :
let dateStr = setDateBTN.titleLabel?.text
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
let date:NSDate = dateFormatter.dateFromString(dateStr!)!