일과 년을 YYYYMMDD로 변환하는 방법?


11

연도 (1-366)와 연도 (예 : 2011)에서 YYYYMMDD 형식의 날짜로 이동하려고합니까?


1
0 일은 어떤 날입니까? 일반적으로 1-366입니다.
Mikel

답변:


17

이 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

이 함수는 Julian 일 0을 전년의 마지막 날로 간주합니다.

그리고 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; }

2
그것은 훌륭한 솔루션입니다. GNU 날짜 만 있지만 훨씬 짧습니다.
Mikel

나는 GNU 날짜가 그렇게 유연하다는 것을 결코 알지 못했습니다. 환상적인.
njd

GNU 날짜를 사용하는 훌륭한 솔루션입니다. macOS와 같은 UNIX 시스템에서 동일한 작업을 수행하려는 경우 다음을 사용할 수 있습니다.jul () { date -v+$2d -v-1d -j -f "%Y-%m-%d" $1-01-01 +%Y%m%d; }
mcantsin

1
@ mcantsin : MacOS 버전에 감사드립니다!
추후 공지가있을 때까지 일시 중지되었습니다.

1
@ mcantsin : 음의 오프셋으로 작동하도록 버전을 수정했습니다.
추후 공지가있을 때까지 일시 중지되었습니다.

4

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;

1
나는 실제로 Bash에서만 할 수 있다고 확신하지만, 우아하지는 않습니다 (최소한 경우 타임 스탬프 형식을 계산하는 경우). 그러나 나는 그것을 시험해보기 위해 리눅스 머신 앞에 있지 않다.
Bobby

바비, 당신은 datecoreutils 의 명령을 생각할 것입니다 . 나는 그것을 확인했고 현재 날짜의 형식을 지정하거나 새로운 값으로 설정하는 것을 지원하므로 옵션이 아닙니다.
반디

은 Using date명령하는 수행 할 수 있습니다. 내 대답을 참조하십시오. 그러나 Perl 방식은 훨씬 쉽고 빠릅니다.
Mikel

2
@bandi : 않는 한date -d
user1686

+1 : 이것을 사용하고있었습니다 – 고맙지 만, 위의 날짜 -d 방법이 나타났습니다.
엄버 페룰

4

실행 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
}

그러나 그것은 꽤 느린 방법입니다.

더 빠르고 복잡한 방법으로 매월 반복을 시도하고 해당 월의 마지막 날을 찾은 다음 Julian 날이 해당 범위에 있는지 확인합니다.

# 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)).
Mikel

Bash는 다음과 같이 감소시킬 수 있습니다 ((day--)). Bash에는 for이와 같은 루프 가 있습니다 for ((m=1; m<=12; m++))(필요 없음 seq). 사용중인 다른 기능 중 일부를 가진 쉘이 있다고 가정하는 것이 안전합니다 local.
추후 공지가있을 때까지 일시 중지되었습니다.

POSIX는 IIRC local을 지정하지 않지만 ksh를 사용하면 typeset이 있으면 zsh는 local을, zsh는 IIRC를 선언합니다. 조판은 3 개 모두에서 작동하지만 재 / 대시에서는 작동하지 않는다고 생각합니다. 나는 ksh 스타일을 사용하는 경향이 있습니다. 생각해 주셔서 감사합니다.
Mikel

@Mikel : Dash는 localBusyBox와 같습니다 ash.
추후 공지가있을 때까지 일시 중지되었습니다.

예, 그러나 IIRC를 조판하지 않았습니다. 다른 모든 사람들은 조판했습니다. 대시가 조판 된 경우 예제에서 사용했을 것입니다.
Mikel

1

연도 (1-366)가 149 이고 연도가 2014 인 경우 ,

$ date -d "148 days 2014-01-01" +"%Y%m%d"
20140529

일 -1 값 을 입력해야 합니다.


0

bash의 내 솔루션

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

0

POSIX 터미널에서 :

jul () { date -v$1y -v1m -v1d -v+$2d -v-1d "+%Y%m%d"; }

그런 다음처럼 전화

jul 2011 012
jul 2017 216
jul 2100 60

0

나는 이것이 오래 전에 요청되었다는 것을 알고 있지만, 내가 보는 대답 중 어느 것도 GNU를 사용하기 때문에 순수한 BASH라고 생각하는 것은 없습니다 date. 나는 대답 할 때 찌르겠다고 생각했다. 그러나 나는 당신에게 미리 경고한다. 파싱 ​​될 수는 있지만 다른 사람들이 쉽게 수행 할 수 있도록하고 싶었습니다.

여기서 1 차 "트릭"은 올해의 MODULUS %를 4로 나눈 다음 한 달을 합산하고 필요한 경우 2 월의 추가 일을 추가 하여 1 년이 윤년인지 여부를 파악하는 것 입니다. 간단한 값 표를 사용합니다.

나는 주로 더 많은 것을 배우기 위해 여기에 있으며, 나는 나 자신을 BASH 초보자로 생각하기 때문에, 이것을 더 잘하는 방법에 대해 의견을 말하고 제안하십시오. 나는 내가 아는 것처럼 이것을 휴대용으로 만들기 위해 최선을 다했으며, 그것은 내 의견에 약간의 타협을 의미합니다.

코드에서 ... 나는 그것이 자명하기를 바랍니다.

#!/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"

참고로, 실제 윤년 계산은 "4로 나눌 수있는 것"보다 약간 더 복잡합니다.
Erich

@erich-정확하지만이 스크립트 (1901-2099)에 의해 유효한 것으로 허용 된 기간 내에 작동하지 않는 상황은 발생하지 않습니다. 사용자가 이것을 확장 해야하는 경우 이러한 경우를 다루기 위해 균등하게 100으로 나눌 수 있지만 400으로 나눌 수없는 년을 처리하기 위해 "또는"테스트를 추가하는 것은 그리 어렵지 않지만 실제로는 필요하다고 생각하지 않았습니다. 이 경우. 아마도 저에 대한 근시안적입니까?
Dave Lydick

0
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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.