{ *********************************************************************** }
{                                                                         }
{ Delphi Visual Component Library                                         }
{                                                                         }
{ Copyright (c) 1995-2004 Borland Software Corporation                    }
{                                                                         }
{ *********************************************************************** }

unit Borland.Vcl.SqlTimSt;

// need to implement CastOLE, dispatch and stream (from Eddie?)

interface

uses
  Variants;

type

{ TSQLTimeStamp }
  TSQLTimeStamp = packed record(IComparable, IConvertible)
  strict private
    class procedure AdjustMonths(var AValue: TSQLTimeStamp; Reverse: Boolean); static;
    class procedure AdjustDays(var AValue: TSQLTimeStamp; Reverse: Boolean); static;
    class procedure AdjustHours(var AValue: TSQLTimeStamp; Reverse: Boolean); static;
    class procedure AdjustMinutes(var AValue: TSQLTimeStamp; Reverse: Boolean); static;
    class procedure AdjustSeconds(var AValue: TSQLTimeStamp; Reverse: Boolean); static;
    class procedure AdjustDate(var AValue: TSQLTimeStamp; Reverse: Boolean); static;
    class function IsTimeStampValid(const AValue: TSQLTimeStamp): Boolean; static;
    class function NDaysInMonth(const AValue: TSQLTimeStamp): Integer; static;

  public
    var
      Year: SmallInt;
      Month: Word;
      Day: Word;
      Hour: Word;
      Minute: Word;
      Second: Word;
      Fractions: LongWord;

    constructor Create(const AValue: SmallInt); overload;
    constructor Create(const AValue: Integer); overload;
    constructor Create(const AValue: TDateTime); overload;

    class function FromBytes(const AValue: TBytes): TSQLTimeStamp; static;
    class function ToBytes(const AValue: TSQLTimeStamp): TBytes; static;
    class function FromObject(AObject: TObject): TSQLTimeStamp; static;

    function DayOfWeek: Integer;
    procedure Validate;

    class function MinValue: TSQLTimeStamp; static; { 01/01/0100 12:00:00.000 AM }
    class function MaxValue: TSQLTimeStamp; static; { 12/31/9999 11:59:59.999 PM }

    class function Parse(const AText: string): TSQLTimeStamp; overload; static;
    class function Parse(const AText: string; const AProvider: IFormatProvider): TSQLTimeStamp; overload; static;
    class function TryParse(const AValue: string; var SQLTimeStamp: TSQLTimeStamp): Boolean; static;
    function ToString: System.String; overload; override;
    /// Note: format is a Delphi format string, not the CLR format string
    function ToString(const Format: string): System.String; reintroduce; overload;

    class function Compare(const Left, Right: TSQLTimeStamp): Integer; static;
    class function Equals(const Left, Right: TSQLTimeStamp): Boolean; overload; static;
    function Equals(AValue: TObject): Boolean; overload; override;

    function IsEmpty: Boolean;
    class function Empty: TSQLTimeStamp; static;

    class operator Trunc(const Value: TSQLTimeStamp): Int64;

    class operator Add(const Left, Right: TSQLTimeStamp): TSQLTimeStamp;
    class operator Subtract(const Left, Right: TSQLTimeStamp): TSQLTimeStamp;
    class operator Equal(const Left, Right: TSQLTimeStamp): Boolean;
    class operator NotEqual(const Left, Right: TSQLTimeStamp): Boolean;
    class operator LessThan(const Left, Right: TSQLTimeStamp): Boolean;
    class operator LessThanOrEqual(const Left, Right: TSQLTimeStamp): Boolean;
    class operator GreaterThan(const Left, Right: TSQLTimeStamp): Boolean;
    class operator GreaterThanOrEqual(const Left, Right: TSQLTimeStamp): Boolean;

    class operator Implicit(const AValue: Integer): TSQLTimeStamp;
    class operator Implicit(const AValue: Int64): TSQLTimeStamp;
    class operator Implicit(const AValue: System.DateTime): TSQLTimeStamp;
    class operator Implicit(const AValue: TDateTime): TSQLTimeStamp;
    class operator Implicit(const AValue: Variant): TSQLTimeStamp;
    class operator Implicit(const AValue: TSQLTimeStamp): System.DateTime;
    class operator Implicit(const AValue: TSQLTimeStamp): TDateTime;

    class operator Explicit(const AValue: TSQLTimeStamp): Integer;
    class operator Explicit(const AValue: TSQLTimeStamp): Int64;

    // IComparable
    function CompareTo(Right: TObject): System.Int32;

    // IConvertible
    function GetTypeCode: TypeCode;
    function ToBoolean(AProvider: IFormatProvider): System.Boolean;
    function ToByte(AProvider: IFormatProvider): System.Byte;
    function ToChar(AProvider: IFormatProvider): System.Char;
    function ToDateTime(AProvider: IFormatProvider): System.DateTime;
    function ToDecimal(AProvider: IFormatProvider): System.Decimal;
    function ToDouble(AProvider: IFormatProvider): System.Double;
    function ToInt16(AProvider: IFormatProvider): System.Int16;
    function ToInt32(AProvider: IFormatProvider): System.Int32;
    function ToInt64(AProvider: IFormatProvider): System.Int64;
    function ToSByte(AProvider: IFormatProvider): System.SByte;
    function ToSingle(AProvider: IFormatProvider): System.Single;
    function ToString(AProvider: IFormatProvider): System.String; overload;
    function ToType(AType: System.Type; AProvider: IFormatProvider): System.Object;
    function ToUInt16(AProvider: IFormatProvider): System.UInt16;
    function ToUInt32(AProvider: IFormatProvider): System.UInt32;
    function ToUInt64(AProvider: IFormatProvider): System.UInt64;
  end;

{ TSQLTimeStamp variant creation utils }

procedure VarSQLTimeStampCreate(var aDest: Variant; const ASQLTimeStamp: TSQLTimeStamp); overload;
function VarSQLTimeStampCreate: Variant; overload;
function VarSQLTimeStampCreate(const AValue: string): Variant; overload;
function VarSQLTimeStampCreate(const AValue: TDateTime): Variant; overload;
function VarSQLTimeStampCreate(const ASQLTimeStamp: TSQLTimeStamp): Variant; overload;
function VarSQLTimeStamp: TVarType;
function VarIsSQLTimeStamp(const aValue: Variant): Boolean; overload;

{ TSQLTimeStamp conversion support }

// converts Variant SQLTimeStamp to record TSQLTimeStamp
function VarToSQLTimeStamp(const aValue: Variant): TSQLTimeStamp; deprecated;
function SQLTimeStampToStr(const Format: string; DateTime: TSQLTimeStamp): string; deprecated;
function SQLDayOfWeek(const DateTime: TSQLTimeStamp): Integer; deprecated;
function DateTimeToSQLTimeStamp(const DateTime: TDateTime): TSQLTimeStamp; deprecated;
function SQLTimeStampToDateTime(const DateTime: TSQLTimeStamp): TDateTime; deprecated;
function TryStrToSQLTimeStamp(const S: string; var TimeStamp: TSQLTimeStamp) : Boolean; deprecated;
function StrToSQLTimeStamp(const S: string): TSQLTimeStamp; deprecated;

{ utility }

procedure CheckSqlTimeStamp(const ASQLTimeStamp: TSQLTimeStamp); deprecated;
function NullSQLTimeStamp: TSQLTimeStamp; deprecated;

implementation

{$RANGECHECKS ON}
{$OVERFLOWCHECKS ON}
{$FINITEFLOAT ON}

uses
  DBConsts, SysUtils, DateUtils, Math, TypInfo, Classes, Types;

{ TSQLTImeStamp }

function TSQLTimeStamp.IsEmpty: Boolean;
begin
  Result := Self = TSQLTimeStamp.Empty;
end;

constructor TSQLTimeStamp.Create(const AValue: Integer);
begin
  inherited Create;
  Day := AValue;
end;

constructor TSQLTimeStamp.Create(const AValue: SmallInt);
begin
  inherited Create;
  Day := AValue;
end;

constructor TSQLTimeStamp.Create(const AValue: TDateTime);
begin
  inherited Create;
  Self := AValue;
end;

class function TSQLTimeStamp.FromObject(AObject: TObject): TSQLTimeStamp;
var
  LDouble: Double;
begin
  if AObject = nil then
    Result := Empty
  else if AObject is TSQLTimeStamp then
    Result := TSQLTimeStamp(AObject)
  else if AObject is DateTime then
    Result := DateTime(AObject)
  else
    Result := TDateTime(Variant(AObject));
end;

function TSQLTimeStamp.CompareTo(Right: TObject): System.Int32;
var
  LRight: TSQLTimeStamp;
begin
  if Right is TSQLTimeStamp then
    LRight := Right as TSQLTimeStamp
  else
    LRight := FromObject(Right);

  Result := Compare(Self, LRight);
end;

function TSQLTimeStamp.GetTypeCode: TypeCode;
begin
  Result := System.TypeCode.Object;
end;

function TSQLTimeStamp.ToBoolean(AProvider: IFormatProvider): System.Boolean;
begin
  Result := not IsZero(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToByte(AProvider: IFormatProvider): System.Byte;
begin
  Result := Trunc(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToChar(AProvider: IFormatProvider): System.Char;
begin
  Result := ToString(AProvider)[1];
end;

function TSQLTimeStamp.ToDateTime(AProvider: IFormatProvider): System.DateTime;
begin
  if IsEmpty then
    Result := DateTime.FromOADate(MinValue.ToDouble(AProvider))
  else
    Result := System.DateTime.Create(Year, Month, Day, Hour, Minute, Second, Fractions);
end;

function TSQLTimeStamp.ToDecimal(AProvider: IFormatProvider): System.Decimal;
begin
  Result := System.Decimal.Create(ToDateTime(AProvider).ToOADate);
end;

function TSQLTimeStamp.ToDouble(AProvider: IFormatProvider): System.Double;
begin
  Result := System.Convert.ToDouble(ToDateTime(AProvider).ToOADate);
end;

function TSQLTimeStamp.ToInt16(AProvider: IFormatProvider): System.Int16;
begin
  Result := Trunc(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToInt32(AProvider: IFormatProvider): System.Int32;
begin
  Result := Trunc(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToInt64(AProvider: IFormatProvider): System.Int64;
begin
  Result := Trunc(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToSByte(AProvider: IFormatProvider): System.SByte;
begin
  Result := Trunc(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToSingle(AProvider: IFormatProvider): System.Single;
begin
  Result := ToDouble(AProvider);
end;

function TSQLTimeStamp.ToString(AProvider: IFormatProvider): System.String;
begin
  if (Hour + Minute + Second + Fractions) > 0 then
    Result := DateTimeToStr(Self, AProvider)
  else
    // Ignore time if it is zero for backwards compatibility
    Result := ToDateTime(AProvider).ToString(AProvider);
end;

function TSQLTimeStamp.ToType(AType: System.Type; AProvider: IFormatProvider): System.Object;
begin
  Result := Self;
  case System.Type.GetTypeCode(AType) of
    TypeCode.Boolean:
      Result := TObject(ToBoolean(AProvider));
    TypeCode.Byte:
      Result := TObject(ToByte(AProvider));
    TypeCode.Char:
      Result := TObject(ToChar(AProvider));
    TypeCode.DateTime:
      Result := TObject(ToDateTime(AProvider));
    TypeCode.Decimal:
      Result := TObject(ToDecimal(AProvider));
    TypeCode.Double:
      Result := TObject(ToDouble(AProvider));
    TypeCode.Empty:
      Result := nil;
    TypeCode.Int16:
      Result := TObject(ToInt16(AProvider));
    TypeCode.Int32:
      Result := TObject(ToInt32(AProvider));
    TypeCode.Int64:
      Result := TObject(ToInt64(AProvider));
    TypeCode.SByte:
      Result := TObject(ToSByte(AProvider));
    TypeCode.Single:
      Result := TObject(ToSingle(AProvider));
    TypeCode.String:
      Result := TObject(ToString(AProvider));
    TypeCode.UInt16:
      Result := TObject(ToUInt16(AProvider));
    TypeCode.UInt32:
      Result := TObject(ToUInt32(AProvider));
    TypeCode.UInt64:
      Result := TObject(ToUInt64(AProvider));
    TypeCode.Object:
      if not AType.IsInstanceOfType(Self) then
        raise System.InvalidCastException.Create;
  else
    raise System.InvalidCastException.Create;
  end;
end;

function TSQLTimeStamp.ToUInt16(AProvider: IFormatProvider): System.UInt16;
begin
  Result := Trunc(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToUInt32(AProvider: IFormatProvider): System.UInt32;
begin
  Result := Trunc(ToDouble(AProvider));
end;

function TSQLTimeStamp.ToUInt64(AProvider: IFormatProvider): System.UInt64;
begin
  Result := Convert.ToUInt64(ToDateTime(AProvider));
end;

class function TSQLTimeStamp.FromBytes(const AValue: TBytes): TSQLTimeStamp;
begin
  with Result do
  begin
    Year := System.BitConverter.ToInt16(AValue, 0);
    Month := System.BitConverter.ToUInt16(AValue, SizeOf(Year));
    Day := System.BitConverter.ToUInt16(AValue, SizeOf(Year) + SizeOf(Month));
    Hour := System.BitConverter.ToUInt16(AValue, SizeOf(Year) +
      SizeOf(Month) + SizeOf(Day));
    Minute := System.BitConverter.ToUInt16(AValue, SizeOf(Year) +
      SizeOf(Month) + SizeOf(Day) + SizeOf(Hour));
    Second := System.BitConverter.ToUInt16(AValue, SizeOf(Year) +
      SizeOf(Month) + SizeOf(Day) + SizeOf(Hour) + SizeOf(Minute));
    Fractions := System.BitConverter.ToUInt32(AValue, SizeOf(Year) +
      SizeOf(Month) + SizeOf(Day) + SizeOf(Hour) + SizeOf(Minute) + SizeOf(Second));
  end;
end;

class function TSQLTimeStamp.ToBytes(const AValue: TSQLTimeStamp): TBytes;
begin
  SetLength(Result, SizeOf(TSQLTimeStamp));
  System.Array(System.BitConverter.GetBytes(AValue.Year)).CopyTo(Result, 0);
  System.Array(System.BitConverter.GetBytes(AValue.Month)).CopyTo(
    Result, sizeof(AValue.Year));
  System.Array(System.BitConverter.GetBytes(AValue.Day)).CopyTo(
    Result, Sizeof(AValue.Year) + Sizeof(AValue.Month));
  System.Array(System.BitConverter.GetBytes(AValue.Hour)).CopyTo(
    Result, Sizeof(AValue.Year) + Sizeof(AValue.Month) +
      SizeOf(AValue.Day));
  System.Array(System.BitConverter.GetBytes(AValue.Minute)).CopyTo(
    Result, Sizeof(AValue.Year) + Sizeof(AValue.Month) +
      SizeOf(AValue.Day) + SizeOf(AValue.Hour));
  System.Array(System.BitConverter.GetBytes(AValue.Second)).CopyTo(
    Result, Sizeof(AValue.Year) + Sizeof(AValue.Month) +
      SizeOf(AValue.Day) + SizeOf(AValue.Hour) +
      SizeOf(AValue.Minute));
  System.Array(System.BitConverter.GetBytes(AValue.Fractions)).CopyTo(
    Result, Sizeof(AValue.Year) + Sizeof(AValue.Month) +
      SizeOf(AValue.Day) + SizeOf(AValue.Hour) +
      SizeOf(AValue.Minute) + SizeOf(AValue.Second));
end;

function TSQLTimeStamp.DayOfWeek: Integer;
begin
  Result := SysUtils.DayOfWeek(Self);
end;

class function TSQLTimeStamp.Empty: TSQLTimeStamp;
begin
  // Do nothing - value types are always zero initialized
end;

procedure TSQLTimeStamp.Validate;
begin  // only check if not an empty timestamp
  if Year + Month + Day + Hour + Minute + Second > 0 then
  begin
    if Year + Month + Day > 0 then
      if (Year = 0) or (Month = 0) or (Day =0) or (Month > 31) or
         (Day > DaysInAMonth(Year, Month)) then
        raise EConvertError.Create(SInvalidSQLTimeStamp);
    if Hour + Minute + Second > 0 then
      if (Hour > 23) or (Second > 59) or (Minute > 59) then
        raise EConvertError.Create(SInvalidSQLTimeStamp);
  end;
end;

class function TSQLTimeStamp.MinValue: TSQLTimeStamp;
begin
  with Result do
  begin
    Year := 100;
    Month := 1;
    Day := 1;
    Hour := 0;
    Minute := 0;
    Second := 0;
    Fractions := 0;
  end;
end;

class function TSQLTimeStamp.MaxValue: TSQLTimeStamp;
begin
  with Result do
  begin
    Year := 9999;
    Month := 12;
    Day := 31;
    Hour := 23;
    Minute := 59;
    Second := 59;
    Fractions := 999;
  end;
end;

class function TSQLTimeStamp.Parse(const AText: string): TSQLTimeStamp;
begin
  Result := Parse(AText, nil);
end;

                                         
// Need to detemine if we can use any info from the IFormatProvider
// in our conversion from string
class function TSQLTimeStamp.Parse(const AText: string; const AProvider: IFormatProvider): TSQLTimeStamp;
begin
  if not TSQLTimeStamp.TryParse(AText, Result) then
    raise EConvertError.Create(SCouldNotParseTimeStamp);
end;

class function TSQLTimeStamp.TryParse(const AValue: string; var SQLTimeStamp: TSQLTimeStamp): Boolean;
var
  LDateTime: TDateTime;
begin
  Result := TryStrToDateTime(AValue, LDateTime);
  if Result then
  begin
    SQLTimeStamp := LDateTime;
    Result := IsTimeStampValid(SQLTimeStamp);
  end;
  if not Result then
    SQLTimeStamp := TSQLTimeStamp.Empty;
end;

function TSQLTimeStamp.ToString: System.String;
begin
  Result := ToString(nil);
end;

function TSQLTimeStamp.ToString(const Format: string): System.String;
begin
  if Format = '' then
    Result := ToString(nil)
  else
    DateTimeToString(Result, Format, Self);
end;

class function TSQLTimeStamp.Compare(const Left, Right: TSQLTimeStamp): Integer;
begin
  Result := Left.Year - Right.Year;
  if Result <> 0 then
    Exit;
  Result := Left.Month - Right.Month;
  if Result <> 0 then
    Exit;
  Result := Left.Day - Right.Day;
  if Result <> 0 then
    Exit;
  Result := Left.Hour - Right.Hour;
  if Result <> 0 then
    Exit;
  Result := Left.Minute - Right.Minute;
  if Result <> 0 then
    Exit;
  Result := Left.Second - Right.Second;
  if Result <> 0 then
    Exit;
  Result := Left.Fractions - Right.Fractions;
end;

class function TSQLTimeStamp.Equals(const Left, Right: TSQLTimeStamp): Boolean;
begin
  Result := Left = Right;
end;

function TSQLTimeStamp.Equals(AValue: TObject): Boolean;
begin
  Result := (AValue is TSQLTimeStamp) and (Self = TSQLTimeStamp(AValue));
end;

const
  IncrementAmount: array[Boolean] of Integer = (1, -1);
  AdjustMonthAmt: array[Boolean] of Integer = (-12, 12);
  AdjustHoursAmt: array[Boolean] of Integer = (-24, 24);
  AdjustMinutesAmt: array[Boolean] of Integer = (-60, 60);
  AdjustSecondsAmt: array[Boolean] of Integer = (-60, 60);

class function TSQLTimeStamp.NDaysInMonth(const AValue: TSQLTimeStamp): Integer;
begin
  Result := DaysInAMonth(AValue.Year, AValue.Month);
end;

// Adjust for Month > 12 or < 1
class procedure TSQLTimeStamp.AdjustMonths(var AValue: TSQLTimeStamp; Reverse: Boolean);
begin
  while (AValue.Month < 1) or (AValue.Month > 12) do
  begin
    Inc(AValue.Year, IncrementAmount[Reverse]);
    Inc(AValue.Month, AdjustMonthAmt[Reverse]);
  end;
end;

// Adjust for Days > 28/30/31 or < 1
class procedure TSQLTimeStamp.AdjustDays(var AValue: TSQLTimeStamp; Reverse: Boolean);
var
  Days: Integer;
begin
  Days := NDaysInMonth(AValue);
  while (AValue.Day < 1) or (AValue.Day > Days) do
  begin
    Inc(AValue.Month, IncrementAmount[Reverse]);
    if Reverse then
      Dec(AValue.Day, Days)
    else
      Inc(AValue.Day, Days);
    AdjustMonths(AValue, Reverse);
    Days := NDaysInMonth(AValue);
  end;
end;

// Adjust for Hours over 23 or less than 0
class procedure TSQLTimeStamp.AdjustHours(var AValue: TSQLTimeStamp; Reverse: Boolean);
begin
  while (AValue.Hour > 23) or (Integer(AValue.Hour) < 0) do
  begin
    Inc(AValue.Day, IncrementAmount[Reverse]);
    Inc(AValue.Hour, AdjustHoursAmt[Reverse]);
    AdjustDays(Avalue, Reverse);
  end;
end;

// Adjust Minutes for Hours over 59 or less than 0
class procedure TSQLTimeStamp.AdjustMinutes(var AValue: TSQLTimeStamp; Reverse: Boolean);
begin
  while (AValue.Minute > 59) or (Integer(AValue.Minute) < 0) do
  begin
    Inc(AValue.Hour, IncrementAmount[Reverse]);
    Inc(AValue.Minute, AdjustMinutesAmt[Reverse]);
    AdjustHours(AValue, Reverse);
  end;
end;

// Adjust Seconds for Hours over 59 or less than 0
class procedure TSQLTimeStamp.AdjustSeconds(var AValue: TSQLTimeStamp; Reverse: Boolean);
begin
  while (AValue.Second > 59) or (Integer(AValue.Second) < 0) do
  begin
    Inc(AValue.Minute, IncrementAmount[Reverse]);
    Inc(AValue.Second, AdjustSecondsAmt[Reverse]);
    AdjustMinutes(AValue, Reverse);
  end;
end;

class procedure TSQLTimeStamp.AdjustDate(var AValue: TSQLTimeStamp; Reverse: Boolean);
begin
  if Reverse then
  begin
    AdjustSeconds(AValue, Reverse);
{
    AdjustMinutes(AValue, Reverse);
    AdjustHours(AValue, Reverse);
    AdjustDays(AValue, Reverse);
    AdjustMonths(AValue, Reverse);
}
  end else
  begin
    AdjustMonths(AValue, Reverse);
    AdjustDays(AValue, Reverse);
    AdjustHours(AValue, Reverse);
    AdjustMinutes(AValue, Reverse);
    AdjustSeconds(AValue, Reverse);
  end;
end;

class operator TSQLTimeStamp.Trunc(const Value: TSQLTimeStamp): Int64;
begin
  Result := Trunc(TDateTime(Value));
end;

class operator TSQLTimeStamp.Add(const Left, Right: TSQLTimeStamp): TSQLTimeStamp;
begin
  with Result do
  begin
    Year := Left.Year + Right.Year;
    Month := Left.Month + Right.Month;
    Day := Left.Day + Right.Day;
    Hour := Left.Hour + Right.Hour;
    Minute := Left.Minute + Right.Minute;
    Second := Left.Second + Right.Second;
    Fractions := Left.Fractions + Right.Fractions;
  end;
  AdjustDate(Result, False);
end;

class operator TSQLTimeStamp.Subtract(const Left, Right: TSQLTimeStamp): TSQLTimeStamp;
begin
  with Result do
  begin
    Year := Left.Year - Right.Year;
    Month := Left.Month - Right.Month;
    Day := Left.Day - Right.Day;
    Hour := Left.Hour - Right.Hour;
    Minute := Left.Minute - Right.Minute;
    Second := Left.Second - Right.Second;
    Fractions := Left.Fractions - Right.Fractions;
  end;
  AdjustDate(Result, True);
end;

class operator TSQLTimeStamp.Equal(const Left, Right: TSQLTimeStamp): Boolean;
begin
  Result := False;
  if Left.Year <> Right.Year then
    Exit;
  if Left.Month <> Right.Month then
    Exit;
  if Left.Day <> Right.Day then
    Exit;
  if Left.Hour <> Right.Hour then
    Exit;
  if Left.Minute <> Right.Minute then
    Exit;
  if Left.Second <> Right.Second then
    Exit;
  if Left.Fractions <> Right.Fractions then
    Exit;
  Result := True;
end;

class operator TSQLTimeStamp.NotEqual(const Left, Right: TSQLTimeStamp): Boolean;
begin
  Result := not (Left = Right);
end;

class operator TSQLTimeStamp.LessThan(const Left, Right: TSQLTimeStamp): Boolean;
begin
  Result := TSQLTimeStamp.Compare(Left, Right) < 0;
end;

class operator TSQLTimeStamp.LessThanOrEqual(const Left, Right: TSQLTimeStamp): Boolean;
begin
  Result := TSQLTimeStamp.Compare(Left, Right) <= 0;
end;

class operator TSQLTimeStamp.GreaterThan(const Left, Right: TSQLTimeStamp): Boolean;
begin
  Result := TSQLTimeStamp.Compare(Left, Right) > 0;
end;

class operator TSQLTimeStamp.GreaterThanOrEqual(const Left, Right: TSQLTimeStamp): Boolean;
begin
  Result := TSQLTimeStamp.Compare(Left, Right) >= 0;
end;

class operator TSQLTimeStamp.Implicit(const AValue: Integer): TSQLTimeStamp;
begin
  Result := TDateTime(AValue);
end;

class operator TSQLTimeStamp.Implicit(const AValue: Int64): TSQLTimeStamp;
begin
  Result := TDateTime(AValue);
end;

class operator TSQLTimeStamp.Implicit(const AValue: System.DateTime): TSQLTimeStamp;
begin
  with Result do
  begin
    Year := AValue.Year;
    Month := AValue.Month;
    Day := AValue.Day;
    Hour := AValue.Hour;
    Minute := AValue.Minute;
    Second := AValue.Second;
    Fractions := AValue.Millisecond;
  end;
end;

class operator TSQLTimeStamp.Implicit(const AValue: TDateTime): TSQLTimeStamp;
var
  LYear, LFractions: Word;
begin
  with Result do
  begin
    TDateTime.DecodeDateTime(AValue, LYear, Month, Day, Hour, Minute, Second, LFractions);
    Year := LYear;
    Fractions := LFractions;
  end;
end;

class operator TSQLTimeStamp.Implicit(const AValue: Variant): TSQLTimeStamp;
var
  LData: TObject;
begin
  LData := TObject(AValue);
  if LData is TSQLTimeStamp then
    Result := TSQLTimeStamp(LData)
  else
    if not TryParse(AValue, Result) then
      raise System.InvalidCastException.Create;
end;

class operator TSQLTimeStamp.Implicit(const AValue: TSQLTimeStamp): System.DateTime;
begin
  with AValue do
    Result := System.DateTime.Create(Year, Month, Day, Hour, Minute, Second, Fractions);
end;

class operator TSQLTimeStamp.Implicit(const AValue: TSQLTimeStamp): TDateTime;
begin
  with AValue do
    if IsEmpty then
      Result := 0
    else
      Result := TDateTime.Create(Year, Month, Day, Hour, Minute, Second, Fractions);
end;

class operator TSQLTimeStamp.Explicit(const AValue: TSQLTimeStamp): Integer;
begin
  Result := Trunc(AValue);
end;

class operator TSQLTimeStamp.Explicit(const AValue: TSQLTimeStamp): Int64;
begin
  Result := Trunc(AValue);
end;

class function TSQLTimeStamp.IsTimeStampValid(const AValue: TSQLTimeStamp): Boolean;
begin
  with AValue do
    if (Month > 12) or (Day > DaysInAMonth(Year, Month)) or (Hour > 23) or (Minute > 59) or (Second > 59) then
      Result := False
    else
      Result := True;
end;

{ SQLTimeStamp variant create utils }

function VarSQLTimeStampCreate(const AValue: string): Variant; overload;
begin
  Result := Variant(TSQLTimeStamp.Parse(AValue));
end;

function VarSQLTimeStampCreate(const AValue: TDateTime): Variant; overload;
begin
  Result := Variant(TSQLTimeStamp.Create(AValue));
end;

procedure VarSQLTimeStampCreate(var aDest: Variant; const ASQLTimeStamp: TSQLTimeStamp);
begin
  aDest :=  Variant(ASQLTimeStamp);
end;

function VarSQLTimeStampCreate: Variant;
begin
  VarSQLTimeStampCreate(Result, TSQLTimeStamp.Empty);
end;

function VarSQLTimeStampCreate(const ASQLTimeStamp: TSQLTimeStamp): Variant;
begin
  VarSQLTimeStampCreate(Result, ASQLTimeStamp);
end;

var
  FVarType: TVarType;

function VarSQLTimeStamp: TVarType;
begin
  Result := FVarType;
end;

function VarIsSQLTimeStamp(const aValue: Variant): Boolean;
begin
  Result := TObject(aValue) is TSQLTimeStamp;
end;

function VarToSQLTimeStamp(const AValue: Variant): TSQLTimeStamp;
begin
  Result := AValue;
end;

{ SQLTimeStamp to string conversion }

function SQLTimeStampToStr(const Format: string; DateTime: TSQLTimeStamp): string;
begin
  Result := DateTime.ToString(Format);
end;

function TryStrToSQLTimeStamp(const S: string; var TimeStamp: TSQLTimeStamp): Boolean;
begin
  Result := TSQLTimeStamp.TryParse(S, TimeStamp);
end;

function StrToSQLTimeStamp(const S: string): TSQLTimeStamp;
begin
  Result := TSQLTimeStamp.Parse(S);
end;

function DateTimeToSQLTimeStamp(const DateTime: TDateTime): TSQLTimeStamp;
begin
  Result := DateTime;
end;

function SQLTimeStampToDateTime(const DateTime: TSQLTimeStamp): TDateTime;
begin
  Result := DateTime;
end;

function SQLDayOfWeek(const DateTime: TSQLTimeStamp): integer;
begin
  Result := DateTime.DayOfWeek;
end;

procedure CheckSqlTimeStamp(const ASQLTimeStamp: TSQLTimeStamp);
begin
  ASQLTimeStamp.Validate;
end;

function NullSQLTimeStamp: TSQLTimeStamp;
begin
  Result := TSQLTimeStamp.Empty;
end;

initialization
  FVarType := RegisterCustomVariantType(TypeOf(TSQLTimeStamp));
end.
