我有以下格式的数据集
第一和第二字段表示研究开始和结束的date(M / D / YYYY)。
如何将数据扩展为所需的输出格式,并考虑使用AWK或BASH脚本的闰年?
非常感激你的帮助。
input
7/2/2009 7/7/2009 2/28/1996 3/3/1996 12/30/2001 1/4/2002
期望的输出
7/7/2009 7/6/2009 7/5/2009 7/4/2009 7/3/2009 7/2/2009 3/3/1996 3/2/1996 3/1/1996 2/29/1996 2/28/1996 1/4/2002 1/3/2002 1/2/2002 1/1/2002 12/31/2001 12/30/2001
如果你有gawk
:
#!/usr/bin/gawk -f { split($1,s,"/") split($2,e,"/") st=mktime(s[3] " " s[1] " " s[2] " 0 0 0") et=mktime(e[3] " " e[1] " " e[2] " 0 0 0") for (i=et;i>=st;i-=60*60*24) print strftime("%m/%d/%Y",i) }
示范:
./daterange.awk inputfile
输出:
07/07/2009 07/06/2009 07/05/2009 07/04/2009 07/03/2009 07/02/2009 03/03/1996 03/02/1996 03/01/1996 02/29/1996 02/28/1996 01/04/2002 01/03/2002 01/02/2002 01/01/2002 12/31/2001 12/30/2001
编辑:
上面的脚本对天的长度有一个天真的假设。 这是一个小问题,但在某些情况下可能会产生意想不到的结果。 至少有一个其他答案也有这个问题。 据推测,减去(或加入)天数的date
命令没有这个问题。
有些答案要求你提前知道天数。
这里有另外一个方法可以解决这些问题:
while read -r d1 d2 do t1=$(date -d "$d1 12:00 PM" +%s) t2=$(date -d "$d2 12:00 PM" +%s) if ((t2 > t1)) # swap times/dates if needed then temp_t=$t1; temp_d=$d1 t1=$t2; d1=$d2 t2=$temp_t; d2=$temp_d fi t3=$t1 days=0 while ((t3 > t2)) do read -r -u 3 d3 t3 3<<< "$(date -d "$d1 12:00 PM - $days days" '+%m/%d/%Y %s')" ((++days)) echo "$d3" done done < inputfile
只用bash就可以很好地完成:
for i in `seq 1 5`; do; date -d "2014-02-01 $i days" +%Y-%m-%d; done;
或用管道:
seq 1 5 | xargs -I {} date -d "2014-02-01 {} days" +%Y-%m-%d
你可以在没有awk的情况下在shell中完成这个工作,假设你有GNU日期(这是date -d @nnn
表单所需要的,也可能是在单个数字天和几个月内date -d @nnn
前导零的能力):
while read start end ; do for d in $(seq $(date +%s -d $end) -86400 $(date +%s -d $start)) ; do date +%-m/%-d/%Y -d @$d done done
如果您处于夏令时的地区,那么如果请求日光节约时间,则可能会出现混乱现象。 使用-u强制为UTC,这也严格遵守每天86400秒。 喜欢这个:
while read start end ; do for d in $(seq $(date -u +%s -d $end) -86400 $(date -u +%s -d $start)) ; do date -u +%-m/%-d/%Y -d @$d done done
只要在stdin中输入你的输入即可。
数据的输出是:
7/7/2009 7/6/2009 7/5/2009 7/4/2009 7/3/2009 7/2/2009 3/3/1996 3/2/1996 3/1/1996 2/29/1996 2/28/1996 1/4/2002 1/3/2002 1/2/2002 1/1/2002 12/31/2001 12/30/2001
我更喜欢ISO 8601格式日期 – 这里是使用它们的解决方案。 如果你愿意,你可以很容易地适应美国的格式。
BEGIN { days[ 1] = 31; days[ 2] = 28; days[ 3] = 31; days[ 4] = 30; days[ 5] = 31; days[ 6] = 30; days[ 7] = 31; days[ 8] = 31; days[ 9] = 30; days[10] = 31; days[11] = 30; days[12] = 31; } function leap(y){ return ((y %4) == 0 && (y % 100 != 0 || y % 400 == 0)); } function last(m, l, d){ d = days[m] + (m == 2) * l; return d; } function prev_day(date, y, m, d){ y = substr(date, 1, 4) m = substr(date, 6, 2) d = substr(date, 9, 2) #print d "/" m "/" y if (d+0 == 1 && m+0 == 1){ d = 31; m = 12; y--; } else if (d+0 == 1){ m--; d = last(m, leap(y)); } else d-- return sprintf("%04d-%02d-%02d", y, m, d); } { d1 = $1; d2 = $2; print d2; while (d2 != d1){ d2 = prev_day(d2); print d2; } }
调用这个文件: dates.awk
2009-07-02 2009-07-07 1996-02-28 1996-03-03 2001-12-30 2002-01-04
调用这个文件: dates.txt
执行的命令:
awk -f dates.awk dates.txt
输出:
2009-07-07 2009-07-06 2009-07-05 2009-07-04 2009-07-03 2009-07-02 1996-03-03 1996-03-02 1996-03-01 1996-02-29 1996-02-28 2002-01-04 2002-01-03 2002-01-02 2002-01-01 2001-12-31 2001-12-30
另一种选择是使用dateutils中的dateseq( http://www.fresse.org/dateutils/#dateseq )。 -i
更改输入格式, -f
更改输出格式。 当第一个日期晚于第二个日期时,必须将-1
指定为增量。
$ dateseq -i %m/%d/%Y -f %m/%d/%Y 7/7/2009 -1 7/2/2009 07/07/2009 07/06/2009 07/05/2009 07/04/2009 07/03/2009 07/02/2009 $ dateseq 2017-04-01 2017-04-05 2017-04-01 2017-04-02 2017-04-03 2017-04-04 2017-04-05