IdTime.pas 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.10 2/10/2005 2:24:42 PM JPMugaas
  18. Minor Restructures for some new UnixTime Service components.
  19. Rev 1.9 2004.02.03 5:44:34 PM czhower
  20. Name changes
  21. Rev 1.8 1/21/2004 4:20:56 PM JPMugaas
  22. InitComponent
  23. Rev 1.7 1/3/2004 1:00:00 PM JPMugaas
  24. These should now compile with Kudzu's change in IdCoreGlobal.
  25. Rev 1.6 4/11/2003 02:45:44 PM JPMugaas
  26. Rev 1.5 4/5/2003 7:23:56 PM BGooijen
  27. Raises exception on timeout now
  28. Rev 1.4 4/4/2003 8:02:34 PM BGooijen
  29. made host published
  30. Rev 1.3 2/24/2003 10:37:00 PM JPMugaas
  31. Should compile. TODO: Figure out what to do with TIdTime and the timeout
  32. feature.
  33. Rev 1.2 12/7/2002 06:43:38 PM JPMugaas
  34. These should now compile except for Socks server. IPVersion has to be a
  35. property someplace for that.
  36. Rev 1.1 12/6/2002 05:30:48 PM JPMugaas
  37. Now descend from TIdTCPClientCustom instead of TIdTCPClient.
  38. Rev 1.0 11/13/2002 08:03:14 AM JPMugaas
  39. }
  40. unit IdTime;
  41. {*******************************************************}
  42. { }
  43. { Indy Time Client TIdTime }
  44. { }
  45. { Copyright (C) 2000 Winshoes Working Group }
  46. { Original author J. Peter Mugaas }
  47. { 2000-April-24 }
  48. { Based on RFC RFC 868 }
  49. { }
  50. {*******************************************************}
  51. {
  52. 2001-Sep -21 J. Peter Mugaas
  53. - adjusted formula as suggested by Vaclav Korecek. The old
  54. one would give wrong date, time if RoundTripDelay was over
  55. a value of 1000
  56. 2000-May -04 J. Peter Mugaas
  57. -Changed RoundTripDelay to a cardinal and I now use the
  58. GetTickCount function for more accuracy
  59. -The formula had to adjusted for this.
  60. 2000-May -03 J. Peter Mugaas
  61. -Added BaseDate to the date the calculations are based on can be
  62. adjusted to work after the year 2035
  63. 2000-Apr.-29 J. Peter Mugaas
  64. -Made the time more accurate by taking into account time-zone
  65. bias by subtracting IdGlobal.TimeZoneBias.
  66. -I also added a correction for the time it took to receive the
  67. Integer from the server ( ReadInteger )
  68. -Changed Time property to DateTime and TimeCard to DateTimeCard
  69. to be more consistant with TIdSNTP.
  70. }
  71. interface
  72. {$i IdCompilerDefines.inc}
  73. uses
  74. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  75. Classes,
  76. {$ENDIF}
  77. IdGlobal,
  78. IdAssignedNumbers, IdGlobalProtocols, IdTCPClient;
  79. const
  80. TIME_TIMEOUT = 2500;
  81. type
  82. TIdCustomTime = class(TIdTCPClientCustom)
  83. protected
  84. FBaseDate: TDateTime;
  85. FRoundTripDelay: UInt32;
  86. FTimeout: Integer;
  87. //
  88. function GetDateTimeCard: UInt32;
  89. function GetDateTime: TDateTime;
  90. procedure InitComponent; override;
  91. public
  92. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  93. constructor Create(AOwner: TComponent); reintroduce; overload;
  94. {$ENDIF}
  95. {This synchronizes the local clock with the Time Server}
  96. function SyncTime: Boolean;
  97. {This is the number of seconds since 12:00 AM, 1900 - Jan-1}
  98. property DateTimeCard: UInt32 read GetDateTimeCard;
  99. {This is the current time according to the server. TimeZone and Time used
  100. to receive the data are accounted for}
  101. property DateTime: TDateTime read GetDateTime;
  102. {This is the time it took to receive the Time from the server. There is no
  103. need to use this to calculate the current time when using DateTime property
  104. as we have done that here}
  105. property RoundTripDelay: UInt32 read FRoundTripDelay;
  106. published
  107. property Timeout: Integer read FTimeout write FTimeout default TIME_TIMEOUT;
  108. property Host;
  109. end;
  110. TIdTime = class(TIdCustomTime)
  111. published
  112. {This property is used to set the Date that the Time server bases its
  113. calculations from. If both the server and client are based from the same
  114. date which is higher than the original date, you can extend it beyond the
  115. year 2035}
  116. property BaseDate: TDateTime read FBaseDate write FBaseDate;
  117. property Timeout: Integer read FTimeout write FTimeout default TIME_TIMEOUT;
  118. property Port default IdPORT_TIME;
  119. end;
  120. implementation
  121. uses
  122. {$IFDEF USE_VCL_POSIX}
  123. {$IFDEF OSX}
  124. Macapi.CoreServices,
  125. {$ENDIF}
  126. Posix.SysTime,
  127. {$ENDIF}
  128. IdTCPConnection;
  129. { TIdCustomTime }
  130. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  131. constructor TIdCustomTime.Create(AOwner: TComponent);
  132. begin
  133. inherited Create(AOwner);
  134. end;
  135. {$ENDIF}
  136. procedure TIdCustomTime.InitComponent;
  137. begin
  138. inherited;
  139. Port := IdPORT_TIME;
  140. {This indicates that the default date is Jan 1, 1900 which was specified
  141. by RFC 868.}
  142. FBaseDate := TIME_BASEDATE;
  143. FTimeout := TIME_TIMEOUT;
  144. end;
  145. function TIdCustomTime.GetDateTime: TDateTime;
  146. var
  147. BufCard: UInt32;
  148. begin
  149. BufCard := GetDateTimeCard;
  150. if BufCard <> 0 then begin
  151. {The formula is The Time cardinal we receive divided by (24 * 60*60 for days + RoundTrip divided by one-thousand since this is based on seconds
  152. - the Time Zone difference}
  153. Result := UTCTimeToLocalTime( ((BufCard + (FRoundTripDelay div 1000))/ (24 * 60 * 60) ) + Int(fBaseDate) );
  154. end else begin
  155. { Somehow, I really doubt we are ever going to really get a time such as
  156. 12/30/1899 12:00 am so use that as a failure test}
  157. Result := 0;
  158. end;
  159. end;
  160. function TIdCustomTime.GetDateTimeCard: UInt32;
  161. var
  162. LTimeBeforeRetrieve: TIdTicks;
  163. begin
  164. Connect; try
  165. // Check for timeout
  166. // Timeout is actually a time with no traffic, not a total timeout.
  167. IOHandler.ReadTimeout:=Timeout;
  168. LTimeBeforeRetrieve := Ticks64;
  169. Result := IOHandler.ReadUInt32;
  170. {Theoritically, it should take about 1/2 of the time to receive the data
  171. but in practice, it could be any portion depending upon network conditions. This is also
  172. as per RFC standard}
  173. {This is just in case the TickCount rolled back to zero}
  174. FRoundTripDelay := GetElapsedTicks(LTimeBeforeRetrieve) div 2;
  175. finally Disconnect; end;
  176. end;
  177. function TIdCustomTime.SyncTime: Boolean;
  178. var
  179. LBufTime: TDateTime;
  180. begin
  181. LBufTime := DateTime;
  182. Result := LBufTime <> 0;
  183. if Result then begin
  184. Result := IndySetLocalTime(LBufTime);
  185. end;
  186. end;
  187. end.