regebro / tzlocal

A Python module that tries to figure out what your local timezone is
MIT License
184 stars 58 forks source link

Empty ubuntu docker container (UTC) failure to run `get_localzone` #70

Closed asottile closed 5 years ago

asottile commented 5 years ago

I'm not sure what to expect, the readme leads me to believe

Please note that if you getting a time zone called local, this is not a bug, it's actually the main feature of tzlocal, that even if your system does NOT have a configuration file with the zoneinfo name of your time zone, it will still work.

that I shouldn't get a crash.

Here's a reproduction with docker:

docker run --rm -ti ubuntu:bionic bash -euxc 'apt-get update -qqq && DEBIAN_FRONTEND=noninteractive apt-get install -qqq -y --no-install-recommends python3-pip python3-setuptools && pip3 install tzlocal && python3 -c "import tzlocal; tzlocal.get_localzone()"'
...
Installing collected packages: pytz, tzlocal
  Running setup.py install for tzlocal ... done
Successfully installed pytz-2018.7 tzlocal-1.5.1
+ python3 -c 'import tzlocal; tzlocal.get_localzone()'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/tzlocal/unix.py", line 131, in get_localzone
    _cache_tz = _get_localzone()
  File "/usr/local/lib/python3.6/dist-packages/tzlocal/unix.py", line 125, in _get_localzone
    raise pytz.UnknownTimeZoneError('Can not find any timezone configuration')
pytz.exceptions.UnknownTimeZoneError: 'Can not find any timezone configuration'

The timezone in the container is UTC:

$ docker run --rm -ti ubuntu:bionic date +%Z
UTC
regebro commented 5 years ago

I'm not sure what "empty" means when it comes to docker. It should reasonably have SOME sort of time zone configuration?

I fiddled round a bit and I think having TZ set, but to an empty string, could be the cause of this behavior.

asottile commented 5 years ago

empty in that I haven't installed anything

there isn't a TZ variable set, here's the contents of the environment:

$ docker run --rm ubuntu:bionic env | sort
HOME=/root
HOSTNAME=7e1433ab994c
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

or from a shell:

$ docker run --rm ubuntu:bionic bash -c env | sort
HOME=/root
HOSTNAME=bfc808504f4b
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
SHLVL=1
_=/usr/bin/env

or from an interactive login shell:

$ docker run --rm -ti ubuntu:bionic bash -ilc env | sort
HOME=/root
HOSTNAME=40ebadebee99
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
SHLVL=1
TERM=xterm
_=/usr/bin/env
thanatos commented 5 years ago

I can confirm this. tzlocal.get_localzone() raises an exception in a ubuntu:bionic container w/ nothing added to it except Python 3 & tzlocal.

The difference between the bionic and xenial (the preceding Ubuntu LTS) containers seems to be that bionic lacks both /etc/localtime and /etc/timezone. Without those, tzlocal raises.

The readme also states,

Any unix-like system with a /etc/localtime or /usr/local/etc/localtime

If I copy the /etc/localtime from Xenial into the Bionic container, tzlocal still raises. On Xenial, it's a symlink:

lrwxrwxrwx 1 root root      27 Jan 23  2018 localtime -> /usr/share/zoneinfo/Etc/UTC

Now, it's also a broken symlink in Xenial. It's adding /etc/timezone that actually works for me in Bionic. (Which is a text file whose content is a zone name followed by a newline.) So I suspect on both Xenial, tzlocal ignores the broken symlink, and gets the information from /etc/timezone.

I agree w/ @asottile: the README suggests this should work. Additionally, glibc/C programs can get the timezone just fine; glibc seems to default to UTC in the presence of no config files and no environment variables. I think tzlocal should mirror glibc's behavior here. That is, the following:

#define _GNU_SOURCE /* for tm_gmtoff and tm_zone */

#include <stdio.h>
#include <time.h>

/* Checking errors returned by system calls was omitted for the sake of readability. */
int main(void)
{
  time_t t = time(NULL);
  struct tm lt = {0};

  struct tm *result = localtime_r(&t, &lt);
  if(!result) { printf("localtime_r() error."); }

  printf("Offset to GMT is %lds.\n", lt.tm_gmtoff);
  printf("The time zone is '%s'.\n", lt.tm_zone);

  return 0;
}

Runs on bionic; it emits,

# ./tztest
Offset to GMT is 0s.
The time zone is 'UTC'.

…while tzlocal raises.

asottile commented 5 years ago

Empty ubuntu:bionic doesn't ship with the tzdata package by default (which is where those files come from), whereas ubuntu:xenial does

regebro commented 5 years ago

So, in fact, an empty ubuntu:bionic doesn't have any timezone configuration. Crazy....

Anyway, I actually use docker in my new job, I'll try to get time to verify this. If there really is no config, then the only way out of this is to have a default when no config is found, and I really don't want to do that.

tommycahir commented 5 years ago

Try edit the container and then create a environment variable

name = TZ value = your time zone eg America/Toronto

regebro commented 5 years ago

Would be cool if someone that had this issue in real life can test this out from master before I release it.

asottile commented 5 years ago

Using a tweaked reproduction of above:

$ docker run --rm -ti ubuntu:bionic bash -euxc 'apt-get update -qqq && DEBIAN_FRONTEND=noninteractive apt-get install -qqq -y --no-install-recommends python3-pip python3-setuptools git ca-certificates && pip3 install git+https://github.com/regebro/tzlocal && python3 -c "import tzlocal; tzlocal.get_localzone()"'

...

+ pip3 install git+https://github.com/regebro/tzlocal
Collecting git+https://github.com/regebro/tzlocal
  Cloning https://github.com/regebro/tzlocal to /tmp/pip-ja8wz1tp-build
Collecting pytz (from tzlocal==2.0.0.dev0)
  Downloading https://files.pythonhosted.org/packages/3d/73/fe30c2daaaa0713420d0382b16fbb761409f532c56bdcc514bf7b6262bb6/pytz-2019.1-py2.py3-none-any.whl (510kB)
    100% |################################| 512kB 1.7MB/s 
Installing collected packages: pytz, tzlocal
  Running setup.py install for tzlocal ... done
Successfully installed pytz-2019.1 tzlocal-2.0.0.dev0
+ python3 -c 'import tzlocal; tzlocal.get_localzone()'
/usr/local/lib/python3.6/dist-packages/tzlocal/unix.py:158: UserWarning: Can not find any timezone configuration, defaulting to UTC.
  warnings.warn('Can not find any timezone configuration, defaulting to UTC.')
regebro commented 5 years ago

OK, that looks good. Thanks!