using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
namespace Vanara.PInvoke
{
///
/// Specifies a date and time, using individual members for the month, day, year, weekday, hour, minute, second, and millisecond. The
/// time is either in coordinated universal time (UTC) or local time, depending on the function that is being called.
///
[PInvokeData("winbase.h")]
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct SYSTEMTIME : IEquatable, IComparable
{
/// The year. The valid values for this member are 1601 through 30827.
public ushort wYear;
///
/// The month. This member can be one of the following values.
///
///
/// Value
/// Meaning
///
/// -
/// 1
/// January
///
/// -
/// 2
/// February
///
/// -
/// 3
/// March
///
/// -
/// 4
/// April
///
/// -
/// 5
/// May
///
/// -
/// 6
/// June
///
/// -
/// 7
/// July
///
/// -
/// 8
/// August
///
/// -
/// 9
/// September
///
/// -
/// 10
/// October
///
/// -
/// 11
/// November
///
/// -
/// 12
/// December
///
///
///
public ushort wMonth;
///
/// The day of the week. This member can be one of the following values.
///
///
/// Value
/// Meaning
///
/// -
/// 0
/// Sunday
///
/// -
/// 1
/// Monday
///
/// -
/// 2
/// Tuesday
///
/// -
/// 3
/// Wednesday
///
/// -
/// 4
/// Thursday
///
/// -
/// 5
/// Friday
///
/// -
/// 6
/// Saturday
///
///
///
public ushort wDayOfWeek;
/// The day of the month. The valid values for this member are 1 through 31.
public ushort wDay;
/// The hour. The valid values for this member are 0 through 23.
public ushort wHour;
/// The minute. The valid values for this member are 0 through 59.
public ushort wMinute;
/// The second. The valid values for this member are 0 through 59.
public ushort wSecond;
/// The millisecond. The valid values for this member are 0 through 999.
public ushort wMilliseconds;
private static readonly int[] DaysToMonth365 = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
private static readonly int[] DaysToMonth366 = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
/// Initializes a new instance of the struct with a .
/// The value.
/// Indicates whether the should represent the local, universal or unknown time.
/// dt - Year value must be 1601 through 30827
public SYSTEMTIME(DateTime dt, DateTimeKind toKind = DateTimeKind.Unspecified)
{
dt = toKind == DateTimeKind.Local ? dt.ToLocalTime() : (toKind == DateTimeKind.Utc ? dt.ToUniversalTime() : dt);
wYear = Convert.ToUInt16(dt.Year);
if (wYear < 1601) throw new ArgumentOutOfRangeException(nameof(dt), @"Year value must be 1601 through 30827");
wMonth = Convert.ToUInt16(dt.Month);
wDayOfWeek = Convert.ToUInt16(dt.DayOfWeek);
wDay = Convert.ToUInt16(dt.Day);
wHour = Convert.ToUInt16(dt.Hour);
wMinute = Convert.ToUInt16(dt.Minute);
wSecond = Convert.ToUInt16(dt.Second);
wMilliseconds = Convert.ToUInt16(dt.Millisecond);
}
/// Initializes a new instance of the struct.
/// The year. The valid values for this member are 1601 through 30827.
///
/// The month. This member can be one of the following values.
///
///
/// Value
/// Meaning
///
/// -
/// 1
/// January
///
/// -
/// 2
/// February
///
/// -
/// 3
/// March
///
/// -
/// 4
/// April
///
/// -
/// 5
/// May
///
/// -
/// 6
/// June
///
/// -
/// 7
/// July
///
/// -
/// 8
/// August
///
/// -
/// 9
/// September
///
/// -
/// 10
/// October
///
/// -
/// 11
/// November
///
/// -
/// 12
/// December
///
///
///
/// The day of the month. The valid values for this member are 1 through 31.
/// The hour. The valid values for this member are 0 through 23.
/// The minute. The valid values for this member are 0 through 59.
/// The second. The valid values for this member are 0 through 59.
/// The millisecond. The valid values for this member are 0 through 999.
public SYSTEMTIME(ushort year, ushort month, ushort day, ushort hour = 0, ushort minute = 0, ushort second = 0,
ushort millisecond = 0)
{
if (year < 1601 && year != 0) throw new ArgumentOutOfRangeException(nameof(year), @"year value must be 1601 through 30827 or 0");
wYear = year;
if (month < 1 || month > 12)
throw new ArgumentOutOfRangeException(nameof(month), @"month value must be 1 through 12");
wMonth = month;
if (day < 1 || day > 31) throw new ArgumentOutOfRangeException(nameof(day), @"day value must be 1 through 31");
wDay = day;
if (hour > 23) throw new ArgumentOutOfRangeException(nameof(hour), @"hour value must be 0 through 23");
wHour = hour;
if (minute > 59) throw new ArgumentOutOfRangeException(nameof(minute), @"minute value must be 0 through 59");
wMinute = minute;
if (second > 59) throw new ArgumentOutOfRangeException(nameof(second), @"second value must be 0 through 59");
wSecond = second;
if (millisecond > 999)
throw new ArgumentOutOfRangeException(nameof(millisecond), @"millisecond value must be 0 through 999");
wMilliseconds = millisecond;
wDayOfWeek = 0;
//wDayOfWeek = (ushort)ComputedDayOfWeek;
}
/// Gets or sets the day of the week.
/// The day of the week.
[ExcludeFromCodeCoverage]
public DayOfWeek DayOfWeek
{
get => (DayOfWeek)wDayOfWeek;
set => wDayOfWeek = (ushort)value;
}
/// Gets the number of ticks that represent the date and time of this instance.
public long Ticks
{
get
{
if (ToUInt64 == 0) return 0;
var days = IsLeapYear(wYear) ? DaysToMonth366 : DaysToMonth365;
var y = wYear - 1;
var n = y * 365 + y / 4 - y / 100 + y / 400 + days[wMonth - 1] + wDay - 1;
return new TimeSpan(n, wHour, wMinute, wSecond, wMilliseconds).Ticks;
}
}
/// Indicates if two values are equal.
/// The first value.
/// The second value.
/// true if both values are equal; otherwise false.
public static bool operator ==(SYSTEMTIME s1, SYSTEMTIME s2) => s1.wYear == s2.wYear && s1.wMonth == s2.wMonth &&
s1.wDay == s2.wDay &&
s1.wHour == s2.wHour && s1.wMinute == s2.wMinute &&
s1.wSecond == s2.wSecond && s1.wMilliseconds ==
s2.wMilliseconds;
/// Indicates if two values are not equal.
/// The first value.
/// The second value.
/// true if both values are not equal; otherwise false.
public static bool operator !=(SYSTEMTIME s1, SYSTEMTIME s2) => !(s1 == s2);
/// Determines whether one specified is greater than another specified .
/// The first value.
/// The second value.
/// true if is greater than ; otherwise, false.
public static bool operator >(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 > s2.ToUInt64;
/// Determines whether one specified is greater than or equal to another specified .
/// The first value.
/// The second value.
/// true if is greater than or equal to ; otherwise, false.
public static bool operator >=(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 >= s2.ToUInt64;
/// Determines whether one specified is less than another specified .
/// The first value.
/// The second value.
/// true if is less than ; otherwise, false.
public static bool operator <(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 < s2.ToUInt64;
/// Determines whether one specified is less than or equal to another specified .
/// The first value.
/// The second value.
/// true if is less than or equal to ; otherwise, false.
public static bool operator <=(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 <= s2.ToUInt64;
/// The minimum value supported by .
public static readonly SYSTEMTIME MinValue = new SYSTEMTIME(1601, 1, 1);
/// The maximum value supported by .
public static readonly SYSTEMTIME MaxValue = new SYSTEMTIME(30827, 12, 31, 23, 59, 59, 999);
/// Compares two instances of and returns an integer that indicates whether the first instance is earlier than, the same as, or later than the second instance.
/// The first object to compare.
/// The second object to compare.
/// A signed number indicating the relative values of t1 and t2.
///
/// Value TypeCondition
/// - Less than zerot1 is earlier than t2.
/// - Zerot1 is the same as t2.
/// - Greater than zerot1 is later than t2.
///
///
public static int Compare(SYSTEMTIME t1, SYSTEMTIME t2)
{
var ticks1 = t1.ToUInt64;
var ticks2 = t2.ToUInt64;
if (ticks1 > ticks2) return 1;
if (ticks1 < ticks2) return -1;
return 0;
}
/// Compares the current object with another object of the same type.
/// An object to compare with this object.
///
/// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value
/// Meaning Less than zero This object is less than the parameter.Zero This object is equal to . Greater than zero This object is greater than .
///
public int CompareTo(SYSTEMTIME other) => Compare(this, other);
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(SYSTEMTIME other) => this == other;
/// Determines whether the specified , is equal to this instance.
/// The to compare with this instance.
/// true if the specified is equal to this instance; otherwise, false.
public override bool Equals(object obj) => base.Equals(obj);
/// Returns a hash code for this instance.
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
public override int GetHashCode()
{
var u = ToUInt64;
return unchecked((int)u) ^ (int)(u >> 32);
}
/// Converts this instance to a instance.
/// Indicates whether this instance is local, universal or neither.
/// An equivalent value.
public DateTime ToDateTime(DateTimeKind kind)
{
if (wYear == 0 || this == MinValue)
return DateTime.MinValue;
if (this == MaxValue)
return DateTime.MaxValue;
return new DateTime(wYear, wMonth, wDay, wHour, wMinute, wSecond, wMilliseconds, kind);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString() => ToString(DateTimeKind.Unspecified, null, null);
/// Returns a that represents this instance.
/// A that represents this instance.
public string ToString(DateTimeKind kind, string format, IFormatProvider provider) => ToDateTime(kind).ToString(format, CultureInfo.CurrentCulture);
[ExcludeFromCodeCoverage]
private DayOfWeek ComputedDayOfWeek => (DayOfWeek)((Ticks / 864000000000 + 1) % 7);
private ulong ToUInt64 => ((ulong)wYear << 36) | (((ulong)wMonth & 0x000f) << 32) |
(((ulong)wDay & 0x001f) << 27) | (((ulong)wHour & 0x000f) << 22) |
(((ulong)wMinute & 0x003f) << 16) | (((ulong)wSecond & 0x003f) << 10) |
((ulong)wMilliseconds & 0x3ff);
private static bool IsLeapYear(ushort year) => year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
}