Namespace System /*! @brief 時刻の種類 */ Enum DateTimeKind Local Unspecified Utc End Enum /*! @brief 曜日 */ Enum DayOfWeek Sunday = 0 Monday Tuesday Wednesday Thursday Friday Saturday End Enum /*! @brief 時刻を表すクラス */ Class DateTime m_Date As Int64 Public Static Const MaxValue = 3162240000000000000 As Int64 'Const TicksPerDay*366*10000 Static Const MinValue = 316224000000000 As Int64 'Const TicksPerDay*366 '---------------------------------------------------------------- ' パブリック コンストラクタ '---------------------------------------------------------------- /*! @brief コンストラクタ */ Sub DateTime() initialize(MinValue, DateTimeKind.Unspecified) End Sub /*! @brief 時刻を指定して初期化する @param 100ナノ秒単位で表した時刻 */ Sub DateTime(ticks As Int64) initialize(ticks, DateTimeKind.Unspecified) End Sub /*! @brief 時刻を指定して初期化する @param 100ナノ秒単位で表した時刻 @param 時刻の種類 */ Sub DateTime(ticks As Int64, kind As DateTimeKind) initialize(ticks, kind) End Sub /*! @brief 時刻を指定して初期化する @param 西暦 @param 月 @param 日 */ Sub DateTime(year As Long, month As Long, day As Long) initialize(year, month, day, 0, 0, 0, 0, DateTimeKind.Unspecified) End Sub /*! @brief 時刻を指定して初期化する @param 西暦 @param 月 @param 日 @param 時刻の種類 */ Sub DateTime(year As Long, month As Long, day As Long, kind As DateTimeKind) initialize(year, month, day, 0, 0, 0, 0, kind) End Sub /*! @brief 時刻を指定して初期化する @param 西暦 @param 月 @param 日 @param 時 @param 分 @param 秒 */ Sub DateTime(year As Long, month As Long, day As Long, hour As Long, minute As Long, second As Long) initialize(year, month, day, hour, minute, second, 0, DateTimeKind.Unspecified) End Sub /*! @brief 時刻を指定して初期化する @param 西暦 @param 月 @param 日 @param 時 @param 分 @param 秒 @param 時刻の種類 */ Sub DateTime(year As Long, month As Long, day As Long, hour As Long, minute As Long, second As Long, kind As DateTimeKind) initialize(year, month, day, hour, minute, second, 0, kind) End Sub /*! @brief 時刻を指定して初期化する @param 西暦 @param 月 @param 日 @param 時 @param 分 @param 秒 @param ミリ秒 */ Sub DateTime(year As Long, month As Long, day As Long, hour As Long, minute As Long, second As Long, millisecond As Long) initialize(year, month, day, hour, minute, second, millisecond, DateTimeKind.Unspecified) End Sub /*! @brief 時刻を指定して初期化する @param 西暦 @param 月 @param 日 @param 時 @param 分 @param 秒 @param ミリ秒 @param 時刻の種類 */ Sub DateTime(year As Long, month As Long, day As Long, hour As Long, minute As Long, second As Long, millisecond As Long, kind As DateTimeKind) initialize(year, month, day, hour, minute, second, millisecond, kind) End Sub /*! @brief 時刻を指定して初期化する @param SYSTEMTIME構造体 */ Sub DateTime(ByRef time As SYSTEMTIME) initialize(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds, DateTimeKind.Unspecified) End Sub /*! @brief 時刻を指定して初期化する @param SYSTEMTIME構造体 @param 時刻の種類 */ Sub DateTime(ByRef time As SYSTEMTIME, kind As DateTimeKind) initialize(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds, kind) End Sub /*! @brief コピーコンストラクタ @param コピーするDateTime */ Sub DateTime(dateTime As DateTime) This.m_Date = dateTime.m_Date End Sub '---------------------------------------------------------------- ' オペレータ '---------------------------------------------------------------- Function Operator + (value As TimeSpan) As DateTime Return New DateTime(Ticks + value.Ticks) End Function Function Operator - (value As DateTime) As TimeSpan Return TimeSpan.FromTicks(Ticks - value.Ticks) End Function Function Operator - (value As TimeSpan) As DateTime Return New DateTime(Ticks - value.Ticks) End Function Function Operator == (value As DateTime) As Boolean Return Equals(value) End Function Function Operator <> (value As DateTime) As Boolean Return Not Equals(value) End Function Function Operator > (value As DateTime) As Boolean If DateTime.Compare(This, value) > 0 Then Return True Else Return False End If End Function Function Operator < (value As DateTime) As Boolean If DateTime.Compare(This, value) < 0 Then Return True Else Return False End If End Function Function Operator >= (value As DateTime) As Boolean If DateTime.Compare(This, value) => 0 Then Return True Else Return False End If End Function Function Operator <= (value As DateTime) As Boolean If DateTime.Compare(This, value) <= 0 Then Return True Else Return False End If End Function '---------------------------------------------------------------- ' パブリック プロパティ '---------------------------------------------------------------- /*! @brief 時刻を100ナノ秒単位で取得する @return 時刻 */ Function Ticks() As Int64 Return (m_Date And &H3FFFFFFFFFFFFFFF) End Function /*! @brief 時刻のミリ秒単位部分を取得する @return ミリ秒の部分 */ Function Millisecond() As Long Return (Ticks \ TimeSpan.TicksPerMillisecond Mod 1000) As Long End Function /*! @brief 時刻の秒単位部分を取得する @return 秒の部分 */ Function Second() As Long Return (Ticks \ TimeSpan.TicksPerSecond Mod 60) As Long End Function /*! @brief 時刻の分単位部分を取得する @return 分の部分 */ Function Minute() As Long Return (Ticks \ TimeSpan.TicksPerMinute Mod 60) As Long End Function /*! @brief 時刻の時単位部分を取得する @return 時の部分 */ Function Hour() As Long Return (Ticks \ TimeSpan.TicksPerHour Mod 24) As Long End Function /*! @brief 時刻の日単位部分を取得する @return 日の部分 */ Function Day() As Long Return DayOfYear - totalDaysOfMonth(Year, Month - 1) End Function /*! @brief 時刻の月単位部分を取得する @return 月の部分 */ Function Month() As Long Dim year = Year As Long Dim day = DayOfYear As Long Dim i = 1 As Long While day > totalDaysOfMonth(year, i) i++ Wend Return i End Function /*! @brief 時刻の年単位部分を取得する @return 西暦 */ Function Year() As Long Dim day = (Ticks \ TimeSpan.TicksPerDay) As Long Dim year = Int((day + day \ 36524 - day \ 146097) / 365.25) + 1 As Long If day - yearToDay(year - 1) + 1 = 366 Then Return year + 1 Else Return year End If End Function /*! @brief 時刻の曜日部分を取得する @return 曜日の部分 */ Function DayOfWeek() As DayOfWeek Return New DayOfWeek( (((Ticks \ TimeSpan.TicksPerDay) Mod 7) + 1) As Long, "DayOfWeek") End Function /*! @brief 時刻の種類を取得する @return 種類 */ Function Kind() As DateTimeKind Return kindFromBinary(m_Date) End Function /*! @brief その年の何日目かを取得する @return 日数 */ Function DayOfYear() As Long Return ((Ticks \ TimeSpan.TicksPerDay) - yearToDay(Year) + 1) As Long End Function /*! @brief 時刻の日付の部分を取得する @return 日付 */ Function Date() As DateTime Return New DateTime(Year, Month, Day, Kind) End Function /*! @brief 現在の時刻を取得する @return 時刻 */ Static Function Now() As DateTime Dim time As SYSTEMTIME GetLocalTime(time) Return New DateTime(time, DateTimeKind.Local) End Function /*! @brief 今日を表す時刻を取得する(時間は0時0分) @return 時刻 */ Static Function Today() As DateTime Dim time As SYSTEMTIME GetLocalTime(time) Return New DateTime(time.wYear, time.wMonth, time.wDay, DateTimeKind.Local) End Function /*! @brief 現在の時刻をUTC時刻で取得する @return 時刻 */ Static Function UtcNow() As DateTime Dim time As SYSTEMTIME GetSystemTime(time) Return New DateTime(time, DateTimeKind.Utc) End Function '---------------------------------------------------------------- ' パブリック メソッド '---------------------------------------------------------------- /*! @brief 二つの時刻の差を求める @return 差(単位は100ナノ秒) */ Static Function Compare(t1 As DateTime, t2 As DateTime) As Int64 Return t1.Ticks - t2.Ticks End Function /*! @brief 等しいどうかを取得する @param 比較する値 @retval True 等しい @retval False 等しくない */ Function Equals(value As DateTime) As Boolean If value.m_Date = m_Date Then Return True Else Return False End If End Function /*! @brief 等しいどうかを取得する @param 比較される値 @param 比較する値 @retval True 等しい @retval False 等しくない */ Static Function Equals(t1 As DateTime, t2 As DateTime) As Boolean If t1.m_Date = t2.m_Date Then Return True Else Return False End If End Function /*! @brief ハッシュコードを取得する @return ハッシュコード */ Override Function GetHashCode() As Long Return HIDWORD(m_Date) Xor LODWORD(m_Date) End Function /*! @brief 時刻を進める @param 進める時間 @return 進めた後の時刻 */ Function Add(value As TimeSpan) As DateTime Return This + value End Function /*! @brief 時刻を進める @param 進める時間(100ナノ秒単位) @return 進めた後の時刻 */ Function AddTicks(value As Int64) As DateTime Return New DateTime(Ticks + value, Kind ) End Function /*! @brief 時刻を進める @param 進める時間(ミリ秒単位) @return 進めた後の時刻 */ Function AddMilliseconds(value As Double) As DateTime Return AddTicks((value * TimeSpan.TicksPerMillisecond) As Int64) End Function /*! @brief 時刻を進める @param 進める時間(秒単位) @return 進めた後の時刻 */ Function AddSeconds(value As Double) As DateTime Return AddTicks((value * TimeSpan.TicksPerSecond) As Int64) End Function /*! @brief 時刻を進める @param 進める時間(分単位) @return 進めた後の時刻 */ Function AddMinutes(value As Double) As DateTime Return AddTicks((value * TimeSpan.TicksPerMinute) As Int64) End Function /*! @brief 時刻を進める @param 進める時間(時単位) @return 進めた後の時刻 */ Function AddHours(value As Double) As DateTime Return AddTicks((value * TimeSpan.TicksPerHour) As Int64) End Function /*! @brief 時刻を進める @param 進める時間(日単位) @return 進めた後の時刻 */ Function AddDays(value As Double) As DateTime Return AddTicks((value * TimeSpan.TicksPerDay) As Int64) End Function /*! @brief 時刻を進める @param 進める時間(年単位) @return 進めた後の時刻 */ Function AddYears(value As Double) As DateTime Dim date = New DateTime(Year + Int(value), Month, Day, Hour, Minute, Second, Millisecond, Kind) Dim ticks = Ticks _ - (yearToDay(Year) + totalDaysOfMonth(Year, Month - 1) + Day - 1) * TimeSpan.TicksPerDay _ - Hour * TimeSpan.TicksPerHour _ - Minute * TimeSpan.TicksPerMinute _ - Second * TimeSpan.TicksPerSecond _ - Millisecond * TimeSpan.TicksPerMillisecond As Int64 If IsLeapYear(Year + Int(value)) Then ticks += ( (value - Int(value)) * 366 * TimeSpan.TicksPerDay ) As Int64 Else ticks += ( (value - Int(value)) * 365 * TimeSpan.TicksPerDay ) As Int64 End If Return date.AddTicks(ticks) End Function /*! @brief 時刻の差を取得する @param 比較する値 @return 時刻の差 */ Function Subtract(value As DateTime) As TimeSpan Return This - value End Function /*! @brief 時刻を戻す @param 戻す時間 @return 時刻 */ Function Subtract(value As TimeSpan) As DateTime Return This - value End Function /*! @brief 指定した年月が、その月の何日目かを取得する @param 西暦 @param 月 @return 日数 */ Static Function DaysInMonth(year As Long, month As Long) As Long If year < 1 Or year > 9999 Then Throw New ArgumentOutOfRangeException("DateTime.DaysInMonth: One or more arguments are out of range value.", "year") End If If month < 1 Or month > 12 Then Throw New ArgumentOutOfRangeException("DateTime.DaysInMonth: One or more arguments are out of range value.", "month") End If If IsLeapYear(year) And month = 2 Then Return 29 Else Dim daysInMonth[11] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] As Byte Return daysInMonth[month - 1] End If End Function /*! @brief 年が閏年かどうかを取得する @param 西暦 @retval True 閏年 @retval False 閏年でない */ Static Function IsLeapYear(year As Long) As Boolean If (year Mod 400) = 0 Then Return True If (year Mod 100) = 0 Then Return False If (year Mod 4) = 0 Then Return True Return False End Function /*! @brief 時刻を文字列に変換する @return 時刻を表した文字列 */ Function GetDateTimeFormats() As String Dim time = getSystemTime() 'As SYSTEMTIME を記述すると内部エラーが発生 Dim dateFormatSize = GetDateFormat(LOCALE_USER_DEFAULT, 0, time, NULL, NULL, 0) Dim timeFormatSize = GetTimeFormat(LOCALE_USER_DEFAULT, 0, time, NULL, NULL, 0) Dim strLength = dateFormatSize + timeFormatSize Dim dateTimeFormats = GC_malloc_atomic(SizeOf (TCHAR) * (strLength)) As PTSTR GetDateFormat(LOCALE_USER_DEFAULT, 0, time, NULL, dateTimeFormats, dateFormatSize) dateTimeFormats[dateFormatSize - 1] = &H20 As TCHAR 'Asc(" ") As TCHAR GetTimeFormat(LOCALE_USER_DEFAULT, 0, time, NULL, VarPtr(dateTimeFormats[dateFormatSize]), timeFormatSize) Return New String(dateTimeFormats, strLength) End Function /*! @brief 時刻を指定した書式で文字列に変換する @param 書式 @return 時刻を表した文字列 */ Function GetDateTimeFormats(format As *TCHAR) As String Dim time = getSystemTime() 'As SYSTEMTIME を記述すると内部エラーが発生 Dim dateFormatSize = GetDateFormat(LOCALE_USER_DEFAULT, 0, time, format, NULL, 0) Dim dateFormats = malloc(dateFormatSize) As PTSTR GetDateFormat(LOCALE_USER_DEFAULT, 0, time, format, dateFormats, dateFormatSize) Dim dateTimeFormatSize = GetTimeFormat(LOCALE_USER_DEFAULT, 0, time, dateFormats, NULL, 0) Dim dateTimeFormats = GC_malloc_atomic(dateTimeFormatSize) As PTSTR GetTimeFormat(LOCALE_USER_DEFAULT, 0, time, dateFormats, dateTimeFormats, dateTimeFormatSize) Return New String(dateTimeFormats) End Function /*! @brief バイナリデータからDateTimeを作成する @return DateTimeクラス */ Static Function FromBinary(date As Int64) As DateTime Return New DateTime((date And &H3FFFFFFFFFFFFFFF), kindFromBinary(date)) End Function /*! @brief バイナリデータに変換する @return バイナリデータ */ Function ToBinary() As Int64 Return m_Date End Function /*! @brief FILETIME構造体からDateTimeを作成する @return DateTimeクラス */ Static Function FromFileTime(ByRef fileTime As FILETIME) As DateTime Dim localTime As FILETIME Dim time As SYSTEMTIME FileTimeToLocalFileTime(fileTime, localTime) FileTimeToSystemTime(localTime, time) Return New DateTime(time, DateTimeKind.Local) End Function /*! @brief FILETIME構造体に変換する @return FILETIME構造体 */ Function ToFileTime() As FILETIME Dim time = getSystemTime() 'As SYSTEMTIME を記述すると内部エラーが発生 rev.207 Dim fileTime As FILETIME SystemTimeToFileTime(time, fileTime) Return fileTime End Function /*! @brief UTC時刻を表すFILETIME構造体からDateTimeを作成する @return DateTimeクラス */ Static Function FromFileTimeUtc(ByRef fileTime As FILETIME) As DateTime Dim time As SYSTEMTIME FileTimeToSystemTime(fileTime, time) Return New DateTime(time, DateTimeKind.Utc) End Function /*! @brief UTC時刻のFILETIME構造体に変換する @return FILETIME構造体 */ Function ToFileTimeUtc() As FILETIME Dim fileTime = ToFileTime() 'As FILETIME を記述すると内部エラー rev.207 If Kind = DateTimeKind.Utc Then Return fileTime Else Dim utcTime As FILETIME LocalFileTimeToFileTime(fileTime, utcTime) Return utcTime End If End Function /*! @brief 現地時刻に変換する @return 現地時刻に変換したDateTime */ Function ToLocalTime() As DateTime If Kind = DateTimeKind.Local Then Return New DateTime(This) Else Dim fileTime = ToFileTime() '直接入れると計算できなくなります。 rev.207 Return DateTime.FromFileTime(fileTime) End If End Function /*! @brief このインスタンスを文字列で取得する @return 文字列 */ Override Function ToString() As String Return GetDateTimeFormats() End Function /*! @brief 世界協定時刻(UTC)に変換する @return 世界協定時刻(UTC)に変換したDateTime */ Function ToUniversalTime() As DateTime If Kind = DateTimeKind.Utc Then Return New DateTime(m_Date) Else Dim fileTime = ToFileTimeUtc() '直接入れると計算できなくなります。 rev.207 Return DateTime.FromFileTimeUtc(fileTime) End If End Function '---------------------------------------------------------------- ' プライベート メソッド '---------------------------------------------------------------- Private /*! @brief インスタンスを初期化する @param 時刻(100ナノ秒単位) @param 時刻の種類 */ Sub initialize(ticks As Int64, kind As DateTimeKind) Kind = kind Ticks = ticks End Sub /*! @brief インスタンスを初期化する @param 西暦 @param 月 @param 日 @param 時 @param 分 @param 秒 @param ミリ秒 @param 時刻の種類 */ Sub initialize(year As Long, month As Long, day As Long, hour As Long, minute As Long, second As Long, millisecond As Long, kind As DateTimeKind) If month < 1 Or month > 12 Then Throw New ArgumentOutOfRangeException("DateTime.initialize: One or more arguments are out of range value.", "month") End If If day < 1 Or day > DaysInMonth(year, month) Then Throw New ArgumentOutOfRangeException("DateTime.initialize: One or more arguments are out of range value.", "day") End If If hour < 0 Or hour => 24 Then Throw New ArgumentOutOfRangeException("DateTime.initialize: One or more arguments are out of range value.", "hour") End If If minute < 0 Or minute => 60 Then Throw New ArgumentOutOfRangeException("DateTime.initialize: One or more arguments are out of range value.", "minute") End If If second < 0 Or second => 60 Then Throw New ArgumentOutOfRangeException("DateTime.initialize: One or more arguments are out of range value.", "second") End If If millisecond < 0 Or millisecond => 1000 Then Throw New ArgumentOutOfRangeException("DateTime.initialize: One or more arguments are out of range value.", "millisecond") End If initialize( yearToDay(year) * TimeSpan.TicksPerDay _ + totalDaysOfMonth(year, month - 1) * TimeSpan.TicksPerDay _ + (day - 1) * TimeSpan.TicksPerDay _ + hour * TimeSpan.TicksPerHour _ + minute * TimeSpan.TicksPerMinute _ + second * TimeSpan.TicksPerSecond _ + millisecond * TimeSpan.TicksPerMillisecond, kind ) End Sub /*! @brief 時刻を設定する @param 時刻(100ナノ秒単位) */ Sub Ticks(value As Int64) If value < MinValue Or value > MaxValue Then Throw New ArgumentOutOfRangeException("DateTime.value: One or more arguments are out of range value.", "value") End If Dim temp = Kind As DateTimeKind m_Date = value Kind = temp End Sub /*! @brief 時刻の種類を設定する @param 時刻の種類 */ Sub Kind(kind As DateTimeKind) Dim temp As Int64 temp = kind temp = (temp << 62) And &HC000000000000000 m_Date = (m_Date And &H3FFFFFFFFFFFFFFF) Or temp End Sub /*! @brief バイナリデータから時刻の種類を取得する @return 時刻の種類 */ Static Function kindFromBinary(date As Int64) As DateTimeKind date = (date >> 62) And &H03 If date = &H01 Then Return DateTimeKind.Local ElseIf date = &H02 Then Return DateTimeKind.Unspecified ElseIf date = &H03 Then Return DateTimeKind.Utc End If End Function /*! @brief インスタンスをSYSTEMTIME構造体に変換する @return SYSTEMTIME構造体 */ Function getSystemTime() As SYSTEMTIME Dim dayOfWeek = DayOfWeek As Long Dim time As SYSTEMTIME With time .wYear = Year As Word .wMonth = Month As Word .wDayOfWeek = dayOfWeek As Word .wDay = Day As Word .wHour = Hour As Word .wMinute = Minute As Word .wSecond = Second As Word .wMilliseconds = Millisecond As Word End With Return time End Function /*! @brief 西暦から日数に変換する @return 日数 */ Static Function yearToDay(year As Long) As Long year-- Return (Int(year * 365.25) - Int(year * 0.01) + Int(year * 0.0025)) End Function /*! @brief その月までその年の元旦から何日経っているかを取得する @return 日数 */ Static Function totalDaysOfMonth(year As Long, month As Long) As Long Dim days As Long Dim i As Long For i = 1 To month days += DaysInMonth(year, i) Next Return days End Function End Class End Namespace