Summary

Class:CBAM.SQL.PostgreSQL.PgSQLTimestampTZ
Assembly:CBAM.SQL.PostgreSQL
File(s):/repo-dir/contents/Source/Code/CBAM.SQL.PostgreSQL/Types.cs
Covered lines:0
Uncovered lines:168
Coverable lines:168
Total lines:3985
Line coverage:0%
Branch coverage:0%

Coverage History

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.cctor()100%0%
.ctor(...)400%0%
.ctor(...)100%0%
.ctor(...)100%0%
.ctor(...)200%0%
.ctor(...)200%0%
Equals(...)800%0%
Equals(...)400%0%
GetHashCode()400%0%
System.IComparable.CompareTo(...)400%0%
CompareTo(...)1000%0%
AddDays(...)200%0%
AddYears(...)200%0%
AddMonths(...)200%0%
Add(...)200%0%
Subtract(...)100%0%
Subtract(...)400%0%
Normalize()100%0%
AtTimeZone(...)100%0%
op_Explicit(...)600%0%
op_Explicit(...)400%0%
op_Explicit(...)400%0%
op_Explicit(...)400%0%
op_Equality(...)100%0%
op_Inequality(...)100%0%
op_LessThan(...)100%0%
op_LessThanOrEqual(...)100%0%
op_GreaterThan(...)100%0%
op_GreaterThanOrEqual(...)100%0%
op_Addition(...)100%0%
op_Addition(...)100%0%
op_Subtraction(...)100%0%
op_Subtraction(...)100%0%
GetTextByteCount(...)400%0%
WriteTextBytes(...)400%0%
ParseBinaryText(...)600%0%
ToString()400%0%
Parse(...)200%0%
TryParse(...)100%0%
TryParse(...)1400%0%

File(s)

/repo-dir/contents/Source/Code/CBAM.SQL.PostgreSQL/Types.cs

#LineLine coverage
 1/*
 2 * Copyright 2017 Stanislav Muhametsin. All rights Reserved.
 3 *
 4 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 5 * you may not use  this file  except in  compliance with the License.
 6 * You may obtain a copy of the License at
 7 *
 8 *   http://www.apache.org/licenses/LICENSE-2.0
 9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed  under the  License is distributed on an "AS IS" BASIS,
 12 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 13 * implied.
 14 *
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18using CBAM.Abstractions;
 19using CBAM.SQL.PostgreSQL;
 20using System;
 21using System.Collections.Generic;
 22using System.IO;
 23using System.Text;
 24using System.Threading;
 25using System.Threading.Tasks;
 26using UtilPack;
 27
 28// TODO document these later.
 29// TODO when (if?) we will have extension properties ( https://www.infoq.com/news/2017/08/CSharp-8 , under "extension ev
 30// Writing them now as extension methods would not be feasible in terms of user experience.
 31
 32#pragma warning disable 1591
 33namespace CBAM.SQL.PostgreSQL
 34{
 35   // TODO implement IPgTypeWithBackendTextFormat
 36   public struct PgSQLInterval : IComparable, IComparable<PgSQLInterval>, IEquatable<PgSQLInterval>
 37   {
 38      //// Getting decimal digits from System.Decimal: http://stackoverflow.com/questions/13477689/find-number-of-decima
 39      //private delegate Int32 GetDigitsDelegate( ref Decimal value );
 40
 41      //private static class DecimalHelper
 42      //{
 43      //   public static readonly GetDigitsDelegate GetDigits;
 44
 45      //   static DecimalHelper()
 46      //   {
 47      //      var value = Expression.Parameter( typeof( Decimal ).MakeByRefType(), "value" );
 48
 49      //      //return (value.flags & ~Int32.MinValue) >> 16
 50      //      var digits = Expression.RightShift(
 51      //          Expression.And( Expression.Field( value, "flags" ), Expression.Constant( ~Int32.MinValue, typeof( Int3
 52      //          Expression.Constant( 16, typeof( Int32 ) ) );
 53
 54      //      GetDigits = Expression.Lambda<GetDigitsDelegate>( digits, value ).Compile();
 55      //   }
 56      //}
 57
 58
 59      #region Consts
 60      private const Int32 DAYS_PER_MONTH = 30;
 61      private const Int32 MONTHS_PER_YEAR = 12;
 62      private const Int64 TICKS_PER_MONTH = TimeSpan.TicksPerDay * DAYS_PER_MONTH;
 63      internal const Int64 TICKS_PER_MICROSECOND = TimeSpan.TicksPerMillisecond / 1000;
 64      internal const Int64 MICROSECONDS_PER_SECOND = 1000000;
 65      internal const Int64 MILLISECONDS_PER_SECOND = 1000;
 66      internal const Int64 SECONDS_PER_MINUTE = 60;
 67      internal const Int64 MINUTES_PER_HOUR = 60;
 68
 69      #endregion
 70
 71      #region Static
 72
 73      public static PgSQLInterval MinValue = new PgSQLInterval( Int64.MinValue );
 74      public static PgSQLInterval MaxValue = new PgSQLInterval( Int64.MaxValue );
 75      public static PgSQLInterval Zero = new PgSQLInterval( 0 );
 76
 77      internal static void AppendTimeInformation( StringBuilder sb, Int64 ticks )
 78      {
 79         sb.Append( Math.Abs( CalcHours( ticks ) ).ToString( "D2" ) ) // Hours
 80            .Append( ':' )
 81            .Append( Math.Abs( CalcMinutes( ticks ) ).ToString( "D2" ) ) // Minutes
 82            .Append( ':' )
 83            // Calculate seconds part (total seconds minus whole minutes in seconds)
 84            .Append( Math.Abs( ticks / (Decimal) TimeSpan.TicksPerSecond - ( ticks / TimeSpan.TicksPerMinute ) * 60 ).To
 85      }
 86
 87
 88
 89      internal static Int32 CalcMicroseconds( Int64 ticks )
 90      {
 91         return (Int32) ( ( ticks / TICKS_PER_MICROSECOND ) % MICROSECONDS_PER_SECOND );
 92      }
 93
 94      internal static Int32 CalcMilliseconds( Int64 ticks )
 95      {
 96         return (Int32) ( ( ticks / TimeSpan.TicksPerMillisecond ) % MILLISECONDS_PER_SECOND );
 97      }
 98
 99      internal static Int32 CalcSeconds( Int64 ticks )
 100      {
 101         return (Int32) ( ( ticks / TimeSpan.TicksPerSecond ) % SECONDS_PER_MINUTE );
 102      }
 103
 104      internal static Int32 CalcMinutes( Int64 ticks )
 105      {
 106         return (Int32) ( ( ticks / TimeSpan.TicksPerMinute ) % MINUTES_PER_HOUR );
 107      }
 108
 109      internal static Int32 CalcHours( Int64 ticks )
 110      {
 111         return (Int32) ( ticks / TimeSpan.TicksPerHour );
 112      }
 113
 114      #endregion
 115
 116      #region Fields
 117      private readonly Int32 _months;
 118      private readonly Int32 _days;
 119      private readonly Int64 _ticks;
 120
 121      #endregion
 122
 123      #region Constructors
 124
 125      public PgSQLInterval( Int64 ticks )
 126         : this( 0, 0, ticks )
 127      {
 128
 129      }
 130
 131      public PgSQLInterval( TimeSpan span )
 132         : this( span.Ticks )
 133      {
 134
 135      }
 136
 137      public PgSQLInterval( Int32 months, Int32 days, Int64 ticks )
 138      {
 139         this._months = months;
 140         this._days = days;
 141         this._ticks = ticks;
 142      }
 143
 144      public PgSQLInterval( Int32 days, Int32 hours, Int32 minutes, Int32 seconds )
 145         : this( 0, days, new TimeSpan( hours, minutes, seconds ).Ticks )
 146      {
 147      }
 148
 149      public PgSQLInterval( Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds )
 150         : this( 0, days, new TimeSpan( 0, hours, minutes, seconds, milliseconds ).Ticks )
 151      {
 152      }
 153
 154      public PgSQLInterval( Int32 months, Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds )
 155         : this( months, days, new TimeSpan( 0, hours, minutes, seconds, milliseconds ).Ticks )
 156      {
 157      }
 158
 159      public PgSQLInterval( Int32 years, Int32 months, Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 mill
 160         : this( years * 12 + months, days, new TimeSpan( 0, hours, minutes, seconds, milliseconds ).Ticks )
 161      {
 162      }
 163
 164      #endregion
 165
 166      #region Whole parts
 167
 168      public Int64 Ticks
 169      {
 170         get
 171         {
 172            return this._ticks;
 173         }
 174      }
 175
 176      public Int32 Microseconds
 177      {
 178         get
 179         {
 180            return CalcMicroseconds( this._ticks );
 181         }
 182      }
 183
 184      public Int32 Milliseconds
 185      {
 186         get
 187         {
 188            return CalcMilliseconds( this._ticks );
 189         }
 190      }
 191
 192      public Int32 Seconds
 193      {
 194         get
 195         {
 196            return CalcSeconds( this._ticks );
 197         }
 198      }
 199
 200      public Int32 Minutes
 201      {
 202         get
 203         {
 204            return CalcMinutes( this._ticks );
 205         }
 206      }
 207
 208      public Int32 Hours
 209      {
 210         get
 211         {
 212            return CalcHours( this._ticks );
 213         }
 214      }
 215
 216      public Int32 Days
 217      {
 218         get
 219         {
 220            return this._days;
 221         }
 222      }
 223
 224      public Int32 Months
 225      {
 226         get
 227         {
 228            return this._months;
 229         }
 230      }
 231      #endregion
 232
 233      #region Total parts
 234
 235      public Int64 TotalTicks
 236      {
 237         get
 238         {
 239            return this._ticks + this._days * TimeSpan.TicksPerDay + this._months * TICKS_PER_MONTH;
 240         }
 241      }
 242
 243      public Double TotalMicroseconds
 244      {
 245         get
 246         {
 247            return this.TotalTicks / ( (Double) TICKS_PER_MICROSECOND );
 248         }
 249      }
 250
 251      public Double TotalMilliseconds
 252      {
 253         get
 254         {
 255            return this.TotalTicks / ( (Double) TimeSpan.TicksPerMillisecond );
 256         }
 257      }
 258
 259      public Double TotalSeconds
 260      {
 261         get
 262         {
 263            return this.TotalTicks / ( (Double) TimeSpan.TicksPerSecond );
 264         }
 265      }
 266
 267      public Double TotalMinutes
 268      {
 269         get
 270         {
 271            return this.TotalTicks / ( (Double) TimeSpan.TicksPerMinute );
 272         }
 273      }
 274
 275      public Double TotalHours
 276      {
 277         get
 278         {
 279            return this.TotalTicks / ( (Double) TimeSpan.TicksPerHour );
 280         }
 281      }
 282
 283      public Double TotalDays
 284      {
 285         get
 286         {
 287            return this.TotalTicks / ( (Double) TimeSpan.TicksPerDay );
 288         }
 289      }
 290
 291      public Double TotalMonths
 292      {
 293         get
 294         {
 295            return this.TotalTicks / ( (Double) TICKS_PER_MONTH );
 296         }
 297      }
 298
 299      #endregion
 300
 301      #region Justification
 302
 303      public PgSQLInterval JustifyDays()
 304      {
 305         return new PgSQLInterval( this._months, this._days + (Int32) ( this._ticks / TimeSpan.TicksPerDay ), this._tick
 306      }
 307
 308      public PgSQLInterval UnjustifyDays()
 309      {
 310         return new PgSQLInterval( this._months, 0, this._ticks + this._days * TimeSpan.TicksPerDay );
 311      }
 312
 313      public PgSQLInterval JustifyMonths()
 314      {
 315         return new PgSQLInterval( this._months + this._days / DAYS_PER_MONTH, this._days % DAYS_PER_MONTH, this._ticks 
 316      }
 317
 318      public PgSQLInterval UnjustifyMonths()
 319      {
 320         return new PgSQLInterval( 0, this._days + this._months * DAYS_PER_MONTH, this._ticks );
 321      }
 322
 323      public PgSQLInterval JustifyInterval()
 324      {
 325         return this.JustifyMonths().JustifyDays();
 326      }
 327
 328      public PgSQLInterval UnjustifyInterval()
 329      {
 330         return new PgSQLInterval( 0, 0, this._ticks + this._days * TimeSpan.TicksPerDay + this._months * TICKS_PER_MONT
 331      }
 332
 333      public PgSQLInterval Canonicalize()
 334      {
 335         return new PgSQLInterval( 0, this._days + this._months * DAYS_PER_MONTH + (Int32) ( this._ticks / TimeSpan.Tick
 336      }
 337
 338      #endregion
 339
 340      #region Arithmetic
 341
 342      public PgSQLInterval Add( PgSQLInterval another )
 343      {
 344         return new PgSQLInterval( this._months + another._months, this._days + another._days, this._ticks + another._ti
 345      }
 346
 347      public PgSQLInterval Subtract( PgSQLInterval another )
 348      {
 349         return new PgSQLInterval( this._months - another._months, this._days - another._days, this._ticks - another._ti
 350      }
 351
 352      public PgSQLInterval Negate()
 353      {
 354         return new PgSQLInterval( -this._months, -this._days, -this._ticks );
 355      }
 356
 357      public PgSQLInterval Duration()
 358      {
 359         return this.UnjustifyInterval().Ticks < 0 ? this.Negate() : this;
 360      }
 361
 362      #endregion
 363
 364      #region Comparison
 365
 366      public Int32 CompareTo( PgSQLInterval other )
 367      {
 368         return this.UnjustifyInterval().Ticks.CompareTo( other.UnjustifyInterval().Ticks );
 369      }
 370
 371      Int32 IComparable.CompareTo( Object obj )
 372      {
 373         if ( obj == null )
 374         {
 375            // This is always 'greater' than null
 376            return 1;
 377         }
 378         else if ( obj is PgSQLInterval )
 379         {
 380            return this.CompareTo( (PgSQLInterval) obj );
 381         }
 382         else
 383         {
 384            throw new ArgumentException( "Given object must be of type " + this.GetType() + " or null." );
 385         }
 386      }
 387
 388      public Boolean Equals( PgSQLInterval other )
 389      {
 390         return this._ticks == other._ticks && this._days == other._days && this._months == other._months;
 391      }
 392
 393      public override Boolean Equals( Object obj )
 394      {
 395         return obj != null && obj is PgSQLInterval && this.Equals( (PgSQLInterval) obj );
 396      }
 397
 398      public override Int32 GetHashCode()
 399      {
 400         return this.UnjustifyInterval().Ticks.GetHashCode();
 401      }
 402
 403      #endregion
 404
 405      #region Casts
 406
 407      public static implicit operator TimeSpan( PgSQLInterval x )
 408      {
 409         return new TimeSpan( x._ticks + x._days * TimeSpan.TicksPerDay + x._months * TICKS_PER_MONTH );
 410      }
 411
 412      public static implicit operator PgSQLInterval( TimeSpan x )
 413      {
 414         return new PgSQLInterval( x ).Canonicalize();
 415      }
 416
 417      #endregion
 418
 419      #region Creation from parts
 420
 421      public static PgSQLInterval FromTicks( Int64 ticks )
 422      {
 423         return new PgSQLInterval( ticks ).Canonicalize();
 424      }
 425
 426      public static PgSQLInterval FromMicroseconds( Double microseconds )
 427      {
 428         return FromTicks( (Int64) ( microseconds * ( TimeSpan.TicksPerMillisecond / 1000 ) ) );
 429      }
 430
 431      public static PgSQLInterval FromMilliseconds( Double milliseconds )
 432      {
 433         return FromTicks( (Int64) ( milliseconds * TimeSpan.TicksPerMillisecond ) );
 434      }
 435
 436      public static PgSQLInterval FromSeconds( Double seconds )
 437      {
 438         return FromTicks( (Int64) ( seconds * TimeSpan.TicksPerSecond ) );
 439      }
 440
 441      public static PgSQLInterval FromMinutes( Double minutes )
 442      {
 443         return FromTicks( (Int64) ( minutes * TimeSpan.TicksPerMinute ) );
 444      }
 445
 446      public static PgSQLInterval FromHours( Double hours )
 447      {
 448         return FromTicks( (Int64) ( hours * TimeSpan.TicksPerHour ) );
 449      }
 450
 451      public static PgSQLInterval FromDays( Double days )
 452      {
 453         return FromTicks( (Int64) ( days * TimeSpan.TicksPerDay ) );
 454      }
 455
 456      public static PgSQLInterval FromMonths( Double months )
 457      {
 458         return FromTicks( (Int64) ( months * TICKS_PER_MONTH ) );
 459      }
 460
 461      #endregion
 462
 463      #region Operators
 464
 465      public static PgSQLInterval operator +( PgSQLInterval x, PgSQLInterval y )
 466      {
 467         return x.Add( y );
 468      }
 469
 470      public static PgSQLInterval operator -( PgSQLInterval x, PgSQLInterval y )
 471      {
 472         return x.Subtract( y );
 473      }
 474
 475      public static Boolean operator ==( PgSQLInterval x, PgSQLInterval y )
 476      {
 477         return x.Equals( y );
 478      }
 479
 480      public static Boolean operator !=( PgSQLInterval x, PgSQLInterval y )
 481      {
 482         return !( x == y );
 483      }
 484
 485      public static Boolean operator <( PgSQLInterval x, PgSQLInterval y )
 486      {
 487         return x.UnjustifyInterval().Ticks < y.UnjustifyInterval().Ticks;
 488      }
 489
 490      public static Boolean operator <=( PgSQLInterval x, PgSQLInterval y )
 491      {
 492         return x.UnjustifyInterval().Ticks <= y.UnjustifyInterval().Ticks;
 493      }
 494
 495      public static Boolean operator >( PgSQLInterval x, PgSQLInterval y )
 496      {
 497         return !( x <= y );
 498      }
 499
 500      public static Boolean operator >=( PgSQLInterval x, PgSQLInterval y )
 501      {
 502         return !( x < y );
 503      }
 504
 505      public static PgSQLInterval operator +( PgSQLInterval x )
 506      {
 507         return x;
 508      }
 509
 510      public static PgSQLInterval operator -( PgSQLInterval x )
 511      {
 512         return x.Negate();
 513      }
 514
 515      #endregion
 516
 517      #region To and from string
 518
 519      public override String ToString()
 520      {
 521         var sb = new StringBuilder();
 522
 523         // Months
 524         if ( this._months != 0 )
 525         {
 526            sb.Append( this._months ).Append( Math.Abs( this._months ) == 1 ? " mon " : " mons " );
 527         }
 528
 529         // Days
 530         if ( this._days != 0 )
 531         {
 532            if ( this._months < 0 && this._days > 0 )
 533            {
 534               sb.Append( '+' );
 535            }
 536            sb.Append( this._days ).Append( Math.Abs( this._days ) == 1 ? " day " : " days " );
 537         }
 538
 539         // The rest
 540         if ( this._ticks != 0 || sb.Length == 0 )
 541         {
 542            // The sign
 543            if ( this._ticks < 0 )
 544            {
 545               sb.Append( '-' );
 546            }
 547            else if ( this._days < 0 || ( this._days == 0 && this._months < 0 ) )
 548            {
 549               sb.Append( '+' );
 550            }
 551            AppendTimeInformation( sb, this._ticks );
 552         }
 553
 554         return sb.ToString( 0, sb[sb.Length - 1] == ' ' ? ( sb.Length - 1 ) : sb.Length );
 555      }
 556
 557      public static PgSQLInterval Parse( String str )
 558      {
 559         PgSQLInterval result; Exception error;
 560         TryParse( str, out result, out error );
 561         if ( error != null )
 562         {
 563            throw error;
 564         }
 565         return result;
 566      }
 567
 568      public static Boolean TryParse( String str, out PgSQLInterval result )
 569      {
 570         Exception error;
 571         TryParse( str, out result, out error );
 572         return error == null;
 573      }
 574
 575      private static void TryParse( String str, out PgSQLInterval result, out Exception error )
 576      {
 577         if ( str == null )
 578         {
 579            result = default( PgSQLInterval );
 580            error = new ArgumentNullException( "String" );
 581         }
 582         else
 583         {
 584            // Easymode for plurals
 585            str = str.Replace( 's', ' ' );
 586            error = null;
 587
 588            // Initialize variables
 589            var years = 0;
 590            var months = 0;
 591            var days = 0;
 592            var ticks = 0L;
 593
 594            // Years
 595            var idx = str.IndexOf( "year" );
 596            var start = 0;
 597            if ( idx > 0 && !Int32.TryParse( str.Substring( start, idx - start ), out years ) )
 598            {
 599               error = new FormatException( "Years were in invalid format." );
 600            }
 601            UpdateStartIndex( str, idx, ref start, 5 );
 602
 603            // Months
 604            if ( error == null )
 605            {
 606               idx = str.IndexOf( "mon", start );
 607               if ( idx > 0 && !Int32.TryParse( str.Substring( start, idx - start ), out months ) )
 608               {
 609                  error = new FormatException( "Months were in invalid format." );
 610               }
 611               UpdateStartIndex( str, idx, ref start, 4 );
 612            }
 613
 614            // Days
 615            if ( error == null )
 616            {
 617               idx = str.IndexOf( "day", start );
 618               if ( idx > 0 && !Int32.TryParse( str.Substring( start, idx - start ), out days ) )
 619               {
 620                  error = new FormatException( "Days were in invalid format." );
 621               }
 622               UpdateStartIndex( str, idx, ref start, 4 );
 623            }
 624
 625            // Time
 626            if ( error == null )
 627            {
 628               Int32 hours, minutes; Decimal seconds; Boolean isNegative;
 629               ParseTime( str, start, out hours, out minutes, out seconds, out isNegative, ref error, false );
 630
 631               if ( error == null )
 632               {
 633                  try
 634                  {
 635                     ticks = hours * TimeSpan.TicksPerHour + minutes * TimeSpan.TicksPerMinute + (Int64) ( seconds * Tim
 636                  }
 637                  catch ( Exception exc )
 638                  {
 639                     // E.g. overflow exception
 640                     error = new FormatException( "Error when calculating ticks, ", exc );
 641                  }
 642               }
 643            }
 644
 645            result = error == null ?
 646               new PgSQLInterval( years * MONTHS_PER_YEAR + months, days, ticks ) :
 647               default( PgSQLInterval );
 648
 649         }
 650      }
 651
 652      private static void UpdateStartIndex( String str, Int32 idx, ref Int32 startIdx, Int32 addition )
 653      {
 654         if ( idx >= 0 )
 655         {
 656            startIdx = ( idx + addition ) >= str.Length ? str.Length : ( idx + addition );
 657         }
 658      }
 659
 660      internal static void ParseTime( String str, Int32 start, out Int32 hours, out Int32 minutes, out Decimal seconds, 
 661      {
 662         hours = 0;
 663         minutes = 0;
 664         seconds = 0m;
 665
 666         var seenOtherThanWhitespace = false;
 667         isNegative = false;
 668         var curState = 0; // 0 - hours, 1 - minutes, 2 - seconds
 669         var idx = start;
 670         var len = str.Length;
 671         while ( idx < len && error == null )
 672         {
 673            var c = str[idx];
 674            if ( !Char.IsWhiteSpace( c ) )
 675            {
 676               if ( c == '-' )
 677               {
 678                  if ( seenOtherThanWhitespace || isNegative )
 679                  {
 680                     error = new FormatException( "Unexpected minus sign." );
 681                  }
 682                  else
 683                  {
 684                     isNegative = true;
 685                  }
 686               }
 687               else if ( c == ':' || idx == len - 1 )
 688               {
 689                  var timeStr = str.Substring( start, idx - start + ( c == ':' ? 0 : 1 ) );
 690                  switch ( curState )
 691                  {
 692                     case 0: // Hours
 693                        if ( !Int32.TryParse( timeStr, out hours ) )
 694                        {
 695                           error = new FormatException( "Malformed hours." );
 696                        }
 697                        break;
 698                     case 1: // Minutes
 699                        if ( !Int32.TryParse( timeStr, out minutes ) )
 700                        {
 701                           error = new FormatException( "Malformed minutes." );
 702                        }
 703                        break;
 704                     case 2: // Seconds
 705                        if ( !Decimal.TryParse( timeStr, System.Globalization.NumberStyles.Number, System.Globalization.
 706                        {
 707                           error = new FormatException( "Malformed seconds." );
 708                        }
 709                        break;
 710                  }
 711
 712                  ++curState;
 713                  start = idx + 1;
 714               }
 715               seenOtherThanWhitespace = true;
 716
 717            }
 718            ++idx;
 719         }
 720
 721         if ( curState == 0 && timeIsMandatory )
 722         {
 723            error = new FormatException( "Missing time information." );
 724         }
 725
 726         if ( isNegative )
 727         {
 728            minutes = -minutes;
 729            seconds = -seconds;
 730         }
 731      }
 732
 733      #endregion
 734
 735   }
 736
 737   //public interface IPgSQLDate
 738   //{
 739   //   #region Properties
 740
 741   //   Int32 DayOfYear { get; }
 742
 743   //   Int32 Year { get; }
 744
 745   //   Int32 Month { get; }
 746
 747   //   Int32 Day { get; }
 748
 749   //   DayOfWeek DayOfWeek { get; }
 750
 751   //   Int32 DaysSinceEra { get; }
 752
 753   //   Boolean IsLeapYear { get; }
 754
 755   //   #endregion
 756   //}
 757
 758   public struct PgSQLDate : IEquatable<PgSQLDate>, IComparable, IComparable<PgSQLDate>//, IPgSQLDate
 759   {
 760      #region Consts
 761      public const Int32 MAX_YEAR = 5874897; // As per PostgreSQL documentation
 762      public const Int32 MIN_YEAR = -4714; // As per PostgreSQL documentation
 763
 764      private const Int32 DAYS_IN_YEAR = 365; //Common years
 765      private const Int32 DAYS_IN_4YEARS = 4 * DAYS_IN_YEAR + 1; //Leap year every 4 years.
 766      private const Int32 DAYS_IN_CENTURY = 25 * DAYS_IN_4YEARS - 1; //Except no leap year every 100.
 767      private const Int32 DAYS_IN_4CENTURIES = 4 * DAYS_IN_CENTURY + 1; //Except leap year every 400.
 768
 769      internal const String INFINITY = "infinity";
 770      internal const String MINUS_INFINITY = "-" + INFINITY;
 771
 772
 773
 774      #endregion
 775
 776      #region Static
 777
 778      // Cumulative days in non-leap years
 779      private static readonly Int32[] CommonYearDays = new Int32[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 3
 780
 781      // Cumulative days in leap years
 782      private static readonly Int32[] LeapYearDays = new Int32[] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
 783
 784      // Amount of days in non-leap year months
 785      private static readonly Int32[] CommonYearMaxes = new Int32[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 786
 787      // Amount of days in leap year months
 788      private static readonly Int32[] LeapYearMaxes = new Int32[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 789
 790      private static Boolean IsLeap( Int32 year )
 791      {
 792         //Every 4 years is a leap year
 793         //Except every 100 years isn't a leap year.
 794         //Except every 400 years is.
 795         // Also: http://support.microsoft.com/kb/214019 (doesn't cover 0 and negative years)
 796         if ( year < 1 )
 797         {
 798            ++year;
 799         }
 800         return ( year % 4 == 0 ) && ( ( year % 100 != 0 ) || ( year % 400 == 0 ) );
 801      }
 802
 803      private static Int32 DaysForYears( Int32 years )
 804      {
 805         //Number of years after 1CE (0 for 1CE, -1 for 1BCE, 1 for 2CE).
 806         if ( years >= 1 )
 807         {
 808            --years;
 809         }
 810
 811         return years / 400 * DAYS_IN_4CENTURIES //Blocks of 400 years with their leap and common years
 812                + years % 400 / 100 * DAYS_IN_CENTURY //Remaining blocks of 100 years with their leap and common years
 813                + years % 100 / 4 * DAYS_IN_4YEARS //Remaining blocks of 4 years with their leap and common years
 814                + years % 4 * DAYS_IN_YEAR //Remaining years, all common
 815                + ( years < 0 ? -1 : 0 ); //And 1BCE is leap.
 816      }
 817
 818      private static Int32 ComponentsToDays( Int32 year, Int32 month, Int32 day )
 819      {
 820         if ( year == 0 || year < MIN_YEAR || year > MAX_YEAR )
 821         {
 822            throw new ArgumentOutOfRangeException( "Year" );
 823         }
 824         else if ( month < 1 || month > 12 )
 825         {
 826            throw new ArgumentOutOfRangeException( "Month" );
 827         }
 828         else
 829         {
 830            var isLeap = IsLeap( year );
 831            if ( day < 1 || day > ( isLeap ? 366 : 365 ) )
 832            {
 833               throw new ArgumentOutOfRangeException( "Day" );
 834            }
 835            else
 836            {
 837               return DaysForYears( year ) + ( isLeap ? LeapYearDays : CommonYearDays )[month - 1] + day - 1;
 838            }
 839         }
 840      }
 841
 842      public static readonly PgSQLDate Epoch = new PgSQLDate( 1970, 1, 1 );
 843      public static readonly PgSQLDate MaxValue = new PgSQLDate( MAX_YEAR, 12, 31 );
 844      public static readonly PgSQLDate MinValue = new PgSQLDate( MIN_YEAR, 11, 24 );
 845      public static readonly PgSQLDate Era = new PgSQLDate( 0 );
 846      public static readonly PgSQLDate Infinity = new PgSQLDate( DateTime.MaxValue );
 847      public static readonly PgSQLDate MinusInfinity = new PgSQLDate( DateTime.MinValue );
 848
 849      public static PgSQLDate Now
 850      {
 851         get
 852         {
 853            return new PgSQLDate( DateTime.Now );
 854         }
 855      }
 856
 857      public static PgSQLDate Today
 858      {
 859         get
 860         {
 861            return Now;
 862         }
 863      }
 864
 865      public static PgSQLDate Yesterday
 866      {
 867         get
 868         {
 869            return Now.AddDays( -1 );
 870         }
 871      }
 872
 873      public static PgSQLDate Tomorrow
 874      {
 875         get
 876         {
 877            return Now.AddDays( 1 );
 878         }
 879      }
 880
 881      #endregion
 882
 883      #region Fields
 884
 885      private readonly Int32 _days;
 886
 887      #endregion
 888
 889      #region Constructors
 890
 891      public PgSQLDate( Int32 daysSinceEra )
 892      {
 893         this._days = daysSinceEra;
 894      }
 895
 896      public PgSQLDate( PgSQLDate other )
 897         : this( other._days )
 898      {
 899      }
 900
 901      public PgSQLDate( DateTime datetime )
 902         : this( (Int32) ( datetime.Ticks / TimeSpan.TicksPerDay ) )
 903      {
 904      }
 905
 906      public PgSQLDate( Int32 year, Int32 month, Int32 day )
 907         : this( ComponentsToDays( year, month, day ) )
 908      {
 909      }
 910
 911      #endregion
 912
 913      #region Properties
 914
 915      public Int32 DayOfYear
 916      {
 917         get
 918         {
 919            return this._days - DaysForYears( this.Year ) + 1;
 920         }
 921      }
 922
 923      public Int32 Year
 924      {
 925         get
 926         {
 927            var start = ( (Int32) Math.Round( this._days / 365.2425 ) ) - 1;
 928            while ( DaysForYears( ++start ) <= this._days ) ;
 929            return start - 1;
 930         }
 931      }
 932
 933      public Int32 Month
 934      {
 935         get
 936         {
 937            var max = this.DayOfYear;
 938            var array = this.IsLeapYear ? LeapYearDays : CommonYearDays;
 939            var i = 1;
 940            while ( max > array[i] )
 941            {
 942               ++i;
 943            }
 944            return i;
 945         }
 946      }
 947
 948      public Int32 Day
 949      {
 950         get
 951         {
 952            return this.DayOfYear - ( this.IsLeapYear ? LeapYearDays : CommonYearDays )[this.Month - 1];
 953         }
 954      }
 955
 956      public DayOfWeek DayOfWeek
 957      {
 958         get
 959         {
 960            return (DayOfWeek) ( ( this._days + 1 ) % 7 );
 961         }
 962      }
 963
 964      public Int32 DaysSinceEra
 965      {
 966         get
 967         {
 968            return this._days;
 969         }
 970      }
 971
 972      public Boolean IsLeapYear
 973      {
 974         get
 975         {
 976            return IsLeap( this.Year );
 977         }
 978      }
 979
 980      #endregion
 981
 982      #region Arithmetics
 983
 984      public PgSQLDate AddDays( Int32 days )
 985      {
 986         return new PgSQLDate( this._days + days );
 987      }
 988
 989      public PgSQLDate AddMonths( Int32 months )
 990      {
 991         var newYear = this.Year;
 992         var newMonth = this.Month + months;
 993
 994         while ( newMonth > 12 )
 995         {
 996            newMonth -= 12;
 997            ++newYear;
 998            if ( newYear == 0 )
 999            {
 1000               ++newYear; // No 'zero'eth year.
 1001            }
 1002         };
 1003         while ( newMonth < 1 )
 1004         {
 1005            newMonth += 12;
 1006            --newYear;
 1007            if ( newYear == 0 )
 1008            {
 1009               --newYear; // No 'zero'eth year.
 1010            }
 1011         };
 1012         var maxDay = ( IsLeap( newYear ) ? LeapYearMaxes : CommonYearMaxes )[newMonth - 1];
 1013         var newDay = this.Day > maxDay ? maxDay : this.Day;
 1014         return new PgSQLDate( newYear, newMonth, newDay );
 1015      }
 1016
 1017      public PgSQLDate AddYears( Int32 years )
 1018      {
 1019         var newYear = this.Year + years;
 1020         if ( newYear >= 0 && this._days < 0 ) // No 'zero'eth year.
 1021         {
 1022            ++newYear;
 1023         }
 1024         else if ( newYear <= 0 && this._days >= 0 ) // No 'zero'eth year.
 1025         {
 1026            --newYear;
 1027         }
 1028         return new PgSQLDate( newYear, Month, Day );
 1029      }
 1030
 1031      public PgSQLDate Add( PgSQLInterval interval, Int32 carriedOverflow = 0 )
 1032      {
 1033         return this.AddMonths( interval.Months ).AddDays( interval.Days + carriedOverflow );
 1034      }
 1035
 1036      #endregion
 1037
 1038      #region Comparison
 1039
 1040      public Boolean Equals( PgSQLDate other )
 1041      {
 1042         return this._days == other._days;
 1043      }
 1044
 1045      public override Boolean Equals( Object obj )
 1046      {
 1047         return obj != null && obj is PgSQLDate && this.Equals( (PgSQLDate) obj );
 1048      }
 1049
 1050      public override Int32 GetHashCode()
 1051      {
 1052         return this._days.GetHashCode();
 1053      }
 1054
 1055      Int32 IComparable.CompareTo( Object obj )
 1056      {
 1057         if ( obj == null )
 1058         {
 1059            // This is always 'greater' than null
 1060            return 1;
 1061         }
 1062         else if ( obj is PgSQLDate )
 1063         {
 1064            return this.CompareTo( (PgSQLDate) obj );
 1065         }
 1066         else
 1067         {
 1068            throw new ArgumentException( "Given object must be of type " + this.GetType() + " or null." );
 1069         }
 1070      }
 1071
 1072      public Int32 CompareTo( PgSQLDate other )
 1073      {
 1074         return this._days.CompareTo( other._days );
 1075      }
 1076
 1077      #endregion
 1078
 1079      #region Casts
 1080
 1081      public static explicit operator DateTime( PgSQLDate x )
 1082      {
 1083         try
 1084         {
 1085            return new DateTime( x._days * TimeSpan.TicksPerDay );
 1086         }
 1087         catch
 1088         {
 1089            throw new InvalidCastException( "The given PostgreSQL date " + x + " can not be represented by " + typeof( D
 1090         }
 1091      }
 1092
 1093      public static explicit operator PgSQLDate( DateTime x )
 1094      {
 1095         return new PgSQLDate( (Int32) ( x.Ticks / TimeSpan.TicksPerDay ) );
 1096      }
 1097
 1098      #endregion
 1099
 1100      #region Operators
 1101
 1102      public static Boolean operator ==( PgSQLDate x, PgSQLDate y )
 1103      {
 1104         return x.Equals( y );
 1105      }
 1106
 1107      public static Boolean operator !=( PgSQLDate x, PgSQLDate y )
 1108      {
 1109         return !( x == y );
 1110      }
 1111
 1112      public static Boolean operator <( PgSQLDate x, PgSQLDate y )
 1113      {
 1114         return x._days < y._days;
 1115      }
 1116
 1117      public static Boolean operator >( PgSQLDate x, PgSQLDate y )
 1118      {
 1119         return !( x._days <= y._days );
 1120      }
 1121
 1122      public static Boolean operator <=( PgSQLDate x, PgSQLDate y )
 1123      {
 1124         return x._days <= y._days;
 1125      }
 1126
 1127      public static Boolean operator >=( PgSQLDate x, PgSQLDate y )
 1128      {
 1129         return !( x._days > y._days );
 1130      }
 1131
 1132      public static PgSQLDate operator +( PgSQLDate date, PgSQLInterval interval )
 1133      {
 1134         return date.Add( interval );
 1135      }
 1136
 1137      public static PgSQLDate operator +( PgSQLInterval interval, PgSQLDate date )
 1138      {
 1139         return date.Add( interval );
 1140      }
 1141
 1142      public static PgSQLDate operator -( PgSQLDate date, PgSQLInterval interval )
 1143      {
 1144         return date.Add( -interval );
 1145      }
 1146
 1147      public static PgSQLInterval operator -( PgSQLDate dateX, PgSQLDate dateY )
 1148      {
 1149         return new PgSQLInterval( 0, dateX._days - dateY._days, 0 );
 1150      }
 1151
 1152      #endregion
 1153
 1154      #region To and from string
 1155
 1156      internal const Int32 INFINITY_CHAR_COUNT = 8;
 1157      internal const Int32 MINUS_INFINITY_CHAR_COUNT = INFINITY_CHAR_COUNT + 1;
 1158      private const Int32 MIN_NORMAL_CHAR_COUNT = YEAR_CHAR_COUNT + 1 + MONTH_CHAR_COUNT + 1 + DAY_CHAR_COUNT;
 1159      private const Byte SEPARATOR = (Byte) '-';
 1160      private const Int32 YEAR_CHAR_COUNT = 4;
 1161      private const Int32 MONTH_CHAR_COUNT = 2;
 1162      private const Int32 DAY_CHAR_COUNT = 2;
 1163      private const Int32 BC_CHAR_COUNT = 3; // " BC"
 1164      private const Byte BC_CHAR_1 = (Byte) ' ';
 1165      private const Byte BC_CHAR_2 = (Byte) 'B';
 1166      private const Byte BC_CHAR_3 = (Byte) 'C';
 1167
 1168      public Int32 GetTextByteCount( IEncodingInfo encoding )
 1169      {
 1170         Int32 retVal;
 1171         if ( this == Infinity )
 1172         {
 1173            retVal = INFINITY_CHAR_COUNT * encoding.BytesPerASCIICharacter;
 1174         }
 1175         else if ( this == MinusInfinity )
 1176         {
 1177            retVal = INFINITY_CHAR_COUNT * encoding.BytesPerASCIICharacter;
 1178         }
 1179         else
 1180         {
 1181            // yyyy-MM-dd
 1182            retVal = MIN_NORMAL_CHAR_COUNT * encoding.BytesPerASCIICharacter;
 1183            if ( this._days < 0 )
 1184            {
 1185               // " BC"
 1186               retVal += BC_CHAR_COUNT * encoding.BytesPerASCIICharacter;
 1187            }
 1188         }
 1189         return retVal;
 1190      }
 1191
 1192      public void WriteTextBytes( IEncodingInfo encoding, Byte[] array, ref Int32 offset )
 1193      {
 1194         if ( this == Infinity )
 1195         {
 1196            encoding.WriteString( array, ref offset, INFINITY );
 1197         }
 1198         else if ( this == MinusInfinity )
 1199         {
 1200            encoding.WriteString( array, ref offset, MINUS_INFINITY );
 1201         }
 1202         else
 1203         {
 1204            // Let's not allocate heap objects
 1205            encoding
 1206               .WriteIntegerTextual( array, ref offset, Math.Abs( this.Year ), YEAR_CHAR_COUNT )
 1207               .WriteASCIIByte( array, ref offset, SEPARATOR ) // '-'
 1208               .WriteIntegerTextual( array, ref offset, this.Month, MONTH_CHAR_COUNT )
 1209               .WriteASCIIByte( array, ref offset, SEPARATOR ) // '-'
 1210               .WriteIntegerTextual( array, ref offset, this.Day, DAY_CHAR_COUNT );
 1211            if ( this._days < 0 )
 1212            {
 1213               // " BC"
 1214               encoding
 1215                  .WriteASCIIByte( array, ref offset, BC_CHAR_1 ) // ' '
 1216                  .WriteASCIIByte( array, ref offset, BC_CHAR_2 ) // 'B'
 1217                  .WriteASCIIByte( array, ref offset, BC_CHAR_3 ); // 'C'
 1218            }
 1219         }
 1220      }
 1221
 1222      public static PgSQLDate ParseBinaryText( IEncodingInfo encoding, Byte[] array, ref Int32 offset, Int32 count )
 1223      {
 1224         var increment = encoding.BytesPerASCIICharacter;
 1225         switch ( increment * count )
 1226         {
 1227            case INFINITY_CHAR_COUNT:
 1228               return Infinity;
 1229            case MINUS_INFINITY_CHAR_COUNT:
 1230               return MinusInfinity;
 1231            default:
 1232               var max = offset + count;
 1233               var year = encoding.ParseInt32Textual( array, ref offset, (YEAR_CHAR_COUNT, true) );
 1234               var month = encoding.EqualsOrThrow( array, ref offset, SEPARATOR ).ParseInt32Textual( array, ref offset, 
 1235               var day = encoding.EqualsOrThrow( array, ref offset, SEPARATOR ).ParseInt32Textual( array, ref offset, (D
 1236               if ( offset + 3 * encoding.BytesPerASCIICharacter < max )
 1237               {
 1238                  // " BC" may follow
 1239                  max = offset;
 1240                  if ( encoding.ReadASCIIByte( array, ref offset ) == BC_CHAR_1
 1241                     && encoding.ReadASCIIByte( array, ref offset ) == BC_CHAR_2
 1242                     && encoding.ReadASCIIByte( array, ref offset ) == BC_CHAR_3
 1243                     )
 1244                  {
 1245                     year = -year;
 1246                  }
 1247                  else
 1248                  {
 1249                     // 'Reverse back'
 1250                     offset = max;
 1251                  }
 1252               }
 1253               return new PgSQLDate( year, month, day );
 1254         }
 1255      }
 1256
 1257      public override String ToString()
 1258      {
 1259         // As per PostgreSQL documentation ISO 8601 format (same as in Npgsql)
 1260         // Format of yyyy-MM-dd with " BC" for BCE and optional " AD" for CE which we omit here.
 1261         return
 1262             new StringBuilder( Math.Abs( this.Year ).ToString( "D4" ) ).Append( '-' ).Append( this.Month.ToString( "D2"
 1263                 this.Day.ToString( "D2" ) ).Append( this._days < 0 ? " BC" : "" ).ToString();
 1264      }
 1265
 1266      public static PgSQLDate Parse( String str )
 1267      {
 1268         PgSQLDate result; Exception error;
 1269         TryParse( str, out result, out error );
 1270         if ( error != null )
 1271         {
 1272            throw error;
 1273         }
 1274         return result;
 1275      }
 1276
 1277      public static Boolean TryParse( String str, out PgSQLDate result )
 1278      {
 1279         Exception error;
 1280         return TryParse( str, out result, out error );
 1281      }
 1282
 1283      internal static Boolean TryParse( String str, out PgSQLDate result, out Exception error )
 1284      {
 1285         if ( str == null )
 1286         {
 1287            result = default( PgSQLDate );
 1288            error = new ArgumentNullException( "String" );
 1289         }
 1290         else
 1291         {
 1292            str = str.Trim();
 1293            error = null;
 1294            if ( String.Equals( str, INFINITY, StringComparison.OrdinalIgnoreCase ) )
 1295            {
 1296               result = Infinity;
 1297            }
 1298            else if ( String.Equals( str, MINUS_INFINITY, StringComparison.OrdinalIgnoreCase ) )
 1299            {
 1300               result = MinusInfinity;
 1301            }
 1302            else
 1303            {
 1304               // ISO 8601 format assumed
 1305               var start = 0;
 1306               var idx = 0;
 1307               var year = IntegerOrError( str, ref start, ref idx, ref error, "year", "month", '-', true );
 1308               var month = IntegerOrError( str, ref start, ref idx, ref error, "month", "day", '-', true );
 1309               var day = IntegerOrError( str, ref start, ref idx, ref error, "day", null, ' ', false );
 1310               if ( error == null && start < str.Length && str.IndexOf( "BC", start, StringComparison.OrdinalIgnoreCase 
 1311               {
 1312                  year = -year;
 1313               }
 1314
 1315               result = error == null ?
 1316                  new PgSQLDate( year, month, day ) :
 1317                  default( PgSQLDate );
 1318            }
 1319         }
 1320         return error == null;
 1321      }
 1322
 1323      private static Int32 IntegerOrError( String str, ref Int32 start, ref Int32 idx, ref Exception error, String thisD
 1324      {
 1325         var result = 0;
 1326         if ( error == null )
 1327         {
 1328            if ( start >= str.Length )
 1329            {
 1330               error = new FormatException( "Missing " + thisDatePart );
 1331            }
 1332            else
 1333            {
 1334               idx = str.IndexOf( separator, start );
 1335               if ( idx == -1 && !separatorIsMandatory )
 1336               {
 1337                  idx = str.Length;
 1338               }
 1339
 1340               if ( idx == -1 )
 1341               {
 1342                  error = new FormatException( "Could not find " + thisDatePart + "-" + nextDatePart + " separator." );
 1343               }
 1344               else if ( !Int32.TryParse( str.Substring( start, idx - start ), out result ) )
 1345               {
 1346                  error = new FormatException( thisDatePart + " was malformed." );
 1347               }
 1348               else
 1349               {
 1350                  start = idx + 1;
 1351               }
 1352            }
 1353         }
 1354         return result;
 1355      }
 1356
 1357      #endregion
 1358
 1359   }
 1360
 1361   //public interface IPgSQLTime
 1362   //{
 1363   //   #region Properties
 1364
 1365   //   Int64 Ticks { get; }
 1366
 1367   //   Int32 Microseconds { get; }
 1368
 1369   //   Int32 Milliseconds { get; }
 1370
 1371   //   Int32 Seconds { get; }
 1372
 1373   //   Int32 Minutes { get; }
 1374
 1375   //   Int32 Hours { get; }
 1376
 1377   //   #endregion
 1378   //}
 1379
 1380   public struct PgSQLTime : IEquatable<PgSQLTime>, IComparable, IComparable<PgSQLTime>//, IPgSQLTime
 1381   {
 1382      #region Static
 1383
 1384      public static readonly PgSQLTime AllBalls = new PgSQLTime( 0 );
 1385
 1386      public static PgSQLTime Now
 1387      {
 1388         get
 1389         {
 1390            return new PgSQLTime( DateTime.Now.TimeOfDay );
 1391         }
 1392      }
 1393
 1394      #endregion
 1395
 1396      #region Fields
 1397      private readonly Int64 _ticks;
 1398      #endregion
 1399
 1400      #region Constructors
 1401
 1402      public PgSQLTime( Int64 ticks )
 1403      {
 1404         if ( ticks == TimeSpan.TicksPerDay )
 1405         {
 1406            this._ticks = ticks;
 1407         }
 1408         else
 1409         {
 1410            ticks %= TimeSpan.TicksPerDay;
 1411            this._ticks = ticks < 0 ? ticks + TimeSpan.TicksPerDay : ticks;
 1412         }
 1413      }
 1414
 1415      public PgSQLTime( TimeSpan timeSpan )
 1416         : this( timeSpan.Ticks )
 1417      {
 1418      }
 1419
 1420      public PgSQLTime( DateTime dateTime )
 1421         : this( dateTime.Ticks )
 1422      {
 1423      }
 1424
 1425      public PgSQLTime( PgSQLInterval interval )
 1426         : this( interval.Ticks )
 1427      {
 1428      }
 1429
 1430      public PgSQLTime( PgSQLTime other )
 1431         : this( other._ticks )
 1432      {
 1433      }
 1434
 1435      public PgSQLTime( Int32 hours, Int32 minutes, Int32 seconds )
 1436         : this( hours, minutes, seconds, 0 )
 1437      {
 1438      }
 1439
 1440      public PgSQLTime( Int32 hours, Int32 minutes, Int32 seconds, Int32 microseconds )
 1441         : this(
 1442             hours * TimeSpan.TicksPerHour + minutes * TimeSpan.TicksPerMinute + seconds * TimeSpan.TicksPerSecond +
 1443             microseconds * PgSQLInterval.TICKS_PER_MICROSECOND )
 1444      {
 1445      }
 1446
 1447      public PgSQLTime( Int32 hours, Int32 minutes, Decimal seconds )
 1448         : this( hours * TimeSpan.TicksPerHour + minutes * TimeSpan.TicksPerMinute + (Int64) ( seconds * TimeSpan.TicksP
 1449      {
 1450      }
 1451
 1452      #endregion
 1453
 1454      #region Properties
 1455
 1456      public Int64 Ticks
 1457      {
 1458         get
 1459         {
 1460            return this._ticks;
 1461         }
 1462      }
 1463
 1464      public Int32 Microseconds
 1465      {
 1466         get
 1467         {
 1468            return PgSQLInterval.CalcMicroseconds( this._ticks );
 1469         }
 1470      }
 1471
 1472      public Int32 Milliseconds
 1473      {
 1474         get
 1475         {
 1476            return PgSQLInterval.CalcMilliseconds( this._ticks );
 1477         }
 1478      }
 1479
 1480      public Int32 Seconds
 1481      {
 1482         get
 1483         {
 1484            return PgSQLInterval.CalcSeconds( this._ticks );
 1485         }
 1486      }
 1487
 1488      public Int32 Minutes
 1489      {
 1490         get
 1491         {
 1492            return PgSQLInterval.CalcMinutes( this._ticks );
 1493         }
 1494      }
 1495
 1496      public Int32 Hours
 1497      {
 1498         get
 1499         {
 1500            return PgSQLInterval.CalcHours( this._ticks );
 1501         }
 1502      }
 1503
 1504      #endregion
 1505
 1506      #region Comparison
 1507
 1508      public Boolean Equals( PgSQLTime other )
 1509      {
 1510         return this._ticks == other._ticks;
 1511      }
 1512
 1513      public override Boolean Equals( Object obj )
 1514      {
 1515         return obj != null && obj is PgSQLTime && this.Equals( (PgSQLTime) obj );
 1516      }
 1517
 1518      public override Int32 GetHashCode()
 1519      {
 1520         return this._ticks.GetHashCode();
 1521      }
 1522
 1523      Int32 IComparable.CompareTo( Object obj )
 1524      {
 1525         if ( obj == null )
 1526         {
 1527            // This is always 'greater' than null
 1528            return 1;
 1529         }
 1530         else if ( obj is PgSQLTime )
 1531         {
 1532            return this.CompareTo( (PgSQLTime) obj );
 1533         }
 1534         else
 1535         {
 1536            throw new ArgumentException( "Given object must be of type " + this.GetType() + " or null." );
 1537         }
 1538      }
 1539
 1540      public Int32 CompareTo( PgSQLTime other )
 1541      {
 1542         return this.Normalize()._ticks.CompareTo( other.Normalize()._ticks );
 1543      }
 1544
 1545      #endregion
 1546
 1547      #region Normalization
 1548
 1549      public PgSQLTime Normalize()
 1550      {
 1551         return new PgSQLTime( this._ticks % TimeSpan.TicksPerDay );
 1552      }
 1553
 1554      #endregion
 1555
 1556      #region Arithmetics
 1557
 1558      public PgSQLTime AddTicks( Int64 ticksAdded )
 1559      {
 1560         return new PgSQLTime( ( Ticks + ticksAdded ) % TimeSpan.TicksPerDay );
 1561      }
 1562
 1563      private PgSQLTime AddTicks( Int64 ticksAdded, out Int32 overflow )
 1564      {
 1565         var result = Ticks + ticksAdded;
 1566         overflow = (Int32) ( result / TimeSpan.TicksPerDay );
 1567         result %= TimeSpan.TicksPerDay;
 1568         if ( result < 0 )
 1569         {
 1570            --overflow; //"carry the one"
 1571         }
 1572         return new PgSQLTime( result );
 1573      }
 1574
 1575      public PgSQLTime Add( PgSQLInterval interval )
 1576      {
 1577         return AddTicks( interval.Ticks );
 1578      }
 1579
 1580      internal PgSQLTime Add( PgSQLInterval interval, out Int32 overflow )
 1581      {
 1582         return AddTicks( interval.Ticks, out overflow );
 1583      }
 1584
 1585      public PgSQLTime Subtract( PgSQLInterval interval )
 1586      {
 1587         return AddTicks( -interval.Ticks );
 1588      }
 1589
 1590      public PgSQLInterval Subtract( PgSQLTime earlier )
 1591      {
 1592         return new PgSQLInterval( Ticks - earlier.Ticks );
 1593      }
 1594
 1595      #endregion
 1596
 1597      #region Timezones
 1598
 1599      public PgSQLTimeTZ AtTimeZone( PgSQLTimeZone timeZone )
 1600      {
 1601         return new PgSQLTimeTZ( this ).AtTimeZone( timeZone );
 1602      }
 1603
 1604      #endregion
 1605
 1606      #region Casts
 1607
 1608      public static explicit operator DateTime( PgSQLTime x )
 1609      {
 1610         return new DateTime( x._ticks, DateTimeKind.Unspecified );
 1611      }
 1612
 1613      public static explicit operator PgSQLTime( DateTime x )
 1614      {
 1615         return new PgSQLTime( x.Ticks );
 1616      }
 1617
 1618      public static explicit operator TimeSpan( PgSQLTime x )
 1619      {
 1620         return new TimeSpan( x._ticks );
 1621      }
 1622
 1623      public static explicit operator PgSQLTime( TimeSpan x )
 1624      {
 1625         return new PgSQLTime( x.Ticks );
 1626      }
 1627
 1628      public static explicit operator PgSQLInterval( PgSQLTime x )
 1629      {
 1630         return new PgSQLInterval( x._ticks );
 1631      }
 1632
 1633      public static explicit operator PgSQLTime( PgSQLInterval x )
 1634      {
 1635         return new PgSQLTime( x );
 1636      }
 1637
 1638      #endregion
 1639
 1640      #region Operators
 1641
 1642      public static Boolean operator ==( PgSQLTime x, PgSQLTime y )
 1643      {
 1644         return x.Equals( y );
 1645      }
 1646
 1647      public static Boolean operator !=( PgSQLTime x, PgSQLTime y )
 1648      {
 1649         return !( x == y );
 1650      }
 1651
 1652      public static Boolean operator <( PgSQLTime x, PgSQLTime y )
 1653      {
 1654         return x.Ticks < y.Ticks;
 1655      }
 1656
 1657      public static Boolean operator <=( PgSQLTime x, PgSQLTime y )
 1658      {
 1659         return x.Ticks <= y.Ticks;
 1660      }
 1661
 1662      public static Boolean operator >( PgSQLTime x, PgSQLTime y )
 1663      {
 1664         return !( x.Ticks <= y.Ticks );
 1665      }
 1666
 1667      public static Boolean operator >=( PgSQLTime x, PgSQLTime y )
 1668      {
 1669         return !( x.Ticks < y.Ticks );
 1670      }
 1671
 1672      public static PgSQLTime operator +( PgSQLTime time, PgSQLInterval interval )
 1673      {
 1674         return time.Add( interval );
 1675      }
 1676
 1677      public static PgSQLTime operator +( PgSQLInterval interval, PgSQLTime time )
 1678      {
 1679         return time + interval;
 1680      }
 1681
 1682      public static PgSQLTime operator -( PgSQLTime time, PgSQLInterval interval )
 1683      {
 1684         return time.Subtract( interval );
 1685      }
 1686
 1687      public static PgSQLInterval operator -( PgSQLTime later, PgSQLTime earlier )
 1688      {
 1689         return later.Subtract( earlier );
 1690      }
 1691
 1692      #endregion
 1693
 1694      #region To and from string
 1695
 1696      private const Byte SEPARATOR = (Byte) ':';
 1697      private const Byte MICRO_SEPARATOR = (Byte) '.';
 1698      private const Int32 HOUR_CHAR_COUNT = 2;
 1699      private const Int32 MINUTE_CHAR_COUNT = 2;
 1700      private const Int32 SECOND_CHAR_COUNT = 2;
 1701      private const Int32 MICRO_PRECISION = 6;
 1702
 1703      internal static Int32 GetTextByteCount( IEncodingInfo encoding, Int64 ticks )
 1704      {
 1705         var retVal = 8 * encoding.BytesPerASCIICharacter; // HH:mm:ss
 1706         var microSeconds = Math.Abs( PgSQLInterval.CalcMicroseconds( ticks ) );
 1707         if ( microSeconds > 0 )
 1708         {
 1709            // Need to append '.' and micro second count
 1710            retVal += encoding.BytesPerASCIICharacter + encoding.GetTextualFractionIntegerSize( microSeconds, MICRO_PREC
 1711         }
 1712
 1713         return retVal;
 1714      }
 1715
 1716      internal static void WriteTextBytes( IEncodingInfo encoding, Int64 ticks, Byte[] array, ref Int32 offset )
 1717      {
 1718         encoding
 1719            .WriteIntegerTextual( array, ref offset, Math.Abs( (Int32) ( ticks / TimeSpan.TicksPerHour ) ), HOUR_CHAR_CO
 1720            .WriteASCIIByte( array, ref offset, SEPARATOR ) // ':'
 1721            .WriteIntegerTextual( array, ref offset, Math.Abs( (Int32) ( ( ticks / TimeSpan.TicksPerMinute ) % PgSQLInte
 1722            .WriteASCIIByte( array, ref offset, SEPARATOR ) // ':'
 1723            .WriteIntegerTextual( array, ref offset, Math.Abs( (Int32) ( ( ticks / TimeSpan.TicksPerSecond ) % PgSQLInte
 1724
 1725         var microSeconds = Math.Abs( PgSQLInterval.CalcMicroseconds( ticks ) );
 1726         if ( microSeconds != 0 )
 1727         {
 1728            encoding
 1729               .WriteASCIIByte( array, ref offset, MICRO_SEPARATOR ) // '.'
 1730               .WriteFractionIntegerTextual( array, ref offset, microSeconds, MICRO_PRECISION );
 1731         }
 1732      }
 1733
 1734      public Int32 GetTextByteCount( IEncodingInfo encoding )
 1735      {
 1736         return GetTextByteCount( encoding, this._ticks );
 1737      }
 1738
 1739      public void WriteTextBytes( IEncodingInfo encoding, Byte[] array, ref Int32 offset )
 1740      {
 1741         WriteTextBytes( encoding, this._ticks, array, ref offset );
 1742      }
 1743
 1744      public static PgSQLTime ParseBinaryText( IEncodingInfo encoding, Byte[] array, ref Int32 offset, Int32 count )
 1745      {
 1746         var max = offset + count;
 1747         var hours = encoding.ParseInt32Textual( array, ref offset, (HOUR_CHAR_COUNT, true) );
 1748         var minutes = encoding.EqualsOrThrow( array, ref offset, SEPARATOR ).ParseInt32Textual( array, ref offset, (MIN
 1749         var seconds = encoding.EqualsOrThrow( array, ref offset, SEPARATOR ).ParseInt32Textual( array, ref offset, (SEC
 1750         var oldIdx = offset;
 1751         if ( encoding.TryParseOptionalNumber( array, ref offset, MICRO_SEPARATOR, (MICRO_PRECISION, false), max, out In
 1752         {
 1753            // When calculating trailing zeroes, we must take the prefix (MICRO_SEPARATOR) into account
 1754            var trailingZeroesCount = MICRO_PRECISION - ( offset - oldIdx - encoding.BytesPerASCIICharacter );
 1755            while ( trailingZeroesCount > 0 )
 1756            {
 1757               micros *= 10;
 1758               --trailingZeroesCount;
 1759            }
 1760         }
 1761
 1762         return new PgSQLTime( hours, minutes, seconds, micros );
 1763      }
 1764
 1765      public override String ToString()
 1766      {
 1767         var sb = new StringBuilder();
 1768         PgSQLInterval.AppendTimeInformation( sb, this._ticks );
 1769         return sb.ToString();
 1770      }
 1771
 1772      public static PgSQLTime Parse( String str )
 1773      {
 1774         PgSQLTime result; Exception error;
 1775         TryParse( str, out result, out error );
 1776         if ( error != null )
 1777         {
 1778            throw error;
 1779         }
 1780         return result;
 1781      }
 1782
 1783      public static Boolean TryParse( String str, out PgSQLTime result )
 1784      {
 1785         Exception error;
 1786         return TryParse( str, out result, out error );
 1787      }
 1788
 1789      internal static Boolean TryParse( String str, out PgSQLTime result, out Exception error )
 1790      {
 1791         if ( str == null )
 1792         {
 1793            result = default( PgSQLTime );
 1794            error = new ArgumentNullException( "String" );
 1795         }
 1796         else
 1797         {
 1798            error = null;
 1799            Int32 hours, minutes; Decimal seconds; Boolean isNegative;
 1800            PgSQLInterval.ParseTime( str, 0, out hours, out minutes, out seconds, out isNegative, ref error, true );
 1801            if ( hours < 0 || hours > 24 || minutes < 0 || minutes > 59 || seconds < 0m || seconds >= 60m || ( hours == 
 1802            {
 1803               error = new FormatException( "One of the hours, minutes, or seconds (" + hours + ":" + minutes + ":" + se
 1804            }
 1805
 1806            result = error == null ?
 1807               new PgSQLTime( hours, minutes, seconds ) :
 1808               default( PgSQLTime );
 1809         }
 1810         return error == null;
 1811      }
 1812
 1813      #endregion
 1814
 1815   }
 1816
 1817   public struct PgSQLTimeZone : IEquatable<PgSQLTimeZone>, IComparable, IComparable<PgSQLTimeZone>
 1818   {
 1819      #region Consts
 1820
 1821      private const Int32 MINUTES_PER_HOUR = (Int32) PgSQLInterval.MINUTES_PER_HOUR;
 1822      private const Int32 SECONDS_PER_MINUTE = (Int32) PgSQLInterval.SECONDS_PER_MINUTE;
 1823
 1824      #endregion
 1825
 1826      #region Static
 1827
 1828      public static readonly PgSQLTimeZone UTC = new PgSQLTimeZone( 0 );
 1829
 1830      public static PgSQLTimeZone CurrentTimeZone
 1831      {
 1832         get
 1833         {
 1834            return new PgSQLTimeZone( TimeZoneInfo.Local.GetUtcOffset( DateTime.Now ) );
 1835         }
 1836      }
 1837
 1838      public static PgSQLTimeZone GetSolarTimeZone( Decimal longitude )
 1839      {
 1840         return new PgSQLTimeZone( (Int64) ( longitude / 15m * TimeSpan.TicksPerHour ) );
 1841      }
 1842
 1843      public static PgSQLTimeZone GetLocalTimeZone( PgSQLDate date )
 1844      {
 1845         return new PgSQLTimeZone( TimeZoneInfo.Local.GetUtcOffset(
 1846            date.Year >= 1902 && date.Year < 2038 ?
 1847               (DateTime) date :
 1848               new DateTime( 2000, date.Month, date.Day )
 1849            ) );
 1850      }
 1851
 1852      #endregion
 1853
 1854      #region Fields
 1855
 1856      private readonly Int32 _totalSeconds;
 1857
 1858      #endregion
 1859
 1860      #region Constructors
 1861
 1862      public PgSQLTimeZone( Int64 ticks )
 1863         : this( (Int32) ( ticks / TimeSpan.TicksPerSecond ) )
 1864      {
 1865      }
 1866
 1867      public PgSQLTimeZone( Int32 hours, Int32 minutes )
 1868         : this( hours, minutes, 0 )
 1869      {
 1870      }
 1871
 1872      public PgSQLTimeZone( Int32 hours, Int32 minutes, Int32 seconds )
 1873         : this( hours * MINUTES_PER_HOUR * SECONDS_PER_MINUTE + minutes * SECONDS_PER_MINUTE + seconds )
 1874      {
 1875      }
 1876
 1877      public PgSQLTimeZone( PgSQLTimeZone other )
 1878         : this( other._totalSeconds )
 1879      {
 1880      }
 1881
 1882      public PgSQLTimeZone( PgSQLInterval interval )
 1883         : this( interval.Ticks )
 1884      {
 1885      }
 1886
 1887      public PgSQLTimeZone( TimeSpan timeSpan )
 1888         : this( timeSpan.Ticks )
 1889      {
 1890      }
 1891
 1892      private PgSQLTimeZone( Int32 seconds )
 1893      {
 1894         this._totalSeconds = seconds;
 1895      }
 1896
 1897      #endregion
 1898
 1899      #region Properties
 1900
 1901      public Int32 Hours
 1902      {
 1903         get
 1904         {
 1905            return this._totalSeconds / MINUTES_PER_HOUR / SECONDS_PER_MINUTE;
 1906         }
 1907      }
 1908
 1909      public Int32 Minutes
 1910      {
 1911         get
 1912         {
 1913            return ( this._totalSeconds / MINUTES_PER_HOUR ) % SECONDS_PER_MINUTE;
 1914         }
 1915      }
 1916
 1917      public Int32 Seconds
 1918      {
 1919         get
 1920         {
 1921            return this._totalSeconds % SECONDS_PER_MINUTE;
 1922         }
 1923      }
 1924
 1925      public Int32 TotalSeconds
 1926      {
 1927         get
 1928         {
 1929            return this._totalSeconds;
 1930         }
 1931      }
 1932
 1933      #endregion
 1934
 1935      #region Comparison
 1936
 1937      public Boolean Equals( PgSQLTimeZone other )
 1938      {
 1939         return this._totalSeconds == other._totalSeconds;
 1940      }
 1941
 1942      public override Boolean Equals( Object obj )
 1943      {
 1944         return obj != null && obj is PgSQLTimeZone && this.Equals( (PgSQLTimeZone) obj );
 1945      }
 1946
 1947      public override Int32 GetHashCode()
 1948      {
 1949         return this._totalSeconds.GetHashCode();
 1950      }
 1951
 1952      Int32 IComparable.CompareTo( Object obj )
 1953      {
 1954         if ( obj == null )
 1955         {
 1956            // This is always 'greater' than null
 1957            return 1;
 1958         }
 1959         else if ( obj is PgSQLTimeZone )
 1960         {
 1961            return this.CompareTo( (PgSQLTimeZone) obj );
 1962         }
 1963         else
 1964         {
 1965            throw new ArgumentException( "Given object must be of type " + this.GetType() + " or null." );
 1966         }
 1967      }
 1968
 1969      public Int32 CompareTo( PgSQLTimeZone other )
 1970      {
 1971         // Note: +01:00 is less than -01:00, so we have to invert the result (accomplished by comparing other to this i
 1972         return other._totalSeconds.CompareTo( this._totalSeconds );
 1973      }
 1974
 1975      #endregion
 1976
 1977      #region Casts
 1978
 1979      public static implicit operator PgSQLTimeZone( PgSQLInterval interval )
 1980      {
 1981         return new PgSQLTimeZone( interval );
 1982      }
 1983
 1984      public static implicit operator PgSQLInterval( PgSQLTimeZone timeZone )
 1985      {
 1986         return new PgSQLInterval( timeZone._totalSeconds * TimeSpan.TicksPerSecond );
 1987      }
 1988
 1989      public static implicit operator PgSQLTimeZone( TimeSpan interval )
 1990      {
 1991         return new PgSQLTimeZone( (PgSQLInterval) interval );
 1992      }
 1993
 1994      public static implicit operator TimeSpan( PgSQLTimeZone timeZone )
 1995      {
 1996         return new TimeSpan( timeZone._totalSeconds * TimeSpan.TicksPerSecond );
 1997      }
 1998
 1999      #endregion
 2000
 2001      #region Operators
 2002
 2003      public static Boolean operator ==( PgSQLTimeZone x, PgSQLTimeZone y )
 2004      {
 2005         return x.Equals( y );
 2006      }
 2007
 2008      public static Boolean operator !=( PgSQLTimeZone x, PgSQLTimeZone y )
 2009      {
 2010         return !( x == y );
 2011      }
 2012
 2013      public static Boolean operator <( PgSQLTimeZone x, PgSQLTimeZone y )
 2014      {
 2015         return x._totalSeconds < y._totalSeconds;
 2016      }
 2017
 2018      public static Boolean operator <=( PgSQLTimeZone x, PgSQLTimeZone y )
 2019      {
 2020         return x._totalSeconds <= y._totalSeconds;
 2021      }
 2022
 2023      public static Boolean operator >( PgSQLTimeZone x, PgSQLTimeZone y )
 2024      {
 2025         return !( x <= y );
 2026      }
 2027
 2028      public static Boolean operator >=( PgSQLTimeZone x, PgSQLTimeZone y )
 2029      {
 2030         return !( x < y );
 2031      }
 2032
 2033      public static PgSQLTimeZone operator -( PgSQLTimeZone tz )
 2034      {
 2035         return new PgSQLTimeZone( -tz._totalSeconds );
 2036      }
 2037
 2038      public static PgSQLTimeZone operator +( PgSQLTimeZone tz )
 2039      {
 2040         return tz;
 2041      }
 2042
 2043      #endregion
 2044
 2045      #region To and from string
 2046
 2047      private const Byte PREFIX_POS = (Byte) '+';
 2048      private const Byte PREFIX_NEG = (Byte) '-';
 2049      private const Byte SEPARATOR = (Byte) ':';
 2050      private const Int32 HOURS_CHAR_COUNT = 2;
 2051      private const Int32 MINUTES_CHAR_COUNT = 2;
 2052      private const Int32 SECONDS_CHAR_COUNT = 2;
 2053
 2054      public Int32 GetTextByteCount( IEncodingInfo encoding )
 2055      {
 2056         var retVal = 3 * encoding.BytesPerASCIICharacter; // +/-, and always 2 digits for hours
 2057         var mins = this.Minutes;
 2058         var secs = this.Seconds;
 2059         if ( mins != 0 || secs != 0 )
 2060         {
 2061            retVal += 3 * encoding.BytesPerASCIICharacter; // For minutes, always
 2062            if ( secs != 0 )
 2063            {
 2064               retVal += 3 * encoding.BytesPerASCIICharacter; // For seconds
 2065            }
 2066         }
 2067
 2068         return retVal;
 2069      }
 2070
 2071      public void WriteTextBytes( IEncodingInfo encoding, Byte[] array, ref Int32 offset )
 2072      {
 2073         var hours = this.Hours;
 2074         encoding.WriteASCIIByte( array, ref offset, hours > 0 ? PREFIX_POS : PREFIX_NEG ); // +/-
 2075         hours = Math.Abs( hours );
 2076         encoding.WriteIntegerTextual( array, ref offset, hours, HOURS_CHAR_COUNT );
 2077         var mins = this.Minutes;
 2078         var secs = this.Seconds;
 2079         if ( mins != 0 || secs != 0 )
 2080         {
 2081            encoding
 2082               .WriteASCIIByte( array, ref offset, SEPARATOR ) // ':'
 2083               .WriteIntegerTextual( array, ref offset, Math.Abs( mins ), MINUTES_CHAR_COUNT );
 2084            if ( secs != 0 )
 2085            {
 2086               encoding
 2087                  .WriteASCIIByte( array, ref offset, SEPARATOR ) // ':'
 2088                  .WriteIntegerTextual( array, ref offset, Math.Abs( secs ), SECONDS_CHAR_COUNT );
 2089            }
 2090         }
 2091      }
 2092
 2093      internal static PgSQLTimeZone ParseBinaryText( IEncodingInfo encoding, Byte[] array, ref Int32 offset, Int32 count
 2094      {
 2095         var max = offset + count;
 2096         var prefix = encoding.ReadASCIIByte( array, ref offset );
 2097         if ( prefix != PREFIX_POS && prefix != PREFIX_NEG )
 2098         {
 2099            throw new FormatException( $"Expected timezone string to start with either '{PREFIX_POS}' or '{PREFIX_NEG}' 
 2100         }
 2101         var hours = encoding.ParseInt32Textual( array, ref offset, (HOURS_CHAR_COUNT, true) );
 2102         encoding.TryParseOptionalNumber( array, ref offset, SEPARATOR, (MINUTES_CHAR_COUNT, true), max, out Int32 minut
 2103         encoding.TryParseOptionalNumber( array, ref offset, SEPARATOR, (SECONDS_CHAR_COUNT, true), max, out Int32 secon
 2104         return new PgSQLTimeZone( hours, minutes, seconds );
 2105      }
 2106
 2107      public override String ToString()
 2108      {
 2109         var sb = new StringBuilder( this.Hours.ToString( "+0#;-0#" ) );
 2110         if ( this.Minutes != 0 || this.Seconds != 0 )
 2111         {
 2112            sb.Append( ':' ).Append( Math.Abs( this.Minutes ).ToString( "D2" ) );
 2113            if ( this.Seconds != 0 )
 2114            {
 2115               sb.Append( ':' ).Append( Math.Abs( this.Seconds ).ToString( "D2" ) );
 2116            }
 2117         }
 2118         return sb.ToString();
 2119      }
 2120
 2121      public static PgSQLTimeZone Parse( String str )
 2122      {
 2123         PgSQLTimeZone result; Exception error;
 2124         TryParse( str, out result, out error );
 2125         if ( error != null )
 2126         {
 2127            throw error;
 2128         }
 2129         return result;
 2130      }
 2131
 2132      public static Boolean TryParse( String str, out PgSQLTimeZone result )
 2133      {
 2134         Exception error;
 2135         return TryParse( str, out result, out error );
 2136      }
 2137
 2138      internal static Boolean TryParse( String str, out PgSQLTimeZone result, out Exception error )
 2139      {
 2140         if ( str == null )
 2141         {
 2142            result = default( PgSQLTimeZone );
 2143            error = new ArgumentNullException( "String" );
 2144         }
 2145         else
 2146         {
 2147            error = null;
 2148            var totalSeconds = 0;
 2149            if ( str.Length <= 1 )
 2150            {
 2151               error = new FormatException( "Too short string." );
 2152            }
 2153            else
 2154            {
 2155
 2156               if ( error == null )
 2157               {
 2158                  Int32 hours, minutes; Decimal seconds; Boolean isNegative;
 2159                  PgSQLInterval.ParseTime( str, 0, out hours, out minutes, out seconds, out isNegative, ref error, true 
 2160
 2161                  if ( error == null )
 2162                  {
 2163                     seconds = Decimal.Truncate( seconds );
 2164                     if ( seconds >= new Decimal( Int32.MinValue ) && seconds <= new Decimal( Int32.MaxValue ) )
 2165                     {
 2166                        totalSeconds = hours * MINUTES_PER_HOUR * SECONDS_PER_MINUTE + minutes * SECONDS_PER_MINUTE + (I
 2167                     }
 2168                     else
 2169                     {
 2170                        error = new FormatException( "Seconds (" + seconds + ") are out of range for Int32." );
 2171                     }
 2172                  }
 2173               }
 2174            }
 2175
 2176            result = error == null ?
 2177               new PgSQLTimeZone( totalSeconds ) :
 2178               default( PgSQLTimeZone );
 2179         }
 2180         return error == null;
 2181      }
 2182
 2183      #endregion
 2184   }
 2185
 2186   //public interface IPgSQLTimeTZ : IPgSQLTime
 2187   //{
 2188   //   #region Properties
 2189
 2190   //   PgSQLTime LocalTime { get; }
 2191
 2192   //   PgSQLTimeZone TimeZone { get; }
 2193
 2194   //   PgSQLTime UTCTime { get; }
 2195
 2196   //   #endregion
 2197   //}
 2198
 2199   public struct PgSQLTimeTZ : IEquatable<PgSQLTimeTZ>, IComparable, IComparable<PgSQLTimeTZ>//, IPgSQLTimeTZ
 2200   {
 2201      #region Static
 2202
 2203      public static readonly PgSQLTimeTZ AllBalls = new PgSQLTimeTZ( PgSQLTime.AllBalls, PgSQLTimeZone.UTC );
 2204
 2205      public static PgSQLTimeTZ Now
 2206      {
 2207         get
 2208         {
 2209            return new PgSQLTimeTZ( PgSQLTime.Now );
 2210         }
 2211      }
 2212
 2213      public static PgSQLTimeTZ GetLocalMidnightFor( PgSQLDate date )
 2214      {
 2215         return new PgSQLTimeTZ( PgSQLTime.AllBalls, PgSQLTimeZone.GetLocalTimeZone( date ) );
 2216      }
 2217
 2218      #endregion
 2219
 2220      #region Fields
 2221
 2222      private readonly PgSQLTime _localTime;
 2223      private readonly PgSQLTimeZone _timeZone;
 2224
 2225      #endregion
 2226
 2227      #region Constructors
 2228
 2229      public PgSQLTimeTZ( PgSQLTime localTime, PgSQLTimeZone timeZone )
 2230      {
 2231         _localTime = localTime;
 2232         _timeZone = timeZone;
 2233      }
 2234
 2235      public PgSQLTimeTZ( PgSQLTime localTime )
 2236         : this( localTime, PgSQLTimeZone.CurrentTimeZone )
 2237      {
 2238      }
 2239
 2240      public PgSQLTimeTZ( Int64 ticks )
 2241         : this( new PgSQLTime( ticks ) )
 2242      {
 2243      }
 2244
 2245      public PgSQLTimeTZ( TimeSpan time )
 2246         : this( new PgSQLTime( time ) )
 2247      {
 2248      }
 2249
 2250      public PgSQLTimeTZ( PgSQLInterval time )
 2251         : this( new PgSQLTime( time ) )
 2252      {
 2253      }
 2254
 2255      public PgSQLTimeTZ( PgSQLTimeTZ copyFrom )
 2256         : this( copyFrom._localTime, copyFrom._timeZone )
 2257      {
 2258      }
 2259
 2260      public PgSQLTimeTZ( Int32 hours, Int32 minutes, Int32 seconds )
 2261         : this( new PgSQLTime( hours, minutes, seconds ) )
 2262      {
 2263      }
 2264
 2265      public PgSQLTimeTZ( Int32 hours, Int32 minutes, Int32 seconds, Int32 microseconds )
 2266         : this( new PgSQLTime( hours, minutes, seconds, microseconds ) )
 2267      {
 2268      }
 2269
 2270      public PgSQLTimeTZ( Int32 hours, Int32 minutes, Decimal seconds )
 2271         : this( new PgSQLTime( hours, minutes, seconds ) )
 2272      {
 2273      }
 2274
 2275      public PgSQLTimeTZ( long ticks, PgSQLTimeZone timeZone )
 2276         : this( new PgSQLTime( ticks ), timeZone )
 2277      {
 2278      }
 2279
 2280      public PgSQLTimeTZ( TimeSpan time, PgSQLTimeZone timeZone )
 2281         : this( new PgSQLTime( time ), timeZone )
 2282      {
 2283      }
 2284
 2285      public PgSQLTimeTZ( PgSQLInterval time, PgSQLTimeZone timeZone )
 2286         : this( new PgSQLTime( time ), timeZone )
 2287      {
 2288      }
 2289
 2290      public PgSQLTimeTZ( Int32 hours, Int32 minutes, Int32 seconds, PgSQLTimeZone timeZone )
 2291         : this( new PgSQLTime( hours, minutes, seconds ), timeZone )
 2292      {
 2293      }
 2294
 2295      public PgSQLTimeTZ( Int32 hours, Int32 minutes, Int32 seconds, Int32 microseconds, PgSQLTimeZone timeZone )
 2296         : this( new PgSQLTime( hours, minutes, seconds, microseconds ), timeZone )
 2297      {
 2298      }
 2299
 2300      public PgSQLTimeTZ( Int32 hours, Int32 minutes, Decimal seconds, PgSQLTimeZone timeZone )
 2301         : this( new PgSQLTime( hours, minutes, seconds ), timeZone )
 2302      {
 2303      }
 2304
 2305      #endregion
 2306
 2307      #region Properties
 2308
 2309      public PgSQLTime LocalTime
 2310      {
 2311         get
 2312         {
 2313            return this._localTime;
 2314         }
 2315      }
 2316
 2317      public PgSQLTimeZone TimeZone
 2318      {
 2319         get
 2320         {
 2321            return this._timeZone;
 2322         }
 2323      }
 2324
 2325      public PgSQLTime UTCTime
 2326      {
 2327         get
 2328         {
 2329            return this.AtTimeZone( PgSQLTimeZone.UTC ).LocalTime;
 2330         }
 2331      }
 2332
 2333      public Int32 Microseconds
 2334      {
 2335         get
 2336         {
 2337            return this._localTime.Microseconds;
 2338         }
 2339      }
 2340
 2341      public Int32 Milliseconds
 2342      {
 2343         get
 2344         {
 2345            return this._localTime.Milliseconds;
 2346         }
 2347      }
 2348
 2349      public Int32 Seconds
 2350      {
 2351         get
 2352         {
 2353            return this._localTime.Seconds;
 2354         }
 2355      }
 2356
 2357      public Int32 Minutes
 2358      {
 2359         get
 2360         {
 2361            return this._localTime.Minutes;
 2362         }
 2363      }
 2364
 2365      public Int32 Hours
 2366      {
 2367         get
 2368         {
 2369            return this._localTime.Hours;
 2370         }
 2371      }
 2372
 2373      public Int64 Ticks
 2374      {
 2375         get
 2376         {
 2377            return this._localTime.Ticks;
 2378         }
 2379      }
 2380
 2381      #endregion
 2382
 2383      #region Comparison
 2384
 2385      public Boolean Equals( PgSQLTimeTZ other )
 2386      {
 2387         return this._localTime.Equals( other._localTime ) && this._timeZone.Equals( other._timeZone );
 2388      }
 2389
 2390      public override Boolean Equals( Object obj )
 2391      {
 2392         return obj != null && obj is PgSQLTimeTZ && this.Equals( (PgSQLTimeTZ) obj );
 2393      }
 2394
 2395      public override Int32 GetHashCode()
 2396      {
 2397         // Like in point
 2398         unchecked
 2399         {
 2400            return ( 17 * 23 + this._localTime.GetHashCode() ) * 23 + this._timeZone.GetHashCode();
 2401         }
 2402      }
 2403
 2404      Int32 IComparable.CompareTo( Object obj )
 2405      {
 2406         if ( obj == null )
 2407         {
 2408            // This is always 'greater' than null
 2409            return 1;
 2410         }
 2411         else if ( obj is PgSQLTimeTZ )
 2412         {
 2413            return this.CompareTo( (PgSQLTimeTZ) obj );
 2414         }
 2415         else
 2416         {
 2417            throw new ArgumentException( "Given object must be of type " + this.GetType() + " or null." );
 2418         }
 2419      }
 2420
 2421      public Int32 CompareTo( PgSQLTimeTZ other )
 2422      {
 2423         var utcCompare = this.UTCTime.CompareTo( other.UTCTime );
 2424         return utcCompare == 0 ? this._timeZone.CompareTo( other._timeZone ) : utcCompare;
 2425      }
 2426
 2427      #endregion
 2428
 2429      #region Arithmetics
 2430
 2431      public PgSQLTimeTZ Normalize()
 2432      {
 2433         return new PgSQLTimeTZ( this._localTime.Normalize(), this._timeZone );
 2434      }
 2435
 2436      public PgSQLTimeTZ AtTimeZone( PgSQLTimeZone timeZone )
 2437      {
 2438         return new PgSQLTimeTZ( this._localTime - this._timeZone + timeZone, timeZone );
 2439      }
 2440
 2441      public PgSQLTimeTZ AtTimeZone( PgSQLTimeZone timeZone, out Int32 overflow )
 2442      {
 2443         return new PgSQLTimeTZ( this._localTime.Add( timeZone - (PgSQLInterval) ( _timeZone ), out overflow ), timeZone
 2444      }
 2445
 2446      public PgSQLTimeTZ Add( PgSQLInterval interval )
 2447      {
 2448         return new PgSQLTimeTZ( _localTime.Add( interval ), _timeZone );
 2449      }
 2450
 2451      internal PgSQLTimeTZ Add( PgSQLInterval interval, out Int32 overflow )
 2452      {
 2453         return new PgSQLTimeTZ( _localTime.Add( interval, out overflow ), _timeZone );
 2454      }
 2455
 2456      public PgSQLTimeTZ Subtract( PgSQLInterval interval )
 2457      {
 2458         return new PgSQLTimeTZ( _localTime.Subtract( interval ), _timeZone );
 2459      }
 2460
 2461      public PgSQLInterval Subtract( PgSQLTimeTZ earlier )
 2462      {
 2463         return _localTime.Subtract( earlier.AtTimeZone( _timeZone )._localTime );
 2464      }
 2465
 2466      #endregion
 2467
 2468      #region Casts
 2469
 2470      public static explicit operator DateTime( PgSQLTimeTZ x )
 2471      {
 2472         return new DateTime( x.AtTimeZone( PgSQLTimeZone.CurrentTimeZone ).Ticks, DateTimeKind.Local );
 2473      }
 2474
 2475      public static explicit operator PgSQLTimeTZ( DateTime x )
 2476      {
 2477         return new PgSQLTimeTZ( new PgSQLTime( x ) );
 2478      }
 2479
 2480      public static explicit operator TimeSpan( PgSQLTimeTZ x )
 2481      {
 2482         return (TimeSpan) x.LocalTime;
 2483      }
 2484
 2485      public static explicit operator PgSQLTimeTZ( TimeSpan x )
 2486      {
 2487         return new PgSQLTimeTZ( (PgSQLTime) x );
 2488      }
 2489
 2490      #endregion
 2491
 2492      #region Operators
 2493
 2494      public static Boolean operator ==( PgSQLTimeTZ x, PgSQLTimeTZ y )
 2495      {
 2496         return x.Equals( y );
 2497      }
 2498
 2499      public static Boolean operator !=( PgSQLTimeTZ x, PgSQLTimeTZ y )
 2500      {
 2501         return !( x == y );
 2502      }
 2503
 2504      public static Boolean operator <( PgSQLTimeTZ x, PgSQLTimeTZ y )
 2505      {
 2506         return x.CompareTo( y ) < 0;
 2507      }
 2508
 2509      public static Boolean operator <=( PgSQLTimeTZ x, PgSQLTimeTZ y )
 2510      {
 2511         return x.CompareTo( y ) <= 0;
 2512      }
 2513
 2514      public static Boolean operator >( PgSQLTimeTZ x, PgSQLTimeTZ y )
 2515      {
 2516         return !( x <= y );
 2517      }
 2518
 2519      public static Boolean operator >=( PgSQLTimeTZ x, PgSQLTimeTZ y )
 2520      {
 2521         return !( x < y );
 2522      }
 2523
 2524      public static PgSQLTimeTZ operator +( PgSQLTimeTZ time, PgSQLInterval interval )
 2525      {
 2526         return time.Add( interval );
 2527      }
 2528
 2529      public static PgSQLTimeTZ operator +( PgSQLInterval interval, PgSQLTimeTZ time )
 2530      {
 2531         return time + interval;
 2532      }
 2533
 2534      public static PgSQLTimeTZ operator -( PgSQLTimeTZ time, PgSQLInterval interval )
 2535      {
 2536         return time.Subtract( interval );
 2537      }
 2538
 2539      public static PgSQLInterval operator -( PgSQLTimeTZ later, PgSQLTimeTZ earlier )
 2540      {
 2541         return later.Subtract( earlier );
 2542      }
 2543
 2544      #endregion
 2545
 2546      #region To and from string
 2547
 2548      public Int32 GetTextByteCount( IEncodingInfo encoding )
 2549      {
 2550         return this._localTime.GetTextByteCount( encoding ) + this._timeZone.GetTextByteCount( encoding );
 2551      }
 2552
 2553      public void WriteTextBytes( IEncodingInfo encoding, Byte[] array, ref Int32 offset )
 2554      {
 2555         this._localTime.WriteTextBytes( encoding, array, ref offset );
 2556         this._timeZone.WriteTextBytes( encoding, array, ref offset );
 2557      }
 2558
 2559      public static PgSQLTimeTZ ParseBinaryText( IEncodingInfo encoding, Byte[] array, ref Int32 offset, Int32 count )
 2560      {
 2561         var oldIdx = offset;
 2562         var time = PgSQLTime.ParseBinaryText( encoding, array, ref offset, count );
 2563         count -= offset - oldIdx;
 2564         return new PgSQLTimeTZ(
 2565            time,
 2566            PgSQLTimeZone.ParseBinaryText( encoding, array, ref offset, count )
 2567            );
 2568      }
 2569
 2570
 2571      public override String ToString()
 2572      {
 2573         return this._localTime.ToString() + this._timeZone.ToString();
 2574      }
 2575
 2576      public static PgSQLTimeTZ Parse( String str )
 2577      {
 2578         PgSQLTimeTZ result; Exception error;
 2579         TryParse( str, out result, out error );
 2580         if ( error != null )
 2581         {
 2582            throw error;
 2583         }
 2584         return result;
 2585      }
 2586
 2587      public static Boolean TryParse( String str, out PgSQLTimeTZ result )
 2588      {
 2589         Exception error;
 2590         return TryParse( str, out result, out error );
 2591      }
 2592
 2593      internal static Boolean TryParse( String str, out PgSQLTimeTZ result, out Exception error )
 2594      {
 2595         if ( str == null )
 2596         {
 2597            result = default( PgSQLTimeTZ );
 2598            error = new ArgumentNullException( "String" );
 2599         }
 2600         else
 2601         {
 2602            error = null;
 2603            //Search for timezone offset + or -
 2604            var idx = Math.Max( str.IndexOf( '+' ), str.IndexOf( '-' ) );
 2605            if ( idx < 0 )
 2606            {
 2607               error = new FormatException( "Missing time zone information." );
 2608            }
 2609
 2610            PgSQLTime time; PgSQLTimeZone tz;
 2611            result = error == null && PgSQLTime.TryParse( str.Substring( 0, idx ), out time, out error ) && PgSQLTimeZon
 2612               new PgSQLTimeTZ( time, tz ) :
 2613               default( PgSQLTimeTZ );
 2614         }
 2615         return error == null;
 2616      }
 2617
 2618      #endregion
 2619
 2620   }
 2621
 2622   //public interface IPgSQLTimestamp
 2623   //{
 2624   //   #region Properties
 2625
 2626   //   Boolean IsFinite { get; }
 2627
 2628   //   Boolean IsInfinity { get; }
 2629
 2630   //   Boolean IsMinusInfinity { get; }
 2631
 2632   //   PgSQLDate Date { get; }
 2633
 2634   //   #endregion
 2635   //}
 2636
 2637   public struct PgSQLTimestamp : IEquatable<PgSQLTimestamp>, IComparable, IComparable<PgSQLTimestamp>//, IPgSQLDate, IP
 2638   {
 2639      #region Inner types
 2640
 2641      internal enum TSKind : byte
 2642      {
 2643         Normal,
 2644         Infinity,
 2645         MinusInfinity,
 2646      }
 2647
 2648      #endregion
 2649
 2650      #region Static
 2651
 2652      public static readonly PgSQLTimestamp Epoch = new PgSQLTimestamp( PgSQLDate.Epoch );
 2653      public static readonly PgSQLTimestamp Era = new PgSQLTimestamp( PgSQLDate.Era );
 2654      public static readonly PgSQLTimestamp Infinity = new PgSQLTimestamp( TSKind.Infinity );
 2655      public static readonly PgSQLTimestamp MinusInfinity = new PgSQLTimestamp( TSKind.MinusInfinity );
 2656
 2657      public static PgSQLTimestamp Now
 2658      {
 2659         get
 2660         {
 2661            return new PgSQLTimestamp( PgSQLDate.Now, PgSQLTime.Now );
 2662         }
 2663      }
 2664
 2665      public static PgSQLTimestamp Today
 2666      {
 2667         get
 2668         {
 2669            return new PgSQLTimestamp( PgSQLDate.Now );
 2670         }
 2671      }
 2672
 2673      public static PgSQLTimestamp Yesterday
 2674      {
 2675         get
 2676         {
 2677            return new PgSQLTimestamp( PgSQLDate.Yesterday );
 2678         }
 2679      }
 2680
 2681      public static PgSQLTimestamp Tomorrow
 2682      {
 2683         get
 2684         {
 2685            return new PgSQLTimestamp( PgSQLDate.Tomorrow );
 2686         }
 2687      }
 2688
 2689      #endregion
 2690
 2691      #region Fields
 2692
 2693      private readonly PgSQLDate _date;
 2694      private readonly PgSQLTime _time;
 2695      private readonly TSKind _kind;
 2696
 2697      #endregion
 2698
 2699      #region Constructors
 2700
 2701      private PgSQLTimestamp( TSKind kind )
 2702      {
 2703         if ( kind != TSKind.Infinity && kind != TSKind.MinusInfinity )
 2704         {
 2705            throw new InvalidOperationException( "This constructor may only be used to create infinity or minus infinity
 2706         }
 2707         this._kind = kind;
 2708         this._date = PgSQLDate.Era;
 2709         this._time = PgSQLTime.AllBalls;
 2710      }
 2711
 2712      public PgSQLTimestamp( PgSQLDate date, PgSQLTime time )
 2713      {
 2714         this._kind = TSKind.Normal;
 2715         this._date = date;
 2716         this._time = time;
 2717      }
 2718
 2719      public PgSQLTimestamp( PgSQLDate date )
 2720         : this( date, PgSQLTime.AllBalls )
 2721      {
 2722      }
 2723
 2724      public PgSQLTimestamp( Int32 year, Int32 month, Int32 day, Int32 hours, Int32 minutes, Int32 seconds )
 2725         : this( new PgSQLDate( year, month, day ), new PgSQLTime( hours, minutes, seconds ) )
 2726      {
 2727      }
 2728
 2729      public PgSQLTimestamp( Int32 year, Int32 month, Int32 day, Int32 hours, Int32 minutes, Decimal seconds )
 2730         : this( new PgSQLDate( year, month, day ), new PgSQLTime( hours, minutes, seconds ) )
 2731      {
 2732      }
 2733
 2734      #endregion
 2735
 2736      #region Properties
 2737
 2738      public Boolean IsFinite
 2739      {
 2740         get
 2741         {
 2742            return this._kind == TSKind.Normal;
 2743         }
 2744      }
 2745
 2746      public Boolean IsInfinity
 2747      {
 2748         get
 2749         {
 2750            return this._kind == TSKind.Infinity;
 2751         }
 2752      }
 2753
 2754      public Boolean IsMinusInfinity
 2755      {
 2756         get
 2757         {
 2758            return this._kind == TSKind.MinusInfinity;
 2759         }
 2760      }
 2761
 2762      public PgSQLDate Date
 2763      {
 2764         get
 2765         {
 2766            return this._date;
 2767         }
 2768      }
 2769
 2770      public PgSQLTime Time
 2771      {
 2772         get
 2773         {
 2774            return this._time;
 2775         }
 2776      }
 2777
 2778      public Int32 DayOfYear
 2779      {
 2780         get
 2781         {
 2782            return this._date.DayOfYear;
 2783         }
 2784      }
 2785
 2786      public Int32 Year
 2787      {
 2788         get
 2789         {
 2790            return this._date.Year;
 2791         }
 2792      }
 2793
 2794      public Int32 Month
 2795      {
 2796         get
 2797         {
 2798            return this._date.Month;
 2799         }
 2800      }
 2801
 2802      public Int32 Day
 2803      {
 2804         get
 2805         {
 2806            return this._date.Day;
 2807         }
 2808      }
 2809
 2810      public DayOfWeek DayOfWeek
 2811      {
 2812         get
 2813         {
 2814            return this._date.DayOfWeek;
 2815         }
 2816      }
 2817
 2818      public Int32 DaysSinceEra
 2819      {
 2820         get
 2821         {
 2822            return this._date.DaysSinceEra;
 2823         }
 2824      }
 2825
 2826      public Boolean IsLeapYear
 2827      {
 2828         get
 2829         {
 2830            return this._date.IsLeapYear;
 2831         }
 2832      }
 2833
 2834      public Int64 Ticks
 2835      {
 2836         get
 2837         {
 2838            return this._time.Ticks;
 2839         }
 2840      }
 2841
 2842      public Int32 Microseconds
 2843      {
 2844         get
 2845         {
 2846            return this._time.Microseconds;
 2847         }
 2848      }
 2849
 2850      public Int32 Milliseconds
 2851      {
 2852         get
 2853         {
 2854            return this._time.Milliseconds;
 2855         }
 2856      }
 2857
 2858      public Int32 Seconds
 2859      {
 2860         get
 2861         {
 2862            return this._time.Seconds;
 2863         }
 2864      }
 2865
 2866      public Int32 Minutes
 2867      {
 2868         get
 2869         {
 2870            return this._time.Minutes;
 2871         }
 2872      }
 2873
 2874      public Int32 Hours
 2875      {
 2876         get
 2877         {
 2878            return this._time.Hours;
 2879         }
 2880      }
 2881
 2882      #endregion
 2883
 2884      #region Arithmetics
 2885
 2886      public PgSQLTimestamp AddDays( Int32 days )
 2887      {
 2888         switch ( this._kind )
 2889         {
 2890            case TSKind.Infinity:
 2891            case TSKind.MinusInfinity:
 2892               return this;
 2893            default:
 2894               return new PgSQLTimestamp( this._date.AddDays( days ), this._time );
 2895         }
 2896      }
 2897
 2898      public PgSQLTimestamp AddYears( Int32 years )
 2899      {
 2900         switch ( this._kind )
 2901         {
 2902            case TSKind.Infinity:
 2903            case TSKind.MinusInfinity:
 2904               return this;
 2905            default:
 2906               return new PgSQLTimestamp( this._date.AddYears( years ), this._time );
 2907         }
 2908      }
 2909
 2910      public PgSQLTimestamp AddMonths( Int32 months )
 2911      {
 2912         switch ( this._kind )
 2913         {
 2914            case TSKind.Infinity:
 2915            case TSKind.MinusInfinity:
 2916               return this;
 2917            default:
 2918               return new PgSQLTimestamp( this._date.AddMonths( months ), this._time );
 2919         }
 2920      }
 2921
 2922      public PgSQLTimestamp Add( PgSQLInterval interval )
 2923      {
 2924         switch ( this._kind )
 2925         {
 2926            case TSKind.Infinity:
 2927            case TSKind.MinusInfinity:
 2928               return this;
 2929            default:
 2930               Int32 overflow;
 2931               var time = this._time.Add( interval, out overflow );
 2932               return new PgSQLTimestamp( this._date.Add( interval, overflow ), time );
 2933         }
 2934      }
 2935
 2936      public PgSQLTimestamp Subtract( PgSQLInterval interval )
 2937      {
 2938         return Add( -interval );
 2939      }
 2940
 2941      public PgSQLInterval Subtract( PgSQLTimestamp timestamp )
 2942      {
 2943         switch ( this._kind )
 2944         {
 2945            case TSKind.Infinity:
 2946            case TSKind.MinusInfinity:
 2947               throw new ArgumentOutOfRangeException( "this", "You cannot subtract infinity timestamps" );
 2948         }
 2949         switch ( timestamp._kind )
 2950         {
 2951            case TSKind.Infinity:
 2952            case TSKind.MinusInfinity:
 2953               throw new ArgumentOutOfRangeException( "timestamp", "You cannot subtract infinity timestamps" );
 2954         }
 2955
 2956         return new PgSQLInterval( 0, this._date.DaysSinceEra - timestamp._date.DaysSinceEra, this._time.Ticks - timesta
 2957      }
 2958
 2959      #endregion
 2960
 2961      #region Normalization
 2962
 2963      public PgSQLTimestamp Normalize()
 2964      {
 2965         return this.Add( PgSQLInterval.Zero );
 2966      }
 2967
 2968      #endregion
 2969
 2970      #region Time zone
 2971
 2972      public PgSQLTimestampTZ AtTimeZone( PgSQLTimeZone timeZoneFrom, PgSQLTimeZone timeZoneTo )
 2973      {
 2974         Int32 overflow;
 2975         var adjusted = new PgSQLTimeTZ( _time, timeZoneFrom ).AtTimeZone( timeZoneTo, out overflow );
 2976         return new PgSQLTimestampTZ( _date.AddDays( overflow ), adjusted );
 2977      }
 2978
 2979      public PgSQLTimestampTZ AtTimeZone( PgSQLTimeZone timeZone )
 2980      {
 2981         return AtTimeZone( timeZone, PgSQLTimeZone.GetLocalTimeZone( _date ) );
 2982      }
 2983
 2984      #endregion
 2985
 2986      #region Comparison
 2987
 2988      public Boolean Equals( PgSQLTimestamp other )
 2989      {
 2990         switch ( this._kind )
 2991         {
 2992            case TSKind.Infinity:
 2993               return other._kind == TSKind.Infinity;
 2994            case TSKind.MinusInfinity:
 2995               return other._kind == TSKind.MinusInfinity;
 2996            default:
 2997               return other._kind == TSKind.Normal && this._date.Equals( other._date ) && this._time.Equals( other._time
 2998         }
 2999      }
 3000
 3001      public override Boolean Equals( Object obj )
 3002      {
 3003         return obj != null && obj is PgSQLTimestamp && this.Equals( (PgSQLTimestamp) obj );
 3004      }
 3005
 3006      public override Int32 GetHashCode()
 3007      {
 3008         switch ( this._kind )
 3009         {
 3010            case TSKind.Infinity:
 3011               return Int32.MaxValue;
 3012            case TSKind.MinusInfinity:
 3013               return Int32.MinValue;
 3014            default:
 3015               // Like in point
 3016               unchecked
 3017               {
 3018                  return ( 17 * 23 + this._date.GetHashCode() ) * 23 + this._time.GetHashCode();
 3019               }
 3020         }
 3021      }
 3022
 3023      Int32 IComparable.CompareTo( Object obj )
 3024      {
 3025         if ( obj == null )
 3026         {
 3027            // This is always 'greater' than null
 3028            return 1;
 3029         }
 3030         else if ( obj is PgSQLTimestamp )
 3031         {
 3032            return this.CompareTo( (PgSQLTimestamp) obj );
 3033         }
 3034         else
 3035         {
 3036            throw new ArgumentException( "Given object must be of type " + this.GetType() + " or null." );
 3037         }
 3038      }
 3039
 3040      public Int32 CompareTo( PgSQLTimestamp other )
 3041      {
 3042         switch ( this._kind )
 3043         {
 3044            case TSKind.Infinity:
 3045               return other._kind == TSKind.Infinity ? 0 : 1;
 3046            case TSKind.MinusInfinity:
 3047               return other._kind == TSKind.MinusInfinity ? 0 : -1;
 3048            default:
 3049               var dateCompare = this._date.CompareTo( other._date );
 3050               return dateCompare == 0 ? this._time.CompareTo( other._time ) : dateCompare;
 3051         }
 3052      }
 3053
 3054      #endregion
 3055
 3056      #region Casts and conversions
 3057
 3058      public static explicit operator PgSQLTimestamp( DateTime x )
 3059      {
 3060         return x == DateTime.MaxValue ?
 3061            Infinity : ( x == DateTime.MinValue ? MinusInfinity :
 3062            new PgSQLTimestamp( new PgSQLDate( x ), new PgSQLTime( x.TimeOfDay ) ) );
 3063      }
 3064
 3065      public static explicit operator DateTime( PgSQLTimestamp x )
 3066      {
 3067         return x.ToDateTime( DateTimeKind.Unspecified );
 3068      }
 3069
 3070      public DateTime ToDateTime( DateTimeKind dtKind )
 3071      {
 3072         switch ( this._kind )
 3073         {
 3074            case TSKind.Infinity:
 3075               return DateTime.MaxValue;
 3076            case TSKind.MinusInfinity:
 3077               return DateTime.MinValue;
 3078            default:
 3079               try
 3080               {
 3081                  return new DateTime( this._date.DaysSinceEra * TimeSpan.TicksPerDay + this._time.Ticks, dtKind );
 3082               }
 3083               catch ( Exception exc )
 3084               {
 3085                  throw new InvalidCastException( "", exc );
 3086               }
 3087         }
 3088      }
 3089
 3090      #endregion
 3091
 3092      #region Operators
 3093
 3094      public static Boolean operator ==( PgSQLTimestamp x, PgSQLTimestamp y )
 3095      {
 3096         return x.Equals( y );
 3097      }
 3098
 3099      public static Boolean operator !=( PgSQLTimestamp x, PgSQLTimestamp y )
 3100      {
 3101         return !( x == y );
 3102      }
 3103
 3104      public static Boolean operator <( PgSQLTimestamp x, PgSQLTimestamp y )
 3105      {
 3106         return x.CompareTo( y ) < 0;
 3107      }
 3108
 3109      public static Boolean operator <=( PgSQLTimestamp x, PgSQLTimestamp y )
 3110      {
 3111         return x.CompareTo( y ) <= 0;
 3112      }
 3113
 3114      public static Boolean operator >( PgSQLTimestamp x, PgSQLTimestamp y )
 3115      {
 3116         return !( x <= y );
 3117      }
 3118
 3119      public static Boolean operator >=( PgSQLTimestamp x, PgSQLTimestamp y )
 3120      {
 3121         return !( x < y );
 3122      }
 3123
 3124      public static PgSQLTimestamp operator +( PgSQLTimestamp timestamp, PgSQLInterval interval )
 3125      {
 3126         return timestamp.Add( interval );
 3127      }
 3128
 3129      public static PgSQLTimestamp operator +( PgSQLInterval interval, PgSQLTimestamp timestamp )
 3130      {
 3131         return timestamp.Add( interval );
 3132      }
 3133
 3134      public static PgSQLTimestamp operator -( PgSQLTimestamp timestamp, PgSQLInterval interval )
 3135      {
 3136         return timestamp.Subtract( interval );
 3137      }
 3138
 3139      public static PgSQLInterval operator -( PgSQLTimestamp x, PgSQLTimestamp y )
 3140      {
 3141         return x.Subtract( y );
 3142      }
 3143
 3144      #endregion
 3145
 3146      #region To and from string
 3147
 3148      private const Byte SEPARATOR = (Byte) ' ';
 3149
 3150      public Int32 GetTextByteCount( IEncodingInfo encoding )
 3151      {
 3152         switch ( this._kind )
 3153         {
 3154            case TSKind.Infinity:
 3155               return encoding.Encoding.GetByteCount( PgSQLDate.INFINITY );
 3156            case TSKind.MinusInfinity:
 3157               return encoding.Encoding.GetByteCount( PgSQLDate.MINUS_INFINITY );
 3158            default:
 3159               return encoding.BytesPerASCIICharacter + this._date.GetTextByteCount( encoding ) + this._time.GetTextByte
 3160         }
 3161      }
 3162
 3163      public void WriteTextBytes( IEncodingInfo encoding, Byte[] array, ref Int32 offset )
 3164      {
 3165         switch ( this._kind )
 3166         {
 3167            case TSKind.Infinity:
 3168               encoding.Encoding.GetBytes( PgSQLDate.INFINITY, 0, PgSQLDate.INFINITY.Length, array, offset );
 3169               break;
 3170            case TSKind.MinusInfinity:
 3171               encoding.Encoding.GetBytes( PgSQLDate.MINUS_INFINITY, 0, PgSQLDate.MINUS_INFINITY.Length, array, offset )
 3172               break;
 3173            default:
 3174               this._date.WriteTextBytes( encoding, array, ref offset );
 3175               encoding.WriteASCIIByte( array, ref offset, SEPARATOR ); // ' '
 3176               this._time.WriteTextBytes( encoding, array, ref offset );
 3177               break;
 3178         }
 3179      }
 3180
 3181      public static PgSQLTimestamp ParseBinaryText( IEncodingInfo encoding, Byte[] array, ref Int32 offset, Int32 count 
 3182      {
 3183         switch ( count )
 3184         {
 3185            case PgSQLDate.INFINITY_CHAR_COUNT:
 3186               return Infinity;
 3187            case PgSQLDate.MINUS_INFINITY_CHAR_COUNT:
 3188               return MinusInfinity;
 3189            default:
 3190               var oldIdx = offset;
 3191               var date = PgSQLDate.ParseBinaryText( encoding, array, ref offset, count );
 3192               encoding.EqualsOrThrow( array, ref offset, SEPARATOR );
 3193               count -= offset - oldIdx;
 3194               return new PgSQLTimestamp(
 3195                  date,
 3196                  PgSQLTime.ParseBinaryText( encoding, array, ref offset, count )
 3197                  );
 3198         }
 3199      }
 3200
 3201      public override String ToString()
 3202      {
 3203         switch ( this._kind )
 3204         {
 3205            case TSKind.Infinity:
 3206               return PgSQLDate.INFINITY;
 3207            case TSKind.MinusInfinity:
 3208               return PgSQLDate.MINUS_INFINITY;
 3209            default:
 3210               return this._date.ToString() + " " + this._time.ToString();
 3211         }
 3212      }
 3213
 3214      public static PgSQLTimestamp Parse( String str )
 3215      {
 3216         PgSQLTimestamp result; Exception error;
 3217         TryParse( str, out result, out error );
 3218         if ( error != null )
 3219         {
 3220            throw error;
 3221         }
 3222         return result;
 3223      }
 3224
 3225      public static Boolean TryParse( String str, out PgSQLTimestamp result )
 3226      {
 3227         Exception error;
 3228         return TryParse( str, out result, out error );
 3229      }
 3230
 3231      private static Boolean TryParse( String str, out PgSQLTimestamp result, out Exception error )
 3232      {
 3233         if ( str == null )
 3234         {
 3235            result = default( PgSQLTimestamp );
 3236            error = new ArgumentNullException( "String" );
 3237         }
 3238         else
 3239         {
 3240            str = str.Trim();
 3241            error = null;
 3242            if ( String.Equals( str, PgSQLDate.INFINITY, StringComparison.OrdinalIgnoreCase ) )
 3243            {
 3244               result = Infinity;
 3245            }
 3246            else if ( String.Equals( str, PgSQLDate.MINUS_INFINITY, StringComparison.OrdinalIgnoreCase ) )
 3247            {
 3248               result = MinusInfinity;
 3249            }
 3250            else
 3251            {
 3252               var idx = str.LastIndexOf( ' ' );
 3253               if ( idx == -1 )
 3254               {
 3255                  error = new FormatException( "Failed to distinguish date and time in timestamp." );
 3256               }
 3257
 3258               PgSQLDate date; PgSQLTime time;
 3259               result = error == null && PgSQLDate.TryParse( str.Substring( 0, idx ), out date, out error ) && PgSQLTime
 3260                  new PgSQLTimestamp( date, time ) :
 3261                  default( PgSQLTimestamp );
 3262            }
 3263         }
 3264         return error == null;
 3265      }
 3266
 3267      #endregion
 3268
 3269   }
 3270
 3271   public struct PgSQLTimestampTZ : IEquatable<PgSQLTimestampTZ>, IComparable, IComparable<PgSQLTimestampTZ>//, IPgSQLDa
 3272   {
 3273      #region Static
 3274
 03275      public static readonly PgSQLTimestampTZ Epoch = new PgSQLTimestampTZ( PgSQLDate.Epoch, PgSQLTimeTZ.AllBalls );
 3276
 03277      public static readonly PgSQLTimestampTZ Era = new PgSQLTimestampTZ( PgSQLDate.Era, PgSQLTimeTZ.AllBalls );
 3278
 03279      public static readonly PgSQLTimestampTZ MinusInfinity = new PgSQLTimestampTZ( PgSQLTimestamp.TSKind.MinusInfinity 
 3280
 03281      public static readonly PgSQLTimestampTZ Infinity = new PgSQLTimestampTZ( PgSQLTimestamp.TSKind.Infinity );
 3282
 3283      public static PgSQLTimestampTZ Now
 3284      {
 3285         get
 3286         {
 03287            return new PgSQLTimestampTZ( PgSQLDate.Now, PgSQLTimeTZ.Now );
 3288         }
 3289      }
 3290
 3291      public static PgSQLTimestampTZ Today
 3292      {
 3293         get
 3294         {
 03295            return new PgSQLTimestampTZ( PgSQLDate.Now );
 3296         }
 3297      }
 3298
 3299      public static PgSQLTimestampTZ Yesterday
 3300      {
 3301         get
 3302         {
 03303            return new PgSQLTimestampTZ( PgSQLDate.Yesterday );
 3304         }
 3305      }
 3306
 3307      public static PgSQLTimestampTZ Tomorrow
 3308      {
 3309         get
 3310         {
 03311            return new PgSQLTimestampTZ( PgSQLDate.Tomorrow );
 3312         }
 3313      }
 3314
 3315      #endregion
 3316
 3317      #region Fields
 3318
 3319      private readonly PgSQLDate _date;
 3320      private readonly PgSQLTimeTZ _time;
 3321      private readonly PgSQLTimestamp.TSKind _kind;
 3322
 3323      #endregion
 3324
 3325      #region Constructors
 3326
 3327      private PgSQLTimestampTZ( PgSQLTimestamp.TSKind kind )
 3328      {
 03329         if ( kind != PgSQLTimestamp.TSKind.Infinity && kind != PgSQLTimestamp.TSKind.MinusInfinity )
 3330         {
 03331            throw new InvalidOperationException( "This constructor may only be used to create infinity or minus infinity
 3332         }
 03333         this._kind = kind;
 03334         this._date = PgSQLDate.Era;
 03335         this._time = PgSQLTimeTZ.AllBalls;
 03336      }
 3337
 3338      public PgSQLTimestampTZ( PgSQLDate date, PgSQLTimeTZ time )
 3339      {
 03340         this._kind = PgSQLTimestamp.TSKind.Normal;
 03341         this._date = date;
 03342         this._time = time;
 03343      }
 3344
 3345      public PgSQLTimestampTZ( PgSQLDate date )
 03346         : this( date, PgSQLTimeTZ.AllBalls )
 3347      {
 03348      }
 3349
 3350      public PgSQLTimestampTZ( Int32 year, Int32 month, Int32 day, Int32 hours, Int32 minutes, Int32 seconds, PgSQLTimeZ
 03351         : this( new PgSQLDate( year, month, day ), new PgSQLTimeTZ( hours, minutes, seconds, timeZone.HasValue ? timeZo
 3352      {
 03353      }
 3354
 3355      public PgSQLTimestampTZ( Int32 year, Int32 month, Int32 day, Int32 hours, Int32 minutes, Decimal seconds, PgSQLTim
 03356         : this( new PgSQLDate( year, month, day ), new PgSQLTimeTZ( hours, minutes, seconds, timeZone.HasValue ? timeZo
 3357      {
 03358      }
 3359
 3360      #endregion
 3361
 3362      #region Properties
 3363
 3364      public Int32 DayOfYear
 3365      {
 3366         get
 3367         {
 03368            return this._date.DayOfYear;
 3369         }
 3370      }
 3371
 3372      public Int32 Year
 3373      {
 3374         get
 3375         {
 03376            return this._date.Year;
 3377         }
 3378      }
 3379
 3380      public Int32 Month
 3381      {
 3382         get
 3383         {
 03384            return this._date.Month;
 3385         }
 3386      }
 3387
 3388      public Int32 Day
 3389      {
 3390         get
 3391         {
 03392            return this._date.Day;
 3393         }
 3394      }
 3395
 3396      public DayOfWeek DayOfWeek
 3397      {
 3398         get
 3399         {
 03400            return this._date.DayOfWeek;
 3401         }
 3402      }
 3403
 3404      public Int32 DaysSinceEra
 3405      {
 3406         get
 3407         {
 03408            return this._date.DaysSinceEra;
 3409         }
 3410      }
 3411
 3412      public Boolean IsLeapYear
 3413      {
 3414         get
 3415         {
 03416            return this._date.IsLeapYear;
 3417         }
 3418      }
 3419
 3420      public PgSQLTime LocalTime
 3421      {
 3422         get
 3423         {
 03424            return this._time.LocalTime;
 3425         }
 3426      }
 3427
 3428      public PgSQLTimeZone TimeZone
 3429      {
 3430         get
 3431         {
 03432            return this._time.TimeZone;
 3433         }
 3434      }
 3435
 3436      public PgSQLTime UTCTime
 3437      {
 3438         get
 3439         {
 03440            return this._time.UTCTime;
 3441         }
 3442      }
 3443
 3444      public Int64 Ticks
 3445      {
 3446         get
 3447         {
 03448            return this._time.Ticks;
 3449         }
 3450      }
 3451
 3452      public Int32 Microseconds
 3453      {
 3454         get
 3455         {
 03456            return this._time.Microseconds;
 3457         }
 3458      }
 3459
 3460      public Int32 Milliseconds
 3461      {
 3462         get
 3463         {
 03464            return this._time.Milliseconds;
 3465         }
 3466      }
 3467
 3468      public Int32 Seconds
 3469      {
 3470         get
 3471         {
 03472            return this._time.Seconds;
 3473         }
 3474      }
 3475
 3476      public Int32 Minutes
 3477      {
 3478         get
 3479         {
 03480            return this._time.Minutes;
 3481         }
 3482      }
 3483
 3484      public Int32 Hours
 3485      {
 3486         get
 3487         {
 03488            return this._time.Hours;
 3489         }
 3490      }
 3491
 3492      public Boolean IsFinite
 3493      {
 3494         get
 3495         {
 03496            return this._kind == PgSQLTimestamp.TSKind.Normal;
 3497         }
 3498      }
 3499
 3500      public Boolean IsInfinity
 3501      {
 3502         get
 3503         {
 03504            return this._kind == PgSQLTimestamp.TSKind.Infinity;
 3505         }
 3506      }
 3507
 3508      public Boolean IsMinusInfinity
 3509      {
 3510         get
 3511         {
 03512            return this._kind == PgSQLTimestamp.TSKind.MinusInfinity;
 3513         }
 3514      }
 3515
 3516      public PgSQLDate Date
 3517      {
 3518         get
 3519         {
 03520            return this._date;
 3521         }
 3522      }
 3523
 3524      public PgSQLTimeTZ Time
 3525      {
 3526         get
 3527         {
 03528            return this._time;
 3529         }
 3530      }
 3531
 3532      #endregion
 3533
 3534      #region Comparison
 3535
 3536      public Boolean Equals( PgSQLTimestampTZ other )
 3537      {
 03538         switch ( this._kind )
 3539         {
 3540            case PgSQLTimestamp.TSKind.Infinity:
 03541               return other._kind == PgSQLTimestamp.TSKind.Infinity;
 3542            case PgSQLTimestamp.TSKind.MinusInfinity:
 03543               return other._kind == PgSQLTimestamp.TSKind.MinusInfinity;
 3544            default:
 03545               return other._kind == PgSQLTimestamp.TSKind.Normal && this._date.Equals( other._date ) && this._time.Equa
 3546         }
 3547      }
 3548
 3549      public override Boolean Equals( Object obj )
 3550      {
 03551         return obj != null && obj is PgSQLTimestampTZ && this.Equals( (PgSQLTimestampTZ) obj );
 3552      }
 3553
 3554      public override Int32 GetHashCode()
 3555      {
 03556         switch ( this._kind )
 3557         {
 3558            case PgSQLTimestamp.TSKind.Infinity:
 03559               return Int32.MaxValue;
 3560            case PgSQLTimestamp.TSKind.MinusInfinity:
 03561               return Int32.MinValue;
 3562            default:
 3563               // Like in point
 3564               unchecked
 3565               {
 03566                  return ( 17 * 23 + this._date.GetHashCode() ) * 23 + this._time.GetHashCode();
 3567               }
 3568         }
 3569      }
 3570
 3571      Int32 IComparable.CompareTo( Object obj )
 3572      {
 03573         if ( obj == null )
 3574         {
 3575            // This is always 'greater' than null
 03576            return 1;
 3577         }
 03578         else if ( obj is PgSQLTimestampTZ )
 3579         {
 03580            return this.CompareTo( (PgSQLTimestampTZ) obj );
 3581         }
 3582         else
 3583         {
 03584            throw new ArgumentException( "Given object must be of type " + this.GetType() + " or null." );
 3585         }
 3586      }
 3587
 3588      public Int32 CompareTo( PgSQLTimestampTZ other )
 3589      {
 03590         switch ( this._kind )
 3591         {
 3592            case PgSQLTimestamp.TSKind.Infinity:
 03593               return other._kind == PgSQLTimestamp.TSKind.Infinity ? 0 : 1;
 3594            case PgSQLTimestamp.TSKind.MinusInfinity:
 03595               return other._kind == PgSQLTimestamp.TSKind.MinusInfinity ? 0 : -1;
 3596            default:
 03597               var dateCompare = this._date.CompareTo( other._date );
 03598               return dateCompare == 0 ? this._time.CompareTo( other._time ) : dateCompare;
 3599         }
 3600      }
 3601
 3602      #endregion
 3603
 3604      #region Arithmetics
 3605
 3606      public PgSQLTimestampTZ AddDays( Int32 days )
 3607      {
 03608         switch ( this._kind )
 3609         {
 3610            case PgSQLTimestamp.TSKind.Infinity:
 3611            case PgSQLTimestamp.TSKind.MinusInfinity:
 03612               return this;
 3613            default:
 03614               return new PgSQLTimestampTZ( this._date.AddDays( days ), this._time );
 3615         }
 3616      }
 3617
 3618      public PgSQLTimestampTZ AddYears( Int32 years )
 3619      {
 03620         switch ( this._kind )
 3621         {
 3622            case PgSQLTimestamp.TSKind.Infinity:
 3623            case PgSQLTimestamp.TSKind.MinusInfinity:
 03624               return this;
 3625            default:
 03626               return new PgSQLTimestampTZ( this._date.AddYears( years ), this._time );
 3627         }
 3628      }
 3629
 3630      public PgSQLTimestampTZ AddMonths( Int32 months )
 3631      {
 03632         switch ( this._kind )
 3633         {
 3634            case PgSQLTimestamp.TSKind.Infinity:
 3635            case PgSQLTimestamp.TSKind.MinusInfinity:
 03636               return this;
 3637            default:
 03638               return new PgSQLTimestampTZ( this._date.AddMonths( months ), this._time );
 3639         }
 3640      }
 3641
 3642      public PgSQLTimestampTZ Add( PgSQLInterval interval )
 3643      {
 03644         switch ( this._kind )
 3645         {
 3646            case PgSQLTimestamp.TSKind.Infinity:
 3647            case PgSQLTimestamp.TSKind.MinusInfinity:
 03648               return this;
 3649            default:
 3650               int overflow;
 03651               var time = this._time.Add( interval, out overflow );
 03652               return new PgSQLTimestampTZ( this._date.Add( interval, overflow ), time );
 3653         }
 3654      }
 3655
 3656      public PgSQLTimestampTZ Subtract( PgSQLInterval interval )
 3657      {
 03658         return Add( -interval );
 3659      }
 3660
 3661      public PgSQLInterval Subtract( PgSQLTimestampTZ timestamp )
 3662      {
 03663         switch ( this._kind )
 3664         {
 3665            case PgSQLTimestamp.TSKind.Infinity:
 3666            case PgSQLTimestamp.TSKind.MinusInfinity:
 03667               throw new ArgumentOutOfRangeException( "this", "You cannot subtract infinity timestamps" );
 3668         }
 03669         switch ( timestamp._kind )
 3670         {
 3671            case PgSQLTimestamp.TSKind.Infinity:
 3672            case PgSQLTimestamp.TSKind.MinusInfinity:
 03673               throw new ArgumentOutOfRangeException( "timestamp", "You cannot subtract infinity timestamps" );
 3674         }
 03675         return new PgSQLInterval( 0, this._date.DaysSinceEra - timestamp._date.DaysSinceEra, ( this._time - timestamp._
 3676      }
 3677
 3678      #endregion
 3679
 3680      #region Normalization
 3681
 3682      public PgSQLTimestampTZ Normalize()
 3683      {
 03684         return this.Add( PgSQLInterval.Zero );
 3685      }
 3686
 3687      #endregion
 3688
 3689      #region Time zone
 3690
 3691      public PgSQLTimestamp AtTimeZone( PgSQLTimeZone timeZone )
 3692      {
 3693         Int32 overflow;
 03694         var adjusted = this._time.AtTimeZone( timeZone, out overflow );
 03695         return new PgSQLTimestamp( _date.AddDays( overflow ), adjusted.LocalTime );
 3696      }
 3697
 3698      #endregion
 3699
 3700      #region Casts
 3701
 3702      public static explicit operator PgSQLTimestampTZ( DateTime x )
 3703      {
 03704         return x == DateTime.MaxValue ?
 03705            Infinity : ( x == DateTime.MinValue ? MinusInfinity :
 03706            new PgSQLTimestampTZ( new PgSQLDate( x ), new PgSQLTimeTZ( x.TimeOfDay, x.Kind == DateTimeKind.Utc ? PgSQLTi
 3707      }
 3708
 3709      public static explicit operator DateTime( PgSQLTimestampTZ x )
 3710      {
 03711         switch ( x._kind )
 3712         {
 3713            case PgSQLTimestamp.TSKind.Infinity:
 03714               return DateTime.MaxValue;
 3715            case PgSQLTimestamp.TSKind.MinusInfinity:
 03716               return DateTime.MinValue;
 3717            default:
 03718               var utc = x.AtTimeZone( PgSQLTimeZone.UTC );
 3719               try
 3720               {
 03721                  return new DateTime( utc.Date.DaysSinceEra * TimeSpan.TicksPerDay + utc.Time.Ticks, DateTimeKind.Utc )
 3722               }
 03723               catch ( Exception exc )
 3724               {
 03725                  throw new InvalidCastException( "", exc );
 3726               }
 3727
 3728         }
 03729      }
 3730
 3731      public static explicit operator PgSQLTimestampTZ( DateTimeOffset x )
 3732      {
 03733         return x == DateTimeOffset.MaxValue ?
 03734            Infinity : ( x == DateTimeOffset.MinValue ? MinusInfinity :
 03735            new PgSQLTimestampTZ( new PgSQLDate( x.Year, x.Month, x.Day ), new PgSQLTimeTZ( x.TimeOfDay, new PgSQLTimeZo
 3736      }
 3737
 3738      public static explicit operator DateTimeOffset( PgSQLTimestampTZ x )
 3739      {
 03740         switch ( x._kind )
 3741         {
 3742            case PgSQLTimestamp.TSKind.Infinity:
 03743               return DateTimeOffset.MaxValue;
 3744            case PgSQLTimestamp.TSKind.MinusInfinity:
 03745               return DateTimeOffset.MinValue;
 3746            default:
 3747               try
 3748               {
 03749                  return new DateTimeOffset( x._date.DaysSinceEra * TimeSpan.TicksPerDay + x._time.Ticks, x.TimeZone );
 3750               }
 03751               catch ( Exception exc )
 3752               {
 03753                  throw new InvalidCastException( "", exc );
 3754               }
 3755
 3756         }
 03757      }
 3758
 3759      #endregion
 3760
 3761      #region Operators
 3762
 3763      public static Boolean operator ==( PgSQLTimestampTZ x, PgSQLTimestampTZ y )
 3764      {
 03765         return x.Equals( y );
 3766      }
 3767
 3768      public static Boolean operator !=( PgSQLTimestampTZ x, PgSQLTimestampTZ y )
 3769      {
 03770         return !( x == y );
 3771      }
 3772
 3773      public static Boolean operator <( PgSQLTimestampTZ x, PgSQLTimestampTZ y )
 3774      {
 03775         return x.CompareTo( y ) < 0;
 3776      }
 3777
 3778      public static Boolean operator <=( PgSQLTimestampTZ x, PgSQLTimestampTZ y )
 3779      {
 03780         return x.CompareTo( y ) <= 0;
 3781      }
 3782
 3783      public static Boolean operator >( PgSQLTimestampTZ x, PgSQLTimestampTZ y )
 3784      {
 03785         return !( x <= y );
 3786      }
 3787
 3788      public static Boolean operator >=( PgSQLTimestampTZ x, PgSQLTimestampTZ y )
 3789      {
 03790         return !( x < y );
 3791      }
 3792
 3793      public static PgSQLTimestampTZ operator +( PgSQLTimestampTZ timestamp, PgSQLInterval interval )
 3794      {
 03795         return timestamp.Add( interval );
 3796      }
 3797
 3798      public static PgSQLTimestampTZ operator +( PgSQLInterval interval, PgSQLTimestampTZ timestamp )
 3799      {
 03800         return timestamp.Add( interval );
 3801      }
 3802
 3803      public static PgSQLTimestampTZ operator -( PgSQLTimestampTZ timestamp, PgSQLInterval interval )
 3804      {
 03805         return timestamp.Subtract( interval );
 3806      }
 3807
 3808      public static PgSQLInterval operator -( PgSQLTimestampTZ x, PgSQLTimestampTZ y )
 3809      {
 03810         return x.Subtract( y );
 3811      }
 3812
 3813      #endregion
 3814
 3815      #region To and from string
 3816
 3817      public Int32 GetTextByteCount( IEncodingInfo encoding )
 3818      {
 03819         switch ( this._kind )
 3820         {
 3821            case PgSQLTimestamp.TSKind.Infinity:
 03822               return encoding.Encoding.GetByteCount( PgSQLDate.INFINITY );
 3823            case PgSQLTimestamp.TSKind.MinusInfinity:
 03824               return encoding.Encoding.GetByteCount( PgSQLDate.MINUS_INFINITY );
 3825            default:
 03826               return encoding.BytesPerASCIICharacter + this._date.GetTextByteCount( encoding ) + this._time.GetTextByte
 3827         }
 3828      }
 3829
 3830      public void WriteTextBytes( IEncodingInfo encoding, Byte[] array, ref Int32 offset )
 3831      {
 03832         switch ( this._kind )
 3833         {
 3834            case PgSQLTimestamp.TSKind.Infinity:
 03835               encoding.Encoding.GetBytes( PgSQLDate.INFINITY, 0, PgSQLDate.INFINITY.Length, array, offset );
 03836               break;
 3837            case PgSQLTimestamp.TSKind.MinusInfinity:
 03838               encoding.Encoding.GetBytes( PgSQLDate.MINUS_INFINITY, 0, PgSQLDate.MINUS_INFINITY.Length, array, offset )
 03839               break;
 3840            default:
 03841               this._date.WriteTextBytes( encoding, array, ref offset );
 03842               encoding.WriteASCIIByte( array, ref offset, 0x020 ); // ' '
 03843               this._time.WriteTextBytes( encoding, array, ref offset );
 3844               break;
 3845         }
 03846      }
 3847
 3848      public static PgSQLTimestampTZ ParseBinaryText( IEncodingInfo encoding, Byte[] array, ref Int32 offset, Int32 coun
 3849      {
 03850         var oldIdx = offset;
 03851         var ts = PgSQLTimestamp.ParseBinaryText( encoding, array, ref offset, count );
 03852         if ( ts.IsInfinity )
 3853         {
 03854            return Infinity;
 3855         }
 03856         else if ( ts.IsMinusInfinity )
 3857         {
 03858            return MinusInfinity;
 3859         }
 3860         else
 3861         {
 03862            count -= offset - oldIdx;
 03863            if ( count <= 0 )
 3864            {
 03865               throw new FormatException( "No room for time zone information" );
 3866            }
 03867            return new PgSQLTimestampTZ( ts.Date, new PgSQLTimeTZ( ts.Time, PgSQLTimeZone.ParseBinaryText( encoding, arr
 3868         }
 3869      }
 3870
 3871      public override String ToString()
 3872      {
 03873         switch ( this._kind )
 3874         {
 3875            case PgSQLTimestamp.TSKind.Infinity:
 03876               return PgSQLDate.INFINITY;
 3877            case PgSQLTimestamp.TSKind.MinusInfinity:
 03878               return PgSQLDate.MINUS_INFINITY;
 3879            default:
 03880               return this._date.ToString() + " " + this._time.ToString();
 3881         }
 3882      }
 3883
 3884      public static PgSQLTimestampTZ Parse( String str )
 3885      {
 03886         TryParse( str, out PgSQLTimestampTZ result, out Exception error );
 03887         if ( error != null )
 3888         {
 03889            throw error;
 3890         }
 03891         return result;
 3892      }
 3893
 3894      public static Boolean TryParse( String str, out PgSQLTimestampTZ result )
 3895      {
 3896         Exception error;
 03897         return TryParse( str, out result, out error );
 3898      }
 3899
 3900      private static Boolean TryParse( String str, out PgSQLTimestampTZ result, out Exception error )
 3901      {
 03902         if ( str == null )
 3903         {
 03904            result = default( PgSQLTimestampTZ );
 03905            error = new ArgumentNullException( "String" );
 03906         }
 3907         else
 3908         {
 03909            str = str.Trim();
 03910            error = null;
 03911            if ( String.Equals( str, PgSQLDate.INFINITY, StringComparison.OrdinalIgnoreCase ) )
 3912            {
 03913               result = Infinity;
 03914            }
 03915            else if ( String.Equals( str, PgSQLDate.MINUS_INFINITY, StringComparison.OrdinalIgnoreCase ) )
 3916            {
 03917               result = MinusInfinity;
 03918            }
 3919            else
 3920            {
 03921               var idx = str.LastIndexOf( ' ' );
 03922               if ( idx == -1 )
 3923               {
 03924                  error = new FormatException( "Failed to distinguish date and time in timestamp." );
 3925               }
 3926
 3927               PgSQLDate date; PgSQLTimeTZ time;
 03928               result = error == null && PgSQLDate.TryParse( str.Substring( 0, idx ), out date, out error ) && PgSQLTime
 03929                  new PgSQLTimestampTZ( date, time ) :
 03930                  default( PgSQLTimestampTZ );
 3931            }
 3932         }
 03933         return error == null;
 3934      }
 3935
 3936      #endregion
 3937
 3938   }
 3939
 3940
 3941}
 3942
 3943#pragma warning restore 1591
 3944
 3945public static partial class E_CBAM
 3946{
 3947
 3948
 3949   internal static IEncodingInfo EqualsOrThrow( this IEncodingInfo encoding, Byte[] array, ref Int32 offset, Byte b )
 3950   {
 3951      if ( array[offset] != b )
 3952      {
 3953         throw new FormatException( "Missing required " + (Char) b + "." );
 3954      }
 3955      offset += encoding.BytesPerASCIICharacter;
 3956      return encoding;
 3957   }
 3958
 3959   internal static Boolean TryParseOptionalNumber(
 3960      this IEncodingInfo encoding,
 3961      Byte[] array,
 3962      ref Int32 offset,
 3963      Byte prefix,
 3964      (Int32 CharCount, Boolean CharCountExactMatch) charCount,
 3965      Int32 maxOffset, // Exclusive
 3966      out Int32 parsedNumber
 3967      )
 3968   {
 3969      var oldIdx = offset;
 3970      var retVal = offset < maxOffset && encoding.ReadASCIIByte( array, ref offset ) == prefix;
 3971      if ( retVal )
 3972      {
 3973         parsedNumber = encoding.ParseInt32Textual( array, ref offset, charCount );
 3974      }
 3975      else
 3976      {
 3977         // 'Reverse back'
 3978         offset = oldIdx;
 3979         parsedNumber = 0;
 3980      }
 3981
 3982      return retVal;
 3983   }
 3984
 3985}

Methods/Properties

.cctor()
Now()
Today()
Yesterday()
Tomorrow()
.ctor(CBAM.SQL.PostgreSQL.PgSQLTimestamp/TSKind)
.ctor(CBAM.SQL.PostgreSQL.PgSQLDate,CBAM.SQL.PostgreSQL.PgSQLTimeTZ)
.ctor(CBAM.SQL.PostgreSQL.PgSQLDate)
.ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Nullable`1<CBAM.SQL.PostgreSQL.PgSQLTimeZone>)
.ctor(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Decimal,System.Nullable`1<CBAM.SQL.PostgreSQL.PgSQLTimeZone>)
DayOfYear()
Year()
Month()
Day()
DayOfWeek()
DaysSinceEra()
IsLeapYear()
LocalTime()
TimeZone()
UTCTime()
Ticks()
Microseconds()
Milliseconds()
Seconds()
Minutes()
Hours()
IsFinite()
IsInfinity()
IsMinusInfinity()
Date()
Time()
Equals(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
Equals(System.Object)
GetHashCode()
System.IComparable.CompareTo(System.Object)
CompareTo(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
AddDays(System.Int32)
AddYears(System.Int32)
AddMonths(System.Int32)
Add(CBAM.SQL.PostgreSQL.PgSQLInterval)
Subtract(CBAM.SQL.PostgreSQL.PgSQLInterval)
Subtract(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
Normalize()
AtTimeZone(CBAM.SQL.PostgreSQL.PgSQLTimeZone)
op_Explicit(System.DateTime)
op_Explicit(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_Explicit(System.DateTimeOffset)
op_Explicit(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_Equality(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_Inequality(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_LessThan(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_LessThanOrEqual(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_GreaterThan(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_GreaterThanOrEqual(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_Addition(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLInterval)
op_Addition(CBAM.SQL.PostgreSQL.PgSQLInterval,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
op_Subtraction(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLInterval)
op_Subtraction(CBAM.SQL.PostgreSQL.PgSQLTimestampTZ,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ)
GetTextByteCount(UtilPack.IEncodingInfo)
WriteTextBytes(UtilPack.IEncodingInfo,System.Byte[],System.Int32&)
ParseBinaryText(UtilPack.IEncodingInfo,System.Byte[],System.Int32&,System.Int32)
ToString()
Parse(System.String)
TryParse(System.String,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ&)
TryParse(System.String,CBAM.SQL.PostgreSQL.PgSQLTimestampTZ&,System.Exception&)