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  --1
    Цитировать.

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

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

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

    6
  7. agorbachenko
     Add karma Subtract karma  --1
    Цитировать.

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

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

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

    8
  9. SmokE
     Add karma Subtract karma  --1
    Цитировать.

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

    9
  10. fakir
     Add karma Subtract karma  --1
    Цитировать.

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

    10
  11. fRk
     Add karma Subtract karma  --1
    Цитировать.

    косой код!

    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  +1
    Цитировать.

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

    13
  14. zilog
     Add karma Subtract karma  --1
    Цитировать.

    Я посчитал по воскресным дням:

    uses
    DateUtils, SysUtils;
    //..
    function WeeksInMonth(ADateTime: TDateTime): Integer;
    var
    I: Integer;
    vLastDay, vFirstDay, vLastSunday: TDateTime;
    begin
    vLastDay := Trunc(EndOfTheMonth(ADateTime)); // последний день месяца
    vFirstDay := Trunc(StartOfTheMonth(ADateTime)); // первый день месяца
    Result := 0;

    for I := Trunc(vFirstDay) to Trunc(vLastDay) do
    begin
    if DayOfTheWeek(I) = 7 then //если Воскресение, то
    begin
    vLastSunday := I; //записать дату воскресения
    Inc(Result); //прибавить воскресение в счетчик
    end;
    end;

    if vLastDay > vLastSunday then //если последний день позже последнего воскресения
    Inc(Result); //то это еще одна неделя, прибавить счетчик
    end;

    14
  15. zilog
     Add karma Subtract karma  --1
    Цитировать.

    Номер недели по дате так:

    function WeekOfMonth(ADate: TDate): Word;
    var
    vFirstDay, vLastSunday: TDate;
    I: Integer;
    begin
    vFirstDay := Trunc(StartOfTheMonth(ADate));
    Result := 0;
    for I := Trunc(vFirstDay) to Trunc(ADate) do
    begin
    if DayOfTheWeek(I) = 7 then
    begin
    vLastSunday := I;
    Inc(Result);
    end;
    end;
    if ADate > vLastSunday then
    Inc(Result);
    end;

    15
  16. All
     Add karma Subtract karma  +0
    Цитировать.

    function week()
    {
    var date = new Date(myYear, numberMonth, 1);
    if ((date.getDay() == 6 || date.getDay() == 0) && daysMonthYear(numberMonth, myYear) == 31)
    {
    return 6;
    }
    else
    {
    if ((date.getDay() == 0) && daysMonthYear(numberMonth, myYear) == 30)
    {
    return 6;
    };
    };
    return 5;
    };

    16
  17. Cuprum
     Add karma Subtract karma  +0
    Цитировать.

    function WeekOfMonth(ADate: TDate): Word;
    var
    id,im,iy : Word;
    begin
    DecodeDate(ADate,iy,im,id);
    Result:=trunc(1+id/7+(DayOfWeek(StartOfTheMonth(ADate))-1)*0.14286-0.145);
    end;

    17

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



Ссылки в комментариях будут свободны от nofollow.

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

Ссылки в комментариях будут свободны от nofollow.

http://tm-arh.ru/ банк Фридом Финанс.
-->