每月正确减少日期

时间:2017-11-28 18:09:27

标签: linux bash shell date datetime

我有一个简单的代码,它工作但不是按预期的。我的代码的主要建议是将给定日期减少到目标日期2012-01-31,

但结果是:

2017-10-28
2017-09-28
2017-08-28
2017-07-28
2017-06-28
2017-05-28
2017-04-28
2017-03-28
2017-02-28
2017-01-28
2016-12-28
2016-11-28
2016-10-28
2016-09-28
2016-08-28
2016-07-28
2016-06-28
2016-05-28
2016-04-28
2016-03-28
2016-02-28
2016-01-28
...

代码:

#!/bin/bash

d=2017-10-31
while [ "$d" != 2012-01-31 ]; do 
  echo $d
  d=$(date -I -d "$d -1 month")
done

我真的希望正确的月份如下:

2017-10-31
2017-09-30
2017-08-31
2017-07-31
2017-06-30
2017-05-31
2017-04-30
2017-03-31
2017-02-28
2017-01-31
2016-12-31
2016-11-30
2016-10-31
2016-09-30
2016-08-31
2016-07-31
2016-06-30
2016-05-31
2016-04-30
2016-03-31
2016-02-29
2016-01-31
...

我该如何设法做到这一点?

3 个答案:

答案 0 :(得分:1)

也许不是减去一个月(这种行为而臭名昭着),而是减去当月的天数

!/bin/bash

d=2017-10-31
while [ "$d" != 2012-01-31 ]; do
  echo $d
  days_in_month==$(cal $(date +"%m %Y" -d $d) | awk 'NF {DAYS = $NF}; END {print DAYS}')
  d=$(date -I -d "$d $days_in_month day ago")
done

答案 1 :(得分:1)

假设您的开始日期始终是月中的最后一天,如果您使用此算法,则可以让date负责每月的计算天数:

date = date +1 day -1 month -1 day

即,首先将日期翻到下个月的第一天,然后简单地减去一个月以获得当月的第一天,最后只需减去1天即可获得上个月的最后一天。

例如:

#!/bin/bash
d=2017-10-31
while [ "$d" != 2012-01-31 ]; do 
    echo $d
    d=$(date -I -d "$(date -I -d "$(date -I -d "$d +1 day") 1 month ago") 1 day ago")
done

注意1 day ago代替-1 day,因为后者可以被date(已知问题)解释为时区。

或者,您可以手动将日期设置为01(使用suffix removal ${d%-*}的shell参数展开),然后使用1 day简单地减去date

d=2017-10-31
while [ "$d" != 2012-01-31 ]; do 
    echo $d
    d=$(date -I -d "${d%-*}-01 1 day ago")
done

输出(在两种情况下):

2017-10-31
2017-09-30
2017-08-31
2017-07-31
2017-06-30
2017-05-31
2017-04-30
...

答案 2 :(得分:1)

更容易生成月初,并在没有查看日期的情况下完成月末的小技巧

$ d=2017-10-31; bd=$(date -I -d "$d +1 month");   
  while [ $d != 2012-01-31 ];        
  do 
      bd=$(date -I -d "$bd - 1 month");           
      d=$(date -I -d "$bd - 1 day");  
      echo $d;
  done

2017-10-31
2017-09-30
2017-08-31
2017-07-31
...  
2016-07-31
2016-06-30
2016-05-31
2016-04-30
2012-03-31
2012-02-29
2012-01-31