У меня есть функция, возвращающая json
, содержащая timestamptz
. Когда я выполняю это в консоли Datagrip, я получаю json
типа
{..., "time_created":"2024-05-15T11:14:23.384266+00:00",...}.
Но когда я выполняю ту же функцию на C# с помощью ExecuteScalar
, возвращаемое значение похоже на
{..., "time_created":"2024-05-15T09:14:23.384266+02:00",...}
Он ведет себя одинаково, даже если функция возвращает json
, предварительно приведенное к text
. Обратите внимание, что временные метки преобразуются в часовой пояс сервера и смещение времени сервера, если оно добавлено. Это невозможно отобразить в элементах управления Quasar без изменений.
Что заставляет ExecuteScalar
выполнять преобразование, даже если text
возвращается из функции?
Если функция возвращает таблицу, содержащую timestamptz
, значения возвращаются правильно, а сериализация создает значения в форме, например. "time_created":"2024-05-15T11:14:23.384266Z"
, который можно нормально отобразить при вводе Quasar.
Есть ли способ заставить все, что конвертирует строки json во время чтения с помощью ExecuteScalar
, делать это с помощью ...Z
?
Функция
create function data_survey_r("Key" integer) returns text language plpgsql as $$
BEGIN
RETURN (SELECT ROW_TO_JSON(t)::text FROM data.survey t WHERE t.id = "Key");
END $$;
или
create function data_survey_r("Key" integer) returns json language plpgsql as $$
BEGIN
RETURN (SELECT ROW_TO_JSON(t) FROM data.survey t WHERE t.id = "Key");
END $$;
Он вызывается из C# как
ret = (string) await connection.ExecuteScalarAsync("data_survey_r", new { Key = key }, commandType: CommandType.StoredProcedure);
Обновлено: (Текст из этого комментария ):
create function test_now () returns json as $$
BEGIN
return (SELECT json_build_object('now', now()));
END $$
language plpgsql
С#
NpgsqlConnection con = new NpgsqlConnection(_options.ConnectionString);
con.Open();
var ret = await con.ExecuteScalarAsync("test_now");
Результат: {"now" : "2024-06-22T12:53:30.515689+00:00"}
в консоли, {"now" : "2024-06-22T14:53:40.079963+02:00"}
поэтому значения времени точно такие же, но когда вы используете pgadmin4, вы получаете значение UTC (+00:00), а в противном случае вы получаете значение, скорректированное для вашего часового пояса (+02:00). Разве это не автоматическая настройка сеанса в pgadmin4?
Добавил фрагмент кода в вопрос. pgAdmin использовался как «общее» имя, на самом деле я использую консоль DataGrip. Меня озадачивает то, что какое-то промежуточное программное обеспечение изменяет возвращаемый текст в ExecuteScalar
.
@VedranMornar, и вы создали простую тестовую функцию «DateTime.Now» (RETURN NOW())… которая ведет себя так, как вы хотите, при одинаковом вызове для двух сценариев из вашего примера?
pg: create function test_now () returns json as $$ BEGIN return (SELECT json_build_object('now', now())); END $$ language plpgsql
C# NpgsqlConnection con = new NpgsqlConnection(_options.ConnectionString); con.Open(); var ret = await con.ExecuteScalarAsync("test_now");
Результат: {"now" : "2024-06-22T12:53:30.515689+00:00"}
в консоли, {"now" : "2024-06-22T14:53:40.079963+02:00"}
в C#
Я предполагаю, что ваш компьютер локализован в часовом поясе UCT+2. По умолчанию, когда вы делаете это в .NET, он форматирует его в текущем часовом поясе. Чтобы записать дату в формате ISO 8601, вы можете использовать клавишу формата «o»: DateTime.Now.ToString()
: Learn.microsoft.com/fr-fr/dotnet/standard/base-types/…
@Bisjob postgres выполняет форматирование часового пояса в json, смотрите мой ответ.
Пожалуйста, отредактируйте заголовок вопроса, чтобы описать реальную проблему, с которой вы столкнулись, или вопрос, который вы задаете. Ваш нынешний заголовок — это прежде всего бесполезное повторение тегов. Ваш заголовок должен быть достаточно ясным и информативным, чтобы будущий пользователь сайта, просматривая список результатов поиска, мог понять, что он содержит. Твой титул сейчас просто бесполезный шум.
Измененный. Надеюсь, теперь это менее бесполезно.
Думаю, нам удалось добраться до виновника в обсуждении комментариев с помощью диагностической функции test_now
(определение и результаты которой вы позже добавили в вопрос)
Он демонстрирует, что postgres отвечает за генерацию форматирования timespantz
в json на основе часового пояса, установленного для вашего соединения/сеанса (который, предположительно, для Npgsql — это часовой пояс вашего компьютера +02:00, возможно, установленный на +00:00 UTC). для DataGrip)
Эту логику postgres можно быстро протестировать с помощью psql:
select test_now();
test_now
---------------------------------------------
{"now" : "2024-06-22T19:17:45.53308+03:00"}
(1 row)
SET TIME ZONE 'UTC';
SET
select test_now();
test_now
----------------------------------------------
{"now" : "2024-06-22T16:18:11.827584+00:00"}
(1 row)
ExecuteScalar()
не играет роли, предложенной в вашем вопросе
Что заставляет ExecuteScalar выполнять преобразование, даже если текст вернулся из функции?
но подтверждает ваше первоначальное наблюдение:
Он ведет себя одинаково, даже если функция возвращает json, преобразованный в текст. заранее.
Мы могли бы сделать SET TIME ZONE
из нашего кода С#, как предложено здесь , но мы все равно получим время UTC в формате, который вам не нужен (...+00:00 против ...Z), как указано в вашем исходном вопросе. :
Есть ли способ заставить все, что конвертирует строки json, во время чтение с помощью ExecuteScalar, чтобы сделать это с помощью ...Z?
Я не знаком с тем, как Dapper
добавить несколько перехватчиков при выполнении ExecuteScalar
и изменить сгенерированную строку json с помощью выбранного нами формата даты, что было бы идеальным решением. Аналогичный относительно недавний пример использования с некоторыми решениями, а также с комментарием Марка Гравелла здесь.
Дэппер не хочет иметь ничего общего с вашими причудливыми махинациями с сериализацией. :) В принципе нет: читаем из базы как строку, потом десериализовать.
Если это невозможно, у нас есть два варианта:
ExecuteScalar
Ваша исходная функция
create function data_survey_r("Key" integer) returns text language plpgsql as $$
BEGIN
RETURN (SELECT ROW_TO_JSON(t)::text FROM data.survey t WHERE t.id = "Key");
END $$;
использует ROW_TO_JSON
, что не позволяет предлагать форматирование для timespantz
, однако в этом ответе есть предложение, как обойти эту проблему:
Невозможно повлиять на формат, используемый
row_to_json
.Вы можете определить представление для своей таблицы и использовать
to_char(row_added_dttm, 'ГГГГ-ММ-ДД"Т"ЧЧ24:МИ:СС.МС"Z"')
чтобы отформатировать временную метку как строку.
Затем вы можете использовать row_to_json в этом представлении, чтобы получить желаемый результат.
также обратите внимание на комментарий, который предлагает альтернативный подход.
Очень понятный ответ, спасибо большое. Я не знал, что моя среда (я использую DataGrip) работает так, как если бы был установлен SET TIMEZONE 'UTC';
, поэтому я ошибочно обвинил промежуточное программное обеспечение. В pgAdmin по умолчанию все работает иначе. В любом случае, это +00.00 или что-то еще не подходит для Javascript, поэтому я воспользуюсь представлением или какой-нибудь функцией SELECT format_date(date_field),....
Покажите нам определение вашего (PL/pgSQL?)
FUNCTION
.