2014-01-21 2 views
2

Я уже нашел несколько ответов, связанных с преобразованием значения std::time_t в System::DateTime и обратно. Тем не менее, почти все ответы, похоже, не учитывают, что тип std::time_t фактически не определен в стандарте. Большинство решений просто отличает std::time_t к любым необходимым или применяемым арифметическим операциям к объекту std::time_t, который возможен, поскольку он является арифметическим типом, но спецификация о результате такой операции отсутствует. Я знаю, что компиляторы определяют time_t как int определенного размера, но тот факт, что он изменился с int32 на int64 во многих реализациях, недавно показывает, что изменения действительно возможны.Стандартный способ конвертации std :: time_t в System :: DateTime?

Так что я придумал этот раствор, который должен работать с любым типом std::time_t. Это работает из того, что я видел. Но мне было интересно - Есть ли какие-нибудь возможные подводные камни, о которых я мог бы не знать?

template <> 
inline System::DateTime marshal_as(const std::time_t &from_object) 
{ 
    // Returns DateTime in Local time format from time_t (assumed to be UTC) 
    const auto unix_epoch = makeUtcTime(1970, 1, 1, 0, 0, 0); 
    const auto unix_epoch_dt = System::DateTime(1970, 1, 1, 0, 0, 0, System::DateTimeKind::Utc); 
    const auto secondsSinceEpoch = std::difftime(from_object, unix_epoch); 
    return const_cast<System::DateTime&>(unix_epoch_dt).AddSeconds(secondsSinceEpoch).ToLocalTime(); 
} // end of System::DateTime marshal_as(const std::time_t &from_object) 

template <> 
inline std::time_t marshal_as(const System::DateTime &from_object) 
{ 
    // Returns time_t in UTC format from DateTime 
    auto from_dt = const_cast<System::DateTime&>(from_object).ToUniversalTime(); 
    return makeUtcTime(from_dt.Year, from_dt.Month, from_dt.Day, from_dt.Hour, from_dt.Minute, from_dt.Second); 
} // end of std::time_t marshal_as(const System::DateTime &from_object) 
были сделаны

3 предположения:

  • Результирующая std::time_t должно быть в UTC, так как он не содержит никакой информации о локализации
  • причиненный в результате System::DateTime должно быть местное время, так как System::DateTime::Now возвращает локализованное DateTime
  • makeUtcTime - вспомогательная функция, создающая std::tm из приведенных значений и создающая UTC std::time_t из него. В настоящее время это реализовано с использованием _mkgmtime, потому что наш код взаимодействия может безопасно полагаться на существование расширений Microsoft. Тем не менее, версия UTC mktime также доступна в других компиляторах (стандарт mktime ожидает локальное время).

-менее важные вещи, чтобы рассмотреть следующие вопросы:

  • const_cast необходим, потому что marshal_as-шаблон ожидает const T& в качестве параметра, и я не могу получить доступ к свойствам объекта сопзИте .NET значения типа , Однако может быть лучшее решение.
  • Должно ли unix_epoch... материала static const?

(я не был уверен, что это должны быть размещены на «Программисты бирже», так как это больше обсуждения, но так как это очень специфический C вопрос ++ я думал, что SO может быть лучше спросить)

+0

Есть два возможных определений time_t, в зависимости от того, определяется _USE_32BIT_TIME_T. Если он определен, он будет работать до тех пор, пока работает 32-бит time_t. System :: DateTime - 64 бит. – cup

+0

Это правда, спасибо за информацию. Но так как полученное time_t во втором marshal_as построено с использованием календарных значений (Year, Month, ...), самое худшее, что может случиться, это то, что time_t возвращается как (time_t) (- 1), что в основном означает преобразование завершилось неудачно, потому что выбранная реализация time_t не может представлять DateTime. Но вот вы, причина № 1, почему просто преобразование TotalSeconds в time_t может потерпеть неудачу. – Excelcius

+0

См. Здесь: http://stackoverflow.com/questions/9864339/boost-parse-date-time-string-and-yield-net-compatible-ticks-value – Ben

ответ

11

Просто не очень эффективно настаивать на «стандартном согласованном» способе сделать это преобразование. Единственное место, где встречаются std :: time_t и System :: DateTime, покрывается Ecma-372 standard. Из которых есть прямо сейчас, и, безусловно, когда-либо будет, только один реализации. Проект Mono можно считать наиболее вероятным источником другого, но сейчас они совершенно не заинтересованы в реализации смешанной моды, единственной причиной, которую вы когда-либо рассматривали при использовании C++/CLI.

std :: time_t неуклонно идет к катастрофе Y2K38.Microsoft с предусмотрительно сделала что-то с этим, и на самом деле это нужно сделать, потому что они отправились на LLP64, но все остальные рассчитывали на их LP64 data model, чтобы они не попадали в неприятности. Другими словами, оставшиеся 32-разрядные процессоры все еще работают в 2038 году. Это вполне может быть самоисполняющимся пророчеством.

Независимо от того, что конвертация будет работать с истекшими секундами с 01.01.1970. И это может быть 32-битное или 64-битное целочисленное значение, в зависимости от реализации. Единственная гарантия, что я могу дать, что этот код не является, по крайней мере хорошо до 2038:

#include <ctime> 

using namespace System; 

public ref class Conversions { 
public: 
    static DateTime time_t2DateTime(std::time_t date) { 
     double sec = static_cast<double>(date); 
     return DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind::Utc).AddSeconds(sec); 
    } 
    static std::time_t DateTime2time_t(DateTime date) { 
     TimeSpan diff = date.ToUniversalTime() - DateTime(1970, 1, 1); 
     return static_cast<std::time_t>(diff.TotalSeconds); 
    } 
}; 
+0

Спасибо, я надеялся на более прямой обзор моих кода, но вы абсолютно правы, в этом контексте я мог бы также предположить, что 'std :: time_t' будет тем, чем Microsoft хочет это, и свести код к минимуму. Спасибо за подробный ответ. – Excelcius

+4

time_t находится в секундах, но вы добавляете значение в миллисекундах. Я думаю, вы должны использовать 'AddSeconds' и' TotalSeconds'. –

+0

Отличный ответ, но он должен быть .AddSeconds вместо .AddMilliSeconds. Также для локального использования используйте DateTime :: Local вместо DateTime :: Utc. –

0

Вот решение моя команда прибыла на:

DateTime представляет собой число смешанных дробно-дней с полуночи 30 декабря, 1899, выраженный как двойной. Я считаю, что эта эпоха была использована для учета того факта, что 1900 год был не високосным годом, и это позволяет дополнительно два дня (почему два и не один?) Мне непонятно, почему 31 декабря 1899 года не были выбраны в качестве их эпохи.)

Таким образом, DateTime of 2.50 будет эквивалентно 1 января 1900 года 12:00:00 (т.е. доля составляет 1/2 дня - 12 вечера).

Мы подсчитали, что 1 января 1970 года - эпоха Unix - это 25569 дней после эпохи DateTime.

Таким образом, эквивалентная формула будет:

#include <time.h> 
System::DateTime toDateTime(time_t &t) 
{ 
    return 25569.0 + t/86400.0; // ensure you use floating point math or you will truncate the fractional portion 
} 
+0

В документах Microsoft говорится об однопараметрическом конструкторе DateTime: Дата и время, выраженные в числе 100-наносекундных интервалов, прошедших с 1 января 0001 года в 00: 00: 00.000 в григорианском календаре. Какую информацию вы имеете в виду? –

+0

'Почему два, а не один? - Мне непонятно, почему 31 декабря 1899 года не были выбраны в качестве их эпохи.) 'Я думаю, что 30 декабря может быть ошибкой/опечаткой, потому что я не могу найти никакой информации об этом, фактически, 30 декабря, а не 31 декабря Фактически, я могу найти по крайней мере [одну ссылку] (https://msdn.microsoft.com/en-us/library/w4ddyt9h.aspx) в Microsoft, используя 31 декабря 1899 года в качестве эпохи, но ни один из Dec 30. – Synetech

+0

Не может быть связано с этим: http://www.cpearson.com/excel/datetime.htm –