У меня была похожая проблема несколько месяцев назад, и я закончил писать быстрый скрипт для создания записей в базе данных для каждого периода оплаты, поэтому мне никогда не приходилось делать математику. Таким образом, система работает с той же скоростью и не должна выполнять медленные итерации каждый раз, когда запрашивается период.
При этом вы всегда можете взять начальную дату и добавлять две недели (или сколько бы ни были ваши периоды) снова и снова, пока не достигнете дат, указанных в вызове функции. Это немного уродливо, и чем дольше он работает, тем медленнее он становится (поскольку даты становятся все дальше и дальше друг от друга).
Оба способа тривиальны для реализации, вопрос только в том, какие ресурсы у вас под рукой для решения этой проблемы.
Итак, для номера 1: начните с 04.01.2009 или 01.01.2009 (в зависимости от четной / нечетной недели оплаты) и добавьте 2 недели, пока значение данного даты не станет меньше даты, которую вы тестируете + 2 недель. Это начало периода.
Для номера 2: то же самое, начните с даты и добавьте 2 недели, пока вы не окажетесь в пределах диапазона дат. Пока вы там, добавьте каждый элемент в список. Как только вы закончите последнее свидание, вырвитесь из цикла и верните новый блестящий список.
Если вы использовали мой метод и использовали базу данных для размещения всей этой информации, она превращается в 2 простых запроса:
1) SELECT * FROM payperiods WHERE startdate<=givenDate ORDER BY startdate LIMIT 1
2) SELECT * FROM payperiods WHERE startdate>=givenDate AND enddate<=givenDate ORDER BY startdate