循环遍历 Unix 日期

循环遍历 Unix 日期

我有相当基本的任务,但无法找到适当的解决方案。我想迭代自 2008 年至今的日期间隔,并且需要循环每次迭代的纪元值。我也对迭代年份、半年和月份感兴趣。
我写了这样的脚本

#!/bin/bash
initial_date=`date -d "2008-02-04 00:00:00 UTC" +%s`
end_date=`date +%s`
n=0
until [ $initial_date -gt $end_date ]; do
 echo $initial_date
 let n+=1
 readable_date=`date +"%Y-%m-%d %T" -d "1970-01-01 $initial_date sec"`
 echo $readable_date
 readable_date=`date -d "$readable_date + $n year"`
 initial_date=`date -d "$readable_date" +%s`
done

但它的输出对我来说相当奇怪:

1202083200
2008-02-0400:00:00
1233702000
2009-02-0323:00:00
1265230800
2010-02-0321:00:00
1296756000
2011-02-0318:00:00
1328277600
2012-02-0315:00:00
1359885600
2013-02-0311:00:00
1391403600
2014-02-0306:00:00
1422918000
2015-02-0223:00:00
1454425200
2016-02-0215:00:00

为什么年份没有正确增加?在我看来,时间转移似乎是 unix>>UTC>>unix 转换的副作用。有没有直接(无需重新转换)的方法来执行此操作?

聚苯乙烯
是的,我检查过,问题并没有找到明确的方法来做到这一点。所有它们都是基于增加数字并在其帮助下转换日期,这对我来说似乎并不精确。

是的,我考虑过添加60*60*24*30*365初始日期,但这是否正确?此方法不考虑闰年、由 31 天组成的月份等。

答案1

使用日期:

$ dateA="2008-02-04 00:00:00 UTC"

获取时间变化的日期的第一个原因是请求“本地”日期:

$ date -d "$dateA"
Sun Feb  3 19:00:00 EST 2008

如果您指定 UTC 时间,则会更正该错误:

$ TZ=UTC0 date -d "$dateA"
Mon Feb  4 00:00:00 UTC 2008

或者更好(养成使用它的习惯)使用-u日期选项:

$ date -ud "$dateA"
Mon Feb  4 00:00:00 UTC 2008

第二个原因是要求带有标志的“相关物品”:

$ date -ud "$(date +"%Y-%m-%d %T" -ud "$dateA") +1 year"  ### wrong
Tue Feb  3 23:00:00 UTC 2009

$ date -ud "$(date +"%Y-%m-%d %T" -ud "$dateA") 1 year"  ### better
Mon Feb  4 00:00:00 UTC 2008

但是当使用“时区”(-z)时,所有时间问题(通常)都会消失:

$ date -ud "$(date +"%Y-%m-%d %T %z" -ud "$dateA") +1 year"  ### Best
Wed Feb  4 00:00:00 UTC 2009

$ date -ud "$(date +"%Y-%m-%d %T %z" -ud "$dateA") 1 year"  ### Preferred
Wed Feb  4 00:00:00 UTC 2009

该问题与解析日期字符串的方式有关,如果缺少 TZ 值,+1 可能会被解释为时区值:

$ date -ud "2008-02-04 00:00:00 +3 year"
Tue Feb  3 21:00:00 UTC 2009

即使已设置 TZ 的环境值:

$ TZ=UTC0 date -ud "2008-02-04 00:00:00 +3 year"
Tue Feb  3 21:00:00 UTC 2009

是的,年份增加了一次(2009 年而不是 2008 年),但+3用于更改所呈现的时间(不是预期的)。

我重复一遍:问题是+3可能会被解析为时区值。


另外,要将(纪元)秒转换为日期,GNU 日期可以使用“@”

$ dateAsec="$( date -ud "$dateA" +"%s" )

$ date -ud @"$dateAsec"
Mon Feb  4 00:00:00 UTC 2008

该脚本(使用 (newdate) 函数)可以这样编写:

#!/bin/bash
dateI="$(date -ud "2008-02-04 00:00:00 UTC" +%s)"
dateF="$(date -u +%s)"

newdate(){ date -ud "$(date +"%Y-%m-%d %T %z" -ud @"$dateI") $1 year" +"%s"; }

n=0
while 
    dateN="$( newdate "$n" )"
    (( dateN < dateF ));
do
    dateNhuman="$(date +"%Y-%m-%d %T %z" -ud @"$dateN")"
    echo "dateN=$dateN --- $dateNhuman ---> $dateF $(( $dateF - $dateN )) $n"
    (( n++ ))
done

结果:

dateN=1202083200 --- 2008-02-04 00:00:00 +0000 ---> 1470543626 268460426 0
dateN=1233705600 --- 2009-02-04 00:00:00 +0000 ---> 1470543626 236838026 1
dateN=1265241600 --- 2010-02-04 00:00:00 +0000 ---> 1470543626 205302026 2
dateN=1296777600 --- 2011-02-04 00:00:00 +0000 ---> 1470543626 173766026 3
dateN=1328313600 --- 2012-02-04 00:00:00 +0000 ---> 1470543626 142230026 4
dateN=1359936000 --- 2013-02-04 00:00:00 +0000 ---> 1470543626 110607626 5
dateN=1391472000 --- 2014-02-04 00:00:00 +0000 ---> 1470543626 79071626 6
dateN=1423008000 --- 2015-02-04 00:00:00 +0000 ---> 1470543626 47535626 7
dateN=1454544000 --- 2016-02-04 00:00:00 +0000 ---> 1470543626 15999626 8

所有的岁月都出现了,一切似乎都是正确的。

答案2

上的迭代器相对日期从起点来看可能更有意义:

#!/usr/bin/env bash

START='2008-02-04 00:00:00 UTC'
END=$( date +%s )

for yearnum in $( seq 1 999 ); do
  NUDATE=$( date -d "$START + $yearnum year" +%s )
  PUNY_HUMAN_DATE=$( date -d "@$NUDATE" "+%Y-%m-%d %T %Z" )
  if [[ $NUDATE -gt $END ]]; then
    break
  fi
  echo $NUDATE $PUNY_HUMAN_DATE
done

结果如下:

-bash-4.1$ bash iter
1233705600 2009-02-04 00:00:00 UTC
1265241600 2010-02-04 00:00:00 UTC
1296777600 2011-02-04 00:00:00 UTC
1328313600 2012-02-04 00:00:00 UTC
1359936000 2013-02-04 00:00:00 UTC
1391472000 2014-02-04 00:00:00 UTC
1423008000 2015-02-04 00:00:00 UTC
1454544000 2016-02-04 00:00:00 UTC
-bash-4.1$ 

相关内容