#!/bin/bash
# Skrypt daysuntil, bardzo podobny do poprzedniego skryptu daysago z tą różnicą, 
# że w obliczeniach jako data początkowa jest przyjmowana bieżąca data,
# a podana data jako data końcowa.

# Jak w poprzednim skrypcie, w systemie macOS poniższa zmienna musi
# zawierać ciąg `which gdate`, a w Linux `which date`.
date="`which date`"
#date="$(which gdate)"

function daysInMonth
{
  case $1 in
    1|3|5|7|8|10|12 ) dim=31 ;;  # Najczęstsza wartość.
    4|6|9|11        ) dim=30 ;;
    2               ) dim=29 ;;  # Wartość zależna od tego, czy rok jest przestępny.
    *               ) dim=-1 ;;  # Nieznany numer miesiąca.
  esac
}

function isleap
{
  # Funkcja przypisująca zmiennej $leapyear wartość różną od zera, jeżeli rok podany
  # w argumencie $1 jest rokiem przestępnym.
  leapyear=$($date -d 12/31/$1 +%j | grep 366)
}

######################
#### GŁÓWNA CZĘŚĆ KODU

if [ $# -ne 3 ] ; then
  echo "Użycie: $(basename $0) dzień miesiąc rok"
  echo "Data wyrażona za pomocą liczb (np. 1 1 2020)."
  exit 1
fi

$date --version > /dev/null 2>&1         # Ukrycie ewentualnych komunikatów o błędach.   

if [ $? -ne 0 ] ; then
  echo "Niestety, skrypt $(basename $0) nie działa bez polecenia date GNU." >&2
  exit 1
fi

eval $($date "+thismon=%m;thisday=%d;thisyear=%Y;dayofyear=%j")

endmon=$2; endday=$1; endyear=$3

# Trzeba sprawdzić mnóstwo przypadków...

daysInMonth $endmon # Nadanie wartości globalnej zmiennej dim.
if [ $endday -lt 0 -o $endday -gt $dim ] ; then
  echo "Błąd: miesiąc $endmon ma $dim dni." >&2 ; exit 1
fi

if [ $endmon -eq 2 -a $endday -eq 29 ] ; then
  isleap $endyear
  if [ -z "$leapyear" ] ; then
    echo "Rok: $endyear nie będzie rokiem przestępnym, " \
         "więc luty będzie miał 28 dni." >&2
    exit 1
  fi
fi

if [ $endyear -lt $thisyear ] ; then
  echo "Błąd: data $endyear-$endmon-$endday określa dzień wcześniejszy, " \
       "niż bieżący." >&2 ; exit 1
fi

if [ $endyear -eq $thisyear -a $endmon -lt $thismon ] ; then
  echo "Błąd: data $endyear-$endmon-$endday określa dzień wcześniejszy, " \
       "niż bieżący." >&2 ; exit 1
fi

if [ $endyear -eq $thisyear -a $endmon -eq $thismon -a $endday -lt $thisday ] ; then
  echo "Błąd: data $endyear-$endmon-$endday określa dzień wcześniejszy, "\
       "niż bieżący." >&2 ; exit 1
fi

if [ $endyear -eq $thisyear -a $endmon -eq $thismon -a $endday -eq $thisday ] ; then
  echo "Do dnia $endyear-$endmon-$endday pozostało 0 dni."
  exit 0
fi

# Jeżeli podany rok jest rokiem bieżącym, obliczenia są nieco inne.

if [ $endyear -eq $thisyear ] ; then
  totaldays=$(( $($date -d "$endmon/$endday/$endyear" +%j) - $($date +%j) ))
else

  # Obliczenia podzielone są na części. Najpierw określamy liczbę dni pozostałych
  # do końca bieżącego roku.

  ### OKREŚLENIE LICZBY DNI W ROKU POCZĄTKOWYM.

  # Przygotowanie ciągu znaków reprezentującego datę początkową.

  thisdatefmt="$thismon/$thisday/$thisyear"

  calculate="$($date -d "12/31/$thisyear" +%j) - $($date -d $thisdatefmt +%j)"

  daysleftinyear=$(( $calculate ))

  ### OKREŚLENIE LICZBY DNI W KAŻDYM MINIONYM ROKU.

  daysbetweenyears=0
  tempyear=$(( $thisyear + 1 ))

  while [ $tempyear -lt $endyear ] ; do
    daysbetweenyears=$(( $daysbetweenyears + $($date -d "12/31/$tempyear" +%j) ))
    tempyear=$(( $tempyear + 1 ))
  done

  ### OKREŚLENIE LICZBY DNI W KOŃCOWYM ROKU.

  dayofyear=$($date --date $endmon/$endday/$endyear +%j)  # To proste!

  ### ZSUMOWANIE WSZYSTKICH WARTOŚCI.

  totaldays=$(( $daysleftinyear + $daysbetweenyears + $dayofyear ))
fi

echo "Do dnia $endyear-$endmon-$endday pozostało $totaldays dni."
exit 0
