Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 09.03.2018, 17:25   #1
exyl
Пользователь
 
Регистрация: 15.11.2014
Сообщений: 75
По умолчанию Перевод строки в TDateTime

Добрый день.
Раньше не вникал в нюансы перевода вышеуказанных типов, а тут понадобилось переводить даты вида
Цитата:
4 March 2018 13:10
в TDateTime и появились вопросы (естественно после ловли ошибок о том, что такого вида строка не является верной датой / временем).

Итак, есть замечательная функция StrToDateTime.
Есть компухтер с русской локализацией Виндовз.

Уже проблемка. Месяц-то на английском. Ок, сделал вариант "в лоб":
Код:
FormatSettings.ShortDateFormat:='d mmmm yyyy';
  //FormatSettings.DateSeparator:=' ';
  FormatSettings.LongTimeFormat:='hh:nn';
  //FormatSettings.TimeSeparator:=':';
  FormatSettings.LongMonthNames[1]:='January';
  {...}
  FormatSettings.LongMonthNames[12]:='December';
Хотя лично меня это не устраивает, т.к. естественно, что после такого финта лог в программе (где указывается время той или иной записи) начинает писАться со всеми вытекающими последствиями и в непривычной форме. Менять значения обратно перед тем, как вывести очередную запись в лог, меня напрягает (тем более придется опять ее менять перед очередным переводом строки в дату). Мне кажется что можно это разрулить проще. Вопрос как?

Далее все равно ловлю ошибку перевода. Я догадываюсь, что строки:
Код:
FormatSettings.ShortDateFormat:='d mmmm yyyy';
FormatSettings.LongTimeFormat:='hh:nn';
влияют только на обратную операцию (DateTimeToStr), но не на ту, что нужна мне. Так что второй вопрос: как сделать, чтоб строка все-таки переводилась в TDateTime так, как нужно мне?

И, наконец, третий (возможно главный) вопрос: а как это правильнее решить задачу перевода подобных строк в TDateTime? Ну то есть и айфоном можно гвозди в стену вбивать, но молотком вроде правильнее.
exyl вне форума Ответить с цитированием
Старый 09.03.2018, 18:47   #2
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Цитата:
Менять значения обратно перед тем, как вывести очередную запись в лог, меня напрягает
А его и не надо менять, получай локальную FS, можно и с помощью GetLocaleFormatSettings, и настраивай как хочешь. В StrToDateTime подставляй свой FS. Но не поможет, как минимум день, месяц и год через пробел StrToDateTime не съест, даже если месяц цифровой. имхо. Свою процедуру конвертации пиши
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию

Последний раз редактировалось Аватар; 09.03.2018 в 18:49.
Аватар вне форума Ответить с цитированием
Старый 09.03.2018, 20:00   #3
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

Вот накидал процедуру конвертации из вашей строки

Код:
function MyStrToDateTime(str: string): TDateTime;
const
  Mes: array [0..12] of string = (
    (''),
    ('January'),//Январь
    ('February'),//Февраль
    ('March'),//Март
    ('April'),//Апрель
    ('May'),//Май
    ('June'),//Июнь
    ('July'),//Июль
    ('August'),//Август
    ('September'),//Сетябрь
    ('October'),//Октябрь
    ('November'),//Ноябрь
    ('December'));//Декабрь
var
i: Integer;
begin
Result:='';
for i := 1 to Length(Mes)-1 do
if Pos(AnsiLowerCase(Mes[i]), AnsiLowerCase(str)) <> 0 then
begin
Str:= StringReplace(Str, Mes[i], inttostr(i), [rfReplaceAll, rfIgnoreCase]);
Str:= StringReplace(Str, #32, '.', [rfReplaceAll, rfIgnoreCase]);
Result:= StrToDateTime(formatDateTime('dd.mm.yy hh:mm', StrToDateTime(str)));
break;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(DateTimeToStr(MyStrToDateTime('4 March 2018 13:10')));
end;
а вот обратная конвертация
Код:
function MyDateTimeToStr(DatesTimes: TDateTime): string;
var
MM: Integer;
 M: string;
begin
MM:= strtoint(formatDateTime('mm',DatesTimes));
    case MM of
    1: M := 'January';//Январь
    2: M := 'February';//Февраль
    3: M := 'March';//Март
    4: M := 'April';//Апрель
    5: M := 'May';//Май
    6: M := 'June';//Июнь
    7: M := 'July';//Июль
    8: M := 'August';//Август
    9: M := 'September';//Сетябрь
   10: M := 'October';//Октябрь
   11: M := 'November';//Ноябрь
   12: M := 'December';//Декабрь
 end;

Result:= format('%s %s %s %s', [formatDateTime('dd',DatesTimes), M,
formatDateTime('YYYY',DatesTimes), formatDateTime('hh:mm:ss',DatesTimes)]);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
showmessage(MyDateTimeToStr(now));
end;
Можно сделать код компактным не повторять месяцы в процедурах, а сделать общий массив месяцев, это я для примера сделал не критикуйте меня строго)

Последний раз редактировалось Aliens_wolfs; 09.03.2018 в 20:19.
Aliens_wolfs вне форума Ответить с цитированием
Старый 09.03.2018, 20:37   #4
Avazart
Форумчанин
 
Аватар для Avazart
 
Регистрация: 13.08.2011
Сообщений: 184
По умолчанию

В новых средах разработки типа RAD XE3 и старше, можно создавать отдельный экземпляр TFormatSettings настраивать и передавать последним параметром в StrToDateTime() и др. ф-ции преобразования.

Т.е нет необходимости менять глобальные настройки.

http://docwiki.embarcadero.com/Libra....StrToDateTime
http://docwiki.embarcadero.com/Libra...FormatSettings

Последний раз редактировалось Avazart; 09.03.2018 в 20:41.
Avazart вне форума Ответить с цитированием
Старый 10.03.2018, 01:53   #5
northener
ПШП
Участник клуба
 
Регистрация: 15.07.2013
Сообщений: 1,869
По умолчанию

Цитата:
Сообщение от Avazart Посмотреть сообщение
Т.е нет необходимости менять глобальные настройки.
Про глобальные настройки пока никто не упоминал, кроме вас.
Цитата:
Сообщение от Аватар Посмотреть сообщение
А его и не надо менять, получай локальную FS
northener вне форума Ответить с цитированием
Старый 10.03.2018, 02:06   #6
northener
ПШП
Участник клуба
 
Регистрация: 15.07.2013
Сообщений: 1,869
По умолчанию

Собственно и в гораздо более ранних версиях Дельфи можно было менять "локальные" настройки. Ну разве что это было чуть сложнее.
northener вне форума Ответить с цитированием
Старый 10.03.2018, 08:24   #7
exyl
Пользователь
 
Регистрация: 15.11.2014
Сообщений: 75
По умолчанию

Цитата:
Сообщение от northener Посмотреть сообщение
Про глобальные настройки пока никто не упоминал, кроме вас.
Я и упоминал, только достаточно завуалированно, т.к. не разобрался с тем, что:
Цитата:
Сообщение от Avazart Посмотреть сообщение
...можно создавать отдельный экземпляр TFormatSettings настраивать и передавать последним параметром в StrToDateTime() и др. ф-ции преобразования.
Вообще большое спасибо за идеи, советы и даже готовые примеры.
Aliens_wolfs, благодарю.




Добавлено позже

Aliens_wolfs, выдает ошибку: '4.Март.2018 13:10' не является верным датой / временем. Отсюда вопрос, что ему не нравится?

1. Не понял, зачем так: array [0..12] of string = ((''), когда можно указать [1..12] и не ставить пустое значение.

2. Поменял formatDateTime('dd.mm.yy hh:mm', StrToDateTime(str)) на FormatDateTime('d.mmmm.yyyy hh:nn', StrToDateTime(Str)), т.к. в примере не сокращенные имена месяцев, а полные, а вот дни без первого нуля и год не двумя цифрами обозначается, а всеми четыремя.

3. Спасибо, но обратная функция не нужна (перевод из TDateTime в подобный формат), т.к. вся дальнейшая работа работа с этими данными будет уже в родном для дельфина формате, а выводится обратно будет с учетом региональных настроек компьютера конкретного пользователя.




Еще чуть подумал

Вот этого делать не надо:
Код:
Result:= StrToDateTime(formatDateTime('dd.mm.yy hh:mm', StrToDateTime(str)));
Достаточно так:
Код:
Result:= StrToDateTime(Str);
Проблема решена, спасибо за оказанное внимание, всем мир )

Последний раз редактировалось exyl; 10.03.2018 в 10:02.
exyl вне форума Ответить с цитированием
Старый 10.03.2018, 20:12   #8
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

Цитата:
выдает ошибку: '4.Март.2018 13:10' не является верным датой / временем. Отсюда вопрос, что ему не нравится?/
Я делал под это
Цитата:
4 March 2018 13:10
что в первом посту вы написали.
Да и вообще я делал для примера, что бы вы сами для себя сделали нужное вам преобразование
Цитата:
Проблема решена, спасибо за оказанное внимание, всем мир )
Думаю что вы уже разобрались и сделали то что нужно.
Цитата:
Не понял, зачем так: array [0..12] of string = ((''), когда можно указать [1..12] и не ставить пустое значение.
Некоторые считают, что работать с массивом, объявленном с индекса 1 удобнее.

Тем не менее, нужно сказать несколько слов о работе с массивами, индексация которых начинается с нуля. Во-первых, очень часто в API-интерфейсах операционных систем Windows и Linux, а также Delphi-библиотеках VCL и CLX предполагается, что первый элемент в массиве имеет индекс 0. Кроме того, в языках программирования С, С++ и Java индексация всех массивов обязательно начинается с 0. Поскольку и Windows, и Linux реализованы на С (или С++), при вызове API-функций считается, что индекс первого элемента массива равен 0.

Во-вторых, индексация динамических массивов начинается с 0. Поэтому, если вы хотите использовать этот очень гибкий тип, начинайте нумерацию элементов массивов с 0.

Далее подробно почитайте здесь
https://it.wikireading.ru/34874

Тогда уж сделайте так
Код:
function MyStrToDateTime(str: string): TDateTime;
const
  Mes: array [0..11] of string = (
    ('January'),//Январь
    ('February'),//Февраль
    ('March'),//Март
    ('April'),//Апрель
    ('May'),//Май
    ('June'),//Июнь
    ('July'),//Июль
    ('August'),//Август
    ('September'),//Сетябрь
    ('October'),//Октябрь
    ('November'),//Ноябрь
    ('December'));//Декабрь
var
i: Integer;
begin
Result:='';
for i:= 0 to Length(Mes) - 1 do
if Pos(AnsiLowerCase(Mes[i]), AnsiLowerCase(str)) <> 0 then
begin
Str:= StringReplace(Str, Mes[i], inttostr(i + 1), [rfReplaceAll, rfIgnoreCase]);

Последний раз редактировалось Aliens_wolfs; 10.03.2018 в 23:03.
Aliens_wolfs вне форума Ответить с цитированием
Старый 11.03.2018, 10:31   #9
exyl
Пользователь
 
Регистрация: 15.11.2014
Сообщений: 75
По умолчанию

Цитата:
Сообщение от Aliens_wolfs Посмотреть сообщение
Я делал под это что в первом посту вы написали.
А самому код попробовать? Неужели другая ошибка вылезет? Ладно, объясню. Получается она из-за странной операции:
Цитата:
Result:= StrToDateTime(formatDateTime('dd.mm.yy hh:mm', StrToDateTime(str)));
Которая два раза пытается преобразовать строку в TDateTime, но достаточно одного, потому что точки уже поставлены, месяц заменен и Делфи сам способен перевести строку в нужный формат. В результате первый раз он это успешно делает, а потом пытается еще раз это сделать и в результате выдает именно такое сообщение:
Цитата:
4.Март.2018 13:10
Не смотря на то, что на входе я подавал:
Цитата:
4 March 2018 13:10


Далее,
Цитата:
Сообщение от Aliens_wolfs Посмотреть сообщение
Некоторые считают, что работать с массивом, объявленном с индекса 1 удобнее.
Да, мне, например, но в данном конкретном случае, потому что месяцам сопоставлена их позиция в году, а год начинается с первого месяца, а не с нулевого.

Цитата:
Сообщение от Aliens_wolfs Посмотреть сообщение
Тем не менее, нужно сказать несколько слов о работе с массивами, индексация которых начинается с нуля.
А вот тут не понял, зачем сказать? Какая связь? Есть подозрение, что я не смогу навести курсор на переменную и увидеть, что за массив используется и какая там индексация?
Кроме того, я не использую ни Win API, ни C-подобные языки, ни Яву, ни фортран, ни машинные коды, только Дельфи.

Цитата:
Сообщение от Aliens_wolfs Посмотреть сообщение
Во-вторых, индексация динамических массивов начинается с 0. Поэтому, если вы хотите использовать этот очень гибкий тип, начинайте нумерацию элементов массивов с 0.
А я вот помню, что был проект, когда прогу написанную на какой-то древней версии дельфей (вроде второй или четвертой) переписывали под динамические массивы, т.к. там брались огромные запасы под нужды статических и в результате она выжирала огромное количество памяти. И знаете как решили проблему индексации? Написали класс, где реализован динамический массив, где индексация начиналась с 1.

Так что не нужно быть столь категоричным в своих высказываниях.
exyl вне форума Ответить с цитированием
Старый 11.03.2018, 12:41   #10
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

Цитата:
Написали класс, где реализован динамический массив, где индексация начиналась с 1
Нужно всегда стараться придерживаться правильных правил в программировании.
С опытом вы поймете, когда начнутся проблемы с индексацией в других проектах и ошибки, но для данного примера согласен что можно оставить так ка вы с 1 но все же привыкайте к правильности.

Часть статьи по поводу индексации массивов я взял от сюда https://it.wikireading.ru/34874

Цитата:
Result:= StrToDateTime(formatDateTime('dd.mm .yy hh:mm', StrToDateTime(str)));
То что вы поменяли эту строчку на Result:= StrToDateTime(Str) ведь что то вы же должны были исправить под ваши нужды, так как я делал для определенного вида результата в самой процедуре и опирался на эту проблемную строку 4 March 2018 13:10 из вашего поста #1
Да и вообще хотел вам показать для примера как сделать свой алгоритм конвертации даты)
Вот сделал более универсальный

Код:
function MyStrToDateTime(str: string): TDateTime;
type
   TMonth  = record
    engMonth: String;
    ruMonth: String;
    end;
const
  Month: array [0..11] of TMonth = (
  (engMonth: 'January'; ruMonth: 'Январь'),
  (engMonth: 'February'; ruMonth: 'Февраль'),
  (engMonth: 'March'; ruMonth: 'Март'),
  (engMonth: 'April'; ruMonth: 'Апрель'),
  (engMonth: 'May'; ruMonth: 'Май'),
  (engMonth: 'June'; ruMonth: 'Июнь'),
  (engMonth: 'July'; ruMonth: 'Июль'),
  (engMonth: 'August'; ruMonth: 'Август'),
  (engMonth: 'September'; ruMonth: 'Сетябрь'),
  (engMonth: 'October'; ruMonth: 'Октябрь'),
  (engMonth: 'November'; ruMonth: 'Ноябрь'),
  (engMonth: 'December'; ruMonth: 'Декабрь'));
var
i: Integer;
SMonth: String;
begin
SMonth:= '';
for i:= 0 to Length(Month) - 1 do
begin
if (Pos(AnsiLowerCase(Month[i].engMonth), AnsiLowerCase(str)) <> 0) then
SMonth:= Month[i].engMonth;
if (Pos(AnsiLowerCase(Month[i].ruMonth), AnsiLowerCase(str)) <> 0)then
SMonth:= Month[i].ruMonth;
if SMonth <> '' then
begin
Str:= StringReplace(Str, SMonth, inttostr(i + 1), [rfReplaceAll, rfIgnoreCase]);
Str:= StringReplace(Str, #32, '.', [rfReplaceAll, rfIgnoreCase]);
Result:= StrToDateTime(Str);
break;
end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Showmessage(DateTimetostr(MyStrToDateTime('4 March 2018 13:10')));
Showmessage(DateTimetostr(MyStrToDateTime('4.March.2018 13:10')));
Showmessage(DateTimetostr(MyStrToDateTime('4.Март.2018 13:10')));
Showmessage(DateTimetostr(MyStrToDateTime('4 Март 2018 13:10')));
end;

Последний раз редактировалось Aliens_wolfs; 11.03.2018 в 13:22.
Aliens_wolfs вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
ввод строки в СС16 и перевод в СС10 и вывод введеной строки после перевода с СС10 alexey961 Помощь студентам 1 09.10.2013 10:00
Перевод строки Waqq Помощь студентам 0 25.09.2011 18:02
Перевод строки BaSoff Общие вопросы C/C++ 1 12.04.2010 18:58
string to TDateTime NieL Общие вопросы Delphi 2 30.03.2009 18:43
Variant в TDateTime SoLL Общие вопросы Delphi 2 13.02.2009 11:49