Assigning NSDateComponents to a label - swift

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.

Related

Getting error when converting date format error | Swift

Essentially I would like to convert the following:
2022-07-01 14:35:00
To simply:
July 1st
The following is what I currently have because the initial input is string, but when I'm converting from string to date time the hour seems to have +2 hours added to it. Why is this happening?
// Create String
let string = "2022-07-01 14:35:00"
// Create Date Formatter
let dateFormatter = DateFormatter()
// Set Date Format
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
// Convert String to Date
let newdate = dateFormatter.date(from: string)
print(newdate!)
Time depends on where you are. So in this case the +2 hours you see, may be due to the difference in TimeZone. So adjust the TimeZone in the format to match the original place, or put everything in GMT TimeZone, or a common TimeZone of your choosing. Alternatively, keep the time difference.
Try something like this:
let string = "2022-07-01 14:35:00"
let readFormatter = DateFormatter()
readFormatter.timeZone = TimeZone(abbreviation: "GMT") // <-- here adjust
readFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let writeFormatter = DateFormatter()
writeFormatter.timeZone = TimeZone(abbreviation: "GMT") // <-- here adjust
writeFormatter.dateFormat = "LLLL dd"
if let theDate = readFormatter.date(from: string) {
print("\n----> theDate: \(theDate)") // ----> theDate: 2022-07-01 14:35:00 +0000
let simpleDate = writeFormatter.string(from: theDate)
print("\n----> simpleDate: \(simpleDate)") // ----> simpleDate: July 01
}

Getting string between exact letters by regex in Swift

I have a String in this format: "2019-03-11T17:04:00+0100". I need to convert that string to the one that will be in this format: "03.11 17:04". I already tried some suggestions for instance this one.
As per my comment, this is a task for DateFormatter rather than RegeX. I threw this together in a playground quickly to demonstrate what I mean.
let inFormatter = DateFormatter()
inFormatter.locale = Locale(identifier: "en_US_POSIX")
inFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
let input = "2019-03-11T17:04:00+0100"
let dateFromInput = inFormatter.date(from: input)! // This should be unwrapped properly in your code.
let outFormatter = DateFormatter()
outFormatter.locale = Locale(identifier: "en_US_POSIX")
outFormatter.dateFormat = "MM. dd HH:mm"
let output = outFormatter.string(from: dateFromInput)
print(output) // Prints 03. 11 16:04.
The premise is that you provide a format for which to parse the input string against, this is transcoded to a Date object which you can then transcode to your desired output format with a second DateFormatter.
EDIT:
As pointed out by #user28434, the input you are passing in looks like CET (Central European Time); When I configure the output DateFormatter, I do not specify a time zone so it defaults to my local time zone, GMT (Greenwich Mean Time). This would obviously cause the output to be different based on the location of the user in the world, which should be expected/desired. But it's worth highlighting. You can use outFormatter.timeZone = TimeZone(identifier: "CET") to force a CET output.
You can use DateFormatter instead of regex,
first, convert the given string to a date with the string format,
then convert the resulted date to a string with the desired format.
func convertISO8601DateStringToDate(dateStr: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
return dateFormatter.date(from: dateStr)
}
func convertDateToReadableOutput(date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM.dd HH:mm"
return dateFormatter.string(from: date)
}
you can use these two methods as below:
if let date = stringToDateConverter(dateStr: "2019-03-11T17:04:00+0100") {
print(dateToStringConverter(date: date))
}

Getting wrong date when converting string with timezone

In Swift Playground, I run this.
let string = "2019-01-14T00:00:00+08:00"
let utcTimezone = TimeZone(abbreviation: "UTC")!
let sgtTimezone = TimeZone(abbreviation: "SGT")!
let dfs = DateFormatter()
dfs.timeZone = sgtTimezone
dfs.locale = Locale(identifier: "en_sg")
dfs.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZ"
dfs.calendar = Calendar(identifier: Calendar.Identifier.iso8601)
let date = dfs.date(from: string)!
Why is date = Jan 13, 2019 at 11:00 PM and not the accurate Jan 14, 2019 at 00:00 AM ?
Tried changing the timezone to UTC but by default the result is UTC
I am expecting Jan 14, 2019 at 00:00 AM.. or at least Jan 14
// This lets us parse a date from the server using the RFC3339 format
let rfc3339DateFormatter = DateFormatter()
rfc3339DateFormatter.locale = Locale(identifier: "en_US_POSIX")
rfc3339DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
rfc3339DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
// This string is just a human readable format.
// The timezone at the end of this string does not mean your date
// will magically contain this timezone.
// It just tells the parser what timezone to use to convert this
// string into a date which is basically just seconds since epoch.
let string = "2019-01-14T00:00:00+08:00"
// At this point the date object has no timezone
let shiftDate = rfc3339DateFormatter.date(from: string)!
// If you want to keep printing in SGT, you have to give the formatter an SGT timezone.
let printFormatter = DateFormatter()
printFormatter.dateStyle = .none
printFormatter.timeStyle = .full
printFormatter.timeZone = TimeZone(abbreviation: "SGT")!
let formattedDate = printFormatter.string(from: shiftDate)
You will notice that it prints 12am. There is nothing wrong with your code. You just misunderstand the Date object. Most people do.
Edit: I used the RFC formatter found in the Apple docs here. The result is the same if you use your formatter. And yes, as rmatty said, there are a few things wrong with your formatter (I stand corrected :))

UTC to local time - wrong result swift

I know there are a lot of threads, but I can't find a solution for my problem. Maybe I can't see the solution...
I receive a UTC Time: for example 12:50
I want convert this time to MEZ respectively to the time zone of the users device. For my example I expect 13:50, because atm is MEZ +1 to UTC.
This is my code
//transform date to string
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let newDateAsString:String = dateFormatter.string(from: self)
//is 12:50 UTC
//transform date to MEZ respectively to the local device timezone
let dateFormatterToDate = DateFormatter()
dateFormatterToDate.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let timeZone = TimeZone.autoupdatingCurrent.identifier as String
dateFormatter.timeZone = TimeZone(identifier: timeZone)
//same result with: dateFormatter.timeZone = NSTimeZone.local
//result is 11:50 but i would expect 13:50
if let result = dateFormatterToDate.date(from: newDateAsString) {
return result
}
The result 11:50 is the time now in my current timezone. But I don't understand this. I give explicitly the date, which should convert. Somebody know where is my mistake?
The conversion that you are doing is the opposite of what you intend. The string newDateAsString, which gives the time as 12:50, does not specify a timezone, because your date format string does not include formatting for a timezone. When you set dateFormatterToDate's timezone to MEZ, and pass newDateAsString to dateFormatterToDate, you are saying: give me a Date object for 12:50 in MEZ.
By default Dates are displayed as UTC, so result is displayed as 11:50, because 12:50 in MEZ is 11:50 in UTC.
To format a date as a string in the local timezone you would use code like this:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
dateFormatter.timeZone = NSTimeZone.local
let localTimeZoneDateString = dateFormatter.string(from: self)

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.