ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/fbintf/IBUtils.pas
(Generate patch)

Comparing ibx/trunk/fbintf/IBUtils.pas (file contents):
Revision 265 by tony, Sat Dec 8 11:22:27 2018 UTC vs.
Revision 354 by tony, Sat Oct 23 14:32:11 2021 UTC

# Line 39 | Line 39 | unit IBUtils;
39   {$IFDEF FPC}
40   {$Mode Delphi}
41   {$codepage UTF8}
42 {$define HASREQEX}
42   {$ENDIF}
43  
44 + { $IF declared(CompilerVersion) and (CompilerVersion >= 22)}
45 + { $define HASDELPHIREQEX}
46 + { $IFEND}
47  
48   interface
49  
# Line 258 | Line 260 | type
260    sqltPlaceholder,
261    sqltSingleQuotes,
262    sqltDoubleQuotes,
263 +  sqltBackslash,
264    sqltComma,
265    sqltPeriod,
266    sqltEquals,
# Line 279 | Line 282 | type
282    sqltOpenBracket,
283    sqltCloseBracket,
284    sqltPipe,
285 +  sqltMinus,
286    sqltConcatSymbol,
287    sqltLT,
288    sqltGT,
# Line 592 | Line 596 | type
596      function TokenFound(var token: TSQLTokens): boolean; override;
597    end;
598  
599 +    { TSQLParamProcessor }
600 +
601 +  TSQLParamProcessor = class(TSQLwithNamedParamsTokeniser)
602 +  private
603 +  const
604 +    sIBXParam = 'IBXParam';  {do not localize}
605 +  private
606 +    FInString: AnsiString;
607 +    FIndex: integer;
608 +    function DoExecute(GenerateParamNames: boolean;
609 +        var slNames: TStrings): AnsiString;
610 +  protected
611 +    function GetChar: AnsiChar; override;
612 +  public
613 +    class function Execute(sSQL: AnsiString; GenerateParamNames: boolean;
614 +        var slNames: TStrings): AnsiString;
615 +  end;
616 +
617 +
618   function Max(n1, n2: Integer): Integer;
619   function Min(n1, n2: Integer): Integer;
620   function RandomString(iLength: Integer): AnsiString;
# Line 613 | Line 636 | function ParseConnectString(ConnectStrin
636                var PortNo: AnsiString): boolean;
637   function GetProtocol(ConnectString: AnsiString): TProtocolAll;
638  
639 + {$IF declared(TFormatSettings)}
640 + function ParseDateTimeTZString(aDateTimeStr: Ansistring; var aDateTime: TDateTime;
641 +              var aTimezone: AnsiString; aFormatSettings: TFormatSettings; TimeOnly: boolean=false): boolean; overload;
642 + {$IFEND}
643 + function ParseDateTimeTZString(aDateTimeStr: Ansistring; var aDateTime: TDateTime;
644 +              var aTimezone: AnsiString; TimeOnly: boolean=false): boolean;  overload;
645 + procedure FBDecodeTime(aTime: TDateTime; var Hour, Minute, Second: word; var DeciMillisecond: cardinal);
646 + function FBEncodeTime(Hour, Minute, Second, DeciMillisecond: cardinal): TDateTime;
647 + function FBFormatDateTime(fmt: AnsiString; aDateTime: TDateTime): AnsiString;
648 + function FormatTimeZoneOffset(EffectiveTimeOffsetMins: integer): AnsiString;
649 + function DecodeTimeZoneOffset(TZOffset: AnsiString; var dstOffset: integer): boolean;
650 + function StripLeadingZeros(Value: AnsiString): AnsiString;
651 + function TryStrToNumeric(S: Ansistring; out Value: int64; out scale: integer): boolean;
652 + function NumericToDouble(aValue: Int64; aScale: integer): double;
653 +
654 +
655   implementation
656  
657 < uses FBMessages
657 > uses FBMessages, Math
658  
659 < {$IFDEF HASREQEX}
659 > {$IFDEF FPC}
660   ,RegExpr
661 + {$ELSE}
662 + {$IF declared(CompilerVersion) and (CompilerVersion >= 22)}
663 + , RegularExpressions
664 + {$IFEND}
665   {$ENDIF};
666  
667 +
668   function Max(n1, n2: Integer): Integer;
669   begin
670    if (n1 > n2) then
# Line 716 | Line 760 | end;
760  
761   function QuoteIdentifier(Dialect: Integer; Value: AnsiString): AnsiString;
762   begin
763 +  Value := TrimRight(Value);
764    if Dialect = 1 then
765 <    Value := AnsiUpperCase(Trim(Value))
765 >    Value := AnsiUpperCase(Value)
766    else
767      Value := '"' + StringReplace (Value, '""', '"', [rfReplaceAll]) + '"';
768    Result := Value;
# Line 737 | Line 782 | begin
782    Result := true;
783   end;
784  
785 + function SchemeToProtocol(scheme: AnsiString): TProtocolAll;
786 + begin
787 +  scheme := AnsiUpperCase(scheme);
788 +  if scheme = 'INET' then
789 +    Result := inet
790 +  else
791 +  if scheme = 'INET4' then
792 +    Result := inet4
793 +  else
794 +  if scheme = 'INET6' then
795 +    Result := inet6
796 +  else
797 +  if scheme = 'XNET' then
798 +    Result := xnet
799 +  else
800 +  if scheme = 'WNET' then
801 +    Result := wnet
802 + end;
803 +
804   {Extracts the Database Connect string from a Create Database Statement}
805  
806 < {$IFDEF HASREQEX}
806 > {$IF declared(TRegexpr)}
807   function ExtractConnectString(const CreateSQL: AnsiString;
808    var ConnectString: AnsiString): boolean;
809   var RegexObj: TRegExpr;
# Line 762 | Line 826 | function ParseConnectString(ConnectStrin
826    DatabaseName: AnsiString; var Protocol: TProtocolAll; var PortNo: AnsiString
827    ): boolean;
828  
765  function GetProtocol(scheme: AnsiString): TProtocolAll;
766  begin
767    scheme := AnsiUpperCase(scheme);
768    if scheme = 'INET' then
769      Result := inet
770    else
771    if scheme = 'INET4' then
772      Result := inet4
773    else
774    if scheme = 'INET6' then
775      Result := inet6
776    else
777    if scheme = 'XNET' then
778      Result := xnet
779    else
780    if scheme = 'WNET' then
781      Result := wnet
782  end;
783
829   var RegexObj: TRegExpr;
830   begin
831    ServerName := '';
# Line 796 | Line 841 | begin
841      if Result then
842      begin
843        {URL type connect string}
844 <      Protocol := GetProtocol(RegexObj.Match[1]);
844 >      Protocol := SchemeToProtocol(RegexObj.Match[1]);
845        ServerName := RegexObj.Match[2];
846        if RegexObj.MatchLen[3] > 0 then
847          PortNo := system.Copy(ConnectString,RegexObj.MatchPos[3]+1,RegexObj.MatchLen[3]-1);
# Line 811 | Line 856 | begin
856        Result := RegexObj.Exec(ConnectString);
857        if Result then
858        begin
859 <        Protocol := GetProtocol(RegexObj.Match[1]);
859 >        Protocol := SchemeToProtocol(RegexObj.Match[1]);
860          DatabaseName := RegexObj.Match[2];
861        end
862        else
# Line 860 | Line 905 | begin
905    end;
906   end;
907  
908 < function GetProtocol(ConnectString: AnsiString): TProtocolAll;
909 < var ServerName,
910 <    DatabaseName: AnsiString;
911 <    PortNo: AnsiString;
908 > {$ELSE}
909 > {$IF declared(TRegex)}
910 > function ExtractConnectString(const CreateSQL: AnsiString;
911 >  var ConnectString: AnsiString): boolean;
912 > var Regex: TRegEx;
913 >    Match: TMatch;
914   begin
915 <  ParseConnectString(ConnectString,ServerName,DatabaseName,Result,PortNo);
915 >  Regex := TRegEx.Create('^ *CREATE +(DATABASE|SCHEMA) +''(.*)''',[roIgnoreCase]);
916 >  {extact database file spec}
917 >  Match := Regex.Match(CreateSQL);
918 >  Result := Match.Success and (Match.Groups.Count = 3);
919 >  if Result then
920 >    ConnectString := Match.Groups[2].Value;
921   end;
922  
923 + function ParseConnectString(ConnectString: AnsiString; var ServerName,
924 +  DatabaseName: AnsiString; var Protocol: TProtocolAll; var PortNo: AnsiString
925 +  ): boolean;
926 +
927 + var Regex: TRegEx;
928 +    Match: TMatch;
929 + begin
930 +  ServerName := '';
931 +  DatabaseName := ConnectString;
932 +  PortNo := '';
933 +  Protocol := unknownProtocol;
934 +  {extact database file spec}
935 +  Match := Regex.Match(ConnectString,'^([a-zA-Z46]+)://([a-zA-Z0-9\-\.]*)(|:[0-9a-zA-Z\-]+)/(.*)$',[roIgnoreCase]);
936 +  Result := Match.Success and (Match.Groups.Count = 5);
937 +  if Result then
938 +  begin
939 +    {URL type connect string}
940 +    Protocol := SchemeToProtocol(Match.Groups[1].Value);
941 +    ServerName := Match.Groups[2].Value;
942 +    PortNo := Match.Groups[3].Value;
943 +    DatabaseName := Match.Groups[4].Value;
944 +    if ServerName = '' then
945 +      DatabaseName := '/' + DatabaseName;
946 +  end
947 +  else
948 +  begin
949 +    {URL type connect string - local loop}
950 +    Match := Regex.Match(ConnectString,'^([a-zA-Z46]+)://(.*)$',[roIgnoreCase]);
951 +    Result := Match.Success and (Match.Groups.Count = 3);
952 +    if Result then
953 +    begin
954 +      Protocol := SchemeToProtocol(Match.Groups[1].Value);
955 +      DatabaseName := Match.Groups[2].Value;
956 +    end
957 +    else
958 +    begin
959 +      Match := Regex.Match(ConnectString,'^([a-zA-Z]:\\.*)',[roIgnoreCase]);
960 +      Result := Match.Success;
961 +      if Result then
962 +        Protocol := Local {Windows with leading drive ID}
963 +      else
964 +      begin
965 +        Match := Regex.Match(ConnectString,'^([a-zA-Z0-9\-\.]+)(|/[0-9a-zA-Z\-]+):(.*)$',[roIgnoreCase]);
966 +        Result := Match.Success and (Match.Groups.Count = 4);
967 +        if Result then
968 +        begin
969 +          {Legacy TCP Format}
970 +          ServerName := Match.Groups[1].Value;
971 +          PortNo := Match.Groups[2].Value;
972 +          DatabaseName := Match.Groups[3].Value;
973 +          Protocol := TCP;
974 +        end
975 +        else
976 +        begin
977 +          Match := Regex.Match(ConnectString,'^\\\\([a-zA-Z0-9\-\.]+)(|@[0-9a-zA-Z\-]+)\\(.*)$',[roIgnoreCase]);
978 +          Result := Match.Success and (Match.Groups.Count = 4);
979 +          if Result then
980 +          begin
981 +            {Netbui}
982 +            ServerName := Match.Groups[1].Value;
983 +            PortNo := Match.Groups[2].Value;
984 +            DatabaseName := Match.Groups[3].Value;
985 +            Protocol := NamedPipe
986 +          end
987 +          else
988 +          begin
989 +            Result := true;
990 +            Protocol := Local; {Assume local}
991 +          end;
992 +        end;
993 +      end;
994 +    end;
995 +  end;
996 + end;
997   {$ELSE}
998 < {cruder version of above for Delphi. Older versions lack regular expression
998 > {cruder version of above for Delphi < XE. Older versions lack regular expression
999   handling.}
1000   function ExtractConnectString(const CreateSQL: AnsiString;
1001    var ConnectString: AnsiString): boolean;
# Line 890 | Line 1016 | begin
1016    end;
1017   end;
1018  
893 function GetProtocol(ConnectString: AnsiString): TProtocolAll;
894 begin
895  Result := unknownProtocol; {not implemented for Delphi}
896 end;
897
1019   function ParseConnectString(ConnectString: AnsiString;
1020                var ServerName, DatabaseName: AnsiString; var Protocol: TProtocolAll;
1021                var PortNo: AnsiString): boolean;
# Line 902 | Line 1023 | begin
1023    Result := false;
1024   end;
1025  
1026 < {$ENDIF}
1026 > {$IFEND}
1027 > {$IFEND}
1028 >
1029 > function GetProtocol(ConnectString: AnsiString): TProtocolAll;
1030 > var ServerName,
1031 >    DatabaseName: AnsiString;
1032 >    PortNo: AnsiString;
1033 > begin
1034 >  if not ParseConnectString(ConnectString,ServerName,DatabaseName,Result,PortNo) then
1035 >    Result := unknownProtocol;
1036 > end;
1037  
1038   {Make a connect string in format appropriate protocol}
1039  
# Line 918 | Line 1049 | function MakeConnectString(ServerName, D
1049    end;
1050  
1051   begin
1052 +  if ServerName = '' then ServerName := 'localhost';
1053    if PortNo <> '' then
1054      case Protocol of
1055      NamedPipe:
# Line 948 | Line 1080 | end;
1080  
1081   function QuoteIdentifierIfNeeded(Dialect: Integer; Value: AnsiString): AnsiString;
1082   begin
1083 +  Value := TrimRight(Value);
1084    if (Dialect = 3) and
1085      (IsReservedWord(Value) or not IsSQLIdentifier(Value) or (AnsiUpperCase(Value) <> Value)) then
1086       Result := '"' + StringReplace (TrimRight(Value), '"', '""', [rfReplaceAll]) + '"'
# Line 974 | Line 1107 | begin
1107    Result := StringReplace(s,'''','''''',[rfReplaceAll]);
1108   end;
1109  
1110 + { TSQLParamProcessor }
1111 +
1112 + function TSQLParamProcessor.DoExecute(GenerateParamNames: boolean;
1113 +  var slNames: TStrings): AnsiString;
1114 + var token: TSQLTokens;
1115 +    iParamSuffix: Integer;
1116 + begin
1117 +  Result := '';
1118 +  iParamSuffix := 0;
1119 +
1120 +  while not EOF do
1121 +  begin
1122 +    token := GetNextToken;
1123 +    case token of
1124 +    sqltParam,
1125 +    sqltQuotedParam:
1126 +      begin
1127 +        Result := Result + '?';
1128 +        slNames.Add(TokenText);
1129 +      end;
1130 +
1131 +    sqltPlaceHolder:
1132 +      if GenerateParamNames then
1133 +      begin
1134 +        Inc(iParamSuffix);
1135 +        slNames.AddObject(sIBXParam + IntToStr(iParamSuffix),self); //Note local convention
1136 +                                            //add pointer to self to mark entry
1137 +        Result := Result + '?';
1138 +      end
1139 +      else
1140 +        IBError(ibxeSQLParseError, [SParamNameExpected]);
1141 +
1142 +    sqltQuotedString:
1143 +      Result := Result + '''' + SQLSafeString(TokenText) + '''';
1144 +
1145 +    sqltIdentifierInDoubleQuotes:
1146 +      Result := Result + '"' + StringReplace(TokenText,'"','""',[rfReplaceAll]) + '"';
1147 +
1148 +    sqltComment:
1149 +      Result := Result + '/*' + TokenText + '*/';
1150 +
1151 +    sqltCommentLine:
1152 +      Result := Result + '--' + TokenText + LineEnding;
1153 +
1154 +    sqltEOL:
1155 +      Result := Result + LineEnding;
1156 +
1157 +    else
1158 +      Result := Result + TokenText;
1159 +    end;
1160 +  end;
1161 + end;
1162 +
1163 + function TSQLParamProcessor.GetChar: AnsiChar;
1164 + begin
1165 +  if FIndex <= Length(FInString) then
1166 +  begin
1167 +    Result := FInString[FIndex];
1168 +    Inc(FIndex);
1169 +  end
1170 +  else
1171 +    Result := #0;
1172 + end;
1173 +
1174 + class function TSQLParamProcessor.Execute(sSQL: AnsiString;
1175 +  GenerateParamNames: boolean; var slNames: TStrings): AnsiString;
1176 + begin
1177 +  with self.Create do
1178 +  try
1179 +    FInString := sSQL;
1180 +    FIndex := 1;
1181 +    Result := DoExecute(GenerateParamNames,slNames);
1182 +  finally
1183 +    Free;
1184 +  end;
1185 + end;
1186 +
1187   { TSQLwithNamedParamsTokeniser }
1188  
1189   procedure TSQLwithNamedParamsTokeniser.Assign(source: TSQLTokeniser);
# Line 1042 | Line 1252 | begin
1252    stInBlock:
1253      begin
1254        case token of
1255 <      sqltBegin:
1255 >      sqltBegin,
1256 >      sqltCase:
1257            Inc(FNested);
1258  
1259        sqltEnd:
# Line 1093 | Line 1304 | begin
1304        Result := sqltSingleQuotes;
1305      '/':
1306        Result := sqltForwardSlash;
1307 +    '\':
1308 +      Result := sqltBackslash;
1309      '*':
1310        Result := sqltAsterisk;
1311      '(':
# Line 1111 | Line 1324 | begin
1324        Result := sqltOpenSquareBracket;
1325      ']':
1326        Result := sqltCloseSquareBracket;
1327 +    '-':
1328 +      Result := sqltMinus;
1329      '<':
1330        Result := sqltLT;
1331      '>':
# Line 1260 | Line 1475 | begin
1475      GetNext;
1476  
1477    repeat
1478 +    if FSkipNext then
1479 +    begin
1480 +      FSkipNext := false;
1481 +      GetNext;
1482 +    end;
1483 +
1484      Result := FNextToken;
1485      C := FLastChar;
1486      GetNext;
1487  
1488 <    if FSkipNext then
1488 >    if (Result = sqltCR) and (FNextToken = sqltEOL) then
1489      begin
1490 <      FSkipNext := false;
1491 <      continue;
1490 >      FSkipNext := true;
1491 >      Result := sqltEOL;
1492 >      C := LF;
1493      end;
1494  
1495      case FState of
# Line 1280 | Line 1502 | begin
1502            GetNext;
1503          end
1504          else
1505 +        if Result = sqltEOL then
1506 +          FString := FString + LineEnding
1507 +        else
1508            FString := FString + C;
1509        end;
1510  
# Line 1292 | Line 1517 | begin
1517              Result := sqltCommentLine;
1518            end;
1519  
1295        sqltCR: {ignore};
1296
1520          else
1521            FString := FString + C;
1522          end;
# Line 1315 | Line 1538 | begin
1538            end;
1539          end
1540          else
1541 +        if Result = sqltEOL then
1542 +          FString := FString + LineEnding
1543 +        else
1544            FString := FString + C;
1545        end;
1546  
# Line 1334 | Line 1560 | begin
1560            end;
1561          end
1562          else
1563 +        if Result = sqltEOL then
1564 +          FString := FString + LineEnding
1565 +        else
1566            FString := FString + C;
1567        end;
1568  
# Line 1383 | Line 1612 | begin
1612                GetNext;
1613                FState := stInComment;
1614              end
1615 <            else
1616 <            if FNextToken = sqltForwardSlash then
1615 >          end;
1616 >
1617 >        sqltMinus:
1618 >          begin
1619 >            if FNextToken = sqltMinus then
1620              begin
1621                FString := '';
1622                GetNext;
# Line 1411 | Line 1643 | begin
1643          sqltNumberString:
1644            if FNextToken in [sqltNumberString,sqltPeriod] then
1645              FState := stInNumeric;
1646 +
1647 +        sqltEOL:
1648 +          FString := LineEnding;
1649          end;
1650        end;
1651      end;
# Line 1420 | Line 1655 | begin
1655    until TokenFound(Result) or EOF;
1656   end;
1657  
1658 + function ParseDateTimeTZString(aDateTimeStr: Ansistring; var aDateTime: TDateTime;
1659 +  var aTimezone: AnsiString; TimeOnly: boolean): boolean;
1660 + {$IF declared(TFormatSettings)}
1661 + begin
1662 +    {$IF declared(DefaultFormatSettings)}
1663 +    Result := ParseDateTimeTZString(aDateTimeStr,aDateTime,aTimeZone,DefaultFormatSettings,TimeOnly);
1664 +    {$ELSE}
1665 +    {$IF declared(FormatSettings)}
1666 +    Result := ParseDateTimeTZString(aDateTimeStr,aDateTime,aTimeZone,FormatSettings,TimeOnly);
1667 +    {$IFEND} {$IFEND}
1668 + end;
1669 +
1670 + function ParseDateTimeTZString(aDateTimeStr: Ansistring; var aDateTime: TDateTime;
1671 +              var aTimezone: AnsiString; aFormatSettings: TFormatSettings; TimeOnly: boolean=false): boolean;
1672 + {$IFEND}
1673 + const
1674 +  whitespacechars = [' ',#$09,#$0A,#$0D];
1675 + var i,j,l: integer;
1676 +    aTime: TDateTime;
1677 +    DMs: longint;
1678 + begin
1679 +  Result := false;
1680 +  aTimezone := '';
1681 +  if aDateTimeStr <> '' then
1682 +  {$if declared(TFormatSettings)}
1683 +  with aFormatSettings do
1684 +  {$IFEND}
1685 +  begin
1686 +    aDateTime := 0;
1687 +    {Parse to get time zone info}
1688 +    i := 1;
1689 +    while (i <= length(aDateTimeStr)) and (aDateTimeStr[i] in whitespacechars) do inc(i); {skip white space}
1690 +    if not TimeOnly then
1691 +    begin
1692 +      {decode date}
1693 +      j := i;
1694 +      while (j <= length(aDateTimeStr)) and (aDateTimeStr[j] in ['0'..'9',DateSeparator]) do inc(j);
1695 +      if TryStrToDate(system.copy(aDateTimeStr,i,j-i),aDateTime) then
1696 +        i := j; {otherwise start again i.e. assume time only}
1697 +    end;
1698 +
1699 +    while (i <= length(aDateTimeStr)) and (aDateTimeStr[i] in whitespacechars) do inc(i); {skip white space}
1700 +    {decode time}
1701 +    j := i;
1702 +    while (j <= length(aDateTimeStr)) and (aDateTimeStr[j] in ['0'..'9',TimeSeparator]) do inc(j);
1703 +    Result := TryStrToTime(system.copy(aDateTimeStr,i,j-i),aTime);
1704 +    if not Result then Exit;
1705 +    aDateTime := aDateTime + aTime;
1706 +    i := j;
1707 +
1708 +    {is there a factional second part}
1709 +    if (i <= length(aDateTimeStr)) and (aDateTimeStr[i] = '.') then
1710 +    begin
1711 +      inc(i);
1712 +      inc(j);
1713 +      while (j <= Length(aDateTimeStr)) and (aDateTimeStr[j] in ['0'..'9']) do inc(j);
1714 +      if j > i then
1715 +      begin
1716 +        l := j-i;
1717 +        if l > 4 then l := 4;
1718 +        Result := TryStrToInt(system.copy(aDateTimeStr,i,l),DMs);
1719 +        if not Result then Exit;
1720 +
1721 +        {adjust for number of significant digits}
1722 +        case l of
1723 +        3:   DMs := DMs * 10;
1724 +        2:   DMs := DMs * 100;
1725 +        1:   DMs := DMs * 1000;
1726 +        end;
1727 +       aDateTime := aDateTime + (DMs / (MsecsPerDay*10));
1728 +      end;
1729 +    end;
1730 +    i := j;
1731 +
1732 +    while (i <= length(aDateTimeStr)) and (aDateTimeStr[i] in whitespacechars) do inc(i); {skip white space}
1733 +    {decode time zone}
1734 +    if i < length(aDateTimeStr) then
1735 +    begin
1736 +      j := i;
1737 +      while (j <= length(aDateTimeStr)) and not (aDateTimeStr[j] in whitespacechars) do inc(j);
1738 +      aTimezone := system.copy(aDateTimeStr,i,j-i);
1739 +    end;
1740 +    Result := true;
1741 +  end
1742 + end;
1743 +
1744 + {The following is similar to FPC DecodeTime except that the Firebird standard
1745 + decimilliseconds is used instead of milliseconds for fractional seconds}
1746 +
1747 + procedure FBDecodeTime(aTime: TDateTime; var Hour, Minute, Second: word;
1748 +  var DeciMillisecond: cardinal);
1749 + var D : Double;
1750 +    l : cardinal;
1751 + begin
1752 +  {conversion to decimilliseconds hacked from FPC DateTimeToTimeStamp}
1753 +  D := aTime * MSecsPerDay *10;
1754 +  if D < 0 then
1755 +    D := D - 0.5
1756 +  else
1757 +    D := D + 0.5;
1758 +  {rest hacked from FPC DecodeTIme}
1759 +  l := Abs(Trunc(D)) Mod (MSecsPerDay*10);
1760 +  Hour   := l div 36000000;
1761 +  l := l mod 36000000;
1762 +  Minute := l div 600000;
1763 +  l := l mod 600000;
1764 +  Second := l div 10000;
1765 +  DeciMillisecond := l mod 10000;
1766 + end;
1767 +
1768 + {The following is similar to FPC EncodeTime except that the Firebird standard
1769 + decimilliseconds is used instead of milliseconds for fractional seconds}
1770 +
1771 + function FBEncodeTime(Hour, Minute, Second, DeciMillisecond: cardinal): TDateTime;
1772 + const DMSecsPerDay = MSecsPerDay*10;
1773 + var DMs: cardinal;
1774 +    D: Double;
1775 + begin
1776 +  if (Hour<24) and (Minute<60) and (Second<60) and (DeciMillisecond<10000) then
1777 +  begin
1778 +    DMs := Hour*36000000+Minute*600000+Second*10000+DeciMillisecond;
1779 +    D := DMs/DMSecsPerDay;
1780 +    Result:=TDateTime(d)
1781 +  end
1782 +  else
1783 +    IBError(ibxeBadTimeSpecification,[Hour, Minute, Second, DeciMillisecond]);
1784 + end;
1785 +
1786 + {The following is similar to FPC FormatDateTime except that it additionally
1787 + allows the timstamp to have a fractional seconds component with a resolution
1788 + of four decimal places. This is appended to the result for FormatDateTime
1789 + if the format string contains a "zzzz' string.}
1790 +
1791 + function FBFormatDateTime(fmt: AnsiString; aDateTime: TDateTime): AnsiString;
1792 + var Hour, Minute, Second: word;
1793 +    DeciMillisecond: cardinal;
1794 + begin
1795 +  if Pos('zzzz',fmt) > 0 then
1796 +  begin
1797 +    FBDecodeTime(aDateTime, Hour, Minute, Second, DeciMillisecond);
1798 +    fmt := StringReplace(fmt, 'zzzz', Format('%.4d',[DeciMillisecond]), [rfReplaceAll]);
1799 +  end;
1800 +  Result := FormatDateTime(fmt,aDateTime);
1801 + end;
1802 +
1803 + function FormatTimeZoneOffset(EffectiveTimeOffsetMins: integer): AnsiString;
1804 + begin
1805 +  if EffectiveTimeOffsetMins > 0 then
1806 +    Result := Format('+%.2d:%.2d',[EffectiveTimeOffsetMins div 60,abs(EffectiveTimeOffsetMins mod 60)])
1807 +  else
1808 +    Result := Format('%.2d:%.2d',[EffectiveTimeOffsetMins div 60,abs(EffectiveTimeOffsetMins mod 60)]);
1809 + end;
1810 +
1811 + function DecodeTimeZoneOffset(TZOffset: AnsiString; var dstOffset: integer): boolean;
1812 + var i: integer;
1813 + begin
1814 +  Result := false;
1815 +  TZOffset := Trim(TZOffset);
1816 +  for i := 1 to Length(TZOffset) do
1817 +    if not (TZOffset[i] in ['0'..'9','-','+',':']) then Exit;
1818 +
1819 +  Result := true;
1820 +  i := Pos(':',TZOffset);
1821 +  if i > 0 then
1822 +    dstOffset := StrToInt(copy(TZOffset,1,i-1)) * 60 + StrToInt(copy(TZOffset,i + 1))
1823 +  else
1824 +    dstOffset := StrToInt(TZOffset) * 60;
1825 + end;
1826 +
1827 + function StripLeadingZeros(Value: AnsiString): AnsiString;
1828 + var i: Integer;
1829 +    start: integer;
1830 + begin
1831 +  Result := '';
1832 +  start := 1;
1833 +  if (Length(Value) > 0) and (Value[1] = '-') then
1834 +  begin
1835 +    Result := '-';
1836 +    start := 2;
1837 +  end;
1838 +  for i := start to Length(Value) do
1839 +    if Value[i] <> '0' then
1840 +    begin
1841 +      Result := Result + system.copy(Value, i, MaxInt);
1842 +      Exit;
1843 +    end;
1844 + end;
1845 +
1846 + function TryStrToNumeric(S: Ansistring; out Value: int64; out scale: integer): boolean;
1847 + var i: integer;
1848 +    ds: integer;
1849 +    exponent: integer;
1850 + begin
1851 +  Result := false;
1852 +  ds := 0;
1853 +  exponent := 0;
1854 +  S := Trim(S);
1855 +  Value := 0;
1856 +  scale := 0;
1857 +  if Length(S) = 0 then
1858 +    Exit;
1859 +  {$IF declared(DefaultFormatSettings)}
1860 +  with DefaultFormatSettings do
1861 +  {$ELSE}
1862 +  {$IF declared(FormatSettings)}
1863 +  with FormatSettings do
1864 +  {$IFEND}
1865 +  {$IFEND}
1866 +  begin
1867 +    {ThousandSeparator not allowed as by Delphi specs}
1868 +    if (ThousandSeparator <> DecimalSeparator) and
1869 +       (Pos(ThousandSeparator, S) <> 0) then
1870 +        Exit;
1871 +
1872 +    for i := length(S) downto 1 do
1873 +    begin
1874 +      if S[i] = AnsiChar(DecimalSeparator) then
1875 +      begin
1876 +          if ds <> 0 then Exit; {only one allowed}
1877 +          ds := i-1;
1878 +          dec(exponent);
1879 +          system.Delete(S,i,1);
1880 +      end
1881 +      else
1882 +      if S[i] in ['+','-'] then
1883 +      begin
1884 +       if (i > 1) and not (S[i-1] in ['e','E']) then
1885 +          Exit; {malformed}
1886 +      end
1887 +      else
1888 +      if S[i] in ['e','E'] then {scientific notation}
1889 +      begin
1890 +        if ds <> 0 then Exit; {not permitted in exponent}
1891 +        if exponent <> 0 then Exit; {only one allowed}
1892 +        exponent := i;
1893 +      end
1894 +      else
1895 +      if not (S[i] in ['0'..'9']) then
1896 +          Exit; {bad character}
1897 +    end;
1898 +
1899 +    if exponent > 0 then
1900 +    begin
1901 +      Result := TryStrToInt(system.copy(S,exponent+1,maxint),Scale);
1902 +      if Result then
1903 +      begin
1904 +        {adjust scale for decimal point}
1905 +        if ds > 0 then
1906 +          Scale := Scale - (exponent - ds - 1);
1907 +        Result := TryStrToInt64(system.copy(S,1,exponent-1),Value);
1908 +      end;
1909 +    end
1910 +    else
1911 +    begin
1912 +      if ds <> 0 then
1913 +        scale := ds - Length(S);
1914 +      Result := TryStrToInt64(S,Value);
1915 +    end;
1916 +  end;
1917 + end;
1918 +
1919 + function NumericToDouble(aValue: Int64; aScale: integer): double;
1920 + begin
1921 +  Result := aValue * IntPower(10,aScale)
1922 + end;
1923 +
1924   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines