Расчет номера недели - PullRequest
       15

Расчет номера недели

0 голосов
/ 08 февраля 2019

У меня проблема с кодом, который использовался для вычисления номера недели с даты.Приложение построитель отчетов.Язык программирования отчетов: RAP. Язык RAP идентичен Object Pascal.

Мой написанный код работает на 2017 и 2018 годы. Но он не работает на 2019 и 2020 годы.

Есть ли у кого-то идея, что я здесь не так делаю?

if (AVX['P_DELDATE'] >= 42737) and (AVX['P_DELDATE'] <= 43100) {= Jahr        2017} then begin
Value:= (AVX['P_DELDATE'] - 1 - (42737-1) + 7-((AVX['P_DELDATE'] -1 - (42737-1) ) mod 7))/7;
end else begin

if (AVX['P_DELDATE'] >= 43101) and (AVX['P_DELDATE'] <= 43465) {= Jahr 2018} then begin
Value:= (AVX['P_DELDATE'] - 1 - (43101-1) + 7-((AVX['P_DELDATE'] -1 -  (43101-1) ) mod 7))/7;
end;
end else begin

if (AVX['P_DELDATE'] >= 43466) and (AVX['P_DELDATE'] <= 43830) {= Jahr  2019} then begin
Value:= (AVX['P_DELDATE'] - 1 - (43466-1) + 7-((AVX['P_DELDATE'] -1 - (43466-1) ) mod 7))/7;
end;
end else begin

if (AVX['P_DELDATE'] >= 43831) and (AVX['P_DELDATE'] <= 44196) {= Jahr 2020} then begin
Value:= (AVX['P_DELDATE'] - 1 - (43831-1) + 7-((AVX['P_DELDATE'] -1 - (43831-1) ) mod 7))/7;
end;
end;
end;

1 Ответ

0 голосов
/ 10 февраля 2019

Вы не говорите, почему спрашиваете о расчете номера недели в коде RAP.Помимо того, что я не могу найти определение функции AVX в документации RAP, попытка вычислить номер недели с использованием AVX в RAP, очевидно, подвержена ошибкам, не является самодокументированием и является практически не подлежащим отладке способом.чтобы сделать это.

Очевидно, ваша функция (? или, может быть, это поле отчета) AVX возвращает числовое значение, целочисленная часть которого соответствует целочисленной части значения TDateTime.В приведенном ниже примере проекта, который я включил, чтобы показать более простой и менее подверженный ошибкам способ решения проблемы, я включил функцию, которая (я надеюсь) соответствует вашей функции RAP, следующим образом:

 function WeekNumber(Day : Integer) : Double;
begin
  if (Day >= 42737) and (Day <= 43100) {= Jahr        2017} then begin
      result:= (Day - 1 - (42737-1) + 7-((Day -1 - (42737-1) ) mod 7))/7;
  end
  else begin
    if (Day >= 43101) and (Day <= 43465) {= Jahr 2018} then begin
      result:= (Day - 1 - (43101-1) + 7-((Day -1 -  (43101-1) ) mod 7))/7;
    end
    else begin
      if (Day >= 43466) and (Day <= 43830) {= Jahr  2019} then begin
        result:= (Day - 1 - (43466-1) + 7-((Day -1 - (43466-1) ) mod 7))/7;
      end
      else begin
        if (Day >= 43831) and (Day <= 44196) {= Jahr 2020} then begin
          result:= (Day - 1 - (43831-1) + 7-((Day -1 - (43831-1) ) mod 7))/7;
        end;
      end;
    end;
  end;
end;

(Кстати, при выполнении этого преобразования мне пришлось исправить ряд синтаксических ошибок в вашем RAP-коде, которые вы вскоре найдете, если попытаетесь выполнить преобразование самостоятельно.)

Затем я добавил процедуру впротестируйте эту функцию:

procedure TForm1.TestWeekNumber;
var
  Date : TDateTime;
  i : integer;

  procedure TestInner(Date : TDateTime);
  var
    WN1,
    WN2 : Double;
    S,
    SError : String;
  begin
    WN1 := WeekOf(Date);
    WN2 := WeekNumber(Trunc(Date));
    if WN1 = WN2 then
      SError := ''
    else
      SError := '***';
    S := Format('Date: %s WeekOf: %g WeekNumber: %g %s',[ DateTimeToStr(Date), WN1, WN2, SError]);
    if SError <> '' then
      Memo1.Lines.Add(S);
  end;
begin
  Date := StrToDateTime('01/01/2017');
  Memo1.Lines.BeginUpdate;
  try
    for i := 0 to 1999 do
      TestInner(Date + i);
   finally
     Memo1.Lines.EndUpdate;
   end;
end;

Это сравнивает вашу WeekNumber со стандартной WeekOf функцией в DateUtils.Pas, которая возвращает номер недели, как определено стандартом ISO 8601, входного значения TDateTime изаписывает значения в TMemo, когда они дают разные результаты.Ниже приводится выдержка из выходных данных:

Date: 01/01/2017 WeekOf: 52 WeekNumber: 1.13989900694441E-307 ***
Date: 31/12/2018 WeekOf: 1 WeekNumber: 53 ***
Date: 07/01/2019 WeekOf: 2 WeekNumber: 1 ***
Date: 14/01/2019 WeekOf: 3 WeekNumber: 2 ***
Date: 21/01/2019 WeekOf: 4 WeekNumber: 3 ***
Date: 28/01/2019 WeekOf: 5 WeekNumber: 4 ***
Date: 04/02/2019 WeekOf: 6 WeekNumber: 5 ***
Date: 11/02/2019 WeekOf: 7 WeekNumber: 6 ***
[...]
Date: 06/01/2020 WeekOf: 2 WeekNumber: 1 ***
Date: 07/01/2020 WeekOf: 2 WeekNumber: 1 ***
Date: 13/01/2020 WeekOf: 3 WeekNumber: 2 ***
Date: 14/01/2020 WeekOf: 3 WeekNumber: 2 ***
Date: 20/01/2020 WeekOf: 4 WeekNumber: 3 ***
Date: 21/01/2020 WeekOf: 4 WeekNumber: 3 ***
Date: 27/01/2020 WeekOf: 5 WeekNumber: 4 ***
Date: 28/01/2020 WeekOf: 5 WeekNumber: 4 ***
Date: 03/02/2020 WeekOf: 6 WeekNumber: 5 ***
Date: 04/02/2020 WeekOf: 6 WeekNumber: 5 ***
Date: 10/02/2020 WeekOf: 7 WeekNumber: 6 ***

Как вы можете видеть, в конце 2018 года все начинает идти не так, и после этого ошибочные значения, по-видимому, говорят о том, что «выключено одним»ошибки.Я оставляю вас самим исследовать и устранять эту проблему.

Причина, по которой я сам больше не разбираюсь в этом, заключается в том, что мне кажется, что то, как вы это делаете, беспорядочно и сложно избежать.Практически тривиально добавить поддержку номеров недель в отчеты RBuilder, добавив вычисляемое поле со значением номера недели в набор данных, который подает отчет.Код для этого показан ниже.

В качестве альтернативы, вы можете использовать средство ReportBuilder для добавления функции WeekNumber к языку времени выполнения RAP, которая может принимать любое значение datetime (например, значение из любого поля datetime набора данных)).См., Например, Extending RAP - RAP Pass-Through Functions в p213 Руководства разработчика ReportBuilder, доступного здесь: Руководство разработчика построителя отчетов .То, что описано, - это, пожалуй, самый гибкий метод отображения функции Delphi в отчете, хотя в ISTR есть и другие.

Код (за исключением компонентов RBuilder для простоты)

  type
    TForm1 = class(TForm)
      CDS1: TClientDataSet;
      DataSource1: TDataSource;
      DBGrid1: TDBGrid;
      DBNavigator1: TDBNavigator;
      procedure FormCreate(Sender: TObject);
      procedure CDS1CalcFields(DataSet: TDataSet);
    private
      procedure CreateDataSetFields;
    protected
    public
    end;
  [...]
  implementation
  uses
    DateUtils;

  procedure TForm1.FormCreate(Sender: TObject);
  var
    i : Integer;
  begin
    CreateDataSetFields;  // see below
    CDS1.CreateDataSet;

    //  Insert one row per day for current year
    for i := 1 to 365 do begin
      CDS1.InsertRecord([i, i + Now - DayOfTheYear(Now)]);
    end;
    CDS1.First;
  end;

  procedure TForm1.CDS1CalcFields(DataSet: TDataSet);
  begin
    //  This sets the WeekNo calculated field to the value returned by the DateUtils.WeekOf function
    DataSet.FieldByName('WeekNo').AsInteger := WeekOf(DataSet.FieldByName('Date').AsDateTime);
  end;

  procedure TForm1.CreateDataSetFields;
  var
    Field : TField;
  begin
    Field := TIntegerField.Create(Self);
    Field.FieldKind := fkData;
    Field.FieldName := 'ID';
    Field.Name := 'ID';
    Field.DataSet := CDS1;

    Field := TDateTimeField.Create(Self);
    Field.FieldKind := fkData;
    Field.FieldName := 'Date';
    Field.Name := 'Date';
    Field.DataSet := CDS1;

    Field := TIntegerField.Create(Self);
    Field.FieldKind := fkInternalCalc;
    Field.FieldName := 'WeekNo';
    Field.Name := 'WeekNo';
    Field.DataSet := CDS1;
  end;
...