X Tutup
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using Npgsql; #pragma warning disable 1591 // ReSharper disable once CheckNamespace namespace NpgsqlTypes { /// /// A struct similar to DateTime but capable of storing PostgreSQL's timestamp and timestamptz types. DateTime /// is capable of storing values from year 1 to 9999 at 100-nanosecond precision, while PostgreSQL's timestamps /// store values from 4713BC to 5874897AD with 1-microsecond precision. /// #if !DNXCORE50 [Serializable] #endif public struct NpgsqlDateTime : IEquatable, IComparable, IComparable, IComparer, IComparer { #region Fields readonly NpgsqlDate _date; readonly TimeSpan _time; readonly InternalType _type; #endregion #region Constants public static readonly NpgsqlDateTime Epoch = new NpgsqlDateTime(NpgsqlDate.Epoch); public static readonly NpgsqlDateTime Era = new NpgsqlDateTime(NpgsqlDate.Era); public static readonly NpgsqlDateTime Infinity = new NpgsqlDateTime(InternalType.Infinity, NpgsqlDate.Era, TimeSpan.Zero); public static readonly NpgsqlDateTime NegativeInfinity = new NpgsqlDateTime(InternalType.NegativeInfinity, NpgsqlDate.Era, TimeSpan.Zero); #endregion #region Constructors NpgsqlDateTime(InternalType type, NpgsqlDate date, TimeSpan time) { _type = type; _date = date; _time = time; } public NpgsqlDateTime(NpgsqlDate date, TimeSpan time, DateTimeKind kind = DateTimeKind.Unspecified) : this(KindToInternalType(kind), date, time) {} public NpgsqlDateTime(NpgsqlDate date) : this(date, TimeSpan.Zero) {} public NpgsqlDateTime(int year, int month, int day, int hours, int minutes, int seconds, DateTimeKind kind=DateTimeKind.Unspecified) : this(new NpgsqlDate(year, month, day), new TimeSpan(0, hours, minutes, seconds), kind) {} public NpgsqlDateTime(int year, int month, int day, int hours, int minutes, int seconds, int milliseconds, DateTimeKind kind = DateTimeKind.Unspecified) : this(new NpgsqlDate(year, month, day), new TimeSpan(0, hours, minutes, seconds, milliseconds), kind) { } public NpgsqlDateTime(DateTime dateTime) : this(new NpgsqlDate(dateTime.Date), dateTime.TimeOfDay, dateTime.Kind) {} public NpgsqlDateTime(long ticks, DateTimeKind kind) : this(new DateTime(ticks, kind)) { } public NpgsqlDateTime(long ticks) : this(new DateTime(ticks, DateTimeKind.Unspecified)) { } #endregion #region Public Properties public NpgsqlDate Date { get { return _date; } } public TimeSpan Time { get { return _time; } } public int DayOfYear { get { return _date.DayOfYear; } } public int Year { get { return _date.Year; } } public int Month { get { return _date.Month; } } public int Day { get { return _date.Day; } } public DayOfWeek DayOfWeek { get { return _date.DayOfWeek; } } public bool IsLeapYear { get { return _date.IsLeapYear; } } public long Ticks { get { return _date.DaysSinceEra * NpgsqlTimeSpan.TicksPerDay + _time.Ticks; } } public int Milliseconds { get { return _time.Milliseconds; } } public int Seconds { get { return _time.Seconds; } } public int Minutes { get { return _time.Minutes; } } public int Hours { get { return _time.Hours; } } public bool IsInfinity { get { return _type == InternalType.Infinity; } } public bool IsNegativeInfinity { get { return _type == InternalType.NegativeInfinity; } } public bool IsFinite { get { switch (_type) { case InternalType.FiniteUnspecified: case InternalType.FiniteUtc: case InternalType.FiniteLocal: return true; case InternalType.Infinity: case InternalType.NegativeInfinity: return false; default: throw PGUtil.ThrowIfReached(); } } } public DateTimeKind Kind { get { switch (_type) { case InternalType.FiniteUtc: return DateTimeKind.Utc; case InternalType.FiniteLocal: return DateTimeKind.Local; case InternalType.FiniteUnspecified: case InternalType.Infinity: case InternalType.NegativeInfinity: return DateTimeKind.Unspecified; default: throw new ArgumentOutOfRangeException(); } } } public DateTime DateTime { get { if (!IsFinite) throw new InvalidCastException("Can't convert infinite timestamp values to DateTime"); if (Year < 1 || Year > 9999) throw new InvalidCastException("Out of the range of DateTime (year must be between 1 and 9999)"); Contract.EndContractBlock(); return new DateTime(Year, Month, Day, 0, 0, 0, Kind) + Time; } } public NpgsqlDateTime ToUniversalTime() { switch (_type) { case InternalType.FiniteUnspecified: // Treat as Local case InternalType.FiniteLocal: return new NpgsqlDateTime(Subtract(TimeZoneInfo.Local.BaseUtcOffset).Ticks, DateTimeKind.Utc); case InternalType.FiniteUtc: case InternalType.Infinity: case InternalType.NegativeInfinity: return this; default: throw PGUtil.ThrowIfReached(); } } public NpgsqlDateTime ToLocalTime() { switch (_type) { case InternalType.FiniteUnspecified: // Treat as UTC case InternalType.FiniteUtc: return new NpgsqlDateTime(Add(TimeZoneInfo.Local.BaseUtcOffset).Ticks, DateTimeKind.Local); case InternalType.FiniteLocal: case InternalType.Infinity: case InternalType.NegativeInfinity: return this; default: throw PGUtil.ThrowIfReached(); } } public static NpgsqlDateTime Now { get { return new NpgsqlDateTime(DateTime.Now); } } #endregion #region String Conversions public override string ToString() { switch (_type) { case InternalType.Infinity: return "infinity"; case InternalType.NegativeInfinity: return "-infinity"; default: return string.Format("{0} {1}", _date, _time); } } public static NpgsqlDateTime Parse(string str) { if (str == null) { throw new NullReferenceException(); } switch (str = str.Trim().ToLowerInvariant()) { case "infinity": return Infinity; case "-infinity": return NegativeInfinity; default: try { int idxSpace = str.IndexOf(' '); string datePart = str.Substring(0, idxSpace); if (str.Contains("bc")) { datePart += " BC"; } int idxSecond = str.IndexOf(' ', idxSpace + 1); if (idxSecond == -1) { idxSecond = str.Length; } string timePart = str.Substring(idxSpace + 1, idxSecond - idxSpace - 1); return new NpgsqlDateTime(NpgsqlDate.Parse(datePart), TimeSpan.Parse(timePart)); } catch (OverflowException) { throw; } catch { throw new FormatException(); } } } #endregion #region Comparisons public bool Equals(NpgsqlDateTime other) { switch (_type) { case InternalType.Infinity: return other._type == InternalType.Infinity; case InternalType.NegativeInfinity: return other._type == InternalType.NegativeInfinity; default: return other._type == _type && _date.Equals(other._date) && _time.Equals(other._time); } } public override bool Equals(object obj) { return obj is NpgsqlDateTime && Equals((NpgsqlDateTime)obj); } public override int GetHashCode() { switch (_type) { case InternalType.Infinity: return int.MaxValue; case InternalType.NegativeInfinity: return int.MinValue; default: return _date.GetHashCode() ^ PGUtil.RotateShift(_time.GetHashCode(), 16); } } public int CompareTo(NpgsqlDateTime other) { switch (_type) { case InternalType.Infinity: return other._type == InternalType.Infinity ? 0 : 1; case InternalType.NegativeInfinity: return other._type == InternalType.NegativeInfinity ? 0 : -1; default: switch (other._type) { case InternalType.Infinity: return -1; case InternalType.NegativeInfinity: return 1; default: int cmp = _date.CompareTo(other._date); return cmp == 0 ? _time.CompareTo(_time) : cmp; } } } public int CompareTo(object obj) { if (obj == null) { return 1; } if (obj is NpgsqlDateTime) { return CompareTo((NpgsqlDateTime)obj); } throw new ArgumentException(); } public int Compare(NpgsqlDateTime x, NpgsqlDateTime y) { return x.CompareTo(y); } public int Compare(object x, object y) { if (x == null) { return y == null ? 0 : -1; } if (y == null) { return 1; } if (!(x is IComparable) || !(y is IComparable)) { throw new ArgumentException(); } return ((IComparable)x).CompareTo(y); } #endregion #region Arithmetic /// /// Returns a new that adds the value of the specified TimeSpan to the value of this instance. /// /// A positive or negative time interval. /// An object whose value is the sum of the date and time represented by this instance and the time interval represented by value. [Pure] public NpgsqlDateTime Add(NpgsqlTimeSpan value) { return AddTicks(value.Ticks); } /// /// Returns a new that adds the value of the specified to the value of this instance. /// /// A positive or negative time interval. /// An object whose value is the sum of the date and time represented by this instance and the time interval represented by value. [Pure] public NpgsqlDateTime Add(TimeSpan value) { return AddTicks(value.Ticks); } /// /// Returns a new that adds the specified number of years to the value of this instance. /// /// A number of years. The value parameter can be negative or positive. /// An object whose value is the sum of the date and time represented by this instance and the number of years represented by value. [Pure] public NpgsqlDateTime AddYears(int value) { switch (_type) { case InternalType.Infinity: case InternalType.NegativeInfinity: return this; default: return new NpgsqlDateTime(_type, _date.AddYears(value), _time); } } /// /// Returns a new that adds the specified number of months to the value of this instance. /// /// A number of months. The months parameter can be negative or positive. /// An object whose value is the sum of the date and time represented by this instance and months. [Pure] public NpgsqlDateTime AddMonths(int value) { switch (_type) { case InternalType.Infinity: case InternalType.NegativeInfinity: return this; default: return new NpgsqlDateTime(_type, _date.AddMonths(value), _time); } } /// /// Returns a new that adds the specified number of days to the value of this instance. /// /// A number of whole and fractional days. The value parameter can be negative or positive. /// An object whose value is the sum of the date and time represented by this instance and the number of days represented by value. [Pure] public NpgsqlDateTime AddDays(double value) { return Add(TimeSpan.FromDays(value)); } /// /// Returns a new that adds the specified number of hours to the value of this instance. /// /// A number of whole and fractional hours. The value parameter can be negative or positive. /// An object whose value is the sum of the date and time represented by this instance and the number of hours represented by value. [Pure] public NpgsqlDateTime AddHours(double value) { return Add(TimeSpan.FromHours(value)); } /// /// Returns a new that adds the specified number of minutes to the value of this instance. /// /// A number of whole and fractional minutes. The value parameter can be negative or positive. /// An object whose value is the sum of the date and time represented by this instance and the number of minutes represented by value. [Pure] public NpgsqlDateTime AddMinutes(double value) { return Add(TimeSpan.FromMinutes(value)); } /// /// Returns a new that adds the specified number of minutes to the value of this instance. /// /// A number of whole and fractional minutes. The value parameter can be negative or positive. /// An object whose value is the sum of the date and time represented by this instance and the number of minutes represented by value. [Pure] public NpgsqlDateTime AddSeconds(double value) { return Add(TimeSpan.FromSeconds(value)); } /// /// Returns a new that adds the specified number of milliseconds to the value of this instance. /// /// A number of whole and fractional milliseconds. The value parameter can be negative or positive. Note that this value is rounded to the nearest integer. /// An object whose value is the sum of the date and time represented by this instance and the number of milliseconds represented by value. [Pure] public NpgsqlDateTime AddMilliseconds(double value) { return Add(TimeSpan.FromMilliseconds(value)); } /// /// Returns a new that adds the specified number of ticks to the value of this instance. /// /// A number of 100-nanosecond ticks. The value parameter can be positive or negative. /// An object whose value is the sum of the date and time represented by this instance and the time represented by value. [Pure] public NpgsqlDateTime AddTicks(long value) { switch (_type) { case InternalType.Infinity: case InternalType.NegativeInfinity: return this; default: return new NpgsqlDateTime(Ticks + value, Kind); } } [Pure] public NpgsqlDateTime Subtract(NpgsqlTimeSpan interval) { return Add(-interval); } [Pure] public NpgsqlTimeSpan Subtract(NpgsqlDateTime timestamp) { switch (_type) { case InternalType.Infinity: case InternalType.NegativeInfinity: throw new ArgumentOutOfRangeException("this", "You cannot subtract infinity timestamps"); } switch (timestamp._type) { case InternalType.Infinity: case InternalType.NegativeInfinity: throw new ArgumentOutOfRangeException("timestamp", "You cannot subtract infinity timestamps"); } return new NpgsqlTimeSpan(0, _date.DaysSinceEra - timestamp._date.DaysSinceEra, _time.Ticks - timestamp._time.Ticks); } #endregion #region Operators public static NpgsqlDateTime operator +(NpgsqlDateTime timestamp, NpgsqlTimeSpan interval) { return timestamp.Add(interval); } public static NpgsqlDateTime operator +(NpgsqlTimeSpan interval, NpgsqlDateTime timestamp) { return timestamp.Add(interval); } public static NpgsqlDateTime operator -(NpgsqlDateTime timestamp, NpgsqlTimeSpan interval) { return timestamp.Subtract(interval); } public static NpgsqlTimeSpan operator -(NpgsqlDateTime x, NpgsqlDateTime y) { return x.Subtract(y); } public static bool operator ==(NpgsqlDateTime x, NpgsqlDateTime y) { return x.Equals(y); } public static bool operator !=(NpgsqlDateTime x, NpgsqlDateTime y) { return !(x == y); } public static bool operator <(NpgsqlDateTime x, NpgsqlDateTime y) { return x.CompareTo(y) < 0; } public static bool operator >(NpgsqlDateTime x, NpgsqlDateTime y) { return x.CompareTo(y) > 0; } public static bool operator <=(NpgsqlDateTime x, NpgsqlDateTime y) { return x.CompareTo(y) <= 0; } public static bool operator >=(NpgsqlDateTime x, NpgsqlDateTime y) { return x.CompareTo(y) >= 0; } #endregion [Pure] public NpgsqlDateTime Normalize() { return Add(NpgsqlTimeSpan.Zero); } static InternalType KindToInternalType(DateTimeKind kind) { switch (kind) { case DateTimeKind.Unspecified: return InternalType.FiniteUnspecified; case DateTimeKind.Utc: return InternalType.FiniteUtc; case DateTimeKind.Local: return InternalType.FiniteLocal; default: throw PGUtil.ThrowIfReached(); } } enum InternalType { FiniteUnspecified, FiniteUtc, FiniteLocal, Infinity, NegativeInfinity } } }
X Tutup