Home > Enterprise >  SwiftUI Text with DateFormatter doesn't respect TimeZone
SwiftUI Text with DateFormatter doesn't respect TimeZone

Time:10-10

I need to display the date/time of an event with a toggle that lets the user switch between their current timezone and the timezone the event takes place in.

I'm trying to use the SwiftUI text formatters but can't figure out how to make it work for the alternate TimeZone.

So I have a simple formatter:

@State var localTimeZone: TimeZone?
let date: Date

var formatter: DateFormatter {
    var dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "dd/MM/yyyy HH:mm:ss"
    dateFormatter.timeZone = localTimeZone
    return dateFormatter
}

and then I try to display the date like so but it seems inconsistent. Why are the following different for example? I would expect them to be the same.

// 'Correctly' displays formatted in the timezone passed in (eg 01/08/2022 23:25:00)
Text(formatter.string(from: net))

// 'Ignores formatter' and displays in the current timezone (eg 01/08/2022 21:25:00)
Text(net, formatter: formatter)

The end goal is to be able to use the following text formats and have a toggle that could switch the timezone of the Date object.

Text(date, format: .dateTime.timeZone(.specificName(.long)))
Text(date, format: .dateTime.day().month().hour().minute().second())

Is this possible, I seem to be drawing a blank here, any points in the correct direction would be greatly appreciated!

Incase it's relevant the only details for the event I have are a lat/long and alpha 3 country code. The only way I have currently found to get any kind of timezone from this is to get a placemark with the following method (Which is why I'm trying to using a TimeZone).

func getTimeZone(location: CLLocation) async -> TimeZone? {
    let geocoder = CLGeocoder()
    do {
        guard let placeMark = try await geocoder.reverseGeocodeLocation(location).first else { return nil }
        return placeMark.timeZone
    } catch {
        print("Filed to get timeZone for event")
        return nil
    }
}

Thanks for any help!

Update: Just found out you can set .environment(\.timeZone, localTimeZone) on a view and everything inside will be displayed accordingly.

eg...

Section("Local") {
    Text(date, formatter: formatter)
}
.environment(\.timeZone, localTimeZone)

however the following doesn't still doesn't work

Section("Local") {
    Text(date, format: .dateTime.timeZone(.specificName(.long)))
    Text(date, format: .dateTime.day().month().hour().minute().second())
}
.environment(\.timeZone, localTimeZone)

CodePudding user response:

Just looking at your end goal here, it seems like you just want to change the timeZone property of Date.FormatStyle, so that dates can be formatted in different time zones.

You can write a little extension to so that you can set it inline more conveniently.

extension Date.FormatStyle {
    func withTimeZone(_ timeZone: TimeZone) -> Date.FormatStyle {
        var copy = self
        copy.timeZone = timeZone
        return copy
    }
}

Then you can do things like this:

Text(date, format: 
    .dateTime.timeZone(.specificName(.long)).withTimeZone(localTimeZone)
)
Text(date, format: 
    .dateTime.day().month().hour().minute().second().withTimeZone(localTimeZone)
)

Also consider using just one Text, with a single format that has both the time zone, date and time in it.

  • Related