Apr 27

Количество недель в месяце

Для календаря, который я делаю, мне понадобилось узнать, сколько недель затрагивает месяц. Решение пришло моментально:

Результат := Неделя_последнего_дня - Неделя_первого_дня;

 

В коде я оформил это так:

function GetWeekInMonth(ADate: TDate): Integer;

begin
  Result := WeekOf(EndOftheMonth(ADate)) - WeekOf(StartOfTheMonth(ADate));
end;

 

Через полчаса понял, что решение не верное :( .

Данная функция неверно считает недели. Вот пример (см. скриншот):

calendar

 Как по вашему, что выдаст данная функция для июня 2008-го года? На скриншоте видно, что месяц затрагивает 6 недель, а функция вернет 5. Но это не самое страшное. Для декабря 2008-го года функция вернет -48!!!

 

Вся проблема кроется в функции DecodeDateWeek в модуле DateUtils. Для данного скриншота она засчитает только последний день месяца, а 1-е число отнесет к предыдущему месяцу. В итоге получится 5 недель, а не 6. А для декабря последний день месяца быдет уже в следующем году, т.е. номер недели для него будет равен еденице, в то время как номер недели для 1-го декабря будет равен 49, что и дает в итоге -48.

 

Решение, в данном случае, очень простое и примитивное, хотя, признаюсь, я на него потратил более 3-х часов жизни. Для того, чтобы узнать сколько недель затрагивает месяц, надо сделать очень просто - прибавить к месяцу столько дней, на сколько первый день смещен относительно понедельника и от получившейся суммы вычислять количество недель:

function GetWeekInMonth(ADate: TDate): Integer;
var
    lDays: Integer;
begin
    lDays := DayOfTheWeek(StartOfTheMonth(FCurrentDate)) +
        DaysInMonth(FCurrentDate);
    Result := lDays div 7;
    if Result * 7 <> lDays then
        Inc(Result);
end;

 

Надеюсь, кому-нибудь пригодится.


  1. Корвин
     Add karma Subtract karma  +0
    Цитировать.

    Хехе

    Начиная по крайней мере с 7 ки, предыдущей нет под рукой, есть стандартная функция для этого дела.
    Unit DateUtils

    Category datetime routines

    Delphi syntax:

    function WeeksBetween(const ANow, AThen: TDateTime): Integer;

    Description

    Call WeeksBetween to obtain the difference, in weeks, between two TDateTime values. WeeksBetween counts only whole Weeks. Thus, WeeksBetween reports the difference between January 1 at 12:00 AM and Jan 6 at 11:58 PM as 0 because the difference is one minute short of an entire week.

    1
  2. aktuba
     Add karma Subtract karma  +0
    Цитировать.
    Комментарий автора

    Я тоже так думал. Сколько недель выдаст эта функция для июня 2008-го? Пять, а должно быть 6…

    2
  3. Корвин
     Add karma Subtract karma  +0
    Цитировать.

    Во первых, по описанию той функции она покажет 4 ! А не 5,
    так что надо хотя бы для себя проверять некоторые моменты, чтобы не делать голословных утверждений.

    Во вторых Ваша функция косячная,
    вот это

    showmessage(inttostr(GetWeekInMonth(encodedate(2008, 4, 2))));

    показывает 5 !!! Хотя должно 6…

    3
  4. aktuba
     Add karma Subtract karma  +0
    Цитировать.
    Комментарий автора

    Почему 6??? В апреле 5 недель ;)

    >чтобы не делать голословных утверждений.
    Виноват, не проверял, сказал на память. Но и утверждение, что функция неверно покажет, не голословно, в чем можно убедиться.

    4
  5. Корвин
     Add karma Subtract karma  +0
    Цитировать.

    У Вас ошибка в алгоритме….
    Надо получить число полных недель (ЧПН) и разницу между Дней в Месяце и ЧПН * 7 – ОСТ.
    И потом проверять как первое число, не является ли оно понедельником, и если нет то число попавших в эту неделю дней не меньше ли ОСТ. Если равно ему то добавляется неделя, если меньше то 2.. Если же 1 число понедельник то + 1…
    Где так если разобраться….

    5
  6. aktuba
     Add karma Subtract karma  +0
    Цитировать.
    Комментарий автора

    Да нет никакой ошибки ;) . Проверь для начала сам.

    6
  7. agorbachenko
     Add karma Subtract karma  +0
    Цитировать.

    Мне кажется, что проблему следует решать не так. Ведь сама неделя может начинаться, к примеру, с воскресенья. Необходимо работать с таким понятием как “календарь”, где бы были учитывающие эти обстоятельства настройки. Это к вопросу об универсальности кода.

    7
  8. aktuba
     Add karma Subtract karma  +0
    Цитировать.
    Комментарий автора

    Это и есть составляющая календаря ;)

    8
  9. SmokE
     Add karma Subtract karma  +0
    Цитировать.

    спасибо за статейку

    9
  10. fakir
     Add karma Subtract karma  +0
    Цитировать.

    огромное спасибо!

    10
  11. fRk
     Add karma Subtract karma  +0
    Цитировать.

    косой код!

    11
  12. fRk
     Add karma Subtract karma  +0
    Цитировать.

    function GetWeekInMonth(ADate: TDate): Integer;
    var
    lDays: Integer;
    begin
    lDays := DayOfTheWeek(StartOfTheMonth(ADate)) +
    DaysInMonth(ADate)-1;
    Result:=lDays div 7;
    if lDays mod 7 (*знак неравенства*) 0 then Inc(Result);
    end;
    aktuba, Вы упустили из виду, что надо отнять единицу…

    12
  13. tetsuo
     Add karma Subtract karma  +0
    Цитировать.

    Автор, нельзя быть таким идиотом. Ваш пример – это пример убожества, как вам только хватило наглости это публиковать.
    Функция, которую вы назвали “неверно работает” может считать недели с воскресенья (для некоторых стран это куда привычнее, чем с понедельника).

    13

Поделитесь вашими мыслями

Поддерживаемые теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>