SNTP サーバーとの時刻比較(その2)の続きです。前回までで、INtpMessage インターフェイス、NtpMessage クラス、INtpClient インターフェイス、NtpClient クラスを書いたので、残りは NtpTimeStampUtil クラス、NtpMessageException クラス、Program クラスです。
まずは NtpTimeStampUtil クラスから。いっきに書くには長いので分割します。
using System;
using System.Runtime.CompilerServices;
// TestTagManage からのアクセスだけは受け入れる
[assembly: InternalsVisibleTo("NtpGetTimestamp.Test")]
namespace Sntp
{
internal static class NtpTimeStampUtil
{
internal const long UNIT_RATING32 = 0x100000000L; // (2 ** 32)
internal const long UNIT_RATING16 = 0x10000L; // (2 ** 16)
internal static readonly byte TIMESTAMP_LENGTH = 8;
internal static readonly DateTime START_DATETIME = new DateTime(1900, 1, 1, 0, 0, 0); // 2036年の桁あふれまで
internal static readonly DateTime PASSED_START_DATETIME = START_DATETIME.AddSeconds(uint.MaxValue); // 2036年以降
/// <summary>
/// NTP タイムスタンプの先頭 bit から NTP タイムスタンプの始点秒数を取得する
/// </summary>
/// <param name="seconds"></param>
/// <returns></returns>
internal static DateTime GetStartingDatetime(ulong timestamp)
{
return (timestamp & 0x8000000000000000) == 0 ? PASSED_START_DATETIME : START_DATETIME;
}
/// <summary>
/// DateTime(UTC) を使用して NTP タイムスタンプの始点秒数を取得する
/// </summary>
/// <param name="datetime"></param>
/// <returns></returns>
internal static DateTime GetStartingDatetime(DateTime datetime)
{
return PASSED_START_DATETIME <= datetime ? PASSED_START_DATETIME : START_DATETIME;
}
//
/// <summary>
/// NTP 64ビット固定小数点数(タイムスタンプ)形式から DateTime(UTC) 型へ変換する
/// </summary>
/// <param name="timeStamp"></param>
/// <returns></returns>
internal static DateTime NtpTimeStamp2Datetime(ulong timeStamp)
{
return GetStartingDatetime(timeStamp) +
TimeSpan.FromMilliseconds(NtpTimeStamp2Milliseconds(timeStamp));
}
/// <summary>
/// NTP 64ビット固定小数点数(タイムスタンプ)形式から ミリ秒へ変換する
/// </summary>
/// <param name="timeStamp"></param>
/// <returns></returns>
internal static ulong NtpTimeStamp2Milliseconds(ulong timeStamp)
{
var seconds = (uint)(timeStamp >> 32);
var fraction = (uint)(timeStamp & uint.MaxValue);
return (ulong)seconds * 1000 + ((ulong)fraction * 1000) / UNIT_RATING32;
}
/// <summary>
/// NTP 64ビット固定小数点数(タイムスタンプ)形式から DateTime(Local Time) 型へ変換する
/// </summary>
/// <param name="timeStamp"></param>
/// <returns></returns>
internal static DateTime NtpTimeStamp2LocalTime(ulong timeStamp)
{
return Utc2LocalTime(NtpTimeStamp2Datetime(timeStamp));
}
//
/// <summary>
/// DateTime(UTC) 型から NTP 64ビット固定小数点数(タイムスタンプ)形式へ変換する
/// </summary>
/// <param name="datetime"></param>
/// <returns></returns>
internal static ulong Datetime2NtpTimeStamp(DateTime datetime)
{
var startDatetime = GetStartingDatetime(datetime);
var milliseconds = (ulong)(datetime - startDatetime).TotalMilliseconds;
var seconds = (uint)(milliseconds / 1000);
var fraction = (uint)((milliseconds % 1000) * UNIT_RATING32 / 1000);
return (ulong)(((ulong)seconds << 32) | fraction);
}
/// <summary>
/// UTC から ローカルタイムへ変換する
/// </summary>
/// <param name="utc"></param>
/// <returns></returns>
internal static DateTime Utc2LocalTime(DateTime utc)
{
// タイムゾーンのオフセットを計算
var offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now);
return utc + offset;
}
}
}
次は NtpMessageException クラス。
using System;
namespace Sntp
{
public class NtpMessageException : ApplicationException
{
public NtpMessageException(string message)
: base(message)
{
}
}
}
そして最後に Program クラス。
《SNTP サーバーの IP アドレス》の処には、ネットワーク的にお近くの SNTP サーバー(ISP の SNTP サーバーなど)の IP アドレス(xxx.xxx.xxx.xxx)を記述してください。
using System;
namespace Sntp
{
class Program
{
static void Main(string[] args)
{
var remoteIpAddr = "《SNTP サーバーの IP アドレス》";
INtpClient ntpClient = null;
ntpClient = new NtpClient();
ntpClient.ServerIpAddr = remoteIpAddr;
ntpClient.Connect();
Console.WriteLine("取得した NTP メッセージ -- (ここから)");
var message = ntpClient.Message;
foreach (var n in message)
{
Console.Write("{0:X2}", n);
}
Console.Write("\r\n");
Console.WriteLine("取得した NTP メッセージ -- (ここまで)");
Console.WriteLine("SNTP server: {0}, stratum: {1}, SNTP Verion: {2}",
ntpClient.ServerIpAddr.ToString(), ntpClient.StratumNumber, ntpClient.VersionNumber);
Console.WriteLine("Leap indicator: {0}", ntpClient.LeapIndicator);
Console.WriteLine("Originate Timestamp: {0:yyyy/MM/dd HH:mm:ss:fff}", ntpClient.OriginateTimestamp);
Console.WriteLine("Receive Timestamp: {0:yyyy/MM/dd HH:mm:ss:fff}", ntpClient.ReceiveTimestamp);
Console.WriteLine("Receive Timestamp: {0:yyyy/MM/dd HH:mm:ss:fff}", ntpClient.TransmitTimestamp);
Console.WriteLine("Destination timestamp: {0:yyyy/MM/dd HH:mm:ss:fff}", ntpClient.DestinationTimestamp);
Console.WriteLine("往復の遅延時間: {0}ms", ntpClient.DelayTime);
Console.WriteLine("ローカル時計の誤差: {0}", ntpClient.LocalClockOffset);
Console.Write("Enter キーを押下すると終了します。");
Console.ReadLine();
}
// NTP メッセージを作成するためのヘルパークラス
private static byte[] createNtpMessage(string source)
{
var length = source.Length / 2;
var bytes = new byte[length];
for (var i = 0; i < length; i++)
{
bytes[i] = Convert.ToByte(source.Substring(i * 2, 2), 16);
}
return bytes;
}
}
}
3回にわたるシリーズみたいになってしまいましたが、とりあえずこれで SNTP サーバとの時刻比較のプログラムが動くようになります 😉