I am trying to implement a code to get time from time.windows.com but it returns a weird date (year of the date I get is 1899). Since the same servers work for my unmanaged C++ code using WinSock, I can imagine that something must be wrong with my code itself. Can someone look at my code below and tell me what I am doing wrong?
typedef unsigned int uint;
typedef unsigned long ulong;
long GetTimestampFromServer()
{
System::String^ server = L"time.windows.com";
array<unsigned char>^ ntpData = gcnew array<unsigned char>(48);
ntpData[0] = 0x1B;
array<System::Net::IPAddress^>^ addresses = System::Net::Dns::GetHostEntry(server)->AddressList;
System::Net::IPEndPoint^ ipEndPoint = gcnew System::Net::IPEndPoint(addresses[0], 123);
System::Net::Sockets::Socket^ socket = gcnew System::Net::Sockets::Socket
(
System::Net::Sockets::AddressFamily::InterNetwork,
System::Net::Sockets::SocketType::Dgram,
System::Net::Sockets::ProtocolType::Udp
);
try
{
socket->Connect(ipEndPoint);
socket->ReceiveTimeout = 3000;
socket->Send(ntpData);
socket->Receive(ntpData);
socket->Close();
}
catch (System::Exception^ e)
{
System::Console::WriteLine(e->Message);
return 0;
}
const System::Byte serverReplyTime = 40;
ulong intPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime);
ulong fractPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime + 4);
intPart = SwapEndianness(intPart);
fractPart = SwapEndianness(fractPart);
long long milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
System::DateTime networkDateTime = (gcnew System::DateTime(1900, 1, 1, 0, 0, 0, System::DateTimeKind::Utc))->AddMilliseconds((long)milliseconds);
std::cout << ConvertToTimestamp(networkDateTime);
return 0;
}
static uint SwapEndianness(ulong x)
{
return (uint)(((x & 0x000000ff) << 24) +
((x & 0x0000ff00) << 8) +
((x & 0x00ff0000) >> 8) +
((x & 0xff000000) >> 24));
}
long ConvertToTimestamp(System::DateTime value)
{
System::TimeZoneInfo^ NYTimeZone = System::TimeZoneInfo::FindSystemTimeZoneById(L"Eastern Standard Time");
System::DateTime NyTime = System::TimeZoneInfo::ConvertTime(value, NYTimeZone);
System::TimeZone^ localZone = System::TimeZone::CurrentTimeZone;
System::Globalization::DaylightTime^ dst = localZone->GetDaylightChanges(NyTime.Year);
NyTime = NyTime.AddHours(-1);
System::DateTime epoch = System::DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime();
System::TimeSpan span = (NyTime - epoch);
return (long)System::Convert::ToDouble(span.TotalSeconds);
}
David Yaw pushed me in the right direction. Big thanks to him, I finally got my code to work. There was a lot of incorrectness associated with different statements throughout the method. I have fixed them and posting the new code below.
ref class SNTP
{
public: static DateTime GetNetworkTime()
{
System::String^ ntpServer = "time.windows.com";
auto ntpData = gcnew cli::array<unsigned char>(48);
ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
auto addresses = System::Net::Dns::GetHostEntry(ntpServer)->AddressList;
auto ipEndPoint = gcnew System::Net::IPEndPoint(addresses[0], 123);
auto socket = gcnew System::Net::Sockets::Socket
(
System::Net::Sockets::AddressFamily::InterNetwork,
System::Net::Sockets::SocketType::Dgram,
System::Net::Sockets::ProtocolType::Udp
);
socket->Connect(ipEndPoint);
socket->ReceiveTimeout = 3000;
socket->Send(ntpData);
socket->Receive(ntpData);
socket->Close();
const System::Byte serverReplyTime = 40;
System::UInt64 intPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime);
System::UInt64 fractPart = System::BitConverter::ToUInt32(ntpData, serverReplyTime + 4);
intPart = SwapEndianness(intPart);
fractPart = SwapEndianness(fractPart);
auto milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
auto networkDateTime = (System::DateTime(1900, 1, 1, 0, 0, 0, System::DateTimeKind::Utc)).AddMilliseconds((System::Int64)milliseconds);
return networkDateTime.ToLocalTime();
}
private: static System::UInt32 SwapEndianness(System::UInt64 x)
{
return (System::UInt32)(((x & 0x000000ff) << 24) +
((x & 0x0000ff00) << 8) +
((x & 0x00ff0000) >> 8) +
((x & 0xff000000) >> 24));
}
};