Пример A-8. days-between: Подсчет числа дней между двумя датами

Пример A-8. days-between: Подсчет числа дней между двумя датами

  1. #!/bin/bash
  2. # days-between.sh:    Подсчет числа дней между двумя датами.
  3. # Порядок использования: ./days-between.sh [M]M/[D]D/YYYY [M]M/[D]D/YYYY
  4. ARGS=2                # Ожидается два аргумента из командной строки.
  5. E_PARAM_ERR=65        # Ошибка в числе ожидаемых аргументов.
  6. REFYR=1600            # Начальный год.
  7. CENTURY=100
  8. DIY=365
  9. ADJ_DIY=367           # Корректировка на високосный год + 1.
  10. MIY=12
  11. DIM=31
  12. LEAPCYCLE=4
  13. MAXRETVAL=255         # Максимально возможное возвращаемое значение
  14.                       # для положительных чисел.
  15. diff=                         # Количество дней между датами.
  16. value=                # Абсолютное значение.
  17. day=                  # день, месяц, год.
  18. month=
  19. year=
  20. Param_Error ()        # Ошибка в пвраметрах командной строки.
  21. {
  22.   echo "Порядок использования: `basename $0` [M]M/[D]D/YYYY [M]M/[D]D/YYYY"
  23.   echo "       (даты должны быть после 1/3/1600)"
  24.   exit $E_PARAM_ERR
  25. }
  26. Parse_Date ()                 # Разбор даты.
  27. {
  28.   month=${1%%/**}
  29.   dm=${1%/**}                 # День и месяц.
  30.   day=${dm#*/}
  31.   let "year = `basename $1`"  # Хотя это и не имя файла, но результат тот же.
  32. }
  33. check_date ()                 # Проверка даты.
  34. {
  35.   [ "$day" -gt "$DIM" ] || [ "$month" -gt "$MIY" ] || [ "$year" -lt "$REFYR" ] && Param_Error
  36.   # Выход из сценария при обнаружении ошибки.
  37.   # Используется комбинация "ИЛИ-списка / И-списка".
  38.   #
  39.   # Упражнение: Реализуйте более строгую проверку даты.
  40. }
  41. strip_leading_zero () # Удалить ведущий ноль
  42. {
  43.   val=${1#0}          # иначе Bash будет считать числа
  44.   return $val         # восьмеричными (POSIX.2, sect 2.9.2.1).
  45. }
  46. day_index ()          # Формула Гаусса:
  47. {                     # Количество дней от 3 Янв. 1600 до заданной даты.
  48.   day=$1
  49.   month=$2
  50.   year=$3
  51.   let "month = $month - 2"
  52.   if [ "$month" -le 0 ]
  53.   then
  54.     let "month += 12"
  55.     let "year -= 1"
  56.   fi
  57.   let "year -= $REFYR"
  58.   let "indexyr = $year / $CENTURY"
  59.   let "Days = $DIY*$year + $year/$LEAPCYCLE - $indexyr + $indexyr/$LEAPCYCLE + $ADJ_DIY*$month/$MIY + $day - $DIM"
  60.   # Более подробное объяснение алгоритма вы найдете в
  61.   # <a href="http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm
  62. " title="http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm
  63. ">http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm
  64. </a>  if [ "$Days" -gt "$MAXRETVAL" ]  # Если больше 255,
  65.  then                             # то поменять знак
  66.    let "dindex = 0 - $Days"       # чтобы функция смогла вернуть полное значение.
  67.  else let "dindex = $Days"
  68.  fi
  69.  return $dindex
  70. }
  71. calculate_difference ()            # Разница между двумя датами.
  72. {
  73.  let "diff = $1 - $2"             # Глобальная переменная.
  74. }
  75. abs ()                             # Абсолютное значение
  76. {                                  # Используется глобальная переменная "value".
  77.  if [ "$1" -lt 0 ]                # Если число отрицательное
  78.  then                             # то
  79.    let "value = 0 - $1"           # изменить знак,
  80.  else                             # иначе
  81.    let "value = $1"               # оставить как есть.
  82.  fi
  83. }
  84. if [ $# -ne "$ARGS" ]              # Требуется два аргумента командной строки.
  85. then
  86.  Param_Error
  87. fi
  88. Parse_Date $1
  89. check_date $day $month $year      # Проверка даты.
  90. strip_leading_zero $day           # Удалить ведущие нули
  91. day=$?                            # в номере дня и/или месяца.
  92. strip_leading_zero $month
  93. month=$?
  94. day_index $day $month $year
  95. date1=$?
  96. abs $date1                         # Абсолютное значение
  97. date1=$value
  98. Parse_Date $2
  99. check_date $day $month $year
  100. strip_leading_zero $day
  101. day=$?
  102. strip_leading_zero $month
  103. month=$?
  104. day_index $day $month $year
  105. date2=$?
  106. abs $date2                         # Абсолютное значение
  107. date2=$value
  108. calculate_difference $date1 $date2
  109. abs $diff                          # Абсолютное значение
  110. diff=$value
  111. echo $diff
  112. exit 0
  113. # Сравните этот сценарий с реализацией формулы Гаусса на C
  114. # http://buschencrew.hypermart.net/software/datedif