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); } }