Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Location with the name "Etc/UTC" doesn't exist #176

Open
bartekpacia opened this issue Jul 3, 2023 · 3 comments
Open

Location with the name "Etc/UTC" doesn't exist #176

bartekpacia opened this issue Jul 3, 2023 · 3 comments

Comments

@bartekpacia
Copy link

That's how I initialize timezone data in my app:

import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:timezone/data/latest.dart' as tz_data;
import 'package:timezone/timezone.dart' as tz;

void main() {
  runApp(const ExampleApp());

  setUpTimezone();
}

Future<void> setUpTimezone() async {
  tz_data.initializeTimeZones();
  final timezone = await FlutterTimezone.getLocalTimezone();
  final location = tz.getLocation(timezone);
  tz.setLocalLocation(location);
}

When I run this code on Android Lollipop emulator (API level 21), the following exception is thrown:

flutter : The following LocationNotFoundException was thrown running a test:
07-03 13:26:09.688  3569  3588 I flutter : Location with the name "Etc/UTC" doesn't exist
07-03 13:26:09.688  3569  3588 I flutter : When the exception was thrown, this was the stack:
07-03 13:26:09.688  3569  3588 I flutter : #0      LocationDatabase.get (package:timezone/src/location_database.dart:40:7)
07-03 13:26:09.688  3569  3588 I flutter : #1      getLocation (package:timezone/src/env.dart:36:20)
07-03 13:26:09.688  3569  3588 I flutter : #2      setUpTimezone (package:example/main.dart:22:23)

I'd be grateful for any support or even an idea how this could be mitigated.

@jamesncl
Copy link

jamesncl commented Jul 11, 2023

Okay so this comes up a lot, so thought I would take the time to give a full answer here. Maybe someone can add this to the docs as away of explanation. It took me a while to figure this all out.

The problem is that this package doesn't support many timezone abbreviations such as Etc/UTC, which is what the flutter_timezone package sometimes returns. This is a longstanding issue which won't be fixed, because this is intentional: abbreviations like this are ambiguous.

The problem is that timezones are really, really complicated, and to deal with them you have to understand a bit of this complexity. I'm not an expert, but I think the problem is that Etc/UTC isn't actually a proper timezone, it's a 'fixed offset'. A timezone is a whole history of what a particular group of people decided how they would handle time, daylight savings, etc. and the historical changes made over time. For example, during World War II the UK went 2 hours ahead at daylight saving time instead of the normal 1 hour, to save fuel. To be accurate with time, you have to take all these little historical details into account, because if you have a date from that time during World War II you could end up being an hour out if you don't.

Etc/UTC (and all other abbreviations with ETC in them) is not a history of timekeeping for a particular country, it's just a fixed number (0 hours offset from UTC). This means, depending on exactly where on earth you're talking about, converting a date from Etc/UTC might give you different results.

Most of the time, the timezone we get back from FlutterTimezone.getLocalTimezone() is a proper timezone, like 'Europe/London'. But occasionally, it will return an ambiguous abbreviation. This is unfortunate, and is a result of the underlying device-specific implementation. For Android, this is what FlutterTimezone does under the hood:

    private fun getLocalTimezone(): String {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            ZoneId.systemDefault().id
        } else {
            TimeZone.getDefault().id
        }
    }

And if we look at the docs for TimeZone.getDefault() (which is what ZoneId.systemDefault() also calls under the hood) it says:

Gets the default TimeZone for this host. The source of the default TimeZone may vary with implementation.

"may vary with implementation" basically means we have to deal with all kinds of garbage results, eg from old devices, SDKs, emulators etc. So, unfortunately this is just a problem we have to deal with ourselves. The docs state this on the front page:

We don't provide any functions to get locations by time zone abbreviations because of the ambiguities.

Alphabetic time zone abbreviations should not be used as unique identifiers for UTC offsets as they are ambiguous in practice. For example, "EST" denotes 5 hours behind UTC in English-speaking North America, but it denotes 10 or 11 hours ahead of UTC in Australia; and French-speaking North Americans prefer "HNE" to "EST".

The tz database

Ideally you should only pass unambiguous abbreviations, or full timezone identifiers. For example see the list in Wikipedia: the full timezone identifier is in the 'TZ identifier' column. Notice how the ETC abbreviations don't have proper timezone identifiers.

As a workaround, we have to manually assign these ambiguous timezones to a best guess. For example:

import 'package:timezone/data/latest_all.dart' as tzdatabase;
import 'package:timezone/timezone.dart' as tz;

...

    tzdatabase.initializeTimeZones();
    String localTimezone = await FlutterTimezone.getLocalTimezone();

    if(localTimezone == "Etc/UTC") {
      localTimezone = "??????"; <- replace this with a valid timezone identifier
    }
    tz.setLocalLocation(tz.getLocation(localTimezone));

    // then use tz.local whenever you need the local timezone

Alternatively you could try setting the timezone on the emulator, or trying a different emulator image which returns a valid timezone.

Also note the use of import 'package:timezone/data/latest_all.dart' as tzdatabase; here: importing the ALL version of the timezone database will include some old / invalid / deprecated timezones such as Etc/GMT+5 but will increase the load time. Again, this is explained in the docs.

See this old closed issue for more discussion.

@bartekpacia
Copy link
Author

Thank you very much @jamesncl for such a thorough explanation. Looks like I unknowingly stepped into an uncanny valley of ambiguity :D

Should I close the issue or leave it open for future wanderers?

And again, I really appreciate your comment. Thank you!

@thomasatbloom
Copy link

Stumbled across this issue here with my own app. Using the all database does indeed fix it, but the page on pub.dev also says:

default: doesn't contain deprecated and historical zones with some exceptions like "US/Eastern" and "Etc/UTC"; this is about 75% the size of the all database.

I would assume then that Etc/UTC is supported.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants