Open jkotas opened 6 years ago
The problem is that the TimeZoneInfo implementation ends up reading all timezones on the system. There is a lot of them and when it is combined with slower I/O of WSL, it takes 1 second to do it all.
We should look into making this more efficient - e.g. read all timezones only when it is actually required; or not read them at all.
This affects other Linux distributions as well, not just WSL.
CC @eerhardt
@jkotas - I don't have WSL set up on my machine, but can you answer a few questions?
Normally (or at least on my Ubuntu machine), /etc/localtime
is a symlink to a zoneinfo file. Can you readlink /etc/localtime
and tell me the results?
I am assuming the reason it is slow is because TryGetLocalTzFile
is trying to get the timezone ID, and it is failing to get the ID using readlink
, so it falls back to scanning the /usr/share/zoneinfo
folder for a matching timezone file.
Another possibility is that the TZ
environment variable is set to an absolute path, and the ID can't be discerned from that path.
Can you readlink
/etc/localtime
and tell me the results?
Right - it is not a symlink. @wfurt could you please comment on what other distros have this as a file, and not symlink?
it falls back to scanning the /usr/share/zoneinfo folder for a matching timezone file.
Right. The code needs to figure out the timezone id just to make some internal implementation detail plumbing happy. Can we delay this until after somebody actually asks about the timezone id?
I don't have WSL set up on my machine, but can you answer a few questions?
BTW: It is very easy to install it. One powershell command and a few mouse clicks: https://docs.microsoft.com/en-us/windows/wsl/install-win10
The symlink is convenience and it does not need to exist (so as all the zones under /usr/share) The file it self has POSIX name in it and that is all what matters for time calculations.
I would be primarily concern about embedded distros like Yocto or Alpine.
BTW The CompareTimeZoneFile() can do fstat() (cheap) first and move on if file size is different. That can save on creating stream and reading any data.
find /usr/share/zoneinfo/ -type f | xargs ls -al
-r--r--r-- 1 root wheel 199 Sep 28 2016 /usr/share/zoneinfo/Pacific/Guam
-r--r--r-- 1 root wheel 250 Sep 28 2016 /usr/share/zoneinfo/Pacific/Honolulu
-r--r--r-- 1 root wheel 250 Sep 28 2016 /usr/share/zoneinfo/Pacific/Johnston
-r--r--r-- 1 root wheel 204 Sep 28 2016 /usr/share/zoneinfo/Pacific/Kiritimati
-r--r--r-- 1 root wheel 204 Sep 28 2016 /usr/share/zoneinfo/Pacific/Kosrae
-r--r--r-- 1 root wheel 211 Sep 28 2016 /usr/share/zoneinfo/Pacific/Kwajalein
-r--r--r-- 1 root wheel 171 Sep 28 2016 /usr/share/zoneinfo/Pacific/Majuro
-r--r--r-- 1 root wheel 162 Sep 28 2016 /usr/share/zoneinfo/Pacific/Marquesas
-r--r--r-- 1 root wheel 250 Sep 28 2016 /usr/share/zoneinfo/Pacific/Midway
-r--r--r-- 1 root wheel 240 Sep 28 2016 /usr/share/zoneinfo/Pacific/Nauru
-r--r--r-- 1 root wheel 200 Sep 28 2016 /usr/share/zoneinfo/Pacific/Niue
-r--r--r-- 1 root wheel 263 Sep 28 2016 /usr/share/zoneinfo/Pacific/Norfolk
To fix just DateTime.Now
, we could change
to not use the TimeZoneInfo.Local
property, and instead mimic closer to what we do on Windows - create and cache a private local TimeZoneInfo that isn't exposed anywhere. We could just use the hard-coded "Local"
value for the ID.
This wouldn't solve it for other code using TimeZoneInfo.Local
, but it would solve it just for the DateTime.Now
case - which is more often used.
Another thought would be to update the ID finding code to also check if tzFilePath
is under GetTimeZoneDirectory()
, and use the relative path under it as the ID.
These two changes can be done independently.
BTW FreeBSD does not have symlink
[furt@toweinfu-d11 ~]$ ls -al /etc/localtime -r--r--r-- 1 root wheel 2819 Sep 27 08:37 /etc/localtime
OSX does macik:~ furt$ ls -al /etc/localtime lrwxr-xr-x 1 root wheel 39 Nov 27 06:30 /etc/localtime -> /usr/share/zoneinfo/America/Los_Angeles
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
we can possibly fall-back to Canonical name for each POSIX zone. Zone mapping does not change much (unlike zone details)
Just listing a temporary workaround for WSL for anybody interested: sudo dpkg-reconfigure tzdata
after that the symlink will be in place.
Repro (windows subsystem for Linux):
Console.WriteLine(DateTime.Now)
Result: The app takes 1.5s to run. 1 second out of that is time spent inside DateTime.Now