Программирование на языке Ruby.

7.5. Вычисление n-ого дня недели в месяце.

Иногда, зная год и месяц, хочется вычислить дату, скажем, третьего понедельника или второго вторника в этом месяце. Такую задачу решает код в листинге 7.1.

Чтобы найти n-ое вхождение данного дня недели, мы передаем n в качестве первого параметра. Второй параметр — номер дня недели (0 — воскресенье, 1 — понедельник и т.д.). Третий и четвертый параметры — месяц и год соответственно.

Листинг 7.1. Вычисление n-ого дня недели в месяце.

Def nth_wday(n, wday, month, year).

 if (!n.between? 1,5) or.

  (!wday.between? 0,6) or.

  (!month.between? 1,12) raise ArgumentError.

 end.

 t = Time.local year, month, 1.

 first = t.wday.

 if first == wday.

  fwd = 1.

 elsif first < wday.

  fwd = wday - first + 1.

 elsif first > wday.

  fwd = (wday+7) - first + 1.

 end.

 target = fwd + (n-1)*7.

 begin.

  t2 = Time.local year, month, target.

 rescue ArgumentError.

  return nil.

 end.

 if t2.mday == target.

  t2.

 else.

  nil.

 end.

End.

Странный код в конце текста метода призван скорректировать давнюю традицию, принятую в функциях работы с датами. Если вы думаете, что попытка создать объект для представления 31 ноября приведет к ошибке, то разочарую вас. Почти все системы молчаливо преобразуют эту дату в 1 декабря. Если вы давным-давно программируете в UNIX, то, наверное, полагаете, что так и должно быть. Другие сочтут это ошибкой.

Не станем спорить о том, что должна делать системная библиотека и должен ли Ruby изменить это поведение. Но мы не хотим, чтобы наша процедура продолжала эту традицию. Если вы ищете, к примеру, пятую пятницу в ноябре 2000 года, то она вернет nil (а не 1 декабря 2000 года).