Vanara/PInvoke/Shared/WinBase/SYSTEMTIME.cs

385 lines
16 KiB
C#

using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
namespace Vanara.PInvoke
{
/// <summary>
/// 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.
/// </summary>
[PInvokeData("winbase.h")]
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct SYSTEMTIME : IEquatable<SYSTEMTIME>, IComparable<SYSTEMTIME>
{
/// <summary>The year. The valid values for this member are 1601 through 30827.</summary>
public ushort wYear;
/// <summary>
/// The month. This member can be one of the following values.
/// <list type="table">
/// <listheader>
/// <term>Value</term>
/// <term>Meaning</term>
/// </listheader>
/// <item>
/// <term>1</term>
/// <term>January</term>
/// </item>
/// <item>
/// <term>2</term>
/// <term>February</term>
/// </item>
/// <item>
/// <term>3</term>
/// <term>March</term>
/// </item>
/// <item>
/// <term>4</term>
/// <term>April</term>
/// </item>
/// <item>
/// <term>5</term>
/// <term>May</term>
/// </item>
/// <item>
/// <term>6</term>
/// <term>June</term>
/// </item>
/// <item>
/// <term>7</term>
/// <term>July</term>
/// </item>
/// <item>
/// <term>8</term>
/// <term>August</term>
/// </item>
/// <item>
/// <term>9</term>
/// <term>September</term>
/// </item>
/// <item>
/// <term>10</term>
/// <term>October</term>
/// </item>
/// <item>
/// <term>11</term>
/// <term>November</term>
/// </item>
/// <item>
/// <term>12</term>
/// <term>December</term>
/// </item>
/// </list>
/// </summary>
public ushort wMonth;
/// <summary>
/// The day of the week. This member can be one of the following values.
/// <list type="table">
/// <listheader>
/// <term>Value</term>
/// <term>Meaning</term>
/// </listheader>
/// <item>
/// <term>0</term>
/// <term>Sunday</term>
/// </item>
/// <item>
/// <term>1</term>
/// <term>Monday</term>
/// </item>
/// <item>
/// <term>2</term>
/// <term>Tuesday</term>
/// </item>
/// <item>
/// <term>3</term>
/// <term>Wednesday</term>
/// </item>
/// <item>
/// <term>4</term>
/// <term>Thursday</term>
/// </item>
/// <item>
/// <term>5</term>
/// <term>Friday</term>
/// </item>
/// <item>
/// <term>6</term>
/// <term>Saturday</term>
/// </item>
/// </list>
/// </summary>
public ushort wDayOfWeek;
/// <summary>The day of the month. The valid values for this member are 1 through 31.</summary>
public ushort wDay;
/// <summary>The hour. The valid values for this member are 0 through 23.</summary>
public ushort wHour;
/// <summary>The minute. The valid values for this member are 0 through 59.</summary>
public ushort wMinute;
/// <summary>The second. The valid values for this member are 0 through 59.</summary>
public ushort wSecond;
/// <summary>The millisecond. The valid values for this member are 0 through 999.</summary>
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 };
/// <summary>Initializes a new instance of the <see cref="SYSTEMTIME"/> struct with a <see cref="DateTime"/>.</summary>
/// <param name="dt">The <see cref="DateTime"/> value.</param>
/// <param name="toKind">Indicates whether the <see cref="SYSTEMTIME"/> should represent the local, universal or unknown time.</param>
/// <exception cref="ArgumentOutOfRangeException">dt - Year value must be 1601 through 30827</exception>
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);
}
/// <summary>Initializes a new instance of the <see cref="SYSTEMTIME"/> struct.</summary>
/// <param name="year">The year. The valid values for this member are 1601 through 30827.</param>
/// <param name="month">
/// The month. This member can be one of the following values.
/// <list type="table">
/// <listheader>
/// <term>Value</term>
/// <term>Meaning</term>
/// </listheader>
/// <item>
/// <term>1</term>
/// <term>January</term>
/// </item>
/// <item>
/// <term>2</term>
/// <term>February</term>
/// </item>
/// <item>
/// <term>3</term>
/// <term>March</term>
/// </item>
/// <item>
/// <term>4</term>
/// <term>April</term>
/// </item>
/// <item>
/// <term>5</term>
/// <term>May</term>
/// </item>
/// <item>
/// <term>6</term>
/// <term>June</term>
/// </item>
/// <item>
/// <term>7</term>
/// <term>July</term>
/// </item>
/// <item>
/// <term>8</term>
/// <term>August</term>
/// </item>
/// <item>
/// <term>9</term>
/// <term>September</term>
/// </item>
/// <item>
/// <term>10</term>
/// <term>October</term>
/// </item>
/// <item>
/// <term>11</term>
/// <term>November</term>
/// </item>
/// <item>
/// <term>12</term>
/// <term>December</term>
/// </item>
/// </list>
/// </param>
/// <param name="day">The day of the month. The valid values for this member are 1 through 31.</param>
/// <param name="hour">The hour. The valid values for this member are 0 through 23.</param>
/// <param name="minute">The minute. The valid values for this member are 0 through 59.</param>
/// <param name="second">The second. The valid values for this member are 0 through 59.</param>
/// <param name="millisecond">The millisecond. The valid values for this member are 0 through 999.</param>
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;
}
/// <summary>Gets or sets the day of the week.</summary>
/// <value>The day of the week.</value>
[ExcludeFromCodeCoverage]
public DayOfWeek DayOfWeek
{
get => (DayOfWeek)wDayOfWeek;
set => wDayOfWeek = (ushort)value;
}
/// <summary>Gets the number of ticks that represent the date and time of this instance.</summary>
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;
}
}
/// <summary>Indicates if two <see cref="SYSTEMTIME"/> values are equal.</summary>
/// <param name="s1">The first <see cref="SYSTEMTIME"/> value.</param>
/// <param name="s2">The second <see cref="SYSTEMTIME"/> value.</param>
/// <returns><c>true</c> if both values are equal; otherwise <c>false</c>.</returns>
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;
/// <summary>Indicates if two <see cref="SYSTEMTIME"/> values are not equal.</summary>
/// <param name="s1">The first <see cref="SYSTEMTIME"/> value.</param>
/// <param name="s2">The second <see cref="SYSTEMTIME"/> value.</param>
/// <returns><c>true</c> if both values are not equal; otherwise <c>false</c>.</returns>
public static bool operator !=(SYSTEMTIME s1, SYSTEMTIME s2) => !(s1 == s2);
/// <summary>Determines whether one specified <see cref="SYSTEMTIME"/> is greater than another specified <see cref="SYSTEMTIME"/>.</summary>
/// <param name="s1">The first <see cref="SYSTEMTIME"/> value.</param>
/// <param name="s2">The second <see cref="SYSTEMTIME"/> value.</param>
/// <returns><c>true</c> if <paramref name="s1"/> is greater than <paramref name="s2"/>; otherwise, <c>false</c>.</returns>
public static bool operator >(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 > s2.ToUInt64;
/// <summary>Determines whether one specified <see cref="SYSTEMTIME"/> is greater than or equal to another specified <see cref="SYSTEMTIME"/>.</summary>
/// <param name="s1">The first <see cref="SYSTEMTIME"/> value.</param>
/// <param name="s2">The second <see cref="SYSTEMTIME"/> value.</param>
/// <returns><c>true</c> if <paramref name="s1"/> is greater than or equal to <paramref name="s2"/>; otherwise, <c>false</c>.</returns>
public static bool operator >=(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 >= s2.ToUInt64;
/// <summary>Determines whether one specified <see cref="SYSTEMTIME"/> is less than another specified <see cref="SYSTEMTIME"/>.</summary>
/// <param name="s1">The first <see cref="SYSTEMTIME"/> value.</param>
/// <param name="s2">The second <see cref="SYSTEMTIME"/> value.</param>
/// <returns><c>true</c> if <paramref name="s1"/> is less than <paramref name="s2"/>; otherwise, <c>false</c>.</returns>
public static bool operator <(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 < s2.ToUInt64;
/// <summary>Determines whether one specified <see cref="SYSTEMTIME"/> is less than or equal to another specified <see cref="SYSTEMTIME"/>.</summary>
/// <param name="s1">The first <see cref="SYSTEMTIME"/> value.</param>
/// <param name="s2">The second <see cref="SYSTEMTIME"/> value.</param>
/// <returns><c>true</c> if <paramref name="s1"/> is less than or equal to <paramref name="s2"/>; otherwise, <c>false</c>.</returns>
public static bool operator <=(SYSTEMTIME s1, SYSTEMTIME s2) => s1.ToUInt64 <= s2.ToUInt64;
/// <summary>The minimum value supported by <see cref="SYSTEMTIME"/>.</summary>
public static readonly SYSTEMTIME MinValue = new SYSTEMTIME(1601, 1, 1);
/// <summary>The maximum value supported by <see cref="SYSTEMTIME"/>.</summary>
public static readonly SYSTEMTIME MaxValue = new SYSTEMTIME(30827, 12, 31, 23, 59, 59, 999);
/// <summary>Compares two instances of <see cref="SYSTEMTIME"/> and returns an integer that indicates whether the first instance is earlier than, the same as, or later than the second instance.</summary>
/// <param name="t1">The first object to compare. </param>
/// <param name="t2">The second object to compare. </param>
/// <returns>A signed number indicating the relative values of t1 and t2.
/// <list type="table">
/// <listheader><term>Value Type</term><term>Condition</term></listheader>
/// <item><term>Less than zero</term><term>t1 is earlier than t2.</term></item>
/// <item><term>Zero</term><term>t1 is the same as t2.</term></item>
/// <item><term>Greater than zero</term><term>t1 is later than t2.</term></item>
/// </list>
///</returns>
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;
}
/// <summary>Compares the current object with another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>
/// 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 <paramref name="other"/> parameter.Zero This object is equal to <paramref
/// name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
/// </returns>
public int CompareTo(SYSTEMTIME other) => Compare(this, other);
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
public bool Equals(SYSTEMTIME other) => this == other;
/// <summary>Determines whether the specified <see cref="System.Object"/>, is equal to this instance.</summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj) => base.Equals(obj);
/// <summary>Returns a hash code for this instance.</summary>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode()
{
var u = ToUInt64;
return unchecked((int)u) ^ (int)(u >> 32);
}
/// <summary>Converts this <see cref="SYSTEMTIME"/> instance to a <see cref="DateTime"/> instance.</summary>
/// <param name="kind">Indicates whether this <see cref="SYSTEMTIME"/> instance is local, universal or neither.</param>
/// <returns>An equivalent <see cref="DateTime"/> value.</returns>
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);
}
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
public override string ToString() => ToString(DateTimeKind.Unspecified, null, null);
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
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);
}
}