如何对 24 小时循环进行排序,例如 .. 22 23 00 01

如何对 24 小时循环进行排序,例如 .. 22 23 00 01

我需要仅输出指定的小时数(例如 8 小时)并根据 UTC 时区更改转移时间点。

我想看到的输出是:

Athens     15 16 17 18 19 20 21 22 (+2)
Hong_Kong  21 22 23 00 01 02 03 04 (+8)

我正在玩弄date命令并发现SuperUser 上这段有趣的代码。对我来说有很多东西要学。我根据自己的需要对其进行了调整。但是,我发现时区更改存在逻辑问题:循环不断增加超过 24 小时范围的小时数。因此,它打印的内容不是上述示例,而是:

Athens     15 16 17 18 19 20 21 22 (+2)
Hong_Kong  21 22 23 24 25 26 27 28 (+8)

这显然是错误的。

该代码使用带有排序的嵌套循环。对于我的一生,我无法弄清楚如何将小时的范围限制为 24 位数字。

这是作者代码中发生的事情的粗略想法。如何防止循环超过24小时并从23点循环到00点?

#!/bin/bash
timezones="2" #In the actual script this is a variable generated by a provided array of cities.
workday="08 20" #This specifies the displayed range of hours e.g working hours

for tz in `seq $timezones`
do
    printf "City-$tz  " # Just a placeholder, instead of an actual city from timezone file
    timediff[$tz]=6.    # This is a simplified command. IN the actual script time difference calculated from deducting my local time from enquiring city time zone

    for h in `seq $workday` #loop hours
        do
            printf "%02d " $(($h++${timediff[$tz]}))
        done
        printf " \n" 
done

输出

%
City-1  14 15 16 17 18 19 20 21 22 23 24 25 26  
City-2  14 15 16 17 18 19 20 21 22 23 24 25 26 

PS 上面的示例没有显示时间线中的时区变化。我只是不知道如何执行它。

答案1

您的脚本无法运行,因为点 in 在timediff[$tz]=6.仅支持整数运算的 Bash 中不起作用。

不管怎样,如果你想让序列变成 22, 23, 0, 1, ...,这个概念就是模算术,类 C 语言中的运算符是%。例如

for i in 1 2 3 4 5; do
    echo $(( i % 3 ))
done

印刷1 2 0 1 2% 24当然,对于小时数,您会使用。当然,用户需要知道显示的时间是哪一天。例如,这将从 UTC+0200 转换为 UTC+1200

mytz=2 othertz=12
for h in {8..16}; do
    printf "%02d ($mytz) == %02d ($othertz)\n" "$h" "$(( (h - mytz + othertz) % 24))";
done

给予08 (+2) == 18 (+12)

如果您知道时区的正确文件名,您还可以使用date环境变量进行翻译。TZ此处使用 GNU 日期:

hour=08
tz=Asia/Hong_Kong
printf "local: %s\nother: %s\n" "$(date +"%F %T %Z (%z)" -d "@$ts")" "$(TZ=$tz date +"%F %T %Z (%z)" -d "@$ts")"

输出:

local: 2021-12-08 08:00:00 EET (+0200)
other: 2021-12-08 14:00:00 HKT (+0800)

答案2

如果您可以获得实际时区列表,您可以执行以下操作(假设 GNU date):

#!/bin/bash

timezones=("Europe/Athens" "Asia/Hong_Kong");

workday="08 20" #This specifies the displayed range of hours e.g working hours

printf '%-30s' "Here"
for myHour in $(seq $workday); do
  printf '%02d ' "$myHour"
done
printf '\n'

for tz in "${timezones[@]}"; do
  printf '%-30s' "$tz"
  for myHour in $(seq $workday); do
    theirHour=$(TZ="$tz" date -d "@$(date -d "$myHour" +%s)" +%H);
    printf '%02d ' "$theirHour"
  done
  printf '\n'  
done

其产生:

$ foo.sh
Here                          08 09 10 11 12 13 14 15 16 17 18 19 20 
Europe/Athens                 10 11 12 13 14 15 16 17 18 19 20 21 22 
Asia/Hong_Kong                16 17 18 19 20 21 22 23 00 01 02 03 04 

如果您无法获取时区并且需要使用相对于您自己的时区的偏移量,并且想要在问题中显示的确切输出,您可以尝试:

#!/bin/bash

offsets=("2" "8");
workday="08 20" #This specifies the displayed range of hours e.g working hours

for offset in "${offsets[@]}"; do
  for myHour in $(seq $workday); do
    theirHour=$(date -d "@$(date -d "$myHour + $offset hours" +%s)" +%H)
    printf '%s ' "$theirHour"  
  done
  printf '(+%s)\n' "$offset"
done

其产生:

$ foo.sh
10 11 12 13 14 15 16 17 18 19 20 21 22 (+2)
16 17 18 19 20 21 22 23 00 01 02 03 04 (+8)

答案3

虽然已经有一些不错的答案,但我想我还是会对您的代码进行一些小而微小的更改,以便在第 24 小时将时间重置回 00。只需对结果取 24 的模即可。

printf "%02d " $(($h++${timediff[$tz]})%24))

另请确保修复您的语法错误并进行更改

timediff[$tz]=6.

timediff[$tz]=6

Bash 不理解浮点运算,因此它6.不是一个有效的数字。

答案4

以下bash脚本在命令行上获取多个时区,并打印这些时区中从当前时间到八小时后的时间。

如果脚本被赋予一个 Unix 时间戳作为其-t选项的选项参数,它将使用该时间而不是当前时间进行偏移。

该脚本尝试找出本地时区并在第一行输出。

运行脚本:

$ ./script UTC Europe/Zurich Asia/Kabul Africa/Harare
CET            20:39  21:39  22:39  23:39  00:39  01:39  02:39  03:39  (+0100)
UTC            19:39  20:39  21:39  22:39  23:39  00:39  01:39  02:39  (+0000)
Europe/Zurich  20:39  21:39  22:39  23:39  00:39  01:39  02:39  03:39  (+0100)
Asia/Kabul     00:09  01:09  02:09  03:09  04:09  05:09  06:09  07:09  (+0430)
Africa/Harare  21:39  22:39  23:39  00:39  01:39  02:39  03:39  04:39  (+0200)

使用 GNUdate获取当地时间 08:00(今天)的时间戳:

$ ./script -t "$(date -d '08:00' +%s)"  UTC Europe/Zurich Asia/Kabul Africa/Harare
CET            08:00  09:00  10:00  11:00  12:00  13:00  14:00  15:00  (+0100)
UTC            07:00  08:00  09:00  10:00  11:00  12:00  13:00  14:00  (+0000)
Europe/Zurich  08:00  09:00  10:00  11:00  12:00  13:00  14:00  15:00  (+0100)
Asia/Kabul     11:30  12:30  13:30  14:30  15:30  16:30  17:30  18:30  (+0430)
Africa/Harare  09:00  10:00  11:00  12:00  13:00  14:00  15:00  16:00  (+0200)

剧本:

#!/bin/bash

unset -v t0
while getopts t: opt; do
        case $opt in
                t)      t0=$OPTARG ;;
                *)      echo 'Error' >&2; exit 1
        esac
done
shift "$(( OPTIND - 1 ))"

# Use current time if t0 is still unset.
if [ "${t0:+set}" != set ]; then
    printf -v t0 '%(%s)T' -1
fi

# Try to figure out local time zone.
printf -v local_zone '%(%Z)T' "$t0"

# Prepend local time zone to list if set.
set -- ${local_zone:+"$local_zone"} "$@"

export TZ

for TZ do
        {
                printf '%s\n' "$TZ"
                for (( offset = 0; offset < 8; ++offset )); do
                        printf '%(%H:%M)T\n' "$(( t0 + 3600*offset ))"
                done
                printf '(%(%z)T)\n' "$t0"
        } | paste -s -
done | column -t

该脚本使用printf格式%(...)T字符串来格式化时间戳。此格式字符串是在bash版本 4.2 中引入的。这使得我们甚至可以在没有 GNU 的系统上使用该脚本date

通过依次为每个时区设置TZ环境变量,我们就可以输出所需时区的时间戳。内部循环在当前时间(或给定时间)和未来八小时内循环,在单独的行上输出格式化的时间。然后使用 将它们整理为整行paste,然后使用 格式化为表格column

相关内容