我需要仅输出指定的小时数(例如 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
。