Я хочу перейти від дня року (1-366) та року (наприклад, 2011) до дати у форматі YYYYMMDD?
Я хочу перейти від дня року (1-366) та року (наприклад, 2011) до дати у форматі YYYYMMDD?
Відповіді:
Ця функція 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
Ця функція вважає нульовий день Джуліана останнім днем попереднього року.
А ось функція bash для систем на базі UNIX, таких як macOS:
jul () { (( $2 >=0 )) && local pre=+; date -v$pre$2d -v-1d -j -f "%Y-%m-%d" $1-01-01 +%Y%m%d; }
jul () { date -v+$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;
date
команду в ядрах. Я перевірив це, і він підтримує лише форматування поточної дати або встановлення нового значення, тому це не варіант.
date
команди можна зробити. Дивіться мою відповідь. Але шлях Perl набагато простіше і швидше.
date -d
Запустіть, 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
}
last_day_of_month_jday
також можуть бути реалізовані з використанням , наприклад date -d "$yyyymm01 -1 day"
(тільки дату GNU) або $(($(date +"%s" -d "$yyyymm01") - 86400))
.
((day--))
. Bash має for
подібні петлі for ((m=1; m<=12; m++))
(не потрібно seq
). Досить безпечно припустити, що снаряди мають деякі інші функції, якими ви користуєтесь local
.
local
як і BusyBox ash
.
Якщо день року (1-366) - 149, а рік - 2014 ,
$ date -d "148 days 2014-01-01" +"%Y%m%d"
20140529
Обов’язково введіть значення дня -1 року .
Моє рішення в баш
from_year=2013
from_day=362
to_year=2014
to_day=5
now=`date +"%Y/%m/%d" -d "$from_year/01/01 + $from_day days - 2 day"`
end=`date +"%Y/%m/%d" -d "$to_year/01/01 + $to_day days - 1 day"`
while [ "$now" != "$end" ] ;
do
now=`date +"%Y/%m/%d" -d "$now + 1 day"`;
echo "$now";
calc_day=`date -d "$now" +%G'.'%j`
echo $calc_day
done
Я усвідомлюю, що це було задано століттями тому, але жодна з відповідей, яку я бачу, - це те, що я вважаю чистою БАШ, оскільки всі вони використовують GNU date
. Я думав, що я буду відповідати на відповідь ... Але я попереджаю вас заздалегідь, відповідь не елегантна, а також не коротка на понад 100 рядків. Це можна виправити, але я хотів, щоб інші легко бачили, що це робить.
Основні "трюки" тут з'ясовують, чи рік є високосним (або ні), отримавши МОДУЛЬ %
року, розділений на 4, а потім просто складання днів у кожному місяці плюс додатковий день для лютого, якщо це потрібно , використовуючи просту таблицю значень.
Будь ласка, не соромтесь коментувати та пропонувати пропозиції щодо способів зробити це краще, оскільки я в першу чергу тут, щоб дізнатись більше, і я вважаю себе початківцем БАШу в кращому випадку. Я зробив усе можливе, щоб зробити це настільки портативним, наскільки я знаю, як це, і це означає, що на мою думку є деякі компроміси.
Щодо коду ... Я сподіваюся, що це досить зрозуміло.
#!/bin/sh
# Given a julian day of the year and a year as ddd yyyy, return
# the values converted to yyyymmdd format using ONLY bash:
ddd=$1
yyyy=$2
if [ "$ddd" = "" ] || [ "$yyyy" = "" ]
then
echo ""
echo " Usage: <command> 123 2016"
echo ""
echo " A valid julian day from 1 to 366 is required as the FIRST"
echo " parameter after the command, and a valid 4-digit year from"
echo " 1901 to 2099 is required as the SECOND. The command and each"
echo " of the parameters must be separated from each other with a space."
echo " Please try again."
exit
fi
leap_yr=$(( yyyy % 4 ))
if [ $leap_yr -ne 0 ]
then
leap_yr=0
else
leap_yr=1
fi
last_doy=$(( leap_yr + 365 ))
while [ "$valid" != "TRUE" ]
do
if [ 0 -lt "$ddd" ] && [ "$last_doy" -ge "$ddd" ]
then
valid="TRUE"
else
echo " $ddd is an invalid julian day for the year given."
echo " Please try again with a number from 1 to $last_doy."
exit
fi
done
valid=
while [ "$valid" != "TRUE" ]
do
if [ 1901 -le "$yyyy" ] && [ 2099 -ge "$yyyy" ]
then
valid="TRUE"
else
echo " $yyyy is an invalid year for this script."
echo " Please try again with a number from 1901 to 2099."
exit
fi
done
if [ "$leap_yr" -eq 1 ]
then
jan=31 feb=60 mar=91 apr=121 may=152 jun=182
jul=213 aug=244 sep=274 oct=305 nov=335
else
jan=31 feb=59 mar=90 apr=120 may=151 jun=181
jul=212 aug=243 sep=273 oct=304 nov=334
fi
if [ "$ddd" -gt $nov ]
then
mm="12"
dd=$(( ddd - nov ))
elif [ "$ddd" -gt $oct ]
then
mm="11"
dd=$(( ddd - oct ))
elif [ "$ddd" -gt $sep ]
then
mm="10"
dd=$(( ddd - sep ))
elif [ "$ddd" -gt $aug ]
then
mm="09"
dd=$(( ddd - aug ))
elif [ "$ddd" -gt $jul ]
then
mm="08"
dd=$(( ddd - jul ))
elif [ "$ddd" -gt $jun ]
then
mm="07"
dd=$(( ddd - jun ))
elif [ "$ddd" -gt $may ]
then
mm="06"
dd=$(( ddd - may ))
elif [ "$ddd" -gt $apr ]
then
mm="05"
dd=$(( ddd - apr ))
elif [ "$ddd" -gt $mar ]
then
mm="04"
dd=$(( ddd - mar ))
elif [ "$ddd" -gt $feb ]
then
mm="03"
dd=$(( ddd - feb ))
elif [ "$ddd" -gt $jan ]
then
mm="02"
dd=$(( ddd - jan ))
else
mm="01"
dd="$ddd"
fi
if [ ${#dd} -eq 1 ]
then
dd="0$dd"
fi
if [ ${#yyyy} -lt 4 ]
then
until [ ${#yyyy} -eq 4 ]
do
yyyy="0$yyyy"
done
fi
printf '\n %s%s%s\n\n' "$yyyy" "$mm" "$dd"
OLD_JULIAN_VAR=$(date -u -d 1840-12-31 +%s)
TODAY_DATE=`date --date="$odate" +"%Y-%m-%d"`
TODAY_DATE_VAR=`date -u -d "$TODAY_DATE" +"%s"`
export JULIAN_DATE=$((((TODAY_DATE_VAR - OLD_JULIAN_VAR))/86400))
echo $JULIAN_DATE
математично представлені нижче
[(date in sec)-(1840-12-31 in sec)]/(sec in a day 86400)