X Tutup
// NpgsqlTypes\DateDatatypes.cs // // Author: // Jon Hanna. (jon@hackcraft.net) // // Copyright (C) 2007-2008 The Npgsql Development Team // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // Permission to use, copy, modify, and distribute this software and its // documentation for any purpose, without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph and the following two paragraphs appear in all copies. // // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. // // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. using System; using System.Collections; using System.Collections.Generic; using System.Text; using Npgsql; //TODO: Lots of convenience methods! There should be nothing you can do with datetime and timestamp that you can't //do just as well with these - and hence no reason not to choose these if they are appropriate. //Similarly, lots of documentation is a must. // Keep the xml comment warning quiet for this file. #pragma warning disable 1591 namespace NpgsqlTypes { /// /// Represents the PostgreSQL interval datatype. /// PostgreSQL differs from .NET in how it's interval type doesn't assume 24 hours in a day /// (to deal with 23- and 25-hour days caused by daylight savings adjustments) and has a concept /// of months that doesn't exist in .NET's class. (Neither datatype /// has any concessions for leap-seconds). /// For most uses just casting to and from TimeSpan will work correctly — in particular, /// the results of subtracting one or the PostgreSQL date, time and /// timestamp types from another should be the same whether you do so in .NET or PostgreSQL — /// but if the handling of days and months in PostgreSQL is important to your application then you /// should use this class instead of . /// If you don't know whether these differences are important to your application, they /// probably arent! Just use and do not use this class directly ☺ /// To avoid forcing unnecessary provider-specific concerns on users who need not be concerned /// with them a call to on a field containing an /// value will return a rather than an /// . If you need the extra functionality of /// then use . /// /// /// /// /// /// [Serializable] public struct NpgsqlInterval : IComparable, IComparer, IEquatable, IComparable, IComparer { #region Constants /// /// Represents the number of ticks (100ns periods) in one microsecond. This field is constant. /// public const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond/1000; /// /// Represents the number of ticks (100ns periods) in one millisecond. This field is constant. /// public const long TicksPerMillsecond = TimeSpan.TicksPerMillisecond; /// /// Represents the number of ticks (100ns periods) in one second. This field is constant. /// public const long TicksPerSecond = TimeSpan.TicksPerSecond; /// /// Represents the number of ticks (100ns periods) in one minute. This field is constant. /// public const long TicksPerMinute = TimeSpan.TicksPerMinute; /// /// Represents the number of ticks (100ns periods) in one hour. This field is constant. /// public const long TicksPerHour = TimeSpan.TicksPerHour; /// /// Represents the number of ticks (100ns periods) in one day. This field is constant. /// public const long TicksPerDay = TimeSpan.TicksPerDay; /// /// Represents the number of hours in one day (assuming no daylight savings adjustments). This field is constant. /// public const int HoursPerDay = 24; /// /// Represents the number of days assumed in one month if month justification or unjustifcation is performed. /// This is set to 30 for consistency with PostgreSQL. Note that this is means that month adjustments cause /// a year to be taken as 30 × 12 = 360 rather than 356/366 days. /// public const int DaysPerMonth = 30; /// /// Represents the number of ticks (100ns periods) in one day, assuming 30 days per month. /// public const long TicksPerMonth = TicksPerDay*DaysPerMonth; /// /// Represents the number of months in a year. This field is constant. /// public const int MonthsPerYear = 12; /// /// Represents the maximum . This field is read-only. /// public static readonly NpgsqlInterval MaxValue = new NpgsqlInterval(long.MaxValue); /// /// Represents the minimum . This field is read-only. /// public static readonly NpgsqlInterval MinValue = new NpgsqlInterval(long.MinValue); /// /// Represents the zero . This field is read-only. /// public static readonly NpgsqlInterval Zero = new NpgsqlInterval(0); #endregion private readonly int _months; private readonly int _days; private readonly long _ticks; #region Constructors /// /// Initializes a new to the specified number of ticks. /// /// A time period expressed in 100ns units. public NpgsqlInterval(long ticks) : this(new TimeSpan(ticks)) { } /// /// Initializes a new to hold the same time as a /// /// A time period expressed in a public NpgsqlInterval(TimeSpan timespan) : this(0, timespan.Days, timespan.Ticks - (TicksPerDay * timespan.Days)) { } /// /// Initializes a new to the specified number of months, days /// & ticks. /// /// Number of months. /// Number of days. /// Number of 100ns units. public NpgsqlInterval(int months, int days, long ticks) { _months = months; _days = days; _ticks = ticks; } /// /// Initializes a new to the specified number of /// days, hours, minutes & seconds. /// /// Number of days. /// Number of hours. /// Number of minutes. /// Number of seconds. public NpgsqlInterval(int days, int hours, int minutes, int seconds) : this(0, days, new TimeSpan(hours, minutes, seconds).Ticks) { } /// /// Initializes a new to the specified number of /// days, hours, minutes, seconds & milliseconds. /// /// Number of days. /// Number of hours. /// Number of minutes. /// Number of seconds. /// Number of milliseconds. public NpgsqlInterval(int days, int hours, int minutes, int seconds, int milliseconds) : this(0, days, new TimeSpan(0, hours, minutes, seconds, milliseconds).Ticks) { } /// /// Initializes a new to the specified number of /// months, days, hours, minutes, seconds & milliseconds. /// /// Number of months. /// Number of days. /// Number of hours. /// Number of minutes. /// Number of seconds. /// Number of milliseconds. public NpgsqlInterval(int months, int days, int hours, int minutes, int seconds, int milliseconds) : this(months, days, new TimeSpan(0, hours, minutes, seconds, milliseconds).Ticks) { } /// /// Initializes a new to the specified number of /// years, months, days, hours, minutes, seconds & milliseconds. /// Years are calculated exactly equivalent to 12 months. /// /// Number of years. /// Number of months. /// Number of days. /// Number of hours. /// Number of minutes. /// Number of seconds. /// Number of milliseconds. public NpgsqlInterval(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds) : this(years*12 + months, days, new TimeSpan(0, hours, minutes, seconds, milliseconds).Ticks) { } #endregion #region Whole Parts /// /// The total number of ticks(100ns units) contained. This is the resolution of the /// type. This ignores the number of days and /// months held. If you want them included use first. /// The resolution of the PostgreSQL /// interval type is by default 1µs = 1,000 ns. It may be smaller as follows: /// /// /// interval(0) /// resolution of 1s (1 second) /// /// /// interval(1) /// resolution of 100ms = 0.1s (100 milliseconds) /// /// /// interval(2) /// resolution of 10ms = 0.01s (10 milliseconds) /// /// /// interval(3) /// resolution of 1ms = 0.001s (1 millisecond) /// /// /// interval(4) /// resolution of 100µs = 0.0001s (100 microseconds) /// /// /// interval(5) /// resolution of 10µs = 0.00001s (10 microseconds) /// /// /// interval(6) or interval /// resolution of 1µs = 0.000001s (1 microsecond) /// /// /// As such, if the 100-nanosecond resolution is significant to an application, a PostgreSQL interval will /// not suffice for those purposes. /// In more frequent cases though, the resolution of the interval suffices. /// will always suffice to handle the resolution of any interval value, and upon /// writing to the database, will be rounded to the resolution used. /// /// The number of ticks in the instance. /// public long Ticks { get { return _ticks; } } /// /// Gets the number of whole microseconds held in the instance. /// An in the range [-999999, 999999]. /// public int Microseconds { get { return (int) ((_ticks/10)%1000000); } } /// /// Gets the number of whole milliseconds held in the instance. /// An in the range [-999, 999]. /// public int Milliseconds { get { return (int) ((_ticks/TicksPerMillsecond)%1000); } } /// /// Gets the number of whole seconds held in the instance. /// An in the range [-59, 59]. /// public int Seconds { get { return (int) ((_ticks/TicksPerSecond)%60); } } /// /// Gets the number of whole minutes held in the instance. /// An in the range [-59, 59]. /// public int Minutes { get { return (int) ((_ticks/TicksPerMinute)%60); } } /// /// Gets the number of whole hours held in the instance. /// Note that this can be less than -23 or greater than 23 unless /// has been used to produce this instance. /// public int Hours { get { return (int) (_ticks/TicksPerHour); } } /// /// Gets the number of days held in the instance. /// Note that this does not pay attention to a time component with -24 or less hours or /// 24 or more hours, unless has been called to produce this instance. /// public int Days { get { return _days; } } /// /// Gets the number of months held in the instance. /// Note that this does not pay attention to a day component with -30 or less days or /// 30 or more days, unless has been called to produce this instance. /// public int Months { get { return _months; } } /// /// Returns a representing the time component of the instance. /// Note that this may have a value beyond the range ±23:59:59.9999999 unless /// has been called to produce this instance. /// public TimeSpan Time { get { return new TimeSpan(_ticks); } } #endregion #region Total Parts /// /// The total number of ticks (100ns units) in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public long TotalTicks { get { return Ticks + Days*TicksPerDay + Months*TicksPerMonth; } } /// /// The total number of microseconds in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public double TotalMicroseconds { get { return TotalTicks/10d; } } /// /// The total number of milliseconds in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public double TotalMilliseconds { get { return TotalTicks/(double) TicksPerMillsecond; } } /// /// The total number of seconds in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public double TotalSeconds { get { return TotalTicks/(double) TicksPerSecond; } } /// /// The total number of minutes in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public double TotalMinutes { get { return TotalTicks/(double) TicksPerMinute; } } /// /// The total number of hours in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public double TotalHours { get { return TotalTicks/(double) TicksPerHour; } } /// /// The total number of days in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public double TotalDays { get { return TotalTicks/(double) TicksPerDay; } } /// /// The total number of months in the instance, assuming 24 hours in each day and /// 30 days in a month. /// public double TotalMonths { get { return TotalTicks/(double) TicksPerMonth; } } #endregion #region Create From Part /// /// Creates an from a number of ticks. /// /// The number of ticks (100ns units) in the interval. /// A d with the given number of ticks. public static NpgsqlInterval FromTicks(long ticks) { return new NpgsqlInterval(ticks).Canonicalize(); } /// /// Creates an from a number of microseconds. /// /// The number of microseconds in the interval. /// A d with the given number of microseconds. public static NpgsqlInterval FromMicroseconds(double micro) { return FromTicks((long) (micro*TicksPerMicrosecond)); } /// /// Creates an from a number of milliseconds. /// /// The number of milliseconds in the interval. /// A d with the given number of milliseconds. public static NpgsqlInterval FromMilliseconds(double milli) { return FromTicks((long) (milli*TicksPerMillsecond)); } /// /// Creates an from a number of seconds. /// /// The number of seconds in the interval. /// A d with the given number of seconds. public static NpgsqlInterval FromSeconds(double seconds) { return FromTicks((long) (seconds*TicksPerSecond)); } /// /// Creates an from a number of minutes. /// /// The number of minutes in the interval. /// A d with the given number of minutes. public static NpgsqlInterval FromMinutes(double minutes) { return FromTicks((long) (minutes*TicksPerMinute)); } /// /// Creates an from a number of hours. /// /// The number of hours in the interval. /// A d with the given number of hours. public static NpgsqlInterval FromHours(double hours) { return FromTicks((long) (hours*TicksPerHour)); } /// /// Creates an from a number of days. /// /// The number of days in the interval. /// A d with the given number of days. public static NpgsqlInterval FromDays(double days) { return FromTicks((long) (days*TicksPerDay)); } /// /// Creates an from a number of months. /// /// The number of months in the interval. /// A d with the given number of months. public static NpgsqlInterval FromMonths(double months) { return FromTicks((long) (months*TicksPerMonth)); } #endregion #region Arithmetic /// /// Adds another interval to this instance and returns the result. /// /// An to add to this instance. /// An whose values are the sums of the two instances. public NpgsqlInterval Add(NpgsqlInterval interval) { return new NpgsqlInterval(Months + interval.Months, Days + interval.Days, Ticks + interval.Ticks); } /// /// Subtracts another interval from this instance and returns the result. /// /// An to subtract from this instance. /// An whose values are the differences of the two instances. public NpgsqlInterval Subtract(NpgsqlInterval interval) { return new NpgsqlInterval(Months - interval.Months, Days - interval.Days, Ticks - interval.Ticks); } /// /// Returns an whose value is the negated value of this instance. /// /// An whose value is the negated value of this instance. public NpgsqlInterval Negate() { return new NpgsqlInterval(-Months, -Days, -Ticks); } /// /// This absolute value of this instance. In the case of some, but not all, components being negative, /// the rules used for justification are used to determine if the instance is positive or negative. /// /// An whose value is the absolute value of this instance. public NpgsqlInterval Duration() { return UnjustifyInterval().Ticks < 0 ? Negate() : this; } #endregion #region Justification /// /// Equivalent to PostgreSQL's justify_days function. /// /// An based on this one, but with any hours outside of the range [-23, 23] /// converted into days. public NpgsqlInterval JustifyDays() { return new NpgsqlInterval(Months, Days + (int) (Ticks/TicksPerDay), Ticks%TicksPerDay); } /// /// Opposite to PostgreSQL's justify_days function. /// /// An based on this one, but with any days converted to multiples of ±24hours. public NpgsqlInterval UnjustifyDays() { return new NpgsqlInterval(Months, 0, Ticks + Days*TicksPerDay); } /// /// Equivalent to PostgreSQL's justify_months function. /// /// An based on this one, but with any days outside of the range [-30, 30] /// converted into months. public NpgsqlInterval JustifyMonths() { return new NpgsqlInterval(Months + Days/DaysPerMonth, Days%DaysPerMonth, Ticks); } /// /// Opposite to PostgreSQL's justify_months function. /// /// An based on this one, but with any months converted to multiples of ±30days. public NpgsqlInterval UnjustifyMonths() { return new NpgsqlInterval(0, Days + Months*DaysPerMonth, Ticks); } /// /// Equivalent to PostgreSQL's justify_interval function. /// /// An based on this one, /// but with any months converted to multiples of ±30days /// and then with any days converted to multiples of ±24hours public NpgsqlInterval JustifyInterval() { return JustifyMonths().JustifyDays(); } /// /// Opposite to PostgreSQL's justify_interval function. /// /// An based on this one, but with any months converted to multiples of ±30days and then any days converted to multiples of ±24hours; public NpgsqlInterval UnjustifyInterval() { return new NpgsqlInterval(Ticks + Days*TicksPerDay + Months*DaysPerMonth*TicksPerDay); } /// /// Produces a canonical NpgslInterval with 0 months and hours in the range of [-23, 23]. /// /// /// While the fact that for many purposes, two different instances could be considered /// equivalent (e.g. one with 2days, 3hours and one with 1day 27hours) there are different possible canonical forms. /// /// E.g. we could move all excess hours into days and all excess days into months and have the most readable form, /// or we could move everything into the ticks and have the form that allows for the easiest arithmetic) the form /// chosen has two important properties that make it the best choice. /// First, it is closest two how /// objects are most often represented. Second, it is compatible with results of many /// PostgreSQL functions, particularly with age() and the results of subtracting one date, time or timestamp from /// another. /// /// Note that the results of casting a to is /// canonicalised. /// /// /// An based on this one, but with months converted to multiples of ±30days and with any hours outside of the range [-23, 23] /// converted into days. public NpgsqlInterval Canonicalize() { return new NpgsqlInterval(0, Days + Months*DaysPerMonth + (int) (Ticks/TicksPerDay), Ticks%TicksPerDay); } #endregion #region Casts /// /// Implicit cast of a to an /// /// A /// An eqivalent, canonical, . public static implicit operator NpgsqlInterval(TimeSpan timespan) { return new NpgsqlInterval(timespan).Canonicalize(); } /// /// Implicit cast of an to a . /// /// A . /// An equivalent . public static explicit operator TimeSpan(NpgsqlInterval interval) { return new TimeSpan(interval.Ticks + interval.Days*TicksPerDay + interval.Months*DaysPerMonth*TicksPerDay); } #endregion #region Comparison /// /// Returns true if another is exactly the same as this instance. /// /// An for comparison. /// true if the two instances are exactly the same, /// false otherwise. public bool Equals(NpgsqlInterval other) { return Ticks == other.Ticks && Days == other.Days && Months == other.Months; } /// /// Returns true if another object is an , that is exactly the same as /// this instance /// /// An for comparison. /// true if the argument is an and is exactly the same /// as this one, false otherwise. public override bool Equals(object obj) { if (obj == null) { return false; } if (obj is NpgsqlInterval) { return Equals((NpgsqlInterval) obj); } return false; } /// /// Compares two instances. /// /// The first . /// The second . /// 0 if the two are equal or equivalent. A value greater than zero if x is greater than y, /// a value less than zero if x is less than y. public static int Compare(NpgsqlInterval x, NpgsqlInterval y) { return x.CompareTo(y); } int IComparer.Compare(NpgsqlInterval x, NpgsqlInterval y) { return x.CompareTo(y); } int IComparer.Compare(object x, object y) { if (x == null) { return y == null ? 0 : 1; } if (y == null) { return -1; } try { return ((IComparable) x).CompareTo(y); } catch (Exception) { throw new ArgumentException(); } } /// /// A hash code suitable for uses with hashing algorithms. /// /// An signed integer. public override int GetHashCode() { return UnjustifyInterval().Ticks.GetHashCode(); } /// /// Compares this instance with another/ /// /// An to compare this with. /// 0 if the instances are equal or equivalent. A value less than zero if /// this instance is less than the argument. A value greater than zero if this instance /// is greater than the instance. public int CompareTo(NpgsqlInterval other) { return UnjustifyInterval().Ticks.CompareTo(other.UnjustifyInterval().Ticks); } /// /// Compares this instance with another/ /// /// An object to compare this with. /// 0 if the argument is an and the instances are equal or equivalent. /// A value less than zero if the argument is an and /// this instance is less than the argument. /// A value greater than zero if the argument is an and this instance /// is greater than the instance. /// A value greater than zero if the argument is null. /// The argument is not an . public int CompareTo(object other) { if (other == null) { return 1; } else if (other is NpgsqlInterval) { return CompareTo((NpgsqlInterval) other); } else { throw new ArgumentException(); } } #endregion #region To And From Strings /// /// Parses a and returns a instance. /// Designed to use the formats generally returned by PostgreSQL. /// /// The to parse. /// An represented by the argument. /// The string was null. /// A value obtained from parsing the string exceeded the values allowed for the relevant component. /// The string was not in a format that could be parsed to produce an . public static NpgsqlInterval Parse(string str) { if (str == null) { throw new ArgumentNullException("str"); } str = str.Replace('s', ' '); //Quick and easy way to catch plurals. try { int years = 0; int months = 0; int days = 0; int hours = 0; int minutes = 0; decimal seconds = 0m; int idx = str.IndexOf("year"); if (idx > 0) { years = int.Parse(str.Substring(0, idx)); str = SafeSubstring(str, idx + 5); } idx = str.IndexOf("mon"); if (idx > 0) { months = int.Parse(str.Substring(0, idx)); str = SafeSubstring(str, idx + 4); } idx = str.IndexOf("day"); if (idx > 0) { days = int.Parse(str.Substring(0, idx)); str = SafeSubstring(str, idx + 4).Trim(); } if (str.Length > 0) { bool isNegative = str[0] == '-'; string[] parts = str.Split(':'); switch (parts.Length) //One of those times that fall-through would actually be good. { case 1: hours = int.Parse(parts[0]); break; case 2: hours = int.Parse(parts[0]); minutes = int.Parse(parts[1]); break; default: hours = int.Parse(parts[0]); minutes = int.Parse(parts[1]); seconds = decimal.Parse(parts[2], System.Globalization.CultureInfo.InvariantCulture.NumberFormat); break; } if (isNegative) { minutes *= -1; seconds *= -1; } } long ticks = hours*TicksPerHour + minutes*TicksPerMinute + (long) (seconds*TicksPerSecond); return new NpgsqlInterval(years*MonthsPerYear + months, days, ticks); } catch (OverflowException) { throw; } catch (Exception) { throw new FormatException(); } } private static string SafeSubstring(string s, int startIndex) { if (startIndex >= s.Length) return string.Empty; else return s.Substring(startIndex); } /// /// Attempt to parse a to produce an . /// /// The to parse. /// (out) The produced, or if the parsing failed. /// true if the parsing succeeded, false otherwise. public static bool TryParse(string str, out NpgsqlInterval result) { try { result = Parse(str); return true; } catch (Exception) { result = Zero; return false; } } /// /// Create a representation of the instance. /// The format returned is of the form: /// [M mon[s]] [d day[s]] [HH:mm:ss[.f[f[f[f[f[f[f[f[f]]]]]]]]]] /// A zero is represented as 00:00:00 /// /// Ticks are 100ns, Postgress resolution is only to 1µs at most. Hence we lose 1 or more decimal /// precision in storing values in the database. Despite this, this method will output that extra /// digit of precision. It's forward-compatible with any future increases in resolution up to 100ns, /// and also makes this ToString() more applicable to any other use-case. /// /// /// The representation. public override string ToString() { StringBuilder sb = new StringBuilder(); if (Months != 0) { sb.Append(Months).Append(Math.Abs(Months) == 1 ? " mon " : " mons "); } if (Days != 0) { if (Months < 0 && Days > 0) { sb.Append('+'); } sb.Append(Days).Append(Math.Abs(Days) == 1 ? " day " : " days "); } if (Ticks != 0 || sb.Length == 0) { if(Ticks < 0) { sb.Append('-'); } else if (Days < 0 || (Days == 0 && Months < 0)) { sb.Append('+'); } // calculate total seconds and then subtract total whole minutes in seconds to get just the seconds and fractional part decimal seconds = _ticks / (decimal)TicksPerSecond - (_ticks / TicksPerMinute) * 60; sb.Append(Math.Abs(Hours).ToString("D2")).Append(':').Append(Math.Abs(Minutes).ToString("D2")).Append(':').Append(Math.Abs(seconds).ToString("0#.######", System.Globalization.CultureInfo.InvariantCulture.NumberFormat)); } if (sb[sb.Length - 1] == ' ') { sb.Remove(sb.Length - 1, 1); } return sb.ToString(); } #endregion #region Common Operators /// /// Adds two together. /// /// The first to add. /// The second to add. /// An whose values are the sum of the arguments. public static NpgsqlInterval operator +(NpgsqlInterval x, NpgsqlInterval y) { return x.Add(y); } /// /// Subtracts one from another. /// /// The to subtract the other from. /// The to subtract from the other. /// An whose values are the difference of the arguments public static NpgsqlInterval operator -(NpgsqlInterval x, NpgsqlInterval y) { return x.Subtract(y); } /// /// Returns true if two are exactly the same. /// /// The first to compare. /// The second to compare. /// true if the two arguments are exactly the same, false otherwise. public static bool operator ==(NpgsqlInterval x, NpgsqlInterval y) { return x.Equals(y); } /// /// Returns false if two are exactly the same. /// /// The first to compare. /// The second to compare. /// false if the two arguments are exactly the same, true otherwise. public static bool operator !=(NpgsqlInterval x, NpgsqlInterval y) { return !(x == y); } /// /// Compares two instances to see if the first is less than the second /// /// The first to compare. /// The second to compare. /// true if the first is less than second, false otherwise. public static bool operator <(NpgsqlInterval x, NpgsqlInterval y) { return x.UnjustifyInterval().Ticks < y.UnjustifyInterval().Ticks; } /// /// Compares two instances to see if the first is less than or equivalent to the second /// /// The first to compare. /// The second to compare. /// true if the first is less than or equivalent to second, false otherwise. public static bool operator <=(NpgsqlInterval x, NpgsqlInterval y) { return x.UnjustifyInterval().Ticks <= y.UnjustifyInterval().Ticks; } /// /// Compares two instances to see if the first is greater than the second /// /// The first to compare. /// The second to compare. /// true if the first is greater than second, false otherwise. public static bool operator >(NpgsqlInterval x, NpgsqlInterval y) { return !(x <= y); } /// /// Compares two instances to see if the first is greater than or equivalent the second /// /// The first to compare. /// The second to compare. /// true if the first is greater than or equivalent to the second, false otherwise. public static bool operator >=(NpgsqlInterval x, NpgsqlInterval y) { return !(x < y); } /// /// Returns the instance. /// /// An . /// The argument. public static NpgsqlInterval operator +(NpgsqlInterval x) { return x; } /// /// Negates an instance. /// /// An . /// The negation of the argument. public static NpgsqlInterval operator -(NpgsqlInterval x) { return x.Negate(); } #endregion } [Serializable] public struct NpgsqlDate : IEquatable, IComparable, IComparable, IComparer, IComparer { private static readonly int[] CommonYearDays = new int[] {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; private static readonly int[] LeapYearDays = new int[] {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; private static readonly int[] CommonYearMaxes = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; private static readonly int[] LeapYearMaxes = new int[] {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; public const int MaxYear = 5874897; public const int MinYear = -4714; public static readonly NpgsqlDate Epoch = new NpgsqlDate(1970, 1, 1); public static readonly NpgsqlDate MaxCalculableValue = new NpgsqlDate(MaxYear, 12, 31); public static readonly NpgsqlDate MinCalculableValue = new NpgsqlDate(MinYear, 11, 24); public static readonly NpgsqlDate Era = new NpgsqlDate(0); public static NpgsqlDate Now { get { return new NpgsqlDate(DateTime.Now); } } public static NpgsqlDate Today { get { return Now; } } public static NpgsqlDate Yesterday { get { return Now.AddDays(-1); } } public static NpgsqlDate Tomorrow { get { return Now.AddDays(1); } } public static NpgsqlDate Parse(string str) { if (str == null) { throw new ArgumentNullException("str"); } // Handle -infinity and infinity special values. if (str == "-infinity") return new NpgsqlDate(DateTime.MinValue); if (str == "infinity") return new NpgsqlDate(DateTime.MaxValue); str = str.Trim(); try { int idx = str.IndexOf('-'); if (idx == -1) { throw new FormatException(); } int year = int.Parse(str.Substring(0, idx)); int idxLast = idx + 1; if ((idx = str.IndexOf('-', idxLast)) == -1) { throw new FormatException(); } int month = int.Parse(str.Substring(idxLast, idx - idxLast)); idxLast = idx + 1; if ((idx = str.IndexOf(' ', idxLast)) == -1) { idx = str.Length; } int day = int.Parse(str.Substring(idxLast, idx - idxLast)); if (str.Contains("BC")) { year = -year; } return new NpgsqlDate(year, month, day); } catch (OverflowException) { throw; } catch (Exception) { throw new FormatException(); } } public static bool TryParse(string str, out NpgsqlDate date) { try { date = Parse(str); return true; } catch { date = Era; return false; } } //Number of days since January 1st CE (January 1st EV). 1 Jan 1 CE = 0, 2 Jan 1 CE = 1, 31 Dec 1 BCE = -1, etc. private readonly int _daysSinceEra; public NpgsqlDate(int days) { _daysSinceEra = days; } public NpgsqlDate(DateTime dateTime) : this((int) (dateTime.Ticks/TimeSpan.TicksPerDay)) { } public NpgsqlDate(NpgsqlDate copyFrom) : this(copyFrom._daysSinceEra) { } public NpgsqlDate(int year, int month, int day) { if (year == 0 || year < MinYear || year > MaxYear || month < 1 || month > 12 || day < 1 || (day > (IsLeap(year) ? 366 : 365))) { throw new ArgumentOutOfRangeException(); } _daysSinceEra = DaysForYears(year) + (IsLeap(year) ? LeapYearDays : CommonYearDays)[month - 1] + day - 1; } private const int DaysInYear = 365; //Common years private const int DaysIn4Years = 4*DaysInYear + 1; //Leap year every 4 years. private const int DaysInCentury = 25*DaysIn4Years - 1; //Except no leap year every 100. private const int DaysIn4Centuries = 4*DaysInCentury + 1; //Except leap year every 400. private static int DaysForYears(int years) { //Number of years after 1CE (0 for 1CE, -1 for 1BCE, 1 for 2CE). int calcYear = years < 1 ? years : years - 1; return calcYear/400*DaysIn4Centuries //Blocks of 400 years with their leap and common years + calcYear%400/100*DaysInCentury //Remaining blocks of 100 years with their leap and common years + calcYear%100/4*DaysIn4Years //Remaining blocks of 4 years with their leap and common years + calcYear%4*DaysInYear //Remaining years, all common + (calcYear < 0 ? -1 : 0); //And 1BCE is leap. } public int DayOfYear { get { return _daysSinceEra - DaysForYears(Year) + 1; } } public int Year { get { int guess = (int) Math.Round(_daysSinceEra/365.2425); int test = guess - 1; while (DaysForYears(++test) <= _daysSinceEra) { ; } return test - 1; } } public int Month { get { int i = 1; int target = DayOfYear; int[] array = IsLeapYear ? LeapYearDays : CommonYearDays; while (target > array[i]) { ++i; } return i; } } public int Day { get { return DayOfYear - (IsLeapYear ? LeapYearDays : CommonYearDays)[Month - 1]; } } public DayOfWeek DayOfWeek { get { return (DayOfWeek) ((_daysSinceEra + 1)%7); } } internal int DaysSinceEra { get { return _daysSinceEra; } } public bool IsLeapYear { get { return IsLeap(Year); } } private static bool IsLeap(int year) { //Every 4 years is a leap year //Except every 100 years isn't a leap year. //Except every 400 years is. if (year < 1) { year = year + 1; } return (year%4 == 0) && ((year%100 != 0) || (year%400 == 0)); } public NpgsqlDate AddDays(int days) { return new NpgsqlDate(_daysSinceEra + days); } public NpgsqlDate AddYears(int years) { int newYear = Year + years; if (newYear >= 0 && _daysSinceEra < 0) //cross 1CE/1BCE divide going up { ++newYear; } else if (newYear <= 0 && _daysSinceEra >= 0) //cross 1CE/1BCE divide going down { --newYear; } return new NpgsqlDate(newYear, Month, Day); } public NpgsqlDate AddMonths(int months) { int newYear = Year; int newMonth = Month + months; while (newMonth > 12) { newMonth -= 12; newYear += 1; }; while (newMonth < 1) { newMonth += 12; newYear -= 1; }; int maxDay = (IsLeap(newYear) ? LeapYearMaxes : CommonYearMaxes)[newMonth - 1]; int newDay = Day > maxDay ? maxDay : Day; return new NpgsqlDate(newYear, newMonth, newDay); } public NpgsqlDate Add(NpgsqlInterval interval) { return AddMonths(interval.Months).AddDays(interval.Days); } internal NpgsqlDate Add(NpgsqlInterval interval, int carriedOverflow) { return AddMonths(interval.Months).AddDays(interval.Days + carriedOverflow); } public int Compare(NpgsqlDate x, NpgsqlDate 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); } public bool Equals(NpgsqlDate other) { return _daysSinceEra == other._daysSinceEra; } public override bool Equals(object obj) { return obj != null && obj is NpgsqlDate && Equals((NpgsqlDate) obj); } public int CompareTo(NpgsqlDate other) { return _daysSinceEra.CompareTo(other._daysSinceEra); } public int CompareTo(object obj) { if (obj == null) { return 1; } if (obj is NpgsqlDate) { return CompareTo((NpgsqlDate) obj); } throw new ArgumentException(); } public override int GetHashCode() { return _daysSinceEra; } public override string ToString() { //Format of yyyy-MM-dd with " BC" for BCE and optional " AD" for CE which we omit here. return new StringBuilder(Math.Abs(Year).ToString("D4")).Append('-').Append(Month.ToString("D2")).Append('-').Append( Day.ToString("D2")).Append(_daysSinceEra < 0 ? " BC" : "").ToString(); } public static bool operator ==(NpgsqlDate x, NpgsqlDate y) { return x.Equals(y); } public static bool operator !=(NpgsqlDate x, NpgsqlDate y) { return !(x == y); } public static bool operator <(NpgsqlDate x, NpgsqlDate y) { return x._daysSinceEra < y._daysSinceEra; } public static bool operator >(NpgsqlDate x, NpgsqlDate y) { return x._daysSinceEra > y._daysSinceEra; } public static bool operator <=(NpgsqlDate x, NpgsqlDate y) { return x._daysSinceEra <= y._daysSinceEra; } public static bool operator >=(NpgsqlDate x, NpgsqlDate y) { return x._daysSinceEra >= y._daysSinceEra; } public static explicit operator DateTime(NpgsqlDate date) { try { return new DateTime(date._daysSinceEra*NpgsqlInterval.TicksPerDay); } catch { throw new InvalidCastException(); } } public static explicit operator NpgsqlDate(DateTime date) { return new NpgsqlDate((int) (date.Ticks/NpgsqlInterval.TicksPerDay)); } public static NpgsqlDate operator +(NpgsqlDate date, NpgsqlInterval interval) { return date.Add(interval); } public static NpgsqlDate operator +(NpgsqlInterval interval, NpgsqlDate date) { return date.Add(interval); } public static NpgsqlDate operator -(NpgsqlDate date, NpgsqlInterval interval) { return date.Add(-interval); } public static NpgsqlInterval operator -(NpgsqlDate dateX, NpgsqlDate dateY) { return new NpgsqlInterval(0, dateX._daysSinceEra - dateY._daysSinceEra, 0); } } [Serializable] public struct NpgsqlTimeZone : IEquatable, IComparable, IComparable { public static NpgsqlTimeZone UTC = new NpgsqlTimeZone(0); private readonly int _totalSeconds; public NpgsqlTimeZone(TimeSpan ts) : this(ts.Ticks) { } private NpgsqlTimeZone(long ticks) { _totalSeconds = (int) (ticks/NpgsqlInterval.TicksPerSecond); } public NpgsqlTimeZone(NpgsqlInterval ni) : this(ni.Ticks) { } public NpgsqlTimeZone(NpgsqlTimeZone copyFrom) { _totalSeconds = copyFrom._totalSeconds; } public NpgsqlTimeZone(int hours, int minutes) : this(hours, minutes, 0) { } public NpgsqlTimeZone(int hours, int minutes, int seconds) { _totalSeconds = hours*60*60 + minutes*60 + seconds; } public static implicit operator NpgsqlTimeZone(NpgsqlInterval interval) { return new NpgsqlTimeZone(interval); } public static implicit operator NpgsqlInterval(NpgsqlTimeZone timeZone) { return new NpgsqlInterval(timeZone._totalSeconds*NpgsqlInterval.TicksPerSecond); } public static implicit operator NpgsqlTimeZone(TimeSpan interval) { return new NpgsqlTimeZone(interval); } public static implicit operator TimeSpan(NpgsqlTimeZone timeZone) { return new TimeSpan(timeZone._totalSeconds*NpgsqlInterval.TicksPerSecond); } public static NpgsqlTimeZone SolarTimeZone(decimal longitude) { return new NpgsqlTimeZone((long) (longitude/15m*NpgsqlInterval.TicksPerHour)); } public int Hours { get { return _totalSeconds/60/60; } } public int Minutes { get { return (_totalSeconds/60)%60; } } public int Seconds { get { return _totalSeconds%60; } } public static NpgsqlTimeZone CurrentTimeZone { get { return new NpgsqlTimeZone(TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now)); } } public static NpgsqlTimeZone LocalTimeZone(NpgsqlDate date) { DateTime dt; if (date.Year >= 1902 && date.Year <= 2038) { dt = (DateTime) date; } else { dt = new DateTime(2000, date.Month, date.Day); } return new NpgsqlTimeZone(TimeZone.CurrentTimeZone.GetUtcOffset(dt)); } public bool Equals(NpgsqlTimeZone other) { return _totalSeconds == other._totalSeconds; } public override bool Equals(object obj) { return obj != null && obj is NpgsqlTimeZone && Equals((NpgsqlTimeZone) obj); } public override string ToString() { StringBuilder sb = new StringBuilder(_totalSeconds < 0 ? "-" : "+").Append(Math.Abs(Hours).ToString("D2")); if (Minutes != 0 || Seconds != 0) { sb.Append(':').Append(Math.Abs(Minutes).ToString("D2")); if (Seconds != 0) { sb.Append(":").Append(Math.Abs(Seconds).ToString("D2")); } } return sb.ToString(); } public static NpgsqlTimeZone Parse(string str) { if (str == null) { throw new ArgumentNullException(); } try { str = str.Trim(); bool neg; switch (str[0]) { case '+': neg = false; break; case '-': neg = true; break; default: throw new FormatException(); } int hours; int minutes; int seconds; string[] parts = str.Substring(1).Split(':'); switch (parts.Length) //One of those times that fall-through would actually be good. { case 1: hours = int.Parse(parts[0]); minutes = seconds = 0; break; case 2: hours = int.Parse(parts[0]); minutes = int.Parse(parts[1]); seconds = 0; break; default: hours = int.Parse(parts[0]); minutes = int.Parse(parts[1]); seconds = int.Parse(parts[2]); break; } int totalSeconds = (hours*60*60 + minutes*60 + seconds)*(neg ? -1 : 1); return new NpgsqlTimeZone(totalSeconds*NpgsqlInterval.TicksPerSecond); } catch (OverflowException) { throw; } catch { throw new FormatException(); } } public static bool TryParse(string str, NpgsqlTimeZone tz) { try { tz = Parse(str); return true; } catch { tz = UTC; return false; } } public override int GetHashCode() { return _totalSeconds; } //Note, +01:00 is less than -01:00 public int CompareTo(NpgsqlTimeZone other) { return -(_totalSeconds.CompareTo(other._totalSeconds)); } public int CompareTo(object obj) { if (obj == null) { return 1; } if (obj is NpgsqlTimeZone) { return CompareTo((NpgsqlTimeZone) obj); } throw new ArgumentException(); } public static NpgsqlTimeZone operator -(NpgsqlTimeZone tz) { return new NpgsqlTimeZone(-tz._totalSeconds); } public static NpgsqlTimeZone operator +(NpgsqlTimeZone tz) { return tz; } public static bool operator ==(NpgsqlTimeZone x, NpgsqlTimeZone y) { return x.Equals(y); } public static bool operator !=(NpgsqlTimeZone x, NpgsqlTimeZone y) { return !(x == y); } public static bool operator <(NpgsqlTimeZone x, NpgsqlTimeZone y) { return x.CompareTo(y) < 0; } public static bool operator <=(NpgsqlTimeZone x, NpgsqlTimeZone y) { return x.CompareTo(y) <= 0; } public static bool operator >(NpgsqlTimeZone x, NpgsqlTimeZone y) { return x.CompareTo(y) > 0; } public static bool operator >=(NpgsqlTimeZone x, NpgsqlTimeZone y) { return x.CompareTo(y) >= 0; } } [Serializable] public struct NpgsqlTime : IEquatable, IComparable, IComparable, IComparer, IComparer { public static readonly NpgsqlTime AllBalls = new NpgsqlTime(0); public static NpgsqlTime Now { get { return new NpgsqlTime(DateTime.Now.TimeOfDay); } } private readonly long _ticks; public NpgsqlTime(long ticks) { if (ticks == NpgsqlInterval.TicksPerDay) { _ticks = ticks; } else { ticks %= NpgsqlInterval.TicksPerDay; _ticks = ticks < 0 ? ticks + NpgsqlInterval.TicksPerDay : ticks; } } public NpgsqlTime(TimeSpan time) : this(time.Ticks) { } public NpgsqlTime(NpgsqlInterval time) : this(time.Ticks) { } public NpgsqlTime(NpgsqlTime copyFrom) : this(copyFrom.Ticks) { } public NpgsqlTime(int hours, int minutes, int seconds) : this(hours, minutes, seconds, 0) { } public NpgsqlTime(int hours, int minutes, int seconds, int microseconds) : this( hours*NpgsqlInterval.TicksPerHour + minutes*NpgsqlInterval.TicksPerMinute + seconds*NpgsqlInterval.TicksPerSecond + microseconds*NpgsqlInterval.TicksPerMicrosecond) { } public NpgsqlTime(int hours, int minutes, decimal seconds) : this( hours*NpgsqlInterval.TicksPerHour + minutes*NpgsqlInterval.TicksPerMinute + (long) (seconds*NpgsqlInterval.TicksPerSecond)) { } public NpgsqlTime(int hours, int minutes, double seconds) : this(hours, minutes, (decimal) seconds) { } /// /// The total number of ticks(100ns units) contained. This is the resolution of the /// type. /// The resolution of the PostgreSQL /// interval type is by default 1µs = 1,000 ns. It may be smaller as follows: /// /// /// time(0) /// resolution of 1s (1 second) /// /// /// time(1) /// resolution of 100ms = 0.1s (100 milliseconds) /// /// /// time(2) /// resolution of 10ms = 0.01s (10 milliseconds) /// /// /// time(3) /// resolution of 1ms = 0.001s (1 millisecond) /// /// /// time(4) /// resolution of 100µs = 0.0001s (100 microseconds) /// /// /// time(5) /// resolution of 10µs = 0.00001s (10 microseconds) /// /// /// time(6) or interval /// resolution of 1µs = 0.000001s (1 microsecond) /// /// /// As such, if the 100-nanosecond resolution is significant to an application, a PostgreSQL time will /// not suffice for those purposes. /// In more frequent cases though, the resolution of time suffices. /// will always suffice to handle the resolution of any time value, and upon /// writing to the database, will be rounded to the resolution used. /// /// The number of ticks in the instance. /// public long Ticks { get { return _ticks; } } /// /// Gets the number of whole microseconds held in the instance. /// An integer in the range [0, 999999]. /// public int Microseconds { get { return (int) ((_ticks/10)%1000000); } } /// /// Gets the number of whole milliseconds held in the instance. /// An integer in the range [0, 999]. /// public int Milliseconds { get { return (int) ((_ticks/NpgsqlInterval.TicksPerMillsecond)%1000); } } /// /// Gets the number of whole seconds held in the instance. /// An interger in the range [0, 59]. /// public int Seconds { get { return (int) ((_ticks/NpgsqlInterval.TicksPerSecond)%60); } } /// /// Gets the number of whole minutes held in the instance. /// An integer in the range [0, 59]. /// public int Minutes { get { return (int) ((_ticks/NpgsqlInterval.TicksPerMinute)%60); } } /// /// Gets the number of whole hours held in the instance. /// Note that the time 24:00:00 can be stored for roundtrip compatibility. Any calculations on such a /// value will normalised it to 00:00:00. /// public int Hours { get { return (int) (_ticks/NpgsqlInterval.TicksPerHour); } } /// /// Normalise this time; if it is 24:00:00, convert it to 00:00:00 /// /// This time, normalised public NpgsqlTime Normalize() { return new NpgsqlTime(_ticks%NpgsqlInterval.TicksPerDay); } public bool Equals(NpgsqlTime other) { return Ticks == other.Ticks; } public override bool Equals(object obj) { return obj != null && obj is NpgsqlTime && Equals((NpgsqlTime) obj); } public override int GetHashCode() { return Ticks.GetHashCode(); } public override string ToString() { // calculate total seconds and then subtract total whole minutes in seconds to get just the seconds and fractional part decimal seconds = _ticks / (decimal)NpgsqlInterval.TicksPerSecond - (_ticks / NpgsqlInterval.TicksPerMinute) * 60; StringBuilder sb = new StringBuilder(Hours.ToString("D2")).Append(':').Append(Minutes.ToString("D2")).Append(':').Append( seconds.ToString("0#.######", System.Globalization.CultureInfo.InvariantCulture.NumberFormat)); return sb.ToString(); } public static NpgsqlTime Parse(string str) { if (str == null) { throw new ArgumentNullException(); } try { int hours = 0; int minutes = 0; decimal seconds = 0m; string[] parts = str.Split(':'); switch (parts.Length) //One of those times that fall-through would actually be good. { case 1: hours = int.Parse(parts[0]); break; case 2: hours = int.Parse(parts[0]); minutes = int.Parse(parts[1]); break; default: hours = int.Parse(parts[0]); minutes = int.Parse(parts[1]); seconds = decimal.Parse(parts[2], System.Globalization.CultureInfo.InvariantCulture.NumberFormat); break; } if (hours < 0 || hours > 24 || minutes < 0 || minutes > 59 || seconds < 0m || seconds >= 60 || (hours == 24 && (minutes != 0 || seconds != 0m))) { throw new OverflowException(); } return new NpgsqlTime(hours, minutes, seconds); } catch (OverflowException) { throw; } catch { throw new FormatException(); } } public static bool TryParse(string str, out NpgsqlTime time) { try { time = Parse(str); return true; } catch { time = AllBalls; return false; } } public int CompareTo(NpgsqlTime other) { return Ticks.CompareTo(other.Ticks); } public int CompareTo(object obj) { if (obj == null) { return 1; } if (obj is NpgsqlTime) { return CompareTo((NpgsqlTime) obj); } throw new ArgumentException(); } public int Compare(NpgsqlTime x, NpgsqlTime 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); } public static bool operator ==(NpgsqlTime x, NpgsqlTime y) { return x.Equals(y); } public static bool operator !=(NpgsqlTime x, NpgsqlTime y) { return !(x == y); } public static bool operator <(NpgsqlTime x, NpgsqlTime y) { return x.Ticks < y.Ticks; } public static bool operator >(NpgsqlTime x, NpgsqlTime y) { return x.Ticks > y.Ticks; } public static bool operator <=(NpgsqlTime x, NpgsqlTime y) { return x.Ticks <= y.Ticks; } public static bool operator >=(NpgsqlTime x, NpgsqlTime y) { return x.Ticks >= y.Ticks; } public static explicit operator NpgsqlInterval(NpgsqlTime time) { return new NpgsqlInterval(time.Ticks); } public static explicit operator NpgsqlTime(NpgsqlInterval interval) { return new NpgsqlTime(interval); } public static explicit operator TimeSpan(NpgsqlTime time) { return new TimeSpan(time.Ticks); } public static explicit operator DateTime(NpgsqlTime time) { try { return new DateTime(time.Ticks, DateTimeKind.Unspecified); } catch { throw new InvalidCastException(); } } public static explicit operator NpgsqlTime(TimeSpan interval) { return new NpgsqlTime(interval); } public NpgsqlTime AddTicks(long ticksAdded) { return new NpgsqlTime((Ticks + ticksAdded)%NpgsqlInterval.TicksPerDay); } private NpgsqlTime AddTicks(long ticksAdded, out int overflow) { long result = Ticks + ticksAdded; overflow = (int) (result/NpgsqlInterval.TicksPerDay); result %= NpgsqlInterval.TicksPerDay; if (result < 0) { --overflow; //"carry the one" } return new NpgsqlTime(result); } public NpgsqlTime Add(NpgsqlInterval interval) { return AddTicks(interval.Ticks); } internal NpgsqlTime Add(NpgsqlInterval interval, out int overflow) { return AddTicks(interval.Ticks, out overflow); } public NpgsqlTime Subtract(NpgsqlInterval interval) { return AddTicks(-interval.Ticks); } public NpgsqlInterval Subtract(NpgsqlTime earlier) { return new NpgsqlInterval(Ticks - earlier.Ticks); } public NpgsqlTimeTZ AtTimeZone(NpgsqlTimeZone timeZone) { return new NpgsqlTimeTZ(this).AtTimeZone(timeZone); } public static NpgsqlTime operator +(NpgsqlTime time, NpgsqlInterval interval) { return time.Add(interval); } public static NpgsqlTime operator +(NpgsqlInterval interval, NpgsqlTime time) { return time + interval; } public static NpgsqlTime operator -(NpgsqlTime time, NpgsqlInterval interval) { return time.Subtract(interval); } public static NpgsqlInterval operator -(NpgsqlTime later, NpgsqlTime earlier) { return later.Subtract(earlier); } } [Serializable] public struct NpgsqlTimeTZ : IEquatable, IComparable, IComparable, IComparer, IComparer { public static readonly NpgsqlTimeTZ AllBalls = new NpgsqlTimeTZ(NpgsqlTime.AllBalls, NpgsqlTimeZone.UTC); public static NpgsqlTimeTZ Now { get { return new NpgsqlTimeTZ(NpgsqlTime.Now); } } public static NpgsqlTimeTZ LocalMidnight(NpgsqlDate date) { return new NpgsqlTimeTZ(NpgsqlTime.AllBalls, NpgsqlTimeZone.LocalTimeZone(date)); } private readonly NpgsqlTime _localTime; private readonly NpgsqlTimeZone _timeZone; public NpgsqlTimeTZ(NpgsqlTime localTime, NpgsqlTimeZone timeZone) { _localTime = localTime; _timeZone = timeZone; } public NpgsqlTimeTZ(NpgsqlTime localTime) : this(localTime, NpgsqlTimeZone.CurrentTimeZone) { } public NpgsqlTimeTZ(long ticks) : this(new NpgsqlTime(ticks)) { } public NpgsqlTimeTZ(TimeSpan time) : this(new NpgsqlTime(time)) { } public NpgsqlTimeTZ(NpgsqlInterval time) : this(new NpgsqlTime(time)) { } public NpgsqlTimeTZ(NpgsqlTimeTZ copyFrom) : this(copyFrom._localTime, copyFrom._timeZone) { } public NpgsqlTimeTZ(int hours, int minutes, int seconds) : this(new NpgsqlTime(hours, minutes, seconds)) { } public NpgsqlTimeTZ(int hours, int minutes, int seconds, int microseconds) : this(new NpgsqlTime(hours, minutes, seconds, microseconds)) { } public NpgsqlTimeTZ(int hours, int minutes, decimal seconds) : this(new NpgsqlTime(hours, minutes, seconds)) { } public NpgsqlTimeTZ(int hours, int minutes, double seconds) : this(new NpgsqlTime(hours, minutes, seconds)) { } public NpgsqlTimeTZ(long ticks, NpgsqlTimeZone timeZone) : this(new NpgsqlTime(ticks), timeZone) { } public NpgsqlTimeTZ(TimeSpan time, NpgsqlTimeZone timeZone) : this(new NpgsqlTime(time), timeZone) { } public NpgsqlTimeTZ(NpgsqlInterval time, NpgsqlTimeZone timeZone) : this(new NpgsqlTime(time), timeZone) { } public NpgsqlTimeTZ(int hours, int minutes, int seconds, NpgsqlTimeZone timeZone) : this(new NpgsqlTime(hours, minutes, seconds), timeZone) { } public NpgsqlTimeTZ(int hours, int minutes, int seconds, int microseconds, NpgsqlTimeZone timeZone) : this(new NpgsqlTime(hours, minutes, seconds, microseconds), timeZone) { } public NpgsqlTimeTZ(int hours, int minutes, decimal seconds, NpgsqlTimeZone timeZone) : this(new NpgsqlTime(hours, minutes, seconds), timeZone) { } public NpgsqlTimeTZ(int hours, int minutes, double seconds, NpgsqlTimeZone timeZone) : this(new NpgsqlTime(hours, minutes, seconds), timeZone) { } public override string ToString() { return string.Format("{0}{1}", _localTime, _timeZone); } public static NpgsqlTimeTZ Parse(string str) { if (str == null) { throw new ArgumentNullException(); } try { int idx = Math.Max(str.IndexOf('+'), str.IndexOf('-')); if (idx == -1) { throw new FormatException(); } return new NpgsqlTimeTZ(NpgsqlTime.Parse(str.Substring(0, idx)), NpgsqlTimeZone.Parse(str.Substring(idx))); } catch (OverflowException) { throw; } catch { throw new FormatException(); } } public NpgsqlTime LocalTime { get { return _localTime; } } public NpgsqlTimeZone TimeZone { get { return _timeZone; } } public NpgsqlTime UTCTime { get { return AtTimeZone(NpgsqlTimeZone.UTC).LocalTime; } } public NpgsqlTimeTZ AtTimeZone(NpgsqlTimeZone timeZone) { return new NpgsqlTimeTZ(LocalTime - _timeZone + timeZone, timeZone); } internal NpgsqlTimeTZ AtTimeZone(NpgsqlTimeZone timeZone, out int overflow) { return new NpgsqlTimeTZ(LocalTime.Add(timeZone - (NpgsqlInterval) (_timeZone), out overflow), timeZone); } public long Ticks { get { return _localTime.Ticks; } } /// /// Gets the number of whole microseconds held in the instance. /// An integer in the range [0, 999999]. /// public int Microseconds { get { return _localTime.Microseconds; } } /// /// Gets the number of whole milliseconds held in the instance. /// An integer in the range [0, 999]. /// public int Milliseconds { get { return _localTime.Milliseconds; } } /// /// Gets the number of whole seconds held in the instance. /// An interger in the range [0, 59]. /// public int Seconds { get { return _localTime.Seconds; } } /// /// Gets the number of whole minutes held in the instance. /// An integer in the range [0, 59]. /// public int Minutes { get { return _localTime.Minutes; } } /// /// Gets the number of whole hours held in the instance. /// Note that the time 24:00:00 can be stored for roundtrip compatibility. Any calculations on such a /// value will normalised it to 00:00:00. /// public int Hours { get { return _localTime.Hours; } } /// /// Normalise this time; if it is 24:00:00, convert it to 00:00:00 /// /// This time, normalised public NpgsqlTimeTZ Normalize() { return new NpgsqlTimeTZ(_localTime.Normalize(), _timeZone); } public bool Equals(NpgsqlTimeTZ other) { return _localTime.Equals(other._localTime) && _timeZone.Equals(other._timeZone); } public override bool Equals(object obj) { return obj != null && obj is NpgsqlTimeTZ && Equals((NpgsqlTimeTZ) obj); } public override int GetHashCode() { return _localTime.GetHashCode() ^ PGUtil.RotateShift(_timeZone.GetHashCode(), 24); } /// /// Compares this with another . As per postgres' rules, /// first the times are compared as if they were both in the same timezone. If they are equal then /// then timezones are compared (+01:00 being "smaller" than -01:00). /// /// the to compare with. /// An integer which is 0 if they are equal, < 0 if this is the smaller and > 0 if this is the larger. public int CompareTo(NpgsqlTimeTZ other) { int cmp = AtTimeZone(NpgsqlTimeZone.UTC).LocalTime.CompareTo(other.AtTimeZone(NpgsqlTimeZone.UTC).LocalTime); return cmp == 0 ? _timeZone.CompareTo(other._timeZone) : cmp; } public int CompareTo(object obj) { if (obj == null) { return 1; } if (obj is NpgsqlTimeTZ) { return CompareTo((NpgsqlTimeTZ) obj); } throw new ArgumentException(); } public int Compare(NpgsqlTimeTZ x, NpgsqlTimeTZ 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); } public static bool operator ==(NpgsqlTimeTZ x, NpgsqlTimeTZ y) { return x.Equals(y); } public static bool operator !=(NpgsqlTimeTZ x, NpgsqlTimeTZ y) { return !(x == y); } public static bool operator <(NpgsqlTimeTZ x, NpgsqlTimeTZ y) { return x.CompareTo(y) < 0; } public static bool operator >(NpgsqlTimeTZ x, NpgsqlTimeTZ y) { return x.CompareTo(y) > 0; } public static bool operator <=(NpgsqlTimeTZ x, NpgsqlTimeTZ y) { return x.CompareTo(y) <= 0; } public static bool operator >=(NpgsqlTimeTZ x, NpgsqlTimeTZ y) { return x.CompareTo(y) >= 0; } public NpgsqlTimeTZ Add(NpgsqlInterval interval) { return new NpgsqlTimeTZ(_localTime.Add(interval), _timeZone); } internal NpgsqlTimeTZ Add(NpgsqlInterval interval, out int overflow) { return new NpgsqlTimeTZ(_localTime.Add(interval, out overflow), _timeZone); } public NpgsqlTimeTZ Subtract(NpgsqlInterval interval) { return new NpgsqlTimeTZ(_localTime.Subtract(interval), _timeZone); } public NpgsqlInterval Subtract(NpgsqlTimeTZ earlier) { return _localTime.Subtract(earlier.AtTimeZone(_timeZone)._localTime); } public static NpgsqlTimeTZ operator +(NpgsqlTimeTZ time, NpgsqlInterval interval) { return time.Add(interval); } public static NpgsqlTimeTZ operator +(NpgsqlInterval interval, NpgsqlTimeTZ time) { return time + interval; } public static NpgsqlTimeTZ operator -(NpgsqlTimeTZ time, NpgsqlInterval interval) { return time.Subtract(interval); } public static NpgsqlInterval operator -(NpgsqlTimeTZ later, NpgsqlTimeTZ earlier) { return later.Subtract(earlier); } public static explicit operator NpgsqlTimeTZ(TimeSpan time) { return new NpgsqlTimeTZ(new NpgsqlTime(time)); } public static explicit operator TimeSpan(NpgsqlTimeTZ time) { return (TimeSpan) time.LocalTime; } public static explicit operator DateTime(NpgsqlTimeTZ time) { // LocalTime property is actually time local to TimeZone return new DateTime(time.AtTimeZone(NpgsqlTimeZone.CurrentTimeZone).Ticks, DateTimeKind.Local); } } [Serializable] public struct NpgsqlTimeStamp : IEquatable, IComparable, IComparable, IComparer, IComparer { private enum TimeType { Finite, Infinity, MinusInfinity } public static readonly NpgsqlTimeStamp Epoch = new NpgsqlTimeStamp(NpgsqlDate.Epoch); public static readonly NpgsqlTimeStamp Era = new NpgsqlTimeStamp(NpgsqlDate.Era); public static readonly NpgsqlTimeStamp Infinity = new NpgsqlTimeStamp(TimeType.Infinity, NpgsqlDate.Era, NpgsqlTime.AllBalls); public static readonly NpgsqlTimeStamp MinusInfinity = new NpgsqlTimeStamp(TimeType.MinusInfinity, NpgsqlDate.Era, NpgsqlTime.AllBalls); public static NpgsqlTimeStamp Now { get { return new NpgsqlTimeStamp(NpgsqlDate.Now, NpgsqlTime.Now); } } public static NpgsqlTimeStamp Today { get { return new NpgsqlTimeStamp(NpgsqlDate.Now); } } public static NpgsqlTimeStamp Yesterday { get { return new NpgsqlTimeStamp(NpgsqlDate.Yesterday); } } public static NpgsqlTimeStamp Tomorrow { get { return new NpgsqlTimeStamp(NpgsqlDate.Tomorrow); } } private readonly NpgsqlDate _date; private readonly NpgsqlTime _time; private readonly TimeType _type; private NpgsqlTimeStamp(TimeType type, NpgsqlDate date, NpgsqlTime time) { _type = type; _date = date; _time = time; } public NpgsqlTimeStamp(NpgsqlDate date, NpgsqlTime time) : this(TimeType.Finite, date, time) { } public NpgsqlTimeStamp(NpgsqlDate date) : this(date, NpgsqlTime.AllBalls) { } public NpgsqlTimeStamp(int year, int month, int day, int hours, int minutes, int seconds) : this(new NpgsqlDate(year, month, day), new NpgsqlTime(hours, minutes, seconds)) { } public NpgsqlDate Date { get { return _date; } } public NpgsqlTime 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 NpgsqlTimeStamp AddDays(int days) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: return new NpgsqlTimeStamp(_date.AddDays(days), _time); } } public NpgsqlTimeStamp AddYears(int years) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: return new NpgsqlTimeStamp(_date.AddYears(years), _time); } } public NpgsqlTimeStamp AddMonths(int months) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: return new NpgsqlTimeStamp(_date.AddMonths(months), _time); } } public long Ticks { get { return _date.DaysSinceEra*NpgsqlInterval.TicksPerDay + _time.Ticks; } } public int Microseconds { get { return _time.Microseconds; } } 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 IsFinite { get { return _type == TimeType.Finite; } } public bool IsInfinity { get { return _type == TimeType.Infinity; } } public bool IsMinusInfinity { get { return _type == TimeType.MinusInfinity; } } public NpgsqlTimeStamp Normalize() { return Add(NpgsqlInterval.Zero); } public override string ToString() { switch (_type) { case TimeType.Infinity: return "infinity"; case TimeType.MinusInfinity: return "-infinity"; default: return string.Format("{0} {1}", _date, _time); } } public static NpgsqlTimeStamp Parse(string str) { if (str == null) { throw new NullReferenceException(); } switch (str = str.Trim().ToLowerInvariant()) { case "infinity": return Infinity; case "-infinity": return MinusInfinity; 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 NpgsqlTimeStamp(NpgsqlDate.Parse(datePart), NpgsqlTime.Parse(timePart)); } catch (OverflowException) { throw; } catch { throw new FormatException(); } } } public bool Equals(NpgsqlTimeStamp other) { switch (_type) { case TimeType.Infinity: return other._type == TimeType.Infinity; case TimeType.MinusInfinity: return other._type == TimeType.MinusInfinity; default: return other._type == TimeType.Finite && _date.Equals(other._date) && _time.Equals(other._time); } } public override bool Equals(object obj) { return obj != null && obj is NpgsqlTimeStamp && Equals((NpgsqlTimeStamp) obj); } public override int GetHashCode() { switch (_type) { case TimeType.Infinity: return int.MaxValue; case TimeType.MinusInfinity: return int.MinValue; default: return _date.GetHashCode() ^ PGUtil.RotateShift(_time.GetHashCode(), 16); } } public int CompareTo(NpgsqlTimeStamp other) { switch (_type) { case TimeType.Infinity: return other._type == TimeType.Infinity ? 0 : 1; case TimeType.MinusInfinity: return other._type == TimeType.MinusInfinity ? 0 : -1; default: switch (other._type) { case TimeType.Infinity: return -1; case TimeType.MinusInfinity: 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 NpgsqlTimeStamp) { return CompareTo((NpgsqlTimeStamp) obj); } throw new ArgumentException(); } public int Compare(NpgsqlTimeStamp x, NpgsqlTimeStamp 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); } public NpgsqlTimeStampTZ AtTimeZone(NpgsqlTimeZone timeZoneFrom, NpgsqlTimeZone timeZoneTo) { int overflow; NpgsqlTimeTZ adjusted = new NpgsqlTimeTZ(_time, timeZoneFrom).AtTimeZone(timeZoneTo, out overflow); return new NpgsqlTimeStampTZ(_date.AddDays(overflow), adjusted); } public NpgsqlTimeStampTZ AtTimeZone(NpgsqlTimeZone timeZone) { return AtTimeZone(timeZone, NpgsqlTimeZone.LocalTimeZone(_date)); } public NpgsqlTimeStamp Add(NpgsqlInterval interval) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: int overflow; NpgsqlTime time = _time.Add(interval, out overflow); return new NpgsqlTimeStamp(_date.Add(interval, overflow), time); } } public NpgsqlTimeStamp Subtract(NpgsqlInterval interval) { return Add(-interval); } public NpgsqlInterval Subtract(NpgsqlTimeStamp timestamp) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: throw new ArgumentOutOfRangeException("this", "You cannot subtract infinity timestamps"); } switch (timestamp._type) { case TimeType.Infinity: case TimeType.MinusInfinity: throw new ArgumentOutOfRangeException("timestamp", "You cannot subtract infinity timestamps"); } return new NpgsqlInterval(0, _date.DaysSinceEra - timestamp._date.DaysSinceEra, _time.Ticks - timestamp._time.Ticks); } public static implicit operator NpgsqlTimeStamp(DateTime datetime) { if (datetime == DateTime.MaxValue) { return Infinity; } else if (datetime == DateTime.MinValue) { return MinusInfinity; } else { return new NpgsqlTimeStamp(new NpgsqlDate(datetime), new NpgsqlTime(datetime.TimeOfDay)); } } public static implicit operator DateTime(NpgsqlTimeStamp timestamp) { switch (timestamp._type) { case TimeType.Infinity: return DateTime.MaxValue; case TimeType.MinusInfinity: return DateTime.MinValue; default: try { return new DateTime(timestamp.Date.DaysSinceEra*NpgsqlInterval.TicksPerDay + timestamp._time.Ticks, DateTimeKind.Unspecified); } catch { throw new InvalidCastException(); } } } public static NpgsqlTimeStamp operator +(NpgsqlTimeStamp timestamp, NpgsqlInterval interval) { return timestamp.Add(interval); } public static NpgsqlTimeStamp operator +(NpgsqlInterval interval, NpgsqlTimeStamp timestamp) { return timestamp.Add(interval); } public static NpgsqlTimeStamp operator -(NpgsqlTimeStamp timestamp, NpgsqlInterval interval) { return timestamp.Subtract(interval); } public static NpgsqlInterval operator -(NpgsqlTimeStamp x, NpgsqlTimeStamp y) { return x.Subtract(y); } public static bool operator ==(NpgsqlTimeStamp x, NpgsqlTimeStamp y) { return x.Equals(y); } public static bool operator !=(NpgsqlTimeStamp x, NpgsqlTimeStamp y) { return !(x == y); } public static bool operator <(NpgsqlTimeStamp x, NpgsqlTimeStamp y) { return x.CompareTo(y) < 0; } public static bool operator >(NpgsqlTimeStamp x, NpgsqlTimeStamp y) { return x.CompareTo(y) > 0; } public static bool operator <=(NpgsqlTimeStamp x, NpgsqlTimeStamp y) { return x.CompareTo(y) <= 0; } public static bool operator >=(NpgsqlTimeStamp x, NpgsqlTimeStamp y) { return x.CompareTo(y) >= 0; } } [Serializable] public struct NpgsqlTimeStampTZ : IEquatable, IComparable, IComparable, IComparer, IComparer { private enum TimeType { Finite, Infinity, MinusInfinity } public static readonly NpgsqlTimeStampTZ Epoch = new NpgsqlTimeStampTZ(NpgsqlDate.Epoch, NpgsqlTimeTZ.AllBalls); public static readonly NpgsqlTimeStampTZ Era = new NpgsqlTimeStampTZ(NpgsqlDate.Era, NpgsqlTimeTZ.AllBalls); public static readonly NpgsqlTimeStampTZ Infinity = new NpgsqlTimeStampTZ(TimeType.Infinity, NpgsqlDate.Era, NpgsqlTimeTZ.AllBalls); public static readonly NpgsqlTimeStampTZ MinusInfinity = new NpgsqlTimeStampTZ(TimeType.MinusInfinity, NpgsqlDate.Era, NpgsqlTimeTZ.AllBalls); public static NpgsqlTimeStampTZ Now { get { return new NpgsqlTimeStampTZ(NpgsqlDate.Now, NpgsqlTimeTZ.Now); } } public static NpgsqlTimeStampTZ Today { get { return new NpgsqlTimeStampTZ(NpgsqlDate.Now); } } public static NpgsqlTimeStampTZ Yesterday { get { return new NpgsqlTimeStampTZ(NpgsqlDate.Yesterday); } } public static NpgsqlTimeStampTZ Tomorrow { get { return new NpgsqlTimeStampTZ(NpgsqlDate.Tomorrow); } } private readonly NpgsqlDate _date; private readonly NpgsqlTimeTZ _time; private readonly TimeType _type; private NpgsqlTimeStampTZ(TimeType type, NpgsqlDate date, NpgsqlTimeTZ time) { _type = type; _date = date; _time = time; } public NpgsqlTimeStampTZ(NpgsqlDate date, NpgsqlTimeTZ time) : this(TimeType.Finite, date, time) { } public NpgsqlTimeStampTZ(NpgsqlDate date) : this(date, NpgsqlTimeTZ.LocalMidnight(date)) { } public NpgsqlTimeStampTZ(int year, int month, int day, int hours, int minutes, int seconds, NpgsqlTimeZone? timezone) : this( new NpgsqlDate(year, month, day), new NpgsqlTimeTZ(hours, minutes, seconds, timezone.HasValue ? timezone.Value : NpgsqlTimeZone.LocalTimeZone(new NpgsqlDate(year, month, day))) ) { } public NpgsqlDate Date { get { return _date; } } public NpgsqlTimeTZ 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 NpgsqlTimeStampTZ AddDays(int days) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: return new NpgsqlTimeStampTZ(_date.AddDays(days), _time); } } public NpgsqlTimeStampTZ AddYears(int years) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: return new NpgsqlTimeStampTZ(_date.AddYears(years), _time); } } public NpgsqlTimeStampTZ AddMonths(int months) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: return new NpgsqlTimeStampTZ(_date.AddMonths(months), _time); } } public NpgsqlTime LocalTime { get { return _time.LocalTime; } } public NpgsqlTimeZone TimeZone { get { return _time.TimeZone; } } public NpgsqlTime UTCTime { get { return _time.UTCTime; } } public long Ticks { get { return _date.DaysSinceEra*NpgsqlInterval.TicksPerDay + _time.Ticks; } } public int Microseconds { get { return _time.Microseconds; } } 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 IsFinite { get { return _type == TimeType.Finite; } } public bool IsInfinity { get { return _type == TimeType.Infinity; } } public bool IsMinusInfinity { get { return _type == TimeType.MinusInfinity; } } public NpgsqlTimeStampTZ Normalize() { return Add(NpgsqlInterval.Zero); } public override string ToString() { switch (_type) { case TimeType.Infinity: return "infinity"; case TimeType.MinusInfinity: return "-infinity"; default: return string.Format("{0} {1}", _date, _time); } } public static NpgsqlTimeStampTZ Parse(string str) { if (str == null) { throw new NullReferenceException(); } switch (str = str.Trim().ToLowerInvariant()) { case "infinity": return Infinity; case "-infinity": return MinusInfinity; 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 NpgsqlTimeStampTZ(NpgsqlDate.Parse(datePart), NpgsqlTimeTZ.Parse(timePart)); } catch (OverflowException) { throw; } catch { throw new FormatException(); } } } public bool Equals(NpgsqlTimeStampTZ other) { switch (_type) { case TimeType.Infinity: return other._type == TimeType.Infinity; case TimeType.MinusInfinity: return other._type == TimeType.MinusInfinity; default: return other._type == TimeType.Finite && _date.Equals(other._date) && _time.Equals(other._time); } } public override bool Equals(object obj) { return obj != null && obj is NpgsqlTimeStamp && Equals((NpgsqlTimeStampTZ) obj); } public override int GetHashCode() { switch (_type) { case TimeType.Infinity: return int.MaxValue; case TimeType.MinusInfinity: return int.MinValue; default: return _date.GetHashCode() ^ PGUtil.RotateShift(_time.GetHashCode(), 16); } } public int CompareTo(NpgsqlTimeStampTZ other) { switch (_type) { case TimeType.Infinity: return other._type == TimeType.Infinity ? 0 : 1; case TimeType.MinusInfinity: return other._type == TimeType.MinusInfinity ? 0 : -1; default: switch (other._type) { case TimeType.Infinity: return -1; case TimeType.MinusInfinity: 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 NpgsqlTimeStamp) { return CompareTo((NpgsqlTimeStamp) obj); } throw new ArgumentException(); } public int Compare(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ 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); } public NpgsqlTimeStamp AtTimeZone(NpgsqlTimeZone timeZone) { int overflow; NpgsqlTimeTZ adjusted = _time.AtTimeZone(timeZone, out overflow); return new NpgsqlTimeStamp(_date.AddDays(overflow), adjusted.LocalTime); } public NpgsqlTimeStampTZ Add(NpgsqlInterval interval) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: return this; default: int overflow; NpgsqlTimeTZ time = _time.Add(interval, out overflow); return new NpgsqlTimeStampTZ(_date.Add(interval, overflow), time); } } public NpgsqlTimeStampTZ Subtract(NpgsqlInterval interval) { return Add(-interval); } public NpgsqlInterval Subtract(NpgsqlTimeStampTZ timestamp) { switch (_type) { case TimeType.Infinity: case TimeType.MinusInfinity: throw new ArgumentOutOfRangeException("this", "You cannot subtract infinity timestamps"); } switch (timestamp._type) { case TimeType.Infinity: case TimeType.MinusInfinity: throw new ArgumentOutOfRangeException("timestamp", "You cannot subtract infinity timestamps"); } return new NpgsqlInterval(0, _date.DaysSinceEra - timestamp._date.DaysSinceEra, (_time - timestamp._time).Ticks); } public static implicit operator NpgsqlTimeStampTZ(DateTime datetime) { if (datetime == DateTime.MaxValue) { return Infinity; } else if (datetime == DateTime.MinValue) { return MinusInfinity; } else { NpgsqlDate newDate = new NpgsqlDate(datetime); return new NpgsqlTimeStampTZ(newDate, new NpgsqlTimeTZ(datetime.TimeOfDay, datetime.Kind == DateTimeKind.Utc ? NpgsqlTimeZone.UTC : NpgsqlTimeZone.LocalTimeZone(newDate))); } } public static explicit operator DateTime(NpgsqlTimeStampTZ timestamp) { switch (timestamp._type) { case TimeType.Infinity: return DateTime.MaxValue; case TimeType.MinusInfinity: return DateTime.MinValue; default: try { NpgsqlTimeStamp utc = timestamp.AtTimeZone(NpgsqlTimeZone.UTC); return new DateTime(utc.Date.DaysSinceEra*NpgsqlInterval.TicksPerDay + utc.Time.Ticks, DateTimeKind.Utc); } catch { throw new InvalidCastException(); } } } public static implicit operator NpgsqlTimeStampTZ(DateTimeOffset datetimeoffset) { if (datetimeoffset == DateTimeOffset.MaxValue) { return Infinity; } else if (datetimeoffset == DateTimeOffset.MinValue) { return MinusInfinity; } else { NpgsqlDate newDate = new NpgsqlDate(datetimeoffset.Year, datetimeoffset.Month, datetimeoffset.Day); return new NpgsqlTimeStampTZ(newDate, new NpgsqlTimeTZ(datetimeoffset.TimeOfDay, new NpgsqlTimeZone(datetimeoffset.Offset))); } } public static explicit operator DateTimeOffset(NpgsqlTimeStampTZ timestamp) { switch (timestamp._type) { case TimeType.Infinity: return DateTimeOffset.MaxValue; case TimeType.MinusInfinity: return DateTimeOffset.MinValue; default: try { return new DateTimeOffset(timestamp.Date.DaysSinceEra * NpgsqlInterval.TicksPerDay + timestamp.Time.Ticks, timestamp.TimeZone); } catch { throw new InvalidCastException(); } } } public static NpgsqlTimeStampTZ operator +(NpgsqlTimeStampTZ timestamp, NpgsqlInterval interval) { return timestamp.Add(interval); } public static NpgsqlTimeStampTZ operator +(NpgsqlInterval interval, NpgsqlTimeStampTZ timestamp) { return timestamp.Add(interval); } public static NpgsqlTimeStampTZ operator -(NpgsqlTimeStampTZ timestamp, NpgsqlInterval interval) { return timestamp.Subtract(interval); } public static NpgsqlInterval operator -(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ y) { return x.Subtract(y); } public static bool operator ==(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ y) { return x.Equals(y); } public static bool operator !=(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ y) { return !(x == y); } public static bool operator <(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ y) { return x.CompareTo(y) < 0; } public static bool operator >(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ y) { return x.CompareTo(y) > 0; } public static bool operator <=(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ y) { return x.CompareTo(y) <= 0; } public static bool operator >=(NpgsqlTimeStampTZ x, NpgsqlTimeStampTZ y) { return x.CompareTo(y) >= 0; } } } #pragma warning restore 1591
X Tutup