The current time in Los Angeles is 18:05. But when I run
TZ=UTC-8 date --iso=ns, I get:
The date utility tells me that the time is 10:05, and even says that it's reporting it as UTC+8. Why?
The reason is that
TZ=UTC-8 is interpreted as a POSIX time zone. In the POSIX timezone format, the 3 letters are the timezone abbreviation (which is arbitrary) and the number is the number of hours the timezone is behind UTC. So
UTC-8 means a timezone abbreviated "UTC" that is −8 hours behind the real UTC, or UTC + 8 hours.
(It works that way because Unix was developed in the US, which is behind UTC. This format allows the US timezones to be represented as EST5, CST6, etc.)
You can see that's what's happening by these examples:
$ TZ=UTC-8 date +'%Z %z' UTC +0800 $ TZ=UTC8 date +'%Z %z' UTC -0800 $ TZ=FOO-8 date +'%Z %z' FOO +0800
-0800 timezone format takes the opposite approach, with
- indicating the zone is behind UTC, and
+ indicating the zone is ahead of UTC.
Whenever you specify a timezone in the format of +/-00:00, you are specifying an offset, not the actual timezone. From the
GNU libc documentation (which follows the POSIX standard):
The offset specifies the time value you must add to the local time to get a Coordinated Universal Time value. It has syntax like [+|-]hh[:mm[:ss]]. This is positive if the local time zone is west of the Prime Meridian and negative if it is east. The hour must be between 0 and 23, and the minute and seconds between 0 and 59.
This is why it appears to be the reverse of what you expect.
Because POSIX requires it.
If preceded by a '-', the timezone shall be east of the Prime Meridian; otherwise, it shall be west (which may be indicated by an optional preceding '+' ).
So, this will give time near
Los Angeles (with any 3 letter label for time zone text):
$ TZ=ANY8 date "+%Y-%m-%d %H:%M:%S %Z%z" 2016-04-23 10:47:12 ANY-0800 $ TZ=GMT+8 date "+%Y-%m-%d %H:%M:%S %Z%z" 2016-04-23 10:47:12 GMT-0800
And this should give the time near
Shanghai, China or
$ TZ=ANY-8 date "+%Y-%m-%d %H:%M:%S %Z%z" 2016-04-24 02:47:12 ANY+0800 $ TZ=CST-8 date "+%Y-%m-%d %H:%M:%S %Z%z" 2016-04-23 02:47:12 CST+0800
 Near because there may be some DST (Daylight Saving Time) in effect that shift the actual "local time".
As an alternative method you can use the command
zdump to show the current time in other timezones + offsets.
Zdump prints the current time in each zonename named on the command line.
The same rules apply with the timezones; west of the prime meridian being "behind" while to the east being "ahead".
$ zdump PST PST Sat Dec 7 03:25:27 2013 PST
I made this script to show several of the timezones + offsets that we're interested in using
date so we could compare them.
$ cat cmd.bash #!/bin/bash printf "\ndate: %s\n\n" "$(date)" for tz in EST PST PST+8 PST-8 UTC UTC+8 UTC-8; do echo "-- timezone $tz" printf "zdump: %s\n" "$(zdump $tz)" printf "date: %s\n" "$(TZ=$tz date +'%a %b %d %T %Y - (%Z %z)')" echo "" done
Then when you run it you can see the comparison of
$ ./cmd.bash date: Sat Dec 7 02:59:05 EST 2013 -- timezone EST zdump: EST Sat Dec 7 02:59:05 2013 EST date: Sat Dec 07 02:59:05 2013 - (EST -0500) -- timezone PST zdump: PST Sat Dec 7 07:59:05 2013 PST date: Sat Dec 07 07:59:05 2013 - (PST +0000) -- timezone PST+8 zdump: PST+8 Fri Dec 6 23:59:05 2013 PST date: Fri Dec 06 23:59:05 2013 - (PST -0800) -- timezone PST-8 zdump: PST-8 Sat Dec 7 15:59:05 2013 PST date: Sat Dec 07 15:59:05 2013 - (PST +0800) -- timezone UTC zdump: UTC Sat Dec 7 07:59:05 2013 UTC date: Sat Dec 07 07:59:05 2013 - (UTC +0000) -- timezone UTC+8 zdump: UTC+8 Fri Dec 6 23:59:05 2013 UTC date: Fri Dec 06 23:59:05 2013 - (UTC -0800) -- timezone UTC-8 zdump: UTC-8 Sat Dec 7 15:59:05 2013 UTC date: Sat Dec 07 15:59:05 2013 - (UTC +0800)