Я столкнулся с этой проблемой сегодня, и после трех часов бегания по Google, глядя на код, мне удалось выяснить, что происходит. Вот мои настройки / ситуация и что я нашел:
Настройка
- (служба OData) Microsoft IIS 8.0 в Windows Server 2012 с использованием
пул приложений по умолчанию.
- (OData Producer) Microsoft WCF среднего уровня
используя Entity Framework и Web Data Services.
- (OData Consumer)
Android-клиент, использующий OData4J v0.8 SNAPSHOT.
Проблема
В моем промежуточном уровне (OData Producer) я использую Entity Framework 5.0 для определения простой таблицы со столбцом Edm.DateTime
. Мой файл MessageTable.edmx
создает простую таблицу:
CREATE TABLE [dbo].[MessageTable] (
[Id] int IDENTITY(1,1) NOT NULL,
[Date1] datetime NULL
);
В службе данных WCF моего среднего уровня (OData Producer) я перехватываю OData POST из моего клиентского приложения Android. Я вручную установил столбец Date1 на среднем уровне, используя некоторый код C #:
private static void ProcessMessage(ODataMessage message)
{
.
.
.
conn.Open();
cmd.ExecuteNonQuery();
int returnCode = (int)cmd.Parameters["@result"].Value;
if (returnCode == 0)
{
message.Processed = true;
message.Date1 = DateTime.Now;
}
.
.
.
}
Обратите внимание, что я использовал статическое свойство C # DateTime.Now . Документация MSDN гласит, что DateTime.Now
:
Получает объект DateTime, для которого установлены текущие дата и время на этом компьютере, выраженные как местное время .
Главное, что нужно понять, это то, что местное время означает, что структура даты и времени в C # теперь включает информацию о часовом поясе.
Итак, после завершения кода среднего уровня служба WCF вставляет OData POST в мою таблицу. Затем Microsoft отправляет столбец [Date1]
обратно на мой клиент Android (Потребитель OData). Похоже, что Microsoft кодирует дату как OData DateTimeOffset, потому что она включает информацию о местном часовом поясе. То есть как 2013-08-26T17:30:00.0000000-7:00
Код
В OData4j есть пакет org.odata.internal
, который обрабатывает строки даты и времени OData. В версии 0.6 я нашел следующий комментарий в строках 40-44 о шаблоне регулярного выражения DATETIME_PATTERN
, используемом для анализа строк даты и времени:
40 // Since not everybody seems to adhere to the spec, we are trying to be
41 // tolerant against different formats
42 // spec says:
43 // Edm.DateTime: yyyy-mm-ddThh:mm[:ss[.fffffff]]
44 // Edm.DateTimeOffset: yyyy-mm-ddThh:mm[:ss[.fffffff]](('+'|'-')hh':'mm)|'Z'
45 private static final Pattern DATETIME_PATTERN =
46 Pattern.compile("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})(:\\d{2})?(\\.\\d{1,7})?((?:(?:\\+|\\-)\\d{2}:\\d{2})|Z)?");
47
48
В OData4j v0.7 DATETIME_PATTERN
стал DATETIME_XML_PATTERN
:
40
41 private static final Pattern DATETIME_XML_PATTERN = Pattern.compile("" +
42 "^" +
43 "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})" + // group 1 (datetime)
44 "(:\\d{2})?" + // group 2 (seconds)
45 "(\\.\\d{1,7})?" + // group 3 (nanoseconds)
46 "(Z)?" + // group 4 (tz, ignored - handles bad services)
47 "$");
48
49 private static final Pattern DATETIMEOFFSET_XML_PATTERN = Pattern.compile("" +
50 "^" +
51 "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})" + // group 1 (datetime)
52 "(\\.\\d{1,7})?" + // group 2 (nanoSeconds)
53 "(((\\+|-)\\d{2}:\\d{2})|(Z))" + // group 3 (offset) / group 6 (utc)
54 "$");
Я думаю, что комментарий к строке 46 объясняет все:
... // группа 4 (tz, игнорируется - обрабатывает плохие сервисы)
Я интерпретирую это как: «Любая информация о часовом поясе будет игнорироваться - это обрабатывает плохие сервисы (например, Microsoft), которые отправляют информацию о часовом поясе»
Мне кажется, что авторы OData4j решили придерживаться своего оружия и принимают только правильный Edm.DateTime
формат строки.
Решение
Исправление очень простое, если у вас есть доступ к коду OData Producer:
private static void ProcessMessage(ODataMessage message)
{
.
.
.
conn.Open();
cmd.ExecuteNonQuery();
int returnCode = (int)cmd.Parameters["@result"].Value;
if (returnCode == 0)
{
message.Processed = true;
message.Date1 = DateTime.UtcNow;
}
.
.
.
}
Вместо этого используйте свойство DateTime.UtcNow . Документация MSDN гласит:
Получает объект DateTime, для которого на данном компьютере установлены текущие дата и время, выраженные как Всемирное координированное время (UTC) .
Если у вас нет доступа к производителю OData, единственное решение, которое я вижу, это изменить код OData4j. Измените шаблон регулярного выражения для DATETIME_XML_PATTERN
:
41 private static final Pattern DATETIME_XML_PATTERN = Pattern.compile("" +
42 "^" +
43 "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})" + // group 1 (datetime)
44 "(:\\d{2})?" + // group 2 (seconds)
45 "(\\.\\d{1,7})?" + // group 3 (nanoseconds)
46 "((?:(?:\\+|\\-)\\d{2}:\\d{2})|Z)?" +
47 "$");
Заключение
Я думаю, что это на самом деле ошибка со стороны Microsoft. Я уверен, что у них есть некоторые обоснования для отправки Edm.DateTimeOffset
, но мой файл MessageTable.edmx
указывает [Date1]
как Edm.DateTime
, поэтому я думаю, что код Microsoft должен либо выполнить преобразование в UTC для меня, либо бросить исключение. Исключение предупредило бы меня о том, что я использовал неправильную структуру DateTime, и помогло бы мне увидеть решение DateTime.UtcNow намного быстрее.