给定时区的GMT / UTC偏移量,以小时/分钟为单位,不带夏令时(DST)

时间:2014-04-22 11:56:03

标签: 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="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。

2 个答案:

答案 0 :(得分:1)

这将在几秒钟内返回给定时区的当前年份与格林尼治标准时间的非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日 - 1970年00:00:00 UTC / GMT)传递给日期来格式化后者。因为Bash数组赋值忽略了前导空格,所以数组的第一个元素实际上是" +"或" - +",显示的是第一个字符(即+或 - )。

顺便说一下,我认为zdump列出的每年都应该与GMT有相同的非DST偏移,而我原来的答案只是抓住它发现的第一年,但我编辑了答案以检查当前年,以适应自1901年以来时区发生变化的理论边缘情况。

(您可以删除尾部反斜杠以在一行中获取所有指令,如果是第二个示例,则可以删除两行。)

答案 1 :(得分:1)

除非我完全曲解你的问题,否则通过使用给定日期的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

$ ./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