The TLDR answer is:
$ POSIXTZ=$(tail -n1 /etc/localtime)
$ echo $POSIXTZ
$ TZNAME=$(find /usr/share/zoneinfo | while read fname; do cmp -s /etc/localtime "$fname" && echo "$fname" | cut -c 21- ; done)
$ echo $TZNAME
The current timezone is stored in the file
/etc/localtime. As @Kusalananda remarks this can be a symbolic link. But as @JdeBP hints that on FreeBSD this file is normally copied from
/usr/share/zoneinfo during setup.
These files originates from textual descriptions in contrib/tzdata
This information is then compiled into a binary format using zic and the format is specified in tzfile
I do not know of a built in utility which directly parses this file. But it should be easy to write in C with the documentation at hand. If we want to stick with what come out of the box we can look at it using
hexdump -v -C /etc/localtime
Or if we just want to look at the magic marker:
$ hexdump -v -s 0 -n 5 -e '1/5 "%s\n"' /etc/localtime
Or the fields:
tzh_ttisgmtcnt The number of UTC/local indicators stored in the file.
tzh_ttisstdcnt The number of standard/wall indicators stored in the file.
tzh_leapcnt The number of leap seconds for which data is stored in the file.
tzh_timecnt The number of ``transition times'' for which data is stored in the file.
tzh_typecnt The number of ``local time types'' for which data is stored in the file (must not be zero).
tzh_charcnt The number of characters of ``time zone abbreviation strings'' stored in the file.
hexdump -v -s 19 -n 4 -e '"tzh_ttisgmtcnt: " 1/4 "%9u\n"' /etc/localtime
hexdump -v -s 23 -n 4 -e '"tzh_ttisstdcnt: " 1/4 "%9u\n"' /etc/localtime
hexdump -v -s 27 -n 4 -e '"tzh_leapcnt: " 1/4 "%9u\n"' /etc/localtime
hexdump -v -s 31 -n 4 -e '"tzh_timecnt: " 1/4 "%9u\n"' /etc/localtime
hexdump -v -s 35 -n 4 -e '"tzh_typecnt: " 1/4 "%9u\n"' /etc/localtime
hexdump -v -s 39 -n 4 -e '"tzh_charcnt: " 1/4 "%9u\n"' /etc/localtime
Results in my case:
Then we do the following math to figure out where the first
43 + (tzh_timecnt * 4) + (tzh_timecnt * 1)
43 + (0 * 4) + (0 * 1) = 43
The wheels slowly falls of:
$ hexdump -v -s 43 -n 6 -e '"ttinfo:\n tt_gmtoff: " 1/4 "%9u\n tt_isdst: " 1/1 "%1d\n tt_abbrind: " 1/1 "%1u\n"' /etc/localtime
With these numbers I am probably a little off. And this is truly a masochistic way of dealing with it. So I am stopped just short of finding the gold using
But if we look at the bottom of the tzfile specification we find this little nugget:
second header and data comes a newline-enclosed, POSIX-TZ-environment-
variable-style string for use in handling instants after the last transi-
tion time stored in the file (with nothing between the newlines if there
is no POSIX representation for such instants).
So it is as easy as:
$ tail -n1 /etc/localtime
When you look closer you will notice that
/etc/localtime does not contain any
Continent/Region setting! But as the file is copied from
/usr/share/zoneinfo you can compare them and find the probable file. I have not dived deep enough to confirm if
/usr/share/zoneinfo might contain duplicates.
But for me - this works nicely:
$ find /usr/share/zoneinfo | while read fname; do cmp -s /etc/localtime "$fname" && echo "$fname" | cut -c 21- ; done
We iterate through all files in
/usr/share/zoneinfo and compare each of them with
cmp using the
-s parameter will not display anything and only exit using a value. If the value is zero we will print the name. When printing the name we use
cut to remove the first 21 characters to get