我已经在其他编程语言或操作系统中看到了一些与此问题相关的问题/答案,但我没有找到适用于我的特定场景的通用解决方案。我想在Linux shell脚本或纯C中获得给定时区的GMT偏移量(像EDT、CET或欧洲/柏林这样的正常标识符)。
一个简单的解决方案是:
$ TZ=":Europe/Berlin" date +%z
但这将打印当前GMT偏移量(如果当前正在使用,则包括夏令时)。
这里有一个更好的例子来说明我的意思:
$ TZ=":Europe/Berlin" date +%z --date="1 Jan 2014"
+0100
$ TZ=":Europe/Berlin" date +%z --date="1 May 2014"
+0200
我需要一个选项,以始终获得任何国家/城市的非夏令时值(此处:+0100)。
其他可以解决我的问题并与此相关的问题:
如何在事先不知道偏移量的情况下找到给定城市(欧洲/柏林)的正确非夏令时时区(Etc/GMT-1)?
您通常如何知道DST在给定的时间戳处于活动状态?
$ TZ=":Europe/Berlin" date --date="2014-01-01"
Wed Jan 1 00:00:00 CET 2014
$ TZ=":Europe/Berlin" date --date="2014-05-01"
Thu May 1 00:00:00 CEST 2014
标识符CEST(中欧夏令时)和CET(中欧时间)在世界其他地区可能并不常见,因此如果有人需要,可以选择简单地获得DST=1或DST=0之类的信息。通过zdump,您可以为所有更改DST的时间戳获得isdst=0或isdst=1,但它不太容易使用
这将返回给定时区当前年份的非夏令时偏移量(以秒为单位):
zdump -v "Europe/Berlin" |
sed ":a;N;$!ba;s/^.*$(echo -n $(date +%Y)) [^ ]* isdst=0
gmtoff=([^n]*)n.*$/1/"
3600
这里的情况是,sed匹配zdump在该时区返回的当前年份的第一条非dst行,然后用第一条匹配行的末尾替换整个zdump输出,即GMT偏移量(以秒为单位)。
如果您需要将其格式化为+HHMM或-HHMM:
offsetArray=($(zdump -v "America/New_York" |
sed ":a;N;$!ba;s/^.*$(echo -n $(date +%Y)) [^ ]* isdst=0
gmtoff=(-*)([^n]*)n.*$/1+ 2/"))
echo "${offsetArray[0]:0:1}$(date -ud @${offsetArray[1]} +%H%M)"
-0500
这将创建一个包含两个元素的数组,符号和偏移量的绝对值(以秒为单位)。它输出前者,并通过将其作为自epoch(1970年1月1日00:00:00UTC/GMT)以来的时间来传递日期来格式化后者。因为Bash数组赋值忽略了前导空格,所以数组的第一个元素实际上是"+"或"-+",显示的是第一个字符(即+或-)。
顺便说一句,我认为zdump列出的每一年都应该与GMT有相同的非夏令时偏移,我最初的答案只是抓住了它发现的第一年,但我编辑了答案来检查当前年份,以适应自1901年以来时区发生变化的理论边缘情况。
(您可以删除后面的反斜杠,以便在一行上获得全部指令,或者在第二个示例中为两行。)
除非我完全误解了你的问题,否则使用给定日期的1月1日作为参考是很容易的,例如:
TZ=":${city}" date +%z --date="$(date +%Y --date="${date}")-01-01"
演示:
$ cat t.sh
#!/bin/bash
get_current_modifier()
{
echo $(TZ=":$1" date +%z --date="$2" 2>/dev/null)
}
get_non_dst_modifier()
{
echo $(TZ=":$1" date +%z --date="$(date +%Y --date="$2")-01-01" 2>/dev/null)
}
is_dst()
{
local city=$1
local date=$2
local offset1=$(get_non_dst_modifier "${city}" "${date}")
local offset2=$(get_current_modifier "${city}" "${date}")
if [ -z "${offset1}" ] || [ -z "${offset2}" ]; then
return 2
fi
[ "${offset1}" != "${offset2}" ]
}
today=$(date +%Y-%m-%d)
printf "%-15s %-10s %-5s %-8s %-8sn" CITY DATE 'DST?' 'current' 'non-DST'
while read -r city date; do
[ -n "${city}" ] || continue
is_dst "${city}" "${date}"
case $? in
0) is_dst=true ;;
1) is_dst=false ;;
2) is_dst=error ;;
esac
cur_mod=$(get_current_modifier "${city}" "${date}")
non_dst=$(get_non_dst_modifier "${city}" "${date}")
printf "%-15s %-10s %-5s %-8s %-8sn" "${city}" "${date}" "${is_dst}" "${cur_mod}" "${non_dst}"
done <<EOT
Europe/Berlin $today
Europe/Berlin 2014-01-01
US/Alaska $today
US/Alaska 2014-01-01
Europe/Moscow $today
Europe/Moscow 2014-01-01
EOT
$ ./t.sh
CITY DATE DST? current non-DST
Europe/Berlin 2014-05-04 true +0200 +0100
Europe/Berlin 2014-01-01 false +0100 +0100
US/Alaska 2014-05-04 true -0800 -0900
US/Alaska 2014-01-01 false -0900 -0900
Europe/Moscow 2014-05-04 false +0400 +0400
Europe/Moscow 2014-01-01 false +0400 +0400