// 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