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



namespace NpgsqlTypes

{

	/// <summary>

	/// Represents the PostgreSQL interval datatype.

	/// <remarks>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 <see cref="TimeSpan"/> class. (Neither datatype

	/// has any concessions for leap-seconds).

	/// <para>For most uses just casting to and from TimeSpan will work correctly &#x2014; in particular,

	/// the results of subtracting one <see cref="DateTime"/> or the PostgreSQL date, time and

	/// timestamp types from another should be the same whether you do so in .NET or PostgreSQL &#x2014;

	/// but if the handling of days and months in PostgreSQL is important to your application then you

	/// should use this class instead of <see cref="TimeSpan"/>.</para>

	/// <para>If you don't know whether these differences are important to your application, they

	/// probably arent! Just use <see cref="TimeSpan"/> and do not use this class directly &#x263a;</para>

	/// <para>To avoid forcing unnecessary provider-specific concerns on users who need not be concerned

	/// with them a call to <see cref="IDataRecord.GetValue(int)"/> on a field containing an

	/// <see cref="NpgsqlInterval"/> value will return a <see cref="TimeSpan"/> rather than an

	/// <see cref="NpgsqlInterval"/>. If you need the extra functionality of <see cref="NpgsqlInterval"/>

	/// then use <see cref="Npgsql.NpgsqlDataReader.GetInterval(Int32)"/>.</para>

	/// </remarks>

	/// <seealso cref="Ticks"/>

	/// <seealso cref="JustifyDays"/>

	/// <seealso cref="JustifyMonths"/>

	/// <seealso cref="Canonicalize()"/>

	/// </summary>

	[Serializable]

	public struct NpgsqlInterval : IComparable, IComparer, IEquatable<NpgsqlInterval>, IComparable<NpgsqlInterval>,

	                               IComparer<NpgsqlInterval>

	{

		#region Constants



		/// <summary>

		/// Represents the number of ticks (100ns periods) in one microsecond. This field is constant.

		/// </summary>

		public const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond/1000;



		/// <summary>

		/// Represents the number of ticks (100ns periods) in one millisecond. This field is constant.

		/// </summary>

		public const long TicksPerMillsecond = TimeSpan.TicksPerMillisecond;



		/// <summary>

		/// Represents the number of ticks (100ns periods) in one second. This field is constant.

		/// </summary>

		public const long TicksPerSecond = TimeSpan.TicksPerSecond;



		/// <summary>

		/// Represents the number of ticks (100ns periods) in one minute. This field is constant.

		/// </summary>

		public const long TicksPerMinute = TimeSpan.TicksPerMinute;



		/// <summary>

		/// Represents the number of ticks (100ns periods) in one hour. This field is constant.

		/// </summary>

		public const long TicksPerHour = TimeSpan.TicksPerHour;



		/// <summary>

		/// Represents the number of ticks (100ns periods) in one day. This field is constant.

		/// </summary>

		public const long TicksPerDay = TimeSpan.TicksPerDay;



		/// <summary>

		/// Represents the number of hours in one day (assuming no daylight savings adjustments). This field is constant.

		/// </summary>

		public const int HoursPerDay = 24;



		/// <summary>

		/// 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 &#xd7; 12 = 360 rather than 356/366 days.

		/// </summary>

		public const int DaysPerMonth = 30;



		/// <summary>

		/// Represents the number of ticks (100ns periods) in one day, assuming 30 days per month. <seealso cref="DaysPerMonth"/>

		/// </summary>

		public const long TicksPerMonth = TicksPerDay*DaysPerMonth;



		/// <summary>

		/// Represents the number of months in a year. This field is constant.

		/// </summary>

		public const int MonthsPerYear = 12;



		/// <summary>

		/// Represents the maximum <see cref="NpgsqlInterval"/>. This field is read-only.

		/// </summary>

		public static readonly NpgsqlInterval MaxValue = new NpgsqlInterval(long.MaxValue);



		/// <summary>

		/// Represents the minimum <see cref="NpgsqlInterval"/>. This field is read-only.

		/// </summary>

		public static readonly NpgsqlInterval MinValue = new NpgsqlInterval(long.MinValue);



		/// <summary>

		/// Represents the zero <see cref="NpgsqlInterval"/>. This field is read-only.

		/// </summary>

		public static readonly NpgsqlInterval Zero = new NpgsqlInterval(0);



		#endregion



		private readonly int _months;

		private readonly int _days;

		private readonly long _ticks;



		#region Constructors



		/// <summary>

		/// Initializes a new <see cref="NpgsqlInterval"/> to the specified number of ticks.

		/// </summary>

		/// <param name="ticks">A time period expressed in 100ns units.</param>

		public NpgsqlInterval(long ticks)

		{

			_months = 0;

			_days = 0;

			_ticks = ticks;

		}



		/// <summary>

		/// Initializes a new <see cref="NpgsqlInterval"/> to hold the same time as a <see cref="TimeSpan"/>

		/// </summary>

		/// <param name="timespan">A time period expressed in a <see cref="TimeSpan"/></param>

		public NpgsqlInterval(TimeSpan timespan)

			: this(timespan.Ticks)

		{

		}



		/// <summary>

		/// Initializes a new <see cref="NpgsqlInterval"/> to the specified number of months, days

		/// &amp; ticks.

		/// </summary>

		/// <param name="months">Number of months.</param>

		/// <param name="days">Number of days.</param>

		/// <param name="ticks">Number of 100ns units.</param>

		public NpgsqlInterval(int months, int days, long ticks)

		{

			_months = months;

			_days = days;

			_ticks = ticks;

		}



		/// <summary>

		/// Initializes a new <see cref="NpgsqlInterval"/> to the specified number of

		/// days, hours, minutes &amp; seconds.

		/// </summary>

		/// <param name="days">Number of days.</param>

		/// <param name="hours">Number of hours.</param>

		/// <param name="minutes">Number of minutes.</param>

		/// <param name="seconds">Number of seconds.</param>

		public NpgsqlInterval(int days, int hours, int minutes, int seconds)

			: this(0, days, new TimeSpan(hours, minutes, seconds).Ticks)

		{

		}



		/// <summary>

		/// Initializes a new <see cref="NpgsqlInterval"/> to the specified number of

		/// days, hours, minutes, seconds &amp; milliseconds.

		/// </summary>

		/// <param name="days">Number of days.</param>

		/// <param name="hours">Number of hours.</param>

		/// <param name="minutes">Number of minutes.</param>

		/// <param name="seconds">Number of seconds.</param>

		/// <param name="milliseconds">Number of milliseconds.</param>

		public NpgsqlInterval(int days, int hours, int minutes, int seconds, int milliseconds)

			: this(0, days, new TimeSpan(0, hours, minutes, seconds, milliseconds).Ticks)

		{

		}



		/// <summary>

		/// Initializes a new <see cref="NpgsqlInterval"/> to the specified number of

		/// months, days, hours, minutes, seconds &amp; milliseconds.

		/// </summary>

		/// <param name="months">Number of months.</param>

		/// <param name="days">Number of days.</param>

		/// <param name="hours">Number of hours.</param>

		/// <param name="minutes">Number of minutes.</param>

		/// <param name="seconds">Number of seconds.</param>

		/// <param name="milliseconds">Number of milliseconds.</param>

		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)

		{

		}



		/// <summary>

		/// Initializes a new <see cref="NpgsqlInterval"/> to the specified number of

		/// years, months, days, hours, minutes, seconds &amp; milliseconds.

		/// <para>Years are calculated exactly equivalent to 12 months.</para>

		/// </summary>

		/// <param name="years">Number of years.</param>

		/// <param name="months">Number of months.</param>

		/// <param name="days">Number of days.</param>

		/// <param name="hours">Number of hours.</param>

		/// <param name="minutes">Number of minutes.</param>

		/// <param name="seconds">Number of seconds.</param>

		/// <param name="milliseconds">Number of milliseconds.</param>

		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



		/// <summary>

		/// The total number of ticks(100ns units) contained. This is the resolution of the

		/// <see cref="NpgsqlInterval"/>  type. This ignores the number of days and

		/// months held. If you want them included use <see cref="UnjustifyInterval()"/> first.

		/// <remarks>The resolution of the PostgreSQL

		/// interval type is by default 1&#xb5;s = 1,000 ns. It may be smaller as follows:

		/// <list type="number">

		/// <item>

		/// <term>interval(0)</term>

		/// <description>resolution of 1s (1 second)</description>

		/// </item>

		/// <item>

		/// <term>interval(1)</term>

		/// <description>resolution of 100ms = 0.1s (100 milliseconds)</description>

		/// </item>

		/// <item>

		/// <term>interval(2)</term>

		/// <description>resolution of 10ms = 0.01s (10 milliseconds)</description>

		/// </item>

		/// <item>

		/// <term>interval(3)</term>

		/// <description>resolution of 1ms = 0.001s (1 millisecond)</description>

		/// </item>

		/// <item>

		/// <term>interval(4)</term>

		/// <description>resolution of 100&#xb5;s = 0.0001s (100 microseconds)</description>

		/// </item>

		/// <item>

		/// <term>interval(5)</term>

		/// <description>resolution of 10&#xb5;s = 0.00001s (10 microseconds)</description>

		/// </item>

		/// <item>

		/// <term>interval(6) or interval</term>

		/// <description>resolution of 1&#xb5;s = 0.000001s (1 microsecond)</description>

		/// </item>

		/// </list>

		/// <para>As such, if the 100-nanosecond resolution is significant to an application, a PostgreSQL interval will

		/// not suffice for those purposes.</para>

		/// <para>In more frequent cases though, the resolution of the interval suffices.

		/// <see cref="NpgsqlInterval"/> will always suffice to handle the resolution of any interval value, and upon

		/// writing to the database, will be rounded to the resolution used.</para>

		/// </remarks>

		/// <returns>The number of ticks in the instance.</returns>

		/// </summary>

		public long Ticks

		{

			get { return _ticks; }

		}



		/// <summary>

		/// Gets the number of whole microseconds held in the instance.

		/// <returns>An  in the range [-999999, 999999].</returns>

		/// </summary>

		public int Microseconds

		{

			get { return (int) ((_ticks/10)%1000000); }

		}



		/// <summary>

		/// Gets the number of whole milliseconds held in the instance.

		/// <returns>An  in the range [-999, 999].</returns>

		/// </summary>

		public int Milliseconds

		{

			get { return (int) ((_ticks/TicksPerMillsecond)%1000); }

		}



		/// <summary>

		/// Gets the number of whole seconds held in the instance.

		/// <returns>An  in the range [-59, 59].</returns>

		/// </summary>

		public int Seconds

		{

			get { return (int) ((_ticks/TicksPerSecond)%60); }

		}



		/// <summary>

		/// Gets the number of whole minutes held in the instance.

		/// <returns>An  in the range [-59, 59].</returns>

		/// </summary>

		public int Minutes

		{

			get { return (int) ((_ticks/TicksPerMinute)%60); }

		}



		/// <summary>

		/// Gets the number of whole hours held in the instance.

		/// <remarks>Note that this can be less than -23 or greater than 23 unless <see cref="JustifyDays()"/>

		/// has been used to produce this instance.</remarks>

		/// </summary>

		public int Hours

		{

			get { return (int) (_ticks/TicksPerHour); }

		}



		/// <summary>

		/// Gets the number of days held in the instance.

		/// <remarks>Note that this does not pay attention to a time component with -24 or less hours or

		/// 24 or more hours, unless <see cref="JustifyDays()"/> has been called to produce this instance.</remarks>

		/// </summary>

		public int Days

		{

			get { return _days; }

		}



		/// <summary>

		/// Gets the number of months held in the instance.

		/// <remarks>Note that this does not pay attention to a day component with -30 or less days or

		/// 30 or more days, unless <see cref="JustifyMonths()"/> has been called to produce this instance.</remarks>

		/// </summary>

		public int Months

		{

			get { return _months; }

		}



		/// <summary>

		/// Returns a <see cref="TimeSpan"/> representing the time component of the instance.

		/// <remarks>Note that this may have a value beyond the range &#xb1;23:59:59.9999999 unless

		/// <see cref="JustifyDays()"/> has been called to produce this instance.</remarks>

		/// </summary>

		public TimeSpan Time

		{

			get { return new TimeSpan(_ticks); }

		}



		#endregion



		#region Total Parts



		/// <summary>

		/// The total number of ticks (100ns units) in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public long TotalTicks

		{

			get { return Ticks + Days*TicksPerDay + Months*TicksPerMonth; }

		}



		/// <summary>

		/// The total number of microseconds in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public double TotalMicroseconds

		{

			get { return TotalTicks/10d; }

		}



		/// <summary>

		/// The total number of milliseconds in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public double TotalMilliseconds

		{

			get { return TotalTicks/(double) TicksPerMillsecond; }

		}



		/// <summary>

		/// The total number of seconds in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public double TotalSeconds

		{

			get { return TotalTicks/(double) TicksPerSecond; }

		}



		/// <summary>

		/// The total number of minutes in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public double TotalMinutes

		{

			get { return TotalTicks/(double) TicksPerMinute; }

		}



		/// <summary>

		/// The total number of hours in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public double TotalHours

		{

			get { return TotalTicks/(double) TicksPerHour; }

		}



		/// <summary>

		/// The total number of days in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public double TotalDays

		{

			get { return TotalTicks/(double) TicksPerDay; }

		}



		/// <summary>

		/// The total number of months in the instance, assuming 24 hours in each day and

		/// 30 days in a month.

		/// </summary>

		public double TotalMonths

		{

			get { return TotalTicks/(double) TicksPerMonth; }

		}



		#endregion



		#region Create From Part



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of ticks.

		/// </summary>

		/// <param name="ticks">The number of ticks (100ns units) in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of ticks.</returns>

		public static NpgsqlInterval FromTicks(long ticks)

		{

			return new NpgsqlInterval(ticks).Canonicalize();

		}



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of microseconds.

		/// </summary>

		/// <param name="ticks">The number of microseconds in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of microseconds.</returns>

		public static NpgsqlInterval FromMicroseconds(double micro)

		{

			return FromTicks((long) (micro*TicksPerMicrosecond));

		}



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of milliseconds.

		/// </summary>

		/// <param name="ticks">The number of milliseconds in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of milliseconds.</returns>

		public static NpgsqlInterval FromMilliseconds(double milli)

		{

			return FromTicks((long) (milli*TicksPerMillsecond));

		}



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of seconds.

		/// </summary>

		/// <param name="ticks">The number of seconds in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of seconds.</returns>

		public static NpgsqlInterval FromSeconds(double seconds)

		{

			return FromTicks((long) (seconds*TicksPerSecond));

		}



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of minutes.

		/// </summary>

		/// <param name="ticks">The number of minutes in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of minutes.</returns>

		public static NpgsqlInterval FromMinutes(double minutes)

		{

			return FromTicks((long) (minutes*TicksPerMinute));

		}



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of hours.

		/// </summary>

		/// <param name="ticks">The number of hours in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of hours.</returns>

		public static NpgsqlInterval FromHours(double hours)

		{

			return FromTicks((long) (hours*TicksPerHour));

		}



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of days.

		/// </summary>

		/// <param name="ticks">The number of days in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of days.</returns>

		public static NpgsqlInterval FromDays(double days)

		{

			return FromTicks((long) (days*TicksPerDay));

		}



		/// <summary>

		/// Creates an <see cref="NpgsqlInterval"/> from a number of months.

		/// </summary>

		/// <param name="ticks">The number of months in the interval.</param>

		/// <returns>A <see cref="Canonicalize()"/>d <see cref="NpgsqlInterval"/> with the given number of months.</returns>

		public static NpgsqlInterval FromMonths(double months)

		{

			return FromTicks((long) (months*TicksPerMonth));

		}



		#endregion



		#region Arithmetic



		/// <summary>

		/// Adds another interval to this instance and returns the result.

		/// </summary>

		/// <param name="interval">An <see cref="NpgsqlInterval"/> to add to this instance.</param>

		/// <returns>An <see cref="NpgsqlInterval"></see> whose values are the sums of the two instances.</returns>

		public NpgsqlInterval Add(NpgsqlInterval interval)

		{

			return new NpgsqlInterval(Months + interval.Months, Days + interval.Days, Ticks + interval.Ticks);

		}



		/// <summary>

		/// Subtracts another interval from this instance and returns the result.

		/// </summary>

		/// <param name="interval">An <see cref="NpgsqlInterval"/> to subtract from this instance.</param>

		/// <returns>An <see cref="NpgsqlInterval"></see> whose values are the differences of the two instances.</returns>

		public NpgsqlInterval Subtract(NpgsqlInterval interval)

		{

			return new NpgsqlInterval(Months - interval.Months, Days - interval.Days, Ticks - interval.Ticks);

		}



		/// <summary>

		/// Returns an <see cref="NpgsqlInterval"/> whose value is the negated value of this instance.

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> whose value is the negated value of this instance.</returns>

		public NpgsqlInterval Negate()

		{

			return new NpgsqlInterval(-Months, -Days, -Ticks);

		}



		/// <summary>

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

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> whose value is the absolute value of this instance.</returns>

		public NpgsqlInterval Duration()

		{

			return UnjustifyInterval().Ticks < 0 ? Negate() : this;

		}



		#endregion



		#region Justification



		/// <summary>

		/// Equivalent to PostgreSQL's justify_days function.

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> based on this one, but with any hours outside of the range [-23, 23]

		/// converted into days.</returns>

		public NpgsqlInterval JustifyDays()

		{

			return new NpgsqlInterval(Months, Days + (int) (Ticks/TicksPerDay), Ticks%TicksPerDay);

		}



		/// <summary>

		/// Opposite to PostgreSQL's justify_days function.

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> based on this one, but with any days converted to multiples of &#xB1;24hours.</returns>

		public NpgsqlInterval UnjustifyDays()

		{

			return new NpgsqlInterval(Months, 0, Ticks + Days*TicksPerDay);

		}



		/// <summary>

		/// Equivalent to PostgreSQL's justify_months function.

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> based on this one, but with any days outside of the range [-30, 30]

		/// converted into months.</returns>

		public NpgsqlInterval JustifyMonths()

		{

			return new NpgsqlInterval(Months + Days/DaysPerMonth, Days%DaysPerMonth, Ticks);

		}



		/// <summary>

		/// Opposite to PostgreSQL's justify_months function.

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> based on this one, but with any months converted to multiples of &#xB1;30days.</returns>

		public NpgsqlInterval UnjustifyMonths()

		{

			return new NpgsqlInterval(0, Days + Months*DaysPerMonth, Ticks);

		}



		/// <summary>

		/// Equivalent to PostgreSQL's justify_interval function.

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> based on this one,

		/// but with any months converted to multiples of &#xB1;30days

		/// and then with any days converted to multiples of &#xB1;24hours</returns>

		public NpgsqlInterval JustifyInterval()

		{

			return JustifyMonths().JustifyDays();

		}



		/// <summary>

		/// Opposite to PostgreSQL's justify_interval function.

		/// </summary>

		/// <returns>An <see cref="NpgsqlInterval"/> based on this one, but with any months converted to multiples of &#xB1;30days and then any days converted to multiples of &#xB1;24hours;</returns>

		public NpgsqlInterval UnjustifyInterval()

		{

			return new NpgsqlInterval(Ticks + Days*TicksPerDay + Months*DaysPerMonth*TicksPerDay);

		}



		/// <summary>

		/// Produces a canonical NpgslInterval with 0 months and hours in the range of [-23, 23].

		/// <remarks>

		/// <para>

		/// While the fact that for many purposes, two different <see cref="NpgsqlInterval"/> instances could be considered

		/// equivalent (e.g. one with 2days, 3hours and one with 1day 27hours) there are different possible canonical forms.

		/// </para><para>

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

		/// </para><para>First, it is closest two how

		/// <see cref="TimeSpan"/> 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.

		/// </para>

		/// <para>Note that the results of casting a <see cref="TimeSpan"/> to <see cref="NpgsqlInterval"/> is

		/// canonicalised.</para>

		/// </remarks>

		/// <returns>An <see cref="NpgsqlInterval"/> based on this one, but with months converted to multiples of &#xB1;30days and with any hours outside of the range [-23, 23]

		/// converted into days.</return>

		public NpgsqlInterval Canonicalize()

		{

			return new NpgsqlInterval(0, Days + Months*DaysPerMonth + (int) (Ticks/TicksPerDay), Ticks%TicksPerDay);

		}



		#endregion



		#region Casts



		/// <summary>

		/// Implicit cast of a <see cref="TimeSpan"/> to an <see cref="NpgsqlInterval"/>

		/// </summary>

		/// <param name="timespan">A <see cref="TimeSpan"/></param>

		/// <returns>An eqivalent, canonical, <see cref="NpgsqlInterval"/>.</returns>

		public static implicit operator NpgsqlInterval(TimeSpan timespan)

		{

			return new NpgsqlInterval(timespan).Canonicalize();

		}



		/// <summary>

		/// Implicit cast of an <see cref="NpgsqlInterval"/> to a <see cref="TimeSpan"/>.

		/// </summary>

		/// <param name="interval">A <see cref="NpgsqlInterval"/>.</param>

		/// <returns>An equivalent <see cref="TimeSpan"/>.</returns>

		public static explicit operator TimeSpan(NpgsqlInterval interval)

		{

			return new TimeSpan(interval.Ticks + interval.Days*TicksPerDay + interval.Months*DaysPerMonth*TicksPerDay);

		}



		#endregion



		#region Comparison



		/// <summary>

		/// Returns true if another <see cref="NpgsqlInterval"/> is exactly the same as this instance.

		/// </summary>

		/// <param name="other">An <see cref="NpgsqlInterval"/> for comparison.</param>

		/// <returns>true if the two <see cref="NpgsqlInterval"/> instances are exactly the same,

		/// false otherwise.</returns>

		public bool Equals(NpgsqlInterval other)

		{

			return Ticks == other.Ticks && Days == other.Days && Months == other.Months;

		}



		/// <summary>

		/// Returns true if another object is an <see cref="NpgsqlInterval"/>, that is exactly the same as

		/// this instance

		/// </summary>

		/// <param name="obj">An <see cref="Object"/> for comparison.</param>

		/// <returns>true if the argument is an <see cref="NpgsqlInterval"/> and is exactly the same

		/// as this one, false otherwise.</returns>

		public override bool Equals(object obj)

		{

			if (obj == null)

			{

				return false;

			}

			if (obj is NpgsqlInterval)

			{

				return Equals((NpgsqlInterval) obj);

			}

			return false;

		}



		/// <summary>

		/// Compares two <see cref="NpgsqlInterval"/> instances.

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/>.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/>.</param>

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

		public static int Compare(NpgsqlInterval x, NpgsqlInterval y)

		{

			return x.CompareTo(y);

		}



		int IComparer<NpgsqlInterval>.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();

			}

		}



		/// <summary>

		/// A hash code suitable for uses with hashing algorithms.

		/// </summary>

		/// <returns>An signed integer.</returns>

		public override int GetHashCode()

		{

			return UnjustifyInterval().Ticks.GetHashCode();

		}



		/// <summary>

		/// Compares this instance with another/

		/// </summary>

		/// <param name="other">An <see cref="NpgsqlInterval"/> to compare this with.</param>

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

		public int CompareTo(NpgsqlInterval other)

		{

			return UnjustifyInterval().Ticks.CompareTo(other.UnjustifyInterval().Ticks);

		}



		/// <summary>

		/// Compares this instance with another/

		/// </summary>

		/// <param name="other">An object to compare this with.</param>

		/// <returns>0 if the argument is an <see cref="NpgsqlInterval"/> and the instances are equal or equivalent.

		/// A value less than zero if the argument is an <see cref="NpgsqlInterval"/> and

		/// this instance is less than the argument.

		/// A value greater than zero if the argument is an <see cref="NpgsqlInterval"/> and this instance

		/// is greater than the instance.</returns>

		/// A value greater than zero if the argument is null.

		/// <exception cref="ArgumentException">The argument is not an <see cref="NpgsqlInterval"/>.</exception>

		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



		/// <summary>

		/// Parses a <see cref="String"/> and returns a <see cref="NpgsqlInterval"/> instance.

		/// Designed to use the formats generally returned by PostgreSQL.

		/// </summary>

		/// <param name="str">The <see cref="String"/> to parse.</param>

		/// <returns>An <see cref="NpgsqlInterval"/> represented by the argument.</returns>

		/// <exception cref="ArgumentNullException">The string was null.</exception>

		/// <exception cref="OverflowException">A value obtained from parsing the string exceeded the values allowed for the relevant component.</exception>

		/// <exception cref="FormatException">The string was not in a format that could be parsed to produce an <see cref="NpgsqlInterval"/>.</exception>

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

        }



		/// <summary>

		/// Attempt to parse a <see cref="String"/> to produce an <see cref="NpgsqlInterval"/>.

		/// </summary>

		/// <param name="str">The <see cref="String"/> to parse.</param>

		/// <param name="result">(out) The <see cref="NpgsqlInterval"/> produced, or <see cref="Zero"/> if the parsing failed.</param>

		/// <returns>true if the parsing succeeded, false otherwise.</returns>

		public static bool TryParse(string str, out NpgsqlInterval result)

		{

			try

			{

				result = Parse(str);

				return true;

			}

			catch (Exception)

			{

				result = Zero;

				return false;

			}

		}



		/// <summary>

		/// Create a <see cref="String"/> representation of the <see cref="NpgsqlInterval"/> 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 <see cref="NpgsqlInterval"/> is represented as 00:00:00

		/// <remarks>

		/// Ticks are 100ns, Postgress resolution is only to 1&#xb5;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.

		/// </remarks>

		/// </summary>

		/// <returns>The <see cref="String"/> representation.</returns>

		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



		/// <summary>

		/// Adds two <see cref="NpgsqlInterval"/> together.

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/> to add.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/> to add.</param>

		/// <returns>An <see cref="NpgsqlInterval"/> whose values are the sum of the arguments.</returns>

		public static NpgsqlInterval operator +(NpgsqlInterval x, NpgsqlInterval y)

		{

			return x.Add(y);

		}



		/// <summary>

		/// Subtracts one <see cref="NpgsqlInterval"/> from another.

		/// </summary>

		/// <param name="x">The <see cref="NpgsqlInterval"/> to subtract the other from.</param>

		/// <param name="y">The <see cref="NpgsqlInterval"/> to subtract from the other.</param>

		/// <returns>An <see cref="NpgsqlInterval"/> whose values are the difference of the arguments</returns>

		public static NpgsqlInterval operator -(NpgsqlInterval x, NpgsqlInterval y)

		{

			return x.Subtract(y);

		}



		/// <summary>

		/// Returns true if two <see cref="NpgsqlInterval"/> are exactly the same.

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/> to compare.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/> to compare.</param>

		/// <returns>true if the two arguments are exactly the same, false otherwise.</returns>

		public static bool operator ==(NpgsqlInterval x, NpgsqlInterval y)

		{

			return x.Equals(y);

		}



		/// <summary>

		/// Returns false if two <see cref="NpgsqlInterval"/> are exactly the same.

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/> to compare.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/> to compare.</param>

		/// <returns>false if the two arguments are exactly the same, true otherwise.</returns>

		public static bool operator !=(NpgsqlInterval x, NpgsqlInterval y)

		{

			return !(x == y);

		}



		/// <summary>

		/// Compares two <see cref="NpgsqlInterval"/> instances to see if the first is less than the second

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/> to compare.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/> to compare.</param>

		/// <returns>true if the first <see cref="NpgsqlInterval"/> is less than second, false otherwise.</returns>

		public static bool operator <(NpgsqlInterval x, NpgsqlInterval y)

		{

			return x.UnjustifyInterval().Ticks < y.UnjustifyInterval().Ticks;

		}



		/// <summary>

		/// Compares two <see cref="NpgsqlInterval"/> instances to see if the first is less than or equivalent to the second

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/> to compare.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/> to compare.</param>

		/// <returns>true if the first <see cref="NpgsqlInterval"/> is less than or equivalent to second, false otherwise.</returns>

		public static bool operator <=(NpgsqlInterval x, NpgsqlInterval y)

		{

			return x.UnjustifyInterval().Ticks <= y.UnjustifyInterval().Ticks;

		}



		/// <summary>

		/// Compares two <see cref="NpgsqlInterval"/> instances to see if the first is greater than the second

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/> to compare.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/> to compare.</param>

		/// <returns>true if the first <see cref="NpgsqlInterval"/> is greater than second, false otherwise.</returns>

		public static bool operator >(NpgsqlInterval x, NpgsqlInterval y)

		{

			return !(x <= y);

		}



		/// <summary>

		/// Compares two <see cref="NpgsqlInterval"/> instances to see if the first is greater than or equivalent the second

		/// </summary>

		/// <param name="x">The first <see cref="NpgsqlInterval"/> to compare.</param>

		/// <param name="y">The second <see cref="NpgsqlInterval"/> to compare.</param>

		/// <returns>true if the first <see cref="NpgsqlInterval"/> is greater than or equivalent to the second, false otherwise.</returns>

		public static bool operator >=(NpgsqlInterval x, NpgsqlInterval y)

		{

			return !(x < y);

		}



		/// <summary>

		/// Returns the instance.

		/// </summary>

		/// <param name="x">An <see cref="NpgsqlInterval"/>.</param>

		/// <returns>The argument.</returns>

		public static NpgsqlInterval operator +(NpgsqlInterval x)

		{

			return x;

		}



		/// <summary>

		/// Negates an <see cref="NpgsqlInterval"/> instance.

		/// </summary>

		/// <param name="x">An <see cref="NpgsqlInterval"/>.</param>

		/// <returns>The negation of the argument.</returns>

		public static NpgsqlInterval operator -(NpgsqlInterval x)

		{

			return x.Negate();

		}



		#endregion

	}



	[Serializable]

	public struct NpgsqlDate : IEquatable<NpgsqlDate>, IComparable<NpgsqlDate>, IComparable, IComparer<NpgsqlDate>,

	                           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<NpgsqlTimeZone>, IComparable<NpgsqlTimeZone>, 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<NpgsqlTime>, IComparable<NpgsqlTime>, IComparable, IComparer<NpgsqlTime>,

	                           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)

		{

		}



		/// <summary>

		/// The total number of ticks(100ns units) contained. This is the resolution of the

		/// <see cref="NpgsqlTime"/>  type.

		/// <remarks>The resolution of the PostgreSQL

		/// interval type is by default 1&#xb5;s = 1,000 ns. It may be smaller as follows:

		/// <list type="number">

		/// <item>

		/// <term>time(0)</term>

		/// <description>resolution of 1s (1 second)</description>

		/// </item>

		/// <item>

		/// <term>time(1)</term>

		/// <description>resolution of 100ms = 0.1s (100 milliseconds)</description>

		/// </item>

		/// <item>

		/// <term>time(2)</term>

		/// <description>resolution of 10ms = 0.01s (10 milliseconds)</description>

		/// </item>

		/// <item>

		/// <term>time(3)</term>

		/// <description>resolution of 1ms = 0.001s (1 millisecond)</description>

		/// </item>

		/// <item>

		/// <term>time(4)</term>

		/// <description>resolution of 100&#xb5;s = 0.0001s (100 microseconds)</description>

		/// </item>

		/// <item>

		/// <term>time(5)</term>

		/// <description>resolution of 10&#xb5;s = 0.00001s (10 microseconds)</description>

		/// </item>

		/// <item>

		/// <term>time(6) or interval</term>

		/// <description>resolution of 1&#xb5;s = 0.000001s (1 microsecond)</description>

		/// </item>

		/// </list>

		/// <para>As such, if the 100-nanosecond resolution is significant to an application, a PostgreSQL time will

		/// not suffice for those purposes.</para>

		/// <para>In more frequent cases though, the resolution of time suffices.

		/// <see cref="NpgsqlTime"/> will always suffice to handle the resolution of any time value, and upon

		/// writing to the database, will be rounded to the resolution used.</para>

		/// </remarks>

		/// <returns>The number of ticks in the instance.</returns>

		/// </summary>

		public long Ticks

		{

			get { return _ticks; }

		}



		/// <summary>

		/// Gets the number of whole microseconds held in the instance.

		/// <returns>An integer in the range [0, 999999].</returns>

		/// </summary>

		public int Microseconds

		{

			get { return (int) ((_ticks/10)%1000000); }

		}



		/// <summary>

		/// Gets the number of whole milliseconds held in the instance.

		/// <returns>An integer in the range [0, 999].</returns>

		/// </summary>

		public int Milliseconds

		{

			get { return (int) ((_ticks/NpgsqlInterval.TicksPerMillsecond)%1000); }

		}



		/// <summary>

		/// Gets the number of whole seconds held in the instance.

		/// <returns>An interger in the range [0, 59].</returns>

		/// </summary>

		public int Seconds

		{

			get { return (int) ((_ticks/NpgsqlInterval.TicksPerSecond)%60); }

		}



		/// <summary>

		/// Gets the number of whole minutes held in the instance.

		/// <returns>An integer in the range [0, 59].</returns>

		/// </summary>

		public int Minutes

		{

			get { return (int) ((_ticks/NpgsqlInterval.TicksPerMinute)%60); }

		}



		/// <summary>

		/// Gets the number of whole hours held in the instance.

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

		/// </summary>

		public int Hours

		{

			get { return (int) (_ticks/NpgsqlInterval.TicksPerHour); }

		}



		/// <summary>

		/// Normalise this time; if it is 24:00:00, convert it to 00:00:00

		/// </summary>

		/// <returns>This time, normalised</returns>

		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<NpgsqlTimeTZ>, IComparable<NpgsqlTimeTZ>, IComparable, IComparer<NpgsqlTimeTZ>,

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

		}



		/// <summary>

		/// Gets the number of whole microseconds held in the instance.

		/// <returns>An integer in the range [0, 999999].</returns>

		/// </summary>

		public int Microseconds

		{

			get { return _localTime.Microseconds; }

		}



		/// <summary>

		/// Gets the number of whole milliseconds held in the instance.

		/// <returns>An integer in the range [0, 999].</returns>

		/// </summary>

		public int Milliseconds

		{

			get { return _localTime.Milliseconds; }

		}



		/// <summary>

		/// Gets the number of whole seconds held in the instance.

		/// <returns>An interger in the range [0, 59].</returns>

		/// </summary>

		public int Seconds

		{

			get { return _localTime.Seconds; }

		}



		/// <summary>

		/// Gets the number of whole minutes held in the instance.

		/// <returns>An integer in the range [0, 59].</returns>

		/// </summary>

		public int Minutes

		{

			get { return _localTime.Minutes; }

		}



		/// <summary>

		/// Gets the number of whole hours held in the instance.

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

		/// </summary>

		public int Hours

		{

			get { return _localTime.Hours; }

		}



		/// <summary>

		/// Normalise this time; if it is 24:00:00, convert it to 00:00:00

		/// </summary>

		/// <returns>This time, normalised</returns>

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

		}



		/// <summary>

		/// Compares this with another <see cref="NpgsqlTimeTZ"/>. 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).

		/// </summary>

		/// <param name="other">the <see cref="NpgsqlTimeTZ"/> to compare with.</param>

		/// <returns>An integer which is 0 if they are equal, &lt; 0 if this is the smaller and &gt; 0 if this is the larger.</returns>

		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<NpgsqlTimeStamp>, IComparable<NpgsqlTimeStamp>, IComparable,

	                                IComparer<NpgsqlTimeStamp>, 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("You cannot subtract infinity timestamps");

			}

			switch (timestamp._type)

			{

				case TimeType.Infinity:

				case TimeType.MinusInfinity:

					throw new ArgumentOutOfRangeException("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<NpgsqlTimeStampTZ>, IComparable<NpgsqlTimeStampTZ>, IComparable,

	                                  IComparer<NpgsqlTimeStampTZ>, 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("You cannot subtract infinity timestamps");

			}

			switch (timestamp._type)

			{

				case TimeType.Infinity:

				case TimeType.MinusInfinity:

					throw new ArgumentOutOfRangeException("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;

		}

	}

}

