Перейти к содержанию
Fire Monkey от А до Я

Евгений Корепов

Пользователи
  • Постов

    738
  • Зарегистрирован

  • Посещение

  • Победитель дней

    100

Сообщения, опубликованные Евгений Корепов

  1. Я использую как разминающее мозги хобби, в свободное от работы время. Доход от приложений - приятное дополнение.

    До основного иструмента Delphi пока не дотягивает - нет компилятора под Linux, основную рабочую лошадку 90% предприятий в моей отрасли (ISP). 

  2. При публикации нового приложения я обнаружил исчезновение Google Cloud Messaging из консоли разработчика, теперь вместо него Firebase Cloud Messaging. 

    Как я понимаю возможность push сообщений для Delphi закончилась? Или как то можно прикрутить FCM к проекту на Delphi?

  3. 5 минут назад, krapotkin сказал:

    у меня аналогичная функция содержит еще один важный момент

    иногда ATextObject не имеет установленной ширины, т.е. значение по умолчанию=0!

    тогда нужно либо всегда задавать ее при создании итема, либо передавать ее отдельным параметром

    думаю над созданием наследников TDrawable которые кроме Offset имеют свойства Margins

    OFF:

    по идее, все это уже пройдено в HTML когда настала эпоха DIV-верстки. Мы пытаемся наощупь идти по тем же граблям

    Полностью согласен. Ширину я устанавливаю в ListViewActionsUpdateObjects, и только после этого вызываю функцию.

  4. Почитал, посмотрел все варианты и сделал работу над ошибками. Переделал свой, показанный выше, код. Проверил на Windows и Android, с стилем по умолчанию и другими стилями - работает безупречно. Так же не нуждается в подгонках типа "Height:= Round(t) + 30;".

    Преимущество кода в том что на вход подаем ATextObject : TListItemText, т.е. можно легко использовать для вычисления Text или Detail. 

    function TFormMain.CalculateListItemObjectHeight(ATextObject : TListItemText) : Single;
    begin
      FTextLayout.BeginUpdate;
      try
        FTextLayout.Text:=ATextObject.Text;
        FTextLayout.MaxSize:=TPointF.Create(ATextObject.Width, TTextLayout.MaxLayoutSize.Y);
        FTextLayout.Font.Assign(ATextObject.Font);
        FTextLayout.WordWrap:=ATextObject.WordWrap;
        FTextLayout.Trimming:=ATextObject.Trimming;
        FTextLayout.HorizontalAlign:=ATextObject.TextAlign;
        FTextLayout.VerticalAlign:=ATextObject.TextVertAlign;
      finally
        FTextLayout.EndUpdate;
      end;
      Result:=FTextLayout.Height;
    end;

    Пример использования: у меня Text непредсказуемой длины, вверху ListItem, по всей ширине ListItem. Под Text располагаются слева Image, справа Detail. Текст Detail может быть от пары слов, до нескольких предложений

    procedure TFormMain.ListViewActionsUpdateObjects(const Sender: TObject; const AItem: TListViewItem);
    Var AItemTextHeight, AItemDetailHeight, AItemImageHeight, AItemHeight : Single;
        AListView: TListView;
    begin
      AListView:=TListView(Sender);
      AItem.Objects.TextObject.Width:=AListView.Width - AListView.ItemSpaces.Left - // Вычисляем ширину Text
                                      AListView.ItemSpaces.Right - DefaultScrollBarWidth;
      AItemTextHeight:=CalculateListItemObjectHeight(AItem.Objects.TextObject); // Вычисляем высоту Text
      AItem.Objects.DetailObject.PlaceOffset.Y:=AItemTextHeight; // Устанавливаем смещение для Detail
      AItem.Objects.ImageObject.PlaceOffset.Y:=AItemTextHeight;  // Устанавливаем смещение для Image
      AItemDetailHeight:=CalculateListItemObjectHeight(AItem.Objects.DetailObject);  // Вычисляем высоту Text
    
      AItemImageHeight:=AItem.Objects.ImageObject.Height;
      AItem.Objects.DetailObject.Height:=AItemImageHeight;
      AItemHeight:=AItemTextHeight + Max(AItemDetailHeight, AItemImageHeight); // Вычисляем высоту всего Item
      AItem.Height:=Round(AItemHeight);
    end;

     

  5. 10 часов назад, d7d1cd сказал:

    Все верно. Счетчик сам ничего послать не может. Он только дает ответы на запросы. Теперь понятно, мне нужен компонент TIdTCPClient. Спасибо за помощь.

    Теперь далее... При работе через СОМ порт я отправляю в него запросы в виде массивов байт. Подскажите, как теперь эти же массивы байт отправлять через компонент TIdTCPClient на определенный IP и порт?

    Вот тут расписано как и что https://forums.embarcadero.com/thread.jspa?threadID=211952

  6. Для десктопа уже 15 лет пользуюсь EhLib. Вот там гриды, так гриды. Вот купила бы их эмбаркадера и включила в Дельфи, было бы хорошо. 

    Для осознания их недосягаемой крутизны достаточно посмотреть скриншоты компонентов http://www.ehlib.com/ru/skrinshoty

  7. 13 минуты назад, Brovin Yaroslav сказал:

    Вот пример вычисления высоты итема. ListViewVariableHeightItems2.zip

    Я этот пример показывал на лонче в питере.

    У меня не заработал на XE8 к сожалению код. Но принцип тот же самый, разве что не совсем понятно что за "m"

        // Get layout height
        Result := Round(Layout.Height);
        // Add one em to the height
        Layout.Text := 'm';
        Result := Result + Round(Layout.Height);

     

    Заметил странность, если установить размер шрифта отличный от стандартного, к примеру 11,99 или 12,01, то все начинает нормально работать, текст влазит. Но размер шрифта на экране в полтора раза меньше "стандартного". Не знаю какой стандартный, но точно не 12, больше похож на 14 или 15. Может из за этого и косяк? В недрах FMX расчет ведется с "стандартным" шрифтом 12, а на экране отрисовывается гораздо более крупным.

  8. Подтверждаю что на андроиде этот код косячит, выдает высоту процентов на 10-15 меньше нужной. Осталось разобраться почему в других моих проектах все работает нормально. Может действительно из за большого текста с сплошными строками накапливается ошибка?

    P.S. Заменил текст на кусок из книги, та же самая фигня, обрезает снизу.

  9. А с каким именно прибором вы хотите установить связь? С их роутером VR-006 или с преобразователями интерфейсов VR-001-5 ? Роутер, как я понял настраивается как прозрачный шлюз на преобразователи интерфейсов? Тупо пересылает пакеты, а преобразователи интерфейсов  пересылают обратно? 

    Если все так, то протокол TCP/IP. Компонет TIdTCPClient, а лучше TIdTCPServer. 

    Если вы умеете общаться с счетчиком по USB, то есть вариант проще - найдите дешевый телефон с поддержкой OTG (можно найти за тысячи полторы), китайский OTG переходник с подзарядкой и втыкайте USB преобразователя интерфейса (или счетчика, если есть такие) прямо в смартфон. На телефоне пишите приложение, которое будет работать с счетчиком по USB, а отдавать данные как вам удобнее, от смс и email, до http сервера и push сообщения. Весь этот колхоз будет раза в три дешевле колхоза с приведенного вами сайта. Хотя на сайте штуки прикольные, подумаю о покупке некоторых изделий.

  10. 2 часа назад, Volt- сказал:

    Пример приложил.
    Запускаете его, заходите в командную строку и набираете nslookup google.ru 127.0.0.1 и увидите что в неё (командную строку) не придет ответ от нашего сервера DNS.
    Вопрос в том, как правильно сформировать ответ что бы он дошел до командной строки.

    DNS.rar

    Для начал у вас не правильно работает function ReplaceSpecSymbol, она оставляет спецсимволы в конце строки. Вот накидал правильно работающую:

    function ReplaceSpecSymbol(S: String): String;
    var Count : Integer;
    begin
      Count:=0;
      Result:='';
      while True do
      begin
        Count:=Ord(S.Chars[0]);
        Result:=Result+S.Substring(1,Count)+'.';
        S:=S.Remove(0,Count+1);
        if Ord(S.Chars[0])=0 Then
          break;
      end;
      Result:=Result.TrimRight(['.']);
    end;

    После этого в вашем проекте нормально все ресолвится, но что отсылать в ответ на lookup я не имею представления. Не знаю как заставить IdDNSResolver отдать данные в сыром формате, надо искать и читать документацию.

    Вот код процедуры, но надо разобраться что слать:

    procedure TMainForm.IdUDPServerUDPRead(AThread: TIdUDPListenerThread;
      const AData: TIdBytes; ABinding: TIdSocketHandle);
    var S, Domain: String;
        I : Integer;
        ABuffer : TIdBytes;
    begin
      S:=ReplaceSpecSymbol(BytesToString(AData,12));
      Memo_Log.Lines.Add(S);
      if Pos('in-addr.arpa',S)=0 then
      begin
        IdDNSResolver.Resolve(S);
        for I := 0 to IdDNSResolver.QueryResult.Count-1 do
          if IdDNSResolver.QueryResult[I].RecType = qtA then
          begin
            S:=TARecord(IdDNSResolver.QueryResult[I]).IPAddress;
            Memo_Log.Lines.Add('IdDNSResolver: '+S);
          end;
    // Вот дальше не знаю что именно пересылать
    //   ABuffer:=DNSHeader.GenerateBinaryHeader;
    //   ABuffer:=IdDNSResolver.PlainTextResult;
    //   ABuffer:=ABuffer+IdDNSResolver.InternalQuery;
    //   ABuffer:=IdDNSResolver.InternalQuery;
        ABinding.Send(ABuffer);
      end;

    Вы можете поразбираться с помощью простейшего DNS прокси, по крайней мере будете видеть что идет в запросе и что в ответе. Код ниже, только пропишите глобальную переменную ALocalPort : Integer, для запоминания забинденного порта на 127.0.0.1

    procedure TMainForm.IdUDPServerUDPRead(AThread: TIdUDPListenerThread;
      const AData: TIdBytes; ABinding: TIdSocketHandle);
    Const ExternalDNSHost = '8.8.8.8';
          LocalHost = '127.0.0.1';
    begin
      if ABinding.PeerIP.Equals(LocalHost) then // если запрос с локального, пересылаем на внешний dns
      begin
        ALocalPort:=ABinding.PeerPort;
        IdUDPServer.SendBuffer(ExternalDNSHost,53,AData);
      end;
      if ABinding.PeerIP.Equals(ExternalDNSHost) then // если с внешнего, пересылаем на локальный
        IdUDPServer.SendBuffer(LocalHost,ALocalPort,AData);
      exit;
    end;

    в коммандной строке используйте "nslookup -retry=1 -timeout=30 google.ru 127.0.0.1 " (один повтор запроса, чтоб не засирать отладку и таймаут сколько нужно секунд, что не отвалился запрос пока будете разбираться)

  11. 1 час назад, HyperZen сказал:

    Можно проект целиком? Berlin ругается на свойство Parent... хм...

    Так весь проект целиков и выложил. В форме только апперанс у ListView выставил в Custom. А куда дели Parent? Это важное свойство хранящее родителя. Может как то переименовали?

     

    P.S. Вы можете AListView заменить за свой конкретный ListView, доступ через парент сделал для красоты и универсальности, если несколько ListView в проекте...

  12. Делать было нечего, дело было вечером... Вот попробовал, свою процедуру чуть изменил, добавил константы ширины скроллбара из ListView

    unit Unit1;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
      FMX.ListView.Types, FMX.ListView, FMX.TextLayout, System.StrUtils;
    
    const
    {$IFDEF IOS}
      DefaultScrollBarWidth = 7;
    {$ELSE}
    {$IFDEF MACOS}
      DefaultScrollBarWidth = 7;
    {$ENDIF}
    {$ENDIF}
    {$IFDEF MSWINDOWS}
      DefaultScrollBarWidth = 16;
    {$ENDIF}
    {$IFDEF ANDROID}
      DefaultScrollBarWidth = 7;
    {$ENDIF}
    
    type
      TForm1 = class(TForm)
        ListView1: TListView;
        procedure FormCreate(Sender: TObject);
        procedure ListView1UpdateObjects(const Sender: TObject;
          const AItem: TListViewItem);
      private
        { Private declarations }
      public
        { Public declarations }
        FTextLayout : TTextLayout;
        procedure CalculateTListViewItemHeight(Sender: TObject);
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.fmx}
    
    procedure TForm1.FormCreate(Sender: TObject);
    Var AText : String;
    begin
      FTextLayout := TTextLayoutManager.DefaultTextLayout.Create;
      AText:=
                'FMX.Dialogs.MessageDlg - RAD Studio API Documentation ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                'docwiki.embarcadero.com/.../en/FMX.Dialogs.MessageDlg ' +
                'Перевести эту страницу ' +
                '11 февр. 2016 г. - function MessageDlg(const AMessage: string; const ADialogType: TMsgDlgType; const ... extern DELPHI_PACKAGE int __fastcall MessageDlg _DEPRECATED_ATTRIBUTE1("Use FMX.DialogService methods") ' +            'Перевести эту страницу ' +
                '11 февр. 2016 г. - function MessageDlg(const AMessage: string; const ADialogType: TMsgDlgType; const ... extern DELPHI_PACKAGE int __fastcall MessageDlg _DEPRECATED_ATTRIBUTE1("Use FMX.DialogService methods") ' +            'Перевести эту страницу ' +
                '11 февр. 2016 г. - function MessageDlg(const AMessage: string; const ADialogType: TMsgDlgType; const ... extern DELPHI_PACKAGE int __fastcall MessageDlg _DEPRECATED_ATTRIBUTE1("Use FMX.DialogService methods") ' +            'Перевести эту страницу ' +
                '11 февр. 2016 г. - function MessageDlg(const AMessage: string; const ADialogType: TMsgDlgType; const ... extern DELPHI_PACKAGE int __fastcall MessageDlg _DEPRECATED_ATTRIBUTE1("Use FMX.DialogService methods") ' +
                '(const System::UnicodeString AMessage, const ... Warning: MessageDlg is deprecated. ***';
    
      with ListView1.Items.Add do
        Text:=AText;
      with ListView1.Items.Add do
        Text:= ReverseString(AText);
      with ListView1.Items.Add do
        Text:= AText;
    end;
    
    procedure TForm1.ListView1UpdateObjects(const Sender: TObject;
      const AItem: TListViewItem);
    begin
      CalculateTListViewItemHeight(AItem);
    end;
    
    procedure TForm1.CalculateTListViewItemHeight(Sender: TObject);
    var AListItem: TListViewItem;
        AListView: TListView;
    begin
      if (Sender is TListViewItem) then
      begin
        AListItem := Sender as TListViewItem;
        if (AListItem.Parent is TListView) then
          AListView:=AListItem.Parent as TListView
        Else
          Exit;
        FTextLayout.BeginUpdate;
        try
          FTextLayout.Text:=AListItem.Text;
          FTextLayout.MaxSize:=TPointF.Create(AListView.Width -
            AListView.ItemSpaces.Left - AListView.ItemSpaces.Right - DefaultScrollBarWidth, 1000);
          FTextLayout.Font:=AListView.ItemAppearanceObjects.ItemObjects.Text.Font;
          FTextLayout.WordWrap:=AListView.ItemAppearanceObjects.ItemObjects.Text.WordWrap;
          FTextLayout.Trimming:=AListView.ItemAppearanceObjects.ItemObjects.Text.Trimming;
          FTextLayout.HorizontalAlign:=AListView.ItemAppearanceObjects.ItemObjects.Text.TextAlign;
          FTextLayout.VerticalAlign:=AListView.ItemAppearanceObjects.ItemObjects.Text.TextVertAlign;
        finally
          FTextLayout.EndUpdate;
        end;
        AListItem.Height:=Round(FTextLayout.Height + AListView.ItemSpaces.Top + AListView.ItemSpaces.Bottom);
      end;
    end;
    
    end.

     

    ListView01.png

    ListView02.png

  13. Возможно вы не учитывате  свойство Trimming, а судя по скринам оно у вас включено.

    Я пользуюсь вот таким кодом, работает идеально на всех платформах.

    procedure TFormMain.CalculateTListViewItemHeight(Sender: TObject);
    var AListItem: TListViewItem;
        AListView: TListView;
    begin
      if (Sender is TListViewItem) then
      begin
        AListItem := Sender as TListViewItem;
        if (AListItem.Parent is TListView) then
          AListView:=AListItem.Parent as TListView
        Else
          Exit;
        FTextLayout.BeginUpdate;
        try
          FTextLayout.Text:=AListItem.Text;
          FTextLayout.MaxSize:=TPointF.Create(AListView.Width -
            AListView.ItemSpaces.Left - AListView.ItemSpaces.Right, 1000);
          FTextLayout.Font:=AListView.ItemAppearanceObjects.ItemObjects.Text.Font;
          FTextLayout.WordWrap:=AListView.ItemAppearanceObjects.ItemObjects.Text.WordWrap;
          FTextLayout.Trimming:=AListView.ItemAppearanceObjects.ItemObjects.Text.Trimming;
          FTextLayout.HorizontalAlign:=AListView.ItemAppearanceObjects.ItemObjects.Text.TextAlign;
          FTextLayout.VerticalAlign:=AListView.ItemAppearanceObjects.ItemObjects.Text.TextVertAlign;
        finally
          FTextLayout.EndUpdate;
        end;
        AListItem.Height:=Round(FTextLayout.Height + AListView.ItemSpaces.Top + AListView.ItemSpaces.Bottom);
      end;
    end;

    При создании формы не забудьте FTextLayout := TTextLayoutManager.DefaultTextLayout.Create;

    Применять вот так:

    procedure TFormMain.ListViewNewsUpdateObjects(const Sender: TObject;
      const AItem: TListViewItem);
    begin
      CalculateTListViewItemHeight(AItem);
    end;

     

  14. Я бы логику работы приложения сделал по другому - не хранил ничего на телефоне пользователя, ибо люди врут и оставлять данные учета им на откуп не очень хорошо. Удалить все, пользователь может просто очистив данные приложения в настройках и обвинить в этом разработчика. И самое обидно вы не докажете обратное.

    Проще и надежнее было бы отправлять на сервер запрос по http с хешем логина/пароля/id маячка(есть такое у них?). Приложение сильно бы упростилось - человек подошел к маячку, запустил приложение, приложение идентифицирует маячек, замешивает хеш и отправляет на сервер, сервер отвечает подтверждением приема данных и приложение демонстрирует подтверждение пользователю.

    Привязка приложения к работнику, как я понял, не осуществляется? Т.е. только по письму полученному от работника? Кто ему мешает отослать поддельное письмо с компьютера? Благо исходящие письма с примерами он может посмотреть в яндексе.

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

    Отсутствие сервера конечно резко снижает эффективность приложение. А так можно было бы натолкать маяков в туалеты, курилки, близлежащие пабы и контролировать рабов сотрудников на полную катушку ;-)

  15. Забавно, мне понравилось! Поставил пятёрку.

    Глюк заметил - на странице меню движущийся фон смотрится не обычно, но в какой то момент дергается и это пугает ;-) 

     

     

  16. Все правильно получаете, кодировка тут не при чем. Согласно спецификации "DNS Packet Structure", вы получаете не строку, а пакет который нужно разобрать. К примеру если запрос будет "nslookup www.google.ru 127.0.0.1", то там где вы получаете имя хоста будет строка "''#3'www'#6'google'#2'ru'#0#0#1#0#1".

    Парсинг простейший:

    #3 - означает что далее идут 3 символа хоста
    'www' - вот ожидаемые 3 символа
    #6 - далее идут еще 6 символов хоста
    'google' - ага, вот они
    #2 - ну и еще 2 символа
    'ru' - ура, они здесь
    #0 - конец имени хоста, складываем в кучу, перемежая точками и получаем www.google.ru
    #0 - дальше у нас служебная информация...
    #1
    #0
    #1

    Вот как то так.

    P.S. А зачем вам на таком низком уровне работать? Может использовать IdDNSServer : TIdDNSServer ?

    P.P.S. Правильно Memo1.Lines.Add(BytesToString(AData,12)); //(12, а не 13)

  17. Возможно ли в ListView сделать кнопку с текстом повернутым на 90 градусов, т.е. вертикальная надпись на кнопке?

    В стиле, в listviewstyle есть button (TStyleObject), у button есть RotationAngle, но установка в 90 никакого результата не дает. Может есть другой путь?

  18. 3 часа назад, sviat сказал:

    Что вы думаете по этому поводу?

    Эта фраза здесь звучала много раз, но повторюсь "Никогда, ни в коем случае, ни при каких обстоятельствах, не работайте с объектами основной формы (основного потока) из другого потока", для этого умные люди специально создали потокобезопасные способы - синхронизация, очереди, события и т.д. Даже если вам кажется что ваш код работает, то это случайность, на пятый или сотый запуск все у вас упадет, или упадет сразу на другой версии андроида или другом производителе железа.

    Такое ощущение что вы не слышите, или не читаете ответы на заданные вами вопросы, и каждое новое сообщение от вас звучит примерно так "А вот если я на эти грабли не наступлю, а с разбега прыгну, что будет?" ;-) 

    Надеюсь я не слишком эмоционально высказался? Не хотел никого обидеть.

  19. Это настраивается в самом андроиде, в настройках клавиатуры. К сожалению в Самсунге не только USB разъем вверх ногами, но и реакция на подключение OTG устройств не адекватная. Но настроить можно, точно не подскажу как, давно было, но после двух часов тыкания в настройках мне это удалось.

    JSON

    Вот пример кода для вашего случая

    Var Source : String;
        JSON : TJSONObject;
        JSONValue : TJSONValue;
        JSONArray : TJSONArray;
        I : Integer;
    begin
      Source:='{"data":["val1", "val2", "val3"]}';
      JSON:=TJSONObject(TJSONObject.ParseJSONValue(Source));
      if Not Assigned(JSON) then
        Exit;
      if JSON.TryGetValue('data',JSONValue) then
        if TJSONObject(JSONValue).ClassName.Equals('TJSONArray') then
        begin
          JSONArray:=TJSONArray(JSONValue);
          if Assigned(JSONArray) then
            for I := 0 to JSONArray.Count-1 do
              Memo.Lines.Add(JSONArray.Items[I].Value);
        end;
    end;

    Для проверок неудачного парсинга есть такие инструменты как "if Assigned(JSON) then" и "JSON.TryGetValue('data',JSONValue)"

  20. 20 минут назад, master webs сказал:

    а что значит  nn ?  yyyy-mm-dd hh:nn:ss

    разве не yyyy-mm-dd hh:mm:ss должно быть

     

    DateTime.Now.ToString("yyyy-mm-dd hh:mm:ss ") 

    mm

     - Displays the month as a number with a leading zero (01-12). If the mm specifier immediately follows an h or hh specifier, the minute rather than the month is displayed.

    nn - Displays the minute with a leading zero (00-59).

    http://docwiki.embarcadero.com/Libraries/XE5/en/System.SysUtils.FormatDateTime

  21. У вас ошибка в коде, неверно SDT:=DateTimeToStr(Now()), а вот так верно SDT:=DateTimeToStr(ADateTime,FormatSettings). Вы не использовали выставленные в FormatSettings значения.

    Но есть и другие варианты хранения:

    var
      IniFile: TIniFile;
      SDT: string;
      FS: TFormatSettings;
      ADateTime : TDateTime;
      ADateTimeUnix : Int64;
    begin
      IniFile := TIniFIle.Create(System.IOUtils.TPath.Combine(
                                   System.IOUtils.TPath.GetDocumentsPath, 'D.ini'));
      try
        ADateTime:=Now();
    // Ваш вариант
        FormatSettings.ShortDateFormat:='yyyy-mm-dd';
        FormatSettings.DateSeparator := '-';
        FormatSettings.LongTimeFormat := 'hh:nn:ss';
        FormatSettings.TimeSeparator := ':';
        SDT:=DateTimeToStr(ADateTime,FormatSettings); // Обратите внимание на второй параметр
    
    // Вариант с использованием настроек устройства
        FS:=TFormatSettings.Create; // В FS теперь настройки локали устройства
        SDT:=DateTimeToStr(ADateTime,FS);
    
    // Вариант с использованием ISO8601
        SDT:=DateToISO8601(ADateTime);
    
    //Вариант хранения в Unix формате - наиболее надежный в плане обратимости
        ADateTimeUnix:=DateTimeToUnix(ADateTime);
        SDT:=ADateTimeUnix.ToString;
    
        IniFile.WriteString('SERVER','LastOnline',SDT);
      finally
        IniFile.free;
      end;
    end;

    Если не нужна человекочитаемость ini файла, то лучше хранить в UNIX формате

×
×
  • Создать...