Das ist eine großartige Lösung. Nur GNU-Datum, aber viel kürzer.
Mikel
Ich wusste nie, dass das GNU-Datum so flexibel ist. Fantastisch.
NJD
Was für eine schöne Lösung mit GNU-Datum. jul () { date -v+$2d -v-1d -j -f "%Y-%m-%d" $1-01-01 +%Y%m%d; }
Wenn
1
@mcantsin: Danke für die MacOS-Version!
Bis auf weiteres angehalten.
1
@mcantsin: Ich habe deine Version so geändert, dass sie mit negativen Offsets funktioniert.
Bis auf weiteres angehalten.
4
Kann nicht nur in Bash gemacht werden, aber wenn Sie Perl haben:
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;
Ich bin mir eigentlich ziemlich sicher, dass dies nur in Bash möglich ist, wenn auch nicht so elegant (im schlimmsten Fall wird der Zeitstempel berechnet, der ihn formatiert). Aber ich bin nicht vor einem Linux-Computer, um es zu testen.
Bobby
Bobby, du denkst vielleicht an den dateBefehl in den Coreutils. Ich habe es überprüft und es unterstützt nur das Formatieren des aktuellen Datums oder das Setzen auf einen neuen Wert, daher ist dies keine Option.
Bandi
Mit dem dateBefehl kann durchgeführt werden. Siehe meine Antwort. Aber der Perl-Weg ist viel einfacher und schneller.
Mikel
2
@ Bandi: Es sei denndate -d
user1686
+1: Ich habe dies verwendet - danke, aber die obige Methode date -d wurde angezeigt.
Umber Ferrule
4
Führen Sie aus, um info 'Date input formats'zu sehen, welche Formate zulässig sind.
$ date -d '2011-011'
date: invalid date `2011-011'
zeigt, dass es nicht funktioniert, daher halte ich es für njdrichtig. Der beste Weg ist, ein anderes externes Tool als bashund zu verwenden date.
Wenn Sie wirklich nur Bash- und grundlegende Befehlszeilentools verwenden möchten, können Sie Folgendes tun:
julian_date_to_yyyymmdd(){
date=$1 # assume all dates are in YYYYMMM format
year=${date%???}
jday=${date#$year}for m in`seq 1 12`;dofor 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"return0fidonedone
echo "Invalid date">&2return1}
Aber das ist ein ziemlich langsamer Weg.
Ein schnellerer, aber komplexerer Weg versucht, jeden Monat eine Schleife zu durchlaufen, findet den letzten Tag in diesem Monat und prüft dann, ob der julianische Tag in diesem Bereich liegt.
# 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 datewhile test $_day -gt 0;doif _jday=$(year_month_day_to_jday $_year $_month $_day 2>/dev/null);then
echo "$_jday"return0fi
_day=$((_day -1))done
echo "Invalid date">&2return1}# 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=1if _jday=$(year_month_day_to_jday $_year $_month 1);then
echo "$_jday"return0else
echo "Invalid date">&2return1fi}# 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 112);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
return0fidone
echo "Invalid date">&2return1}
last_day_of_month_jdaykönnte auch mit zB date -d "$yyyymm01 -1 day"(nur GNU-Datum) oder implementiert werden $(($(date +"%s" -d "$yyyymm01") - 86400)).
Mikel
Bash kann so dekrementieren : ((day--)). Bash hat solche forSchleifen for ((m=1; m<=12; m++))(keine Notwendigkeit seq). Es ist ziemlich sicher anzunehmen, dass Shells, die einige der anderen Funktionen haben, die Sie verwenden local.
Bis auf weiteres angehalten.
IIRC local wird von POSIX nicht angegeben, aber wenn ksh einen Satz hat, hat zsh local und zsh deklariert IIRC. Ich denke, dass der Satz in allen 3 funktioniert, aber nicht in Asche / Strich. Ich neige dazu, ksh-Stil für zu wenig zu verwenden. Danke für die Gedanken.
Mikel
@Mikel: Dash hat localwie BusyBox ash.
Bis auf weiteres angehalten.
Ja, aber kein IIRC-Satz. Alle anderen haben einen Satz. Wenn dash auch gesetzt hätte, hätte ich das im Beispiel verwendet.
Mikel
1
Wenn der Tag des Jahres (1-366) 149 und das Jahr 2014 ist ,
$ date -d "148 days 2014-01-01"+"%Y%m%d"20140529
Stellen Sie sicher, dass Sie den Wert für Tag des Jahres -1 eingeben.
Mir ist klar, dass dies vor Ewigkeiten gefragt wurde, aber keine der Antworten, die ich sehe, ist das, was ich als reines BASH betrachte, da sie alle GNU verwenden date. Ich dachte, ich würde versuchen zu antworten ... Aber ich warne Sie im Voraus, die Antwort ist weder elegant noch kurz mit über 100 Zeilen. Es könnte reduziert werden, aber ich wollte anderen leicht zeigen, was es tut.
Die wichtigsten "Tricks" hier sind herauszufinden, ob ein Jahr ein Schaltjahr ist (oder nicht), indem der MODUL %des Jahres durch 4 geteilt wird und dann nur die Tage in jedem Monat addiert werden, plus ein zusätzlicher Tag für Februar, falls erforderlich unter Verwendung einer einfachen Wertetabelle.
Bitte zögern Sie nicht, Kommentare abzugeben und Vorschläge zu machen, wie Sie dies besser machen können, da ich in erster Linie hier bin, um selbst mehr zu lernen, und ich würde mich bestenfalls als BASH-Neuling betrachten. Ich habe mein Bestes getan, um dies so portabel wie möglich zu machen, und das bedeutet meiner Meinung nach einige Kompromisse.
Weiter zum Code ... Ich hoffe, er ist ziemlich selbsterklärend.
#!/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=0else
leap_yr=1fi
last_doy=$(( leap_yr +365))while["$valid"!="TRUE"]doif[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
fidone
valid=while["$valid"!="TRUE"]doif[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
fidoneif["$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=335else
jan=31 feb=59 mar=90 apr=120 may=151 jun=181
jul=212 aug=243 sep=273 oct=304 nov=334fiif["$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"fiif[ ${#dd}-eq 1]then
dd="0$dd"fiif[ ${#yyyy}-lt 4]thenuntil[ ${#yyyy}-eq 4]do
yyyy="0$yyyy"donefi
printf '\n %s%s%s\n\n'"$yyyy""$mm""$dd"
Zu Ihrer Information, die tatsächliche Berechnung des Schaltjahres ist etwas komplexer als "ist durch 4 teilbar".
Erich
@erich - Sie haben Recht, aber innerhalb des von diesem Skript (1901-2099) gültigen Bereichs von Jahren treten Situationen, die nicht funktionieren, nicht auf. Es ist nicht besonders schwierig, einen "oder" -Test hinzuzufügen, um mit Jahren umzugehen, die gleichmäßig durch 100 teilbar sind, aber nicht gleichmäßig durch 400 teilbar sind, um diese Fälle abzudecken, wenn der Benutzer dies erweitern muss, aber ich hatte nicht das Gefühl, dass dies wirklich notwendig war dieser Fall. Vielleicht war das kurzsichtig von mir?
Antworten:
Diese Bash-Funktion funktioniert für mich auf einem GNU-basierten System:
Einige Beispiele:
Diese Funktion betrachtet den Julianischen Tag Null als den letzten Tag des Vorjahres.
Und hier ist eine Bash-Funktion für UNIX-basierte Systeme wie macOS:
quelle
jul () { date -v+$2d -v-1d -j -f "%Y-%m-%d" $1-01-01 +%Y%m%d; }
Kann nicht nur in Bash gemacht werden, aber wenn Sie Perl haben:
quelle
date
Befehl in den Coreutils. Ich habe es überprüft und es unterstützt nur das Formatieren des aktuellen Datums oder das Setzen auf einen neuen Wert, daher ist dies keine Option.date
Befehl kann durchgeführt werden. Siehe meine Antwort. Aber der Perl-Weg ist viel einfacher und schneller.date -d
Führen Sie aus, um
info 'Date input formats'
zu sehen, welche Formate zulässig sind.Das JJJJ-TTT-Datumsformat scheint nicht vorhanden zu sein und es zu versuchen
zeigt, dass es nicht funktioniert, daher halte ich es für
njd
richtig. Der beste Weg ist, ein anderes externes Tool alsbash
und zu verwendendate
.Wenn Sie wirklich nur Bash- und grundlegende Befehlszeilentools verwenden möchten, können Sie Folgendes tun:
Aber das ist ein ziemlich langsamer Weg.
Ein schnellerer, aber komplexerer Weg versucht, jeden Monat eine Schleife zu durchlaufen, findet den letzten Tag in diesem Monat und prüft dann, ob der julianische Tag in diesem Bereich liegt.
quelle
last_day_of_month_jday
könnte auch mit zBdate -d "$yyyymm01 -1 day"
(nur GNU-Datum) oder implementiert werden$(($(date +"%s" -d "$yyyymm01") - 86400))
.((day--))
. Bash hat solchefor
Schleifenfor ((m=1; m<=12; m++))
(keine Notwendigkeitseq
). Es ist ziemlich sicher anzunehmen, dass Shells, die einige der anderen Funktionen haben, die Sie verwendenlocal
.local
wie BusyBoxash
.Wenn der Tag des Jahres (1-366) 149 und das Jahr 2014 ist ,
Stellen Sie sicher, dass Sie den Wert für Tag des Jahres -1 eingeben.
quelle
Meine Lösung in Bash
quelle
Auf einem POSIX-Terminal:
Dann ruf gerne an
quelle
Mir ist klar, dass dies vor Ewigkeiten gefragt wurde, aber keine der Antworten, die ich sehe, ist das, was ich als reines BASH betrachte, da sie alle GNU verwenden
date
. Ich dachte, ich würde versuchen zu antworten ... Aber ich warne Sie im Voraus, die Antwort ist weder elegant noch kurz mit über 100 Zeilen. Es könnte reduziert werden, aber ich wollte anderen leicht zeigen, was es tut.Die wichtigsten "Tricks" hier sind herauszufinden, ob ein Jahr ein Schaltjahr ist (oder nicht), indem der MODUL
%
des Jahres durch 4 geteilt wird und dann nur die Tage in jedem Monat addiert werden, plus ein zusätzlicher Tag für Februar, falls erforderlich unter Verwendung einer einfachen Wertetabelle.Bitte zögern Sie nicht, Kommentare abzugeben und Vorschläge zu machen, wie Sie dies besser machen können, da ich in erster Linie hier bin, um selbst mehr zu lernen, und ich würde mich bestenfalls als BASH-Neuling betrachten. Ich habe mein Bestes getan, um dies so portabel wie möglich zu machen, und das bedeutet meiner Meinung nach einige Kompromisse.
Weiter zum Code ... Ich hoffe, er ist ziemlich selbsterklärend.
quelle
mathematisch dargestellt unten
quelle