この Bash 関数は、GNU ベースのシステムで動作します:
jul () { date -d "$1-01-01 +$2 days -1 day" "+%Y%m%d"; }
いくつかの例:
$ y=2011; od=0; for d in {-4..4} 59 60 {364..366} 425 426; do (( d > od + 1)) && echo; printf "%3s " $d; jul $y $d; od=$d; done
-4 20101227
-3 20101228
-2 20101229
-1 20101230
0 20101231
1 20110101
2 20110102
3 20110103
4 20110104
59 20110228
60 20110301
364 20111230
365 20111231
366 20120101
425 20120229
426 20120301
この関数は、ユリウス日ゼロを前年の最後の日と見なします。
また、macOS などの UNIX ベースのシステムの bash 関数は次のとおりです。
jul () { (( $2 >=0 )) && local pre=+; date -v$pre$2d -v-1d -j -f "%Y-%m-%d" $1-01-01 +%Y%m%d; }
Bash だけではできませんが、Perl をお持ちの場合:
use POSIX;
my ($jday, $year) = (100, 2011);
# Unix time in seconds since Jan 1st 1970
my $time = mktime(0,0,0, $jday, 0, $year-1900);
# same thing as a list that we can use for date/time formatting
my @tm = localtime $time;
my $yyyymmdd = strftime "%Y%m%d", @tm;
info 'Date input formats'
を実行 許可されている形式を確認してください。
YYYY-DDD の日付形式はないようです。試してみてください
$ date -d '2011-011'
date: invalid date `2011-011'
動作しないことを示しているので、 njd
だと思います 正解です。最善の方法は、bash
以外の外部ツールを使用することです と date
.
本当に bash と基本的なコマンド ライン ツールだけを使用したい場合は、次のようにすることができます:
julian_date_to_yyyymmdd()
{
date=$1 # assume all dates are in YYYYMMM format
year=${date%???}
jday=${date#$year}
for m in `seq 1 12`; do
for d in `seq 1 31`; do
yyyymmdd=$(printf "%d%02d%02d" $year $m $d)
j=$(date +"%j" -d "$yyyymmdd" 2>/dev/null)
if test "$jday" = "$j"; then
echo "$yyyymmdd"
return 0
fi
done
done
echo "Invalid date" >&2
return 1
}
しかし、それはかなり遅い方法です。
より高速ではあるがより複雑な方法は、月ごとにループを試行し、その月の最後の日を見つけて、ユリウス日がその範囲内にあるかどうかを確認します。
# year_month_day_to_jday <year> <month> <day> => <jday>
# returns 0 if date is valid, non-zero otherwise
# year_month_day_to_jday 2011 2 1 => 32
# year_month_day_to_jday 2011 1 32 => error
year_month_day_to_jday()
{
# XXX use local or typeset if your shell supports it
_s=$(printf "%d%02d%02d" "$1" "$2" "$3")
date +"%j" -d "$_s"
}
# last_day_of_month_jday <year> <month>
# last_day_of_month_jday 2011 2 => 59
last_day_of_month_jday()
{
# XXX use local or typeset if you have it
_year=$1
_month=$2
_day=31
# GNU date exits with 0 if day is valid, non-0 if invalid
# try counting down from 31 until we find the first valid date
while test $_day -gt 0; do
if _jday=$(year_month_day_to_jday $_year $_month $_day 2>/dev/null); then
echo "$_jday"
return 0
fi
_day=$((_day - 1))
done
echo "Invalid date" >&2
return 1
}
# first_day_of_month_jday <year> <month>
# first_day_of_month_jday 2011 2 => 32
first_day_of_month_jday()
{
# XXX use local or typeset if you have it
_year=$1
_month=$2
_day=1
if _jday=$(year_month_day_to_jday $_year $_month 1); then
echo "$_jday"
return 0
else
echo "Invalid date" >&2
return 1
fi
}
# julian_date_to_yyyymmdd <julian day> <4-digit year>
# e.g. julian_date_to_yyyymmdd 32 2011 => 20110201
julian_date_to_yyyymmdd()
{
jday=$1
year=$2
for m in $(seq 1 12); do
endjday=$(last_day_of_month_jday $year $m)
if test $jday -le $endjday; then
startjday=$(first_day_of_month_jday $year $m)
d=$((jday - startjday + 1))
printf "%d%02d%02d\n" $year $m $d
return 0
fi
done
echo "Invalid date" >&2
return 1
}