1

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)
Matthew Folbigg
  • 137
  • 1
  • 7
  • "and have a toggle that could switch the timezone of the Date object." You mean, switch between displaying the time zone, and displaying the date? The two lines of code does not display the same date in different timezones. One line displays the local time zone's name, the other displays a `Date` in the local time zone, without the timezone itself. – Sweeper Oct 09 '22 at 12:39
  • Ah sorry, I mean have a view that has the "Name of the timezone being displayed" and the "date time in that time zone" eg... (Not real times) [toggle off - British Summer Time - 8/8/22 10:00] [toggle on - Eastern Standard Time - 8/8/22 19:00] – Matthew Folbigg Oct 09 '22 at 12:41

1 Answers1

2

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.

Sweeper
  • 213,210
  • 22
  • 193
  • 313