Linux 给定时区的GMT/UTC偏移量(小时/分钟),不包括夏令时(DST)

Linux 给定时区的GMT/UTC偏移量(小时/分钟),不包括夏令时(DST),linux,bash,datetime,timezone,utc,Linux,Bash,Datetime,Timezone,Utc,我已经在其他编程语言或操作系统中看到了一些与此问题相关的问题/答案,但我没有找到适合我的特定场景的通用解决方案。我想在Linux shell脚本或纯C中获取给定时区(EDT、CET或Europe/Berlin等普通标识符)的GMT偏移量 一个简单的解决办法是: $ TZ=":Europe/Berlin" date +%z 但这将打印当前GMT偏移量(包括DST,如果它当前正在使用) 这里有一个更好的例子来说明我的意思: $ TZ=":Europe/Berlin" date +%z --date

我已经在其他编程语言或操作系统中看到了一些与此问题相关的问题/答案,但我没有找到适合我的特定场景的通用解决方案。我想在Linux shell脚本或纯C中获取给定时区(EDT、CET或Europe/Berlin等普通标识符)的GMT偏移量

一个简单的解决办法是:

$ TZ=":Europe/Berlin" date +%z
但这将打印当前GMT偏移量(包括DST,如果它当前正在使用)

这里有一个更好的例子来说明我的意思:

$ TZ=":Europe/Berlin" date +%z --date="1 Jan 2014"
+0100

$ TZ=":Europe/Berlin" date +%z --date="1 May 2014"
+0200
我需要一个选项来始终获取任何国家/城市的非DST值(此处:+0100)

其他可以解决我的问题并与此相关的问题:

如何在事先不知道偏移量的情况下找到给定城市(欧洲/柏林)的正确非DST时区(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,但在IMO中使用起来并不容易。

这将以秒为单位返回给定时区当前年份相对于GMT的非DST偏移:

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
这将创建一个包含两个元素的数组,即符号和偏移量的绝对值(以秒为单位)。它输出前者,并通过将日期传递为自历元(1970年1月1日00:00:00 UTC/GMT)以来的时间来格式化后者。因为Bash数组赋值忽略前导空格,所以数组的第一个元素实际上是“+”或“-+”,显示的是第一个字符(即+或-)

顺便说一句,我认为zdump列出的每一年都应该有与GMT相同的非DST偏移量,而我的原始答案刚好是它找到的第一年,但我编辑了答案以检查当前年份,以适应理论上的边缘情况,即自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  %-8s\n" 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  %-8s\n" "${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

你知道这对你有用吗?我很好奇这两个答案是否都达到了你的要求。很抱歉我直到现在才回答,但我正忙于其他问题。我肯定会回到给出的答案,尝试一切并给出合理的回答。正如我写的“Via zdump[…]不太容易使用”,但你让它有点可用,谢谢。关于你关于检查今年而不是第一年的评论,我认为这是非常重要的,如果你考虑到世界上每个国家DST历史上的政治变化,这不是一个边缘案例。一些国家随着时间的推移引入或缔结了DST,甚至多次观察并在事后定期使用,例如:。我认为这在未来不会改变。一个问题:我也想过使用固定的日期,也许我错了,但是南半球的国家呢?难道他们不可能在1月1日(1月是夏季的一部分)进行“可能”的DST吗。
$ ./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