众所周知,每年每个月都有以下最大天数,如下:
Jan - 31 days
Feb - 28 days / 29 days (leap year)
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
如何让 bash 在不使用
if else
或 switch
或 while
循环的情况下返回当年的值(每月的最后一天)?
我的看法:
for m in {1..12}; do
date -d "$m/1 + 1 month - 1 day" "+%b - %d days";
done
解释一下:对于第一次迭代,当 m=1 时,
-d
参数为“1/1 + 1 个月 - 1 天”,“1/1”被解释为 1 月 1 日。因此,1 月 1 + 1 个月 - 1 天是 1 月 31 日。下一次迭代“2/1”是 2 月 1 日,加上一个月减去一天即可得到 2 月 28 日或 29 日。依此类推。
cat <<EOF
Jan - 31 days
Feb - `date -d "yesterday 3/1" +"%d"` days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
EOF
cal $(date +"%m %Y") |
awk 'NF {DAYS = $NF}; END {print DAYS}'
cal
实用程序来显示指定的月份,然后运行一个简单的 Awk 脚本来提取最后一天的数字。
假设您允许“for”,那么 bash 中将出现以下内容
for m in {1..12}; do
echo $(date -d $m/1/1 +%b) - $(date -d "$(($m%12+1))/1 - 1 days" +%d) days
done
产生这个
Jan - 31 days
Feb - 29 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
注意:我删除了
cal
的需要
对于那些喜欢琐事的人:
Number months from 1 to 12 and look at the binary representation in four
bits {b3,b2,b1,b0}. A month has 31 days if and only if b3 differs from b0.
All other months have 30 days except for February.
因此,除了二月以外,这都有效:
for m in {1..12}; do
echo $(date -d $m/1/1 +%b) - $((30+($m>>3^$m&1))) days
done
结果:
Jan - 31 days
Feb - 30 days (wrong)
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
尝试使用此代码
date -d "-$(date +%d) days month" +%Y-%m-%d
此代码测试日期以查看所请求年份的 2 月 29 日是否有效,如果有效,则更新日期偏移字符串中的第二个字符。月份参数选择相应的子字符串并将月份差异添加到 28。
function daysin()
{
s=303232332323 # normal year
((!($2%4)&&($2%100||!($2%400)))) && s=313232332323 # leap year
echo $[ ${s:$[$1-1]:1} + 28 ]
}
daysin $1 $2 #daysin [1-12] [YYYY]
在具有 BSD date 功能的 Mac 上,您可以执行以下操作:
for i in {2..12}; do date -v1d -v"$i"m -v-1d "+%d"; done
快速解释
-v
代表调整。我们正在将日期调整为:
-v1d
代表该月的第一天
-v"$i"m
定义月份,例如(2 月-v2m
)
-v-1d
减去一天(所以我们得到上个月的最后一天)
"+%d"
打印该月的日期
for i in {2..12}; do date -v1d -v"$i"m -v-1d "+%d"; done
31
28
31
30
31
30
31
31
30
31
30
您当然可以添加年份。请参阅联机帮助页中的示例(上面的链接)。
script.sh
的内容:
#!/bin/bash
begin="-$(date +'%-m') + 2"
end="10+$begin"
for ((i=$begin; i<=$end; i++)); do
echo $(date -d "$i month -$(date +%d) days" | awk '{ printf "%s - %s days", $2, $3 }')
done
结果:
Jan - 31 days
Feb - 29 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
基于 patm 的答案,使用 BSD
date
for macOS(patm 的答案在 12 月被遗漏):
for i in {1..12}; do date -v1m -v1d -v+"$i"m -v-1d "+%b - %d days"; done
-v
,当使用BSD date
时,表示将日期调整为:
-v1m
表示转到第一个月(当年的一月)。
-v1d
表示转到第一天(所以现在是 1 月 1 日)。
-v+"$i"m
表示转到下个月。
-v-1d
表示减去一天。这获取上个月的最后一天。
"+%b - %d days"
是您想要输出的任何格式。
这将输出当年的所有月份以及每个月的天数。以下输出适用于 2022 年截至目前的情况:
Jan - 31 days
Feb - 28 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
for m in $(seq 1 12); do cal $(date +"$m %Y") | grep -v "^$" |tail -1|grep -o "..$"; done
避免使用 cal 是没有意义的,因为 POSIX 需要它,所以应该在那里
已接受答案的变体,以显示“昨天”的使用
$ for m in {1..12}; do date -d "yesterday $m/1 + 1 month" "+%b - %d days"; done
Jan - 31 days
Feb - 28 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
如何运作?
日期“month/1”添加 1 个月后显示昨天的日期
我需要这几次,所以当 PHP 中带有 easy 时,bash 中则不是, 所以我用这个直到抛出错误“无效算术运算符” 即使在拼写检查中存在冲突(“mt”代表月份,“yr”代表年份)
last=$(echo $(cal ${mt} ${yr}) | awk '{print $NF}')
所以这很好用...
### get last day of month
#
# implement from PHP
# src: https://www.php.net/manual/en/function.cal-days-in-month.php
#
if [ $mt -eq 2 ];then
if [[ $(bc <<< "${yr} % 4") -gt 0 ]];then
last=28
else
if [[ $(bc <<< "${yr} % 100") -gt 0 ]];then
last=29
else
[[ $(bc <<< "${yr} % 400") -gt 0 ]] && last=28 || last=29
fi
fi
else
[[ $(bc <<< "(${mt}-1) % 7 % 2") -gt 0 ]] && last=30 || last=31
fi
遵循格伦·杰克曼的相同答案,但有更新;他的答案最初使用“+ 1个月 - 1天”配置,但是我尝试了他的代码,但它无法正常工作 - 由于某种未知的原因,我的日期命令返回了日期+ 29天(在我的测试事件中,这是 12 月的日期,但返回的是 12 月 30 日,而不是 12 月 31 日)。
我的建议是您将“1个月”切换为“下个月”,这样会正确返回正确的结果。
就我而言,我必须从另一个变量收集年份和月份,所以结果是这样的:
date -d "$AnalysisYear-$AnalysisMonth-01 00:00:00 + next month - 1 second" "+%d"