【问题标题】:GMT/UTC offset in hours/minutes of a given timezone w/o daylight saving time (DST)以小时/分钟为单位的给定时区的 GMT/UTC 偏移量,不包括夏令时 (DST)
【发布时间】:2014-06-06 18:43:47
【问题描述】:

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

一个简单的解决方案是:

$ 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。

【问题讨论】:

    标签: linux bash datetime timezone utc


    【解决方案1】:

    这将返回给定时区当前年份与 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
    

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

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

    (您可以删除尾随反斜杠以将指令全部放在一行中,或者在第二个示例中为两行。)

    【讨论】:

    • 知道这对你有用吗?好奇这些答案中的任何一个是否实现了您想要的。
    • 很抱歉直到现在才回答,但我正忙于处理其他问题。我肯定会回到给定的答案,尝试一切并给出合理的回应。
    • 正如我写的“通过 zdump [...] 不是那么容易使用”,但你让它有点可用,谢谢。关于您检查当前年份而不是第一年的评论,我认为如果您考虑世界上每个国家/地区 DST 历史上的政治变化,这非常重要,而不是一个边缘案例。一些国家随着时间的推移引入或结束了夏令时,甚至多次观察并在之后定期使用或不使用,例如DST in Australia。我认为这在未来不会改变。
    【解决方案2】:

    除非我完全误解了您的问题,否则使用给定日期的第一年 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
    

    【讨论】:

    • 一个问题:我也想过使用固定日期,也许我弄错了,但是南半球的国家呢?难道他们不可能在 1 月 1 日(1 月是夏季的一部分)有“可能的”夏令时。
    猜你喜欢
    • 2016-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    • 2012-04-15
    • 2015-02-14
    • 2022-01-23
    • 2015-05-11
    相关资源
    最近更新 更多