dateutil.inc 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772
  1. {$mode objfpc}
  2. {$h+}
  3. {
  4. This file is part of the Free Pascal run time library.
  5. Copyright (c) 1999-2000 by the Free Pascal development team
  6. Delphi/Kylix compatibility unit, provides Date/Time handling routines.
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit DateUtils;
  14. interface
  15. {$ifndef FPUNONE}
  16. uses
  17. SysUtils, Math;
  18. { ---------------------------------------------------------------------
  19. Various constants
  20. ---------------------------------------------------------------------}
  21. const
  22. DaysPerWeek = 7;
  23. WeeksPerFortnight = 2;
  24. MonthsPerYear = 12;
  25. YearsPerDecade = 10;
  26. YearsPerCentury = 100;
  27. YearsPerMillennium = 1000;
  28. // ISO day numbers.
  29. DayMonday = 1;
  30. DayTuesday = 2;
  31. DayWednesday = 3;
  32. DayThursday = 4;
  33. DayFriday = 5;
  34. DaySaturday = 6;
  35. DaySunday = 7;
  36. // Fraction of a day
  37. OneHour = TDateTime(1)/HoursPerDay;
  38. OneMinute = TDateTime(1)/MinsPerDay;
  39. OneSecond = TDateTime(1)/SecsPerDay;
  40. OneMillisecond = TDateTime(1)/MSecsPerDay;
  41. { This is actual days per year but you need to know if it's a leap year}
  42. DaysPerYear: array [Boolean] of Word = (365, 366);
  43. { Used in RecodeDate, RecodeTime and RecodeDateTime for those datetime }
  44. { fields you want to leave alone }
  45. RecodeLeaveFieldAsIs = High(Word);
  46. { ---------------------------------------------------------------------
  47. Global variables used in this unit
  48. ---------------------------------------------------------------------}
  49. Const
  50. { Average over a 4 year span. Valid for next 100 years }
  51. ApproxDaysPerMonth: Double = 30.4375;
  52. ApproxDaysPerYear: Double = 365.25;
  53. { ---------------------------------------------------------------------
  54. Simple trimming functions.
  55. ---------------------------------------------------------------------}
  56. Function DateOf(const AValue: TDateTime): TDateTime;
  57. Function TimeOf(const AValue: TDateTime): TDateTime;
  58. { ---------------------------------------------------------------------
  59. Identification functions.
  60. ---------------------------------------------------------------------}
  61. Function IsInLeapYear(const AValue: TDateTime): Boolean;
  62. Function IsPM(const AValue: TDateTime): Boolean;
  63. Function IsValidDate(const AYear, AMonth, ADay: Word): Boolean;
  64. Function IsValidTime(const AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
  65. Function IsValidDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
  66. Function IsValidDateDay(const AYear, ADayOfYear: Word): Boolean;
  67. Function IsValidDateWeek(const AYear, AWeekOfYear, ADayOfWeek: Word): Boolean;
  68. Function IsValidDateMonthWeek(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word): Boolean;
  69. { ---------------------------------------------------------------------
  70. Enumeration functions.
  71. ---------------------------------------------------------------------}
  72. Function WeeksInYear(const AValue: TDateTime): Word;
  73. Function WeeksInAYear(const AYear: Word): Word;
  74. Function DaysInYear(const AValue: TDateTime): Word;
  75. Function DaysInAYear(const AYear: Word): Word;
  76. Function DaysInMonth(const AValue: TDateTime): Word;
  77. Function DaysInAMonth(const AYear, AMonth: Word): Word;
  78. { ---------------------------------------------------------------------
  79. Variations on current date/time.
  80. ---------------------------------------------------------------------}
  81. Function Today: TDateTime;
  82. Function Yesterday: TDateTime;
  83. Function Tomorrow: TDateTime;
  84. Function IsToday(const AValue: TDateTime): Boolean;
  85. Function IsSameDay(const AValue, ABasis: TDateTime): Boolean;
  86. function IsSameMonth(const Avalue, ABasis: TDateTime): Boolean;
  87. Function PreviousDayOfWeek (DayOfWeek : Word) : Word;
  88. { ---------------------------------------------------------------------
  89. Extraction functions.
  90. ---------------------------------------------------------------------}
  91. Function YearOf(const AValue: TDateTime): Word;
  92. Function MonthOf(const AValue: TDateTime): Word;
  93. Function WeekOf(const AValue: TDateTime): Word;
  94. Function DayOf(const AValue: TDateTime): Word;
  95. Function HourOf(const AValue: TDateTime): Word;
  96. Function MinuteOf(const AValue: TDateTime): Word;
  97. Function SecondOf(const AValue: TDateTime): Word;
  98. Function MilliSecondOf(const AValue: TDateTime): Word;
  99. { ---------------------------------------------------------------------
  100. Start/End of year functions.
  101. ---------------------------------------------------------------------}
  102. Function StartOfTheYear(const AValue: TDateTime): TDateTime;
  103. Function EndOfTheYear(const AValue: TDateTime): TDateTime;
  104. Function StartOfAYear(const AYear: Word): TDateTime;
  105. Function EndOfAYear(const AYear: Word): TDateTime;
  106. { ---------------------------------------------------------------------
  107. Start/End of month functions.
  108. ---------------------------------------------------------------------}
  109. Function StartOfTheMonth(const AValue: TDateTime): TDateTime;
  110. Function EndOfTheMonth(const AValue: TDateTime): TDateTime;
  111. Function StartOfAMonth(const AYear, AMonth: Word): TDateTime;
  112. Function EndOfAMonth(const AYear, AMonth: Word): TDateTime;
  113. { ---------------------------------------------------------------------
  114. Start/End of week functions.
  115. ---------------------------------------------------------------------}
  116. Function StartOfTheWeek(const AValue: TDateTime): TDateTime;
  117. Function EndOfTheWeek(const AValue: TDateTime): TDateTime;
  118. Function StartOfAWeek(const AYear, AWeekOfYear: Word; const ADayOfWeek: Word): TDateTime;
  119. Function StartOfAWeek(const AYear, AWeekOfYear: Word): TDateTime; // ADayOFWeek 1
  120. Function EndOfAWeek(const AYear, AWeekOfYear: Word; const ADayOfWeek: Word): TDateTime;
  121. Function EndOfAWeek(const AYear, AWeekOfYear: Word): TDateTime; // const ADayOfWeek: Word = 7
  122. { ---------------------------------------------------------------------
  123. Start/End of day functions.
  124. ---------------------------------------------------------------------}
  125. Function StartOfTheDay(const AValue: TDateTime): TDateTime;
  126. Function EndOfTheDay(const AValue: TDateTime): TDateTime;
  127. Function StartOfADay(const AYear, AMonth, ADay: Word): TDateTime; overload;
  128. Function StartOfADay(const AYear, ADayOfYear: Word): TDateTime; overload;
  129. Function EndOfADay(const AYear, AMonth, ADay: Word): TDateTime; overload;
  130. Function EndOfADay(const AYear, ADayOfYear: Word): TDateTime; overload;
  131. { ---------------------------------------------------------------------
  132. Part of year functions.
  133. ---------------------------------------------------------------------}
  134. Function MonthOfTheYear(const AValue: TDateTime): Word;
  135. Function WeekOfTheYear(const AValue: TDateTime): Word; overload;
  136. Function WeekOfTheYear(const AValue: TDateTime; out AYear: Word): Word; overload;
  137. Function DayOfTheYear(const AValue: TDateTime): Word;
  138. Function HourOfTheYear(const AValue: TDateTime): Word;
  139. Function MinuteOfTheYear(const AValue: TDateTime): LongWord;
  140. Function SecondOfTheYear(const AValue: TDateTime): LongWord;
  141. Function MilliSecondOfTheYear(const AValue: TDateTime): Int64;
  142. { ---------------------------------------------------------------------
  143. Part of month functions.
  144. ---------------------------------------------------------------------}
  145. Function WeekOfTheMonth(const AValue: TDateTime): Word; overload;
  146. Function WeekOfTheMonth(const AValue: TDateTime; out AYear, AMonth: Word): Word; overload;
  147. Function DayOfTheMonth(const AValue: TDateTime): Word;
  148. Function HourOfTheMonth(const AValue: TDateTime): Word;
  149. Function MinuteOfTheMonth(const AValue: TDateTime): Word;
  150. Function SecondOfTheMonth(const AValue: TDateTime): LongWord;
  151. Function MilliSecondOfTheMonth(const AValue: TDateTime): LongWord;
  152. { ---------------------------------------------------------------------
  153. Part of week functions.
  154. ---------------------------------------------------------------------}
  155. Function DayOfTheWeek(const AValue: TDateTime): Word;
  156. Function HourOfTheWeek(const AValue: TDateTime): Word;
  157. Function MinuteOfTheWeek(const AValue: TDateTime): Word;
  158. Function SecondOfTheWeek(const AValue: TDateTime): LongWord;
  159. Function MilliSecondOfTheWeek(const AValue: TDateTime): LongWord;
  160. { ---------------------------------------------------------------------
  161. Part of day functions.
  162. ---------------------------------------------------------------------}
  163. Function HourOfTheDay(const AValue: TDateTime): Word;
  164. Function MinuteOfTheDay(const AValue: TDateTime): Word;
  165. Function SecondOfTheDay(const AValue: TDateTime): LongWord;
  166. Function MilliSecondOfTheDay(const AValue: TDateTime): LongWord;
  167. { ---------------------------------------------------------------------
  168. Part of hour functions.
  169. ---------------------------------------------------------------------}
  170. Function MinuteOfTheHour(const AValue: TDateTime): Word;
  171. Function SecondOfTheHour(const AValue: TDateTime): Word;
  172. Function MilliSecondOfTheHour(const AValue: TDateTime): LongWord;
  173. { ---------------------------------------------------------------------
  174. Part of minute functions.
  175. ---------------------------------------------------------------------}
  176. Function SecondOfTheMinute(const AValue: TDateTime): Word;
  177. Function MilliSecondOfTheMinute(const AValue: TDateTime): LongWord;
  178. { ---------------------------------------------------------------------
  179. Part of second functions.
  180. ---------------------------------------------------------------------}
  181. Function MilliSecondOfTheSecond(const AValue: TDateTime): Word;
  182. { ---------------------------------------------------------------------
  183. Range checking functions.
  184. ---------------------------------------------------------------------}
  185. Function WithinPastYears(const ANow, AThen: TDateTime; const AYears: Integer): Boolean;
  186. Function WithinPastMonths(const ANow, AThen: TDateTime; const AMonths: Integer): Boolean;
  187. Function WithinPastWeeks(const ANow, AThen: TDateTime; const AWeeks: Integer): Boolean;
  188. Function WithinPastDays(const ANow, AThen: TDateTime; const ADays: Integer): Boolean;
  189. Function WithinPastHours(const ANow, AThen: TDateTime; const AHours: Int64): Boolean;
  190. Function WithinPastMinutes(const ANow, AThen: TDateTime; const AMinutes: Int64): Boolean;
  191. Function WithinPastSeconds(const ANow, AThen: TDateTime; const ASeconds: Int64): Boolean;
  192. Function WithinPastMilliSeconds(const ANow, AThen: TDateTime; const AMilliSeconds: Int64): Boolean;
  193. { ---------------------------------------------------------------------
  194. Period functions.
  195. ---------------------------------------------------------------------}
  196. Function YearsBetween(const ANow, AThen: TDateTime; AExact : Boolean = False): Integer;
  197. Function MonthsBetween(const ANow, AThen: TDateTime; AExact : Boolean = False): Integer;
  198. Function WeeksBetween(const ANow, AThen: TDateTime): Integer;
  199. Function DaysBetween(const ANow, AThen: TDateTime): Integer;
  200. Function HoursBetween(const ANow, AThen: TDateTime): Int64;
  201. Function MinutesBetween(const ANow, AThen: TDateTime): Int64;
  202. Function SecondsBetween(const ANow, AThen: TDateTime): Int64;
  203. Function MilliSecondsBetween(const ANow, AThen: TDateTime): Int64;
  204. Procedure PeriodBetween(const ANow, AThen: TDateTime; Out Years, months, days : Word);
  205. { ---------------------------------------------------------------------
  206. Timespan in xxx functions.
  207. ---------------------------------------------------------------------}
  208. { YearSpan and MonthSpan are approximate values }
  209. Function YearSpan(const ANow, AThen: TDateTime): Double;
  210. Function MonthSpan(const ANow, AThen: TDateTime): Double;
  211. Function WeekSpan(const ANow, AThen: TDateTime): Double;
  212. Function DaySpan(const ANow, AThen: TDateTime): Double;
  213. Function HourSpan(const ANow, AThen: TDateTime): Double;
  214. Function MinuteSpan(const ANow, AThen: TDateTime): Double;
  215. Function SecondSpan(const ANow, AThen: TDateTime): Double;
  216. Function MilliSecondSpan(const ANow, AThen: TDateTime): Double;
  217. { ---------------------------------------------------------------------
  218. Increment/decrement functions.
  219. ---------------------------------------------------------------------}
  220. Function IncYear(const AValue: TDateTime; const ANumberOfYears: Integer ): TDateTime;
  221. Function IncYear(const AValue: TDateTime): TDateTime; // ; const ANumberOfYears: Integer = 1)
  222. // Function IncMonth is in SysUtils
  223. Function IncWeek(const AValue: TDateTime; const ANumberOfWeeks: Integer): TDateTime;
  224. Function IncWeek(const AValue: TDateTime): TDateTime; // ; const ANumberOfWeeks: Integer = 1)
  225. Function IncDay(const AValue: TDateTime; const ANumberOfDays: Integer): TDateTime;
  226. Function IncDay(const AValue: TDateTime): TDateTime; //; const ANumberOfDays: Integer = 1)
  227. Function IncHour(const AValue: TDateTime; const ANumberOfHours: Int64): TDateTime;
  228. Function IncHour(const AValue: TDateTime): TDateTime; //; const ANumberOfHours: Int64 = 1
  229. Function IncMinute(const AValue: TDateTime; const ANumberOfMinutes: Int64): TDateTime;
  230. Function IncMinute(const AValue: TDateTime): TDateTime; // ; const ANumberOfMinutes: Int64 = 1
  231. Function IncSecond(const AValue: TDateTime; const ANumberOfSeconds: Int64): TDateTime;
  232. Function IncSecond(const AValue: TDateTime): TDateTime; // ; const ANumberOfSeconds: Int64 = 1
  233. Function IncMilliSecond(const AValue: TDateTime; const ANumberOfMilliSeconds: Int64): TDateTime;
  234. Function IncMilliSecond(const AValue: TDateTime): TDateTime; // ; const ANumberOfMilliSeconds: Int64 = 1
  235. { ---------------------------------------------------------------------
  236. Encode/Decode of complete timestamp
  237. ---------------------------------------------------------------------}
  238. Function EncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime;
  239. Procedure DecodeDateTime(const AValue: TDateTime; out AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word);
  240. Function TryEncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; out AValue: TDateTime): Boolean;
  241. { ---------------------------------------------------------------------
  242. Encode/decode date, specifying week of year and day of week
  243. ---------------------------------------------------------------------}
  244. Function EncodeDateWeek(const AYear, AWeekOfYear: Word; const ADayOfWeek: Word): TDateTime;
  245. Function EncodeDateWeek(const AYear, AWeekOfYear: Word): TDateTime; //; const ADayOfWeek: Word = 1
  246. Procedure DecodeDateWeek(const AValue: TDateTime; out AYear, AWeekOfYear, ADayOfWeek: Word);
  247. Function TryEncodeDateWeek(const AYear, AWeekOfYear: Word; out AValue: TDateTime; const ADayOfWeek: Word): Boolean;
  248. Function TryEncodeDateWeek(const AYear, AWeekOfYear: Word; out AValue: TDateTime): Boolean; //; const ADayOfWeek: Word = 1
  249. { ---------------------------------------------------------------------
  250. Encode/decode date, specifying day of year
  251. ---------------------------------------------------------------------}
  252. Function EncodeDateDay(const AYear, ADayOfYear: Word): TDateTime;
  253. Procedure DecodeDateDay(const AValue: TDateTime; out AYear, ADayOfYear: Word);
  254. Function TryEncodeDateDay(const AYear, ADayOfYear: Word; out AValue: TDateTime): Boolean;
  255. { ---------------------------------------------------------------------
  256. Encode/decode date, specifying week of month
  257. ---------------------------------------------------------------------}
  258. Function EncodeDateMonthWeek(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word): TDateTime;
  259. Procedure DecodeDateMonthWeek(const AValue: TDateTime; out AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word);
  260. Function TryEncodeDateMonthWeek(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word; out AValue: TDateTime): Boolean;
  261. { ---------------------------------------------------------------------
  262. Encode time interval, allowing hours>24
  263. ---------------------------------------------------------------------}
  264. function TryEncodeTimeInterval(Hour, Min, Sec, MSec:word; Out Time : TDateTime) : boolean;
  265. function EncodeTimeInterval(Hour, Minute, Second, MilliSecond:word): TDateTime;
  266. { ---------------------------------------------------------------------
  267. Replace given element with supplied value.
  268. ---------------------------------------------------------------------}
  269. Function RecodeYear(const AValue: TDateTime; const AYear: Word): TDateTime;
  270. Function RecodeMonth(const AValue: TDateTime; const AMonth: Word): TDateTime;
  271. Function RecodeDay(const AValue: TDateTime; const ADay: Word): TDateTime;
  272. Function RecodeHour(const AValue: TDateTime; const AHour: Word): TDateTime;
  273. Function RecodeMinute(const AValue: TDateTime; const AMinute: Word): TDateTime;
  274. Function RecodeSecond(const AValue: TDateTime; const ASecond: Word): TDateTime;
  275. Function RecodeMilliSecond(const AValue: TDateTime; const AMilliSecond: Word): TDateTime;
  276. Function RecodeDate(const AValue: TDateTime; const AYear, AMonth, ADay: Word): TDateTime;
  277. Function RecodeTime(const AValue: TDateTime; const AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime;
  278. Function RecodeDateTime(const AValue: TDateTime; const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime;
  279. Function TryRecodeDateTime(const AValue: TDateTime; const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; out AResult: TDateTime): Boolean;
  280. { ---------------------------------------------------------------------
  281. Comparision of date/time
  282. ---------------------------------------------------------------------}
  283. Function CompareDateTime(const A, B: TDateTime): TValueRelationship;
  284. Function CompareDate(const A, B: TDateTime): TValueRelationship;
  285. Function CompareTime(const A, B: TDateTime): TValueRelationship;
  286. Function SameDateTime(const A, B: TDateTime): Boolean;
  287. Function SameDate(const A, B: TDateTime): Boolean;
  288. Function SameTime(const A, B: TDateTime): Boolean;
  289. function DateTimeInRange(ADateTime: TDateTime; AStartDateTime, AEndDateTime: TDateTime; aInclusive: Boolean = True): Boolean;
  290. function TimeInRange(ATime: TTime; AStartTime, AEndTime: TTime; AInclusive: Boolean = True): Boolean;
  291. function DateInRange(ADate: TDate; AStartDate, AEndDate: TDate; AInclusive: Boolean = True): Boolean;
  292. { For a given date these Functions tell you the which day of the week of the
  293. month (or year). If its a Thursday, they will tell you if its the first,
  294. second, etc Thursday of the month (or year). Remember, even though its
  295. the first Thursday of the year it doesn't mean its the first week of the
  296. year. See ISO 8601 above for more information. }
  297. Function NthDayOfWeek(const AValue: TDateTime): Word;
  298. Procedure DecodeDayOfWeekInMonth(const AValue: TDateTime; out AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word);
  299. Function EncodeDayOfWeekInMonth(const AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word): TDateTime;
  300. Function TryEncodeDayOfWeekInMonth(const AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word; out AValue: TDateTime): Boolean;
  301. { ---------------------------------------------------------------------
  302. Exception throwing routines
  303. ---------------------------------------------------------------------}
  304. Procedure InvalidDateTimeError(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; const ABaseDate: TDateTime);
  305. Procedure InvalidDateTimeError(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word); // const ABaseDate: TDateTime = 0
  306. Procedure InvalidDateWeekError(const AYear, AWeekOfYear, ADayOfWeek: Word);
  307. Procedure InvalidDateDayError(const AYear, ADayOfYear: Word);
  308. Procedure InvalidDateMonthWeekError(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word);
  309. Procedure InvalidDayOfWeekInMonthError(const AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word);
  310. { ---------------------------------------------------------------------
  311. Julian and Modified Julian Date conversion support
  312. ---------------------------------------------------------------------}
  313. Function DateTimeToJulianDate(const AValue: TDateTime): Double;
  314. Function JulianDateToDateTime(const AValue: Double): TDateTime;
  315. Function TryJulianDateToDateTime(const AValue: Double; out ADateTime: TDateTime): Boolean;
  316. Function DateTimeToModifiedJulianDate(const AValue: TDateTime): Double;
  317. Function ModifiedJulianDateToDateTime(const AValue: Double): TDateTime;
  318. Function TryModifiedJulianDateToDateTime(const AValue: Double; out ADateTime: TDateTime): Boolean;
  319. { ---------------------------------------------------------------------
  320. Unix timestamp support.
  321. ---------------------------------------------------------------------}
  322. Function DateTimeToUnix(const AValue: TDateTime; AInputIsUTC: Boolean = True): Int64;
  323. Function UnixToDateTime(const AValue: Int64; aReturnUTC : Boolean = true): TDateTime;
  324. Function UnixTimeStampToMac(const AValue: Int64): Int64;
  325. { ---------------------------------------------------------------------
  326. Mac timestamp support.
  327. ---------------------------------------------------------------------}
  328. Function DateTimeToMac(const AValue: TDateTime): Int64;
  329. Function MacToDateTime(const AValue: Int64): TDateTime;
  330. Function MacTimeStampToUnix(const AValue: Int64): Int64;
  331. { .....................................................................
  332. Dos <-> Delphi datetime support
  333. .....................................................................}
  334. Function DateTimeToDosDateTime(const AValue: TDateTime): longint;
  335. Function DosDateTimeToDateTime( AValue: longint): TDateTime;
  336. { UTC <-> Local time }
  337. Function UniversalTimeToLocal(UT: TDateTime): TDateTime;
  338. Function UniversalTimeToLocal(UT: TDateTime; TZOffset : Integer): TDateTime;
  339. Function LocalTimeToUniversal(LT: TDateTime): TDateTime;
  340. Function LocalTimeToUniversal(LT: TDateTime; TZOffset: Integer): TDateTime;
  341. { ScanDateTime is a limited inverse of formatdatetime }
  342. function ScanDateTime(const Pattern:string;const s:string;const fmt:TFormatSettings;startpos:integer=1) : tdatetime; overload;
  343. function ScanDateTime(const Pattern:string;const s:string;startpos:integer=1) : tdatetime; overload;
  344. // ISO 8601 Date/Time formatting
  345. function DateToISO8601(const ADate: TDateTime; AInputIsUTC: Boolean = True): string;
  346. Function ISO8601ToDate(const DateString: string; ReturnUTC : Boolean): TDateTime;
  347. Function ISO8601ToDateDef(const DateString: string; ReturnUTC : Boolean; aDefault : TDateTime): TDateTime;
  348. Function TryISO8601ToDate(const DateString: string; ReturnUTC : Boolean;out ADateTime: TDateTime) : Boolean;
  349. implementation
  350. uses sysconst;
  351. const
  352. TDateTimeEpsilon = 2.2204460493e-16;
  353. HalfMilliSecond = OneMillisecond /2 ;
  354. { ---------------------------------------------------------------------
  355. Auxiliary routines
  356. ---------------------------------------------------------------------}
  357. Procedure NotYetImplemented (FN : String);
  358. begin
  359. Raise Exception.CreateFmt('Function "%s" (dateutils) is not yet implemented',[FN]);
  360. end;
  361. { ---------------------------------------------------------------------
  362. Simple trimming functions.
  363. ---------------------------------------------------------------------}
  364. Function DateOf(const AValue: TDateTime): TDateTime; inline;
  365. begin
  366. Result:=Trunc(AValue);
  367. end;
  368. Function TimeOf(const AValue: TDateTime): TDateTime; inline;
  369. begin
  370. Result:=Frac(Avalue);
  371. end;
  372. { ---------------------------------------------------------------------
  373. Identification functions.
  374. ---------------------------------------------------------------------}
  375. Function IsInLeapYear(const AValue: TDateTime): Boolean;
  376. begin
  377. Result:=IsLeapYear(YearOf(AValue));
  378. end;
  379. Function IsPM(const AValue: TDateTime): Boolean; inline;
  380. begin
  381. Result:=(HourOf(AValue)>=12);
  382. end;
  383. Function IsValidDate(const AYear, AMonth, ADay: Word): Boolean;
  384. begin
  385. Result:=(AYear<>0) and (AYear<10000)
  386. and (AMonth in [1..12])
  387. and (ADay<>0) and (ADay<=MonthDays[IsleapYear(AYear),AMonth]);
  388. end;
  389. Function IsValidTime(const AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
  390. begin
  391. Result:=(AHour=HoursPerDay) and (AMinute=0) and (ASecond=0) and (AMillisecond=0);
  392. Result:=Result or
  393. ((AHour<HoursPerDay) and (AMinute<MinsPerHour) and (ASecond<SecsPerMin) and
  394. (AMillisecond<MSecsPerSec));
  395. end;
  396. Function IsValidDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
  397. begin
  398. Result:=IsValidDate(AYear,AMonth,ADay) and
  399. IsValidTime(AHour,AMinute,ASecond,AMillisecond)
  400. end;
  401. Function IsValidDateDay(const AYear, ADayOfYear: Word): Boolean;
  402. begin
  403. Result:=(AYear<>0) and (ADayOfYear<>0) and (AYear<10000) and
  404. (ADayOfYear<=DaysPerYear[IsLeapYear(AYear)]);
  405. end;
  406. Function IsValidDateWeek(const AYear, AWeekOfYear, ADayOfWeek: Word): Boolean;
  407. begin
  408. Result:=(AYear<>0) and (AYear<10000)
  409. and (ADayOfWeek in [1..7])
  410. and (AWeekOfYear<>0)
  411. and (AWeekOfYear<=WeeksInaYear(AYear));
  412. { should we not also check whether the day of the week is not
  413. larger than the last day of the last week in the year 9999 ?? }
  414. end;
  415. Function IsValidDateMonthWeek(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word): Boolean;
  416. begin
  417. Result:=(AYear<>0) and (AYear<10000)
  418. and (AMonth in [1..12])
  419. and (AWeekOfMonth in [1..5])
  420. and (ADayOfWeek in [1..7]);
  421. end;
  422. { ---------------------------------------------------------------------
  423. Enumeration functions.
  424. ---------------------------------------------------------------------}
  425. Function WeeksInYear(const AValue: TDateTime): Word;
  426. begin
  427. Result:=WeeksInAYear(YearOf(AValue));
  428. end;
  429. Function WeeksInAYear(const AYear: Word): Word;
  430. Var
  431. DOW : Word;
  432. begin
  433. Result:=52;
  434. DOW:=DayOfTheWeek(StartOfAYear(AYear));
  435. If (DOW=4) or ((DOW=3) and IsLeapYear(AYear)) then
  436. Inc(Result);
  437. end;
  438. Function DaysInYear(const AValue: TDateTime): Word;
  439. begin
  440. Result:=DaysPerYear[IsLeapYear(YearOf(AValue))];
  441. end;
  442. Function DaysInAYear(const AYear: Word): Word;
  443. begin
  444. Result:=DaysPerYear[Isleapyear(AYear)];
  445. end;
  446. Function DaysInMonth(const AValue: TDateTime): Word;
  447. Var
  448. Y,M,D : Word;
  449. begin
  450. Decodedate(AValue,Y,M,D);
  451. Result:=MonthDays[IsLeapYear(Y),M];
  452. end;
  453. Function DaysInAMonth(const AYear, AMonth: Word): Word;
  454. begin
  455. Result:=MonthDays[IsLeapYear(AYear),AMonth];
  456. end;
  457. { ---------------------------------------------------------------------
  458. Variations on current date/time.
  459. ---------------------------------------------------------------------}
  460. Function Today: TDateTime; inline;
  461. begin
  462. Result:=Date();
  463. end;
  464. Function Yesterday: TDateTime;
  465. begin
  466. Result:=Date()-1;
  467. end;
  468. Function Tomorrow: TDateTime;
  469. begin
  470. Result:=Date()+1;
  471. end;
  472. Function IsToday(const AValue: TDateTime): Boolean;
  473. begin
  474. Result:=IsSameDay(AValue,Date());
  475. end;
  476. Function IsSameDay(const AValue, ABasis: TDateTime): Boolean;
  477. Var
  478. D : TDateTime;
  479. begin
  480. D:=AValue-Trunc(ABasis);
  481. Result:=(D>=0) and (D<1);
  482. end;
  483. function IsSameMonth(const Avalue, ABasis: TDateTime): Boolean;
  484. begin
  485. result:=( YearOf(Avalue) = YearOf(Abasis) );
  486. result:=result and ( MonthOf(AValue) = MonthOf(ABasis) );
  487. end;
  488. const
  489. DOWMap: array [1..7] of Word = (7, 1, 2, 3, 4, 5, 6);
  490. Function PreviousDayOfWeek (DayOfWeek : Word) : Word;
  491. begin
  492. If Not (DayOfWeek in [1..7]) then
  493. Raise EConvertError.CreateFmt(SErrInvalidDayOfWeek,[DayOfWeek]);
  494. Result:=DOWMap[DayOfWeek];
  495. end;
  496. { ---------------------------------------------------------------------
  497. Extraction functions.
  498. ---------------------------------------------------------------------}
  499. Function YearOf(const AValue: TDateTime): Word;
  500. Var
  501. D,M : Word;
  502. begin
  503. DecodeDate(AValue,Result,D,M);
  504. end;
  505. Function MonthOf(const AValue: TDateTime): Word;
  506. Var
  507. Y,D : Word;
  508. begin
  509. DecodeDate(AValue,Y,Result,D);
  510. end;
  511. Function WeekOf(const AValue: TDateTime): Word; inline;
  512. begin
  513. Result:=WeekOfTheYear(AValue);
  514. end;
  515. Function DayOf(const AValue: TDateTime): Word;
  516. Var
  517. Y,M : Word;
  518. begin
  519. DecodeDate(AValue,Y,M,Result);
  520. end;
  521. Function HourOf(const AValue: TDateTime): Word;
  522. Var
  523. N,S,MS : Word;
  524. begin
  525. DecodeTime(AValue,Result,N,S,MS);
  526. end;
  527. Function MinuteOf(const AValue: TDateTime): Word;
  528. Var
  529. H,S,MS : Word;
  530. begin
  531. DecodeTime(AValue,H,Result,S,MS);
  532. end;
  533. Function SecondOf(const AValue: TDateTime): Word;
  534. Var
  535. H,N,MS : Word;
  536. begin
  537. DecodeTime(AValue,H,N,Result,MS);
  538. end;
  539. Function MilliSecondOf(const AValue: TDateTime): Word;
  540. Var
  541. H,N,S : Word;
  542. begin
  543. DecodeTime(AValue,H,N,S,Result);
  544. end;
  545. { ---------------------------------------------------------------------
  546. Start/End of year functions.
  547. ---------------------------------------------------------------------}
  548. Function StartOfTheYear(const AValue: TDateTime): TDateTime;
  549. begin
  550. Result:=EncodeDate(YearOf(AValue),1,1);
  551. end;
  552. Function EndOfTheYear(const AValue: TDateTime): TDateTime;
  553. begin
  554. Result:=EncodeDateTime(YearOf(AValue),12,31,23,59,59,999);
  555. end;
  556. Function StartOfAYear(const AYear: Word): TDateTime;
  557. begin
  558. Result:=EncodeDate(AYear,1,1);
  559. end;
  560. Function EndOfAYear(const AYear: Word): TDateTime;
  561. begin
  562. Result:=(EncodeDateTime(AYear,12,31,23,59,59,999));
  563. end;
  564. { ---------------------------------------------------------------------
  565. Start/End of month functions.
  566. ---------------------------------------------------------------------}
  567. Function StartOfTheMonth(const AValue: TDateTime): TDateTime;
  568. Var
  569. Y,M,D : Word;
  570. begin
  571. DecodeDate(AValue,Y,M,D);
  572. Result:=EncodeDate(Y,M,1);
  573. // MonthDays[IsLeapYear(Y),M])
  574. end;
  575. Function EndOfTheMonth(const AValue: TDateTime): TDateTime;
  576. Var
  577. Y,M,D : Word;
  578. begin
  579. DecodeDate(AValue,Y,M,D);
  580. Result:=EncodeDateTime(Y,M,MonthDays[IsLeapYear(Y),M],23,59,59,999);
  581. end;
  582. Function StartOfAMonth(const AYear, AMonth: Word): TDateTime; inline;
  583. begin
  584. Result:=EncodeDate(AYear,AMonth,1);
  585. end;
  586. Function EndOfAMonth(const AYear, AMonth: Word): TDateTime;
  587. begin
  588. Result:=EncodeDateTime(AYear,AMonth,MonthDays[IsLeapYear(AYear),AMonth],23,59,59,999);
  589. end;
  590. { ---------------------------------------------------------------------
  591. Start/End of week functions.
  592. ---------------------------------------------------------------------}
  593. Function StartOfTheWeek(const AValue: TDateTime): TDateTime;
  594. begin
  595. Result:=Trunc(AValue)-DayOfTheWeek(AValue)+1;
  596. end;
  597. Function EndOfTheWeek(const AValue: TDateTime): TDateTime;
  598. begin
  599. Result:=EndOfTheDay(AValue-DayOfTheWeek(AValue)+7);
  600. end;
  601. Function StartOfAWeek(const AYear, AWeekOfYear: Word; const ADayOfWeek: Word): TDateTime;
  602. begin
  603. Result:=EncodeDateWeek(AYear,AWeekOfYear,ADayOfWeek);
  604. end;
  605. Function StartOfAWeek(const AYear, AWeekOfYear: Word): TDateTime; inline; // ADayOFWeek 1
  606. begin
  607. Result:=StartOfAWeek(AYear,AWeekOfYear,1)
  608. end;
  609. Function EndOfAWeek(const AYear, AWeekOfYear: Word; const ADayOfWeek: Word): TDateTime; inline;
  610. begin
  611. Result := EndOfTheDay(EncodeDateWeek(AYear, AWeekOfYear, ADayOfWeek));
  612. end;
  613. Function EndOfAWeek(const AYear, AWeekOfYear: Word): TDateTime; // const ADayOfWeek: Word = 7
  614. begin
  615. Result:=EndOfAWeek(AYear,AWeekOfYear,7);
  616. end;
  617. { ---------------------------------------------------------------------
  618. Start/End of day functions.
  619. ---------------------------------------------------------------------}
  620. Function StartOfTheDay(const AValue: TDateTime): TDateTime; inline;
  621. begin
  622. StartOfTheDay:=Trunc(Avalue);
  623. end;
  624. Function EndOfTheDay(const AValue: TDateTime): TDateTime;
  625. Var
  626. Y,M,D : Word;
  627. begin
  628. DecodeDate(AValue,Y,M,D);
  629. Result:=EncodeDateTime(Y,M,D,23,59,59,999);
  630. end;
  631. Function StartOfADay(const AYear, AMonth, ADay: Word): TDateTime; inline;
  632. begin
  633. Result:=EncodeDate(AYear,AMonth,ADay);
  634. end;
  635. Function StartOfADay(const AYear, ADayOfYear: Word): TDateTime;
  636. begin
  637. Result:=StartOfAYear(AYear)+ADayOfYear-1;
  638. end;
  639. Function EndOfADay(const AYear, AMonth, ADay: Word): TDateTime; inline;
  640. begin
  641. Result:=EndOfTheDay(EncodeDate(AYear,AMonth,ADay));
  642. end;
  643. Function EndOfADay(const AYear, ADayOfYear: Word): TDateTime;
  644. begin
  645. Result:=StartOfAYear(AYear)+ADayOfYear-1+EncodeTime(23,59,59,999);
  646. end;
  647. { ---------------------------------------------------------------------
  648. Part of year functions.
  649. ---------------------------------------------------------------------}
  650. Function MonthOfTheYear(const AValue: TDateTime): Word; inline;
  651. begin
  652. Result:=MonthOf(AValue);
  653. end;
  654. Function WeekOfTheYear(const AValue: TDateTime): Word;
  655. Var
  656. Y,DOW : Word;
  657. begin
  658. DecodeDateWeek(AValue,Y,Result,DOW)
  659. end;
  660. Function WeekOfTheYear(const AValue: TDateTime; out AYear: Word): Word;
  661. Var
  662. DOW : Word;
  663. begin
  664. DecodeDateWeek(AValue,AYear,Result,DOW);
  665. end;
  666. Function DayOfTheYear(const AValue: TDateTime): Word;
  667. begin
  668. Result:=Trunc(AValue-StartOfTheYear(AValue)+1);
  669. end;
  670. Function HourOfTheYear(const AValue: TDateTime): Word;
  671. Var
  672. H,M,S,MS : Word;
  673. begin
  674. DecodeTime(AValue,H,M,S,MS);
  675. Result:=H+((DayOfTheYear(AValue)-1)*24);
  676. end;
  677. Function MinuteOfTheYear(const AValue: TDateTime): LongWord;
  678. Var
  679. H,M,S,MS : Word;
  680. begin
  681. DecodeTime(AValue,H,M,S,MS);
  682. Result:=M+(H+((DayOfTheYear(AValue)-1)*24))*60;
  683. end;
  684. Function SecondOfTheYear(const AValue: TDateTime): LongWord;
  685. Var
  686. H,M,S,MS : Word;
  687. begin
  688. DecodeTime(AValue,H,M,S,MS);
  689. Result:=(M+(H+((DayOfTheYear(AValue)-1)*24))*60)*60+S;
  690. end;
  691. Function MilliSecondOfTheYear(const AValue: TDateTime): Int64;
  692. Var
  693. H,M,S,MS : Word;
  694. begin
  695. DecodeTime(AValue,H,M,S,MS);
  696. Result:=((M+(H+((int64(DayOfTheYear(AValue))-1)*24))*60)*60+S)*1000+MS;
  697. end;
  698. { ---------------------------------------------------------------------
  699. Part of month functions.
  700. ---------------------------------------------------------------------}
  701. Function WeekOfTheMonth(const AValue: TDateTime): Word;
  702. var
  703. Y,M,DOW : word;
  704. begin
  705. DecodeDateMonthWeek(AValue,Y,M,Result,DOW);
  706. end;
  707. Function WeekOfTheMonth(const AValue: TDateTime; out AYear, AMonth: Word): Word;
  708. Var
  709. DOW : Word;
  710. begin
  711. DecodeDateMonthWeek(AValue,AYear,AMonth,Result,DOW);
  712. end;
  713. Function DayOfTheMonth(const AValue: TDateTime): Word;
  714. Var
  715. Y,M : Word;
  716. begin
  717. DecodeDate(AValue,Y,M,Result);
  718. end;
  719. Function HourOfTheMonth(const AValue: TDateTime): Word;
  720. Var
  721. Y,M,D,H,N,S,MS : Word;
  722. begin
  723. DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
  724. Result:=(D-1)*24+H;
  725. end;
  726. Function MinuteOfTheMonth(const AValue: TDateTime): Word;
  727. Var
  728. Y,M,D,H,N,S,MS : Word;
  729. begin
  730. DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
  731. Result:=((D-1)*24+H)*60+N;
  732. end;
  733. Function SecondOfTheMonth(const AValue: TDateTime): LongWord;
  734. Var
  735. Y,M,D,H,N,S,MS : Word;
  736. begin
  737. DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
  738. Result:=(((D-1)*24+H)*60+N)*60+S;
  739. end;
  740. Function MilliSecondOfTheMonth(const AValue: TDateTime): LongWord;
  741. Var
  742. Y,M,D,H,N,S,MS : Word;
  743. begin
  744. DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
  745. Result:=((((D-1)*24+H)*60+N)*60+S)*1000+MS;
  746. end;
  747. { ---------------------------------------------------------------------
  748. Part of week functions.
  749. ---------------------------------------------------------------------}
  750. Function DayOfTheWeek(const AValue: TDateTime): Word;
  751. begin
  752. Result:=DowMAP[DayOfWeek(AValue)];
  753. end;
  754. Function HourOfTheWeek(const AValue: TDateTime): Word;
  755. Var
  756. H,M,S,MS : Word;
  757. begin
  758. DecodeTime(AValue,H,M,S,MS);
  759. Result:=(DayOfTheWeek(AValue)-1)*24+H;
  760. end;
  761. Function MinuteOfTheWeek(const AValue: TDateTime): Word;
  762. Var
  763. H,M,S,MS : Word;
  764. begin
  765. DecodeTime(AValue,H,M,S,MS);
  766. Result:=((DayOfTheWeek(AValue)-1)*24+H)*60+M;
  767. end;
  768. Function SecondOfTheWeek(const AValue: TDateTime): LongWord;
  769. Var
  770. H,M,S,MS : Word;
  771. begin
  772. DecodeTime(AValue,H,M,S,MS);
  773. Result:=(((DayOfTheWeek(AValue)-1)*24+H)*60+M)*60+S;
  774. end;
  775. Function MilliSecondOfTheWeek(const AValue: TDateTime): LongWord;
  776. Var
  777. H,M,S,MS : Word;
  778. begin
  779. DecodeTime(AValue,H,M,S,MS);
  780. Result:=((((DayOfTheWeek(AValue)-1)*24+H)*60+M)*60+S)*1000+MS;
  781. end;
  782. { ---------------------------------------------------------------------
  783. Part of day functions.
  784. ---------------------------------------------------------------------}
  785. Function HourOfTheDay(const AValue: TDateTime): Word; inline;
  786. begin
  787. Result:=HourOf(AValue);
  788. end;
  789. Function MinuteOfTheDay(const AValue: TDateTime): Word;
  790. Var
  791. H,M,S,MS : Word;
  792. begin
  793. DecodeTime(AValue,H,M,S,MS);
  794. Result:=(H*60)+M;
  795. end;
  796. Function SecondOfTheDay(const AValue: TDateTime): LongWord;
  797. Var
  798. H,M,S,MS : Word;
  799. begin
  800. DecodeTime(AValue,H,M,S,MS);
  801. Result:=((H*60)+M)*60+S;
  802. end;
  803. Function MilliSecondOfTheDay(const AValue: TDateTime): LongWord;
  804. Var
  805. H,M,S,MS : Word;
  806. begin
  807. DecodeTime(AValue,H,M,S,MS);
  808. Result:=(((H*60)+M)*60+S)*1000+MS;
  809. end;
  810. { ---------------------------------------------------------------------
  811. Part of hour functions.
  812. ---------------------------------------------------------------------}
  813. Function MinuteOfTheHour(const AValue: TDateTime): Word; inline;
  814. begin
  815. Result:=MinuteOf(AValue);
  816. end;
  817. Function SecondOfTheHour(const AValue: TDateTime): Word;
  818. Var
  819. H,S,M,MS : Word;
  820. begin
  821. DecodeTime(AValue,H,M,S,MS);
  822. Result:=M*60+S;
  823. end;
  824. Function MilliSecondOfTheHour(const AValue: TDateTime): LongWord;
  825. Var
  826. H,S,M,MS : Word;
  827. begin
  828. DecodeTime(AValue,H,M,S,MS);
  829. Result:=(M*60+S)*1000+MS;
  830. end;
  831. { ---------------------------------------------------------------------
  832. Part of minute functions.
  833. ---------------------------------------------------------------------}
  834. Function SecondOfTheMinute(const AValue: TDateTime): Word; inline;
  835. begin
  836. Result:=SecondOf(AValue);
  837. end;
  838. Function MilliSecondOfTheMinute(const AValue: TDateTime): LongWord;
  839. Var
  840. H,S,M,MS : Word;
  841. begin
  842. DecodeTime(AValue,H,M,S,MS);
  843. Result:=S*1000+MS;
  844. end;
  845. { ---------------------------------------------------------------------
  846. Part of second functions.
  847. ---------------------------------------------------------------------}
  848. Function MilliSecondOfTheSecond(const AValue: TDateTime): Word; inline;
  849. begin
  850. Result:=MilliSecondOf(AValue);
  851. end;
  852. { ---------------------------------------------------------------------
  853. Range checking functions.
  854. ---------------------------------------------------------------------}
  855. Function WithinPastYears(const ANow, AThen: TDateTime; const AYears: Integer): Boolean; inline;
  856. begin
  857. Result:=YearsBetween(ANow,AThen)<=AYears;
  858. end;
  859. Function WithinPastMonths(const ANow, AThen: TDateTime; const AMonths: Integer): Boolean; inline;
  860. begin
  861. Result:=MonthsBetween(ANow,AThen)<=AMonths;
  862. end;
  863. Function WithinPastWeeks(const ANow, AThen: TDateTime; const AWeeks: Integer): Boolean; inline;
  864. begin
  865. Result:=WeeksBetween(ANow,AThen)<=AWeeks;
  866. end;
  867. Function WithinPastDays(const ANow, AThen: TDateTime; const ADays: Integer): Boolean; inline;
  868. begin
  869. Result:=DaysBetween(ANow,AThen)<=ADays;
  870. end;
  871. Function WithinPastHours(const ANow, AThen: TDateTime; const AHours: Int64): Boolean; inline;
  872. begin
  873. Result:=HoursBetween(ANow,AThen)<=AHours;
  874. end;
  875. Function WithinPastMinutes(const ANow, AThen: TDateTime; const AMinutes: Int64): Boolean; inline;
  876. begin
  877. Result:=MinutesBetween(ANow,AThen)<=AMinutes;
  878. end;
  879. Function WithinPastSeconds(const ANow, AThen: TDateTime; const ASeconds: Int64): Boolean; inline;
  880. begin
  881. Result:=SecondsBetween(ANow,Athen)<=ASeconds;
  882. end;
  883. Function WithinPastMilliSeconds(const ANow, AThen: TDateTime; const AMilliSeconds: Int64): Boolean; inline;
  884. begin
  885. Result:=MilliSecondsBetween(ANow,AThen)<=AMilliSeconds;
  886. end;
  887. function DateTimeInRange(ADateTime: TDateTime; AStartDateTime, AEndDateTime: TDateTime; aInclusive: Boolean = True): Boolean;
  888. begin
  889. if aInclusive then
  890. Result:=(AStartDateTime<=ADateTime) and (ADateTime<=AEndDateTime)
  891. else
  892. Result:=(AStartDateTime<ADateTime) and (ADateTime<AEndDateTime);
  893. end;
  894. function TimeInRange(ATime: TTime; AStartTime, AEndTime: TTime; AInclusive: Boolean = True): Boolean;
  895. var
  896. LTime, LStartTime, LEndTime: TTime;
  897. begin
  898. LTime:=TimeOf(ATime);
  899. LStartTime:=TimeOf(AStartTime);
  900. LEndTime:=TimeOf(AEndTime);
  901. if LEndTime<LStartTime then
  902. if AInclusive then
  903. Result:=(LStartTime<=LTime) or (LTime<=LEndTime)
  904. else
  905. Result:=(LStartTime<LTime) or (LTime<LEndTime)
  906. else
  907. if AInclusive then
  908. Result:=(LStartTime<=LTime) and (LTime<=LEndTime)
  909. else
  910. Result:=(LStartTime<LTime) and (LTime<LEndTime);
  911. end;
  912. function DateInRange(ADate: TDate; AStartDate, AEndDate: TDate; AInclusive: Boolean = True): Boolean;
  913. begin
  914. if AInclusive then
  915. Result:=(DateOf(AStartDate)<=DateOf(ADate)) and (DateOf(ADate)<=DateOf(AEndDate))
  916. else
  917. Result:=(DateOf(AStartDate)<DateOf(ADate)) and (DateOf(ADate)<DateOf(AEndDate));
  918. end;
  919. { ---------------------------------------------------------------------
  920. Period functions.
  921. ---------------------------------------------------------------------}
  922. {
  923. These functions are declared as approximate by Borland.
  924. A bit strange, since it can be calculated exactly ?
  925. -- No, because you need rounding or truncating (JM)
  926. }
  927. Function DateTimeDiff(const ANow, AThen: TDateTime): TDateTime;
  928. begin
  929. Result:= ANow - AThen;
  930. if (ANow>0) and (AThen<0) then
  931. Result:=Result-0.5
  932. else if (ANow<-1.0) and (AThen>-1.0) then
  933. Result:=Result+0.5;
  934. end;
  935. Function YearsBetween(const ANow, AThen: TDateTime; AExact : Boolean = False): Integer;
  936. var
  937. yy, mm, dd: Word;
  938. begin
  939. if AExact and (ANow >= -DateDelta) and (AThen >= -DateDelta) and
  940. (ANow <= MaxDateTime) and (AThen <= MaxDateTime) then
  941. begin
  942. PeriodBetween(ANow, AThen, yy , mm, dd);
  943. Result := yy;
  944. end
  945. else
  946. Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)/ApproxDaysPerYear);
  947. end;
  948. Function MonthsBetween(const ANow, AThen: TDateTime; AExact : Boolean = False): Integer;
  949. var
  950. y, m, d: Word;
  951. begin
  952. if AExact and (ANow >= -DateDelta) and (AThen >= -DateDelta) and
  953. (ANow <= MaxDateTime) and (AThen <= MaxDateTime) then
  954. begin
  955. PeriodBetween(ANow, AThen, y, m, d);
  956. Result := y*12 + m;
  957. end
  958. else
  959. Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)/ApproxDaysPerMonth);
  960. end;
  961. Function WeeksBetween(const ANow, AThen: TDateTime): Integer;
  962. begin
  963. Result:=Trunc(Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond) div 7;
  964. end;
  965. Function DaysBetween(const ANow, AThen: TDateTime): Integer;
  966. begin
  967. Result:=Trunc(Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond);
  968. end;
  969. Function HoursBetween(const ANow, AThen: TDateTime): Int64;
  970. begin
  971. Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)*HoursPerDay);
  972. end;
  973. Function MinutesBetween(const ANow, AThen: TDateTime): Int64;
  974. begin
  975. Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)*MinsPerDay);
  976. end;
  977. Function SecondsBetween(const ANow, AThen: TDateTime): Int64;
  978. begin
  979. Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)*SecsPerDay);
  980. end;
  981. Function MilliSecondsBetween(const ANow, AThen: TDateTime): Int64;
  982. begin
  983. Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)*MSecsPerDay);
  984. end;
  985. Procedure PeriodBetween(Const ANow, AThen: TDateTime; Out Years, months, days : Word);
  986. var
  987. Y1, Y2, M1, M2, D1, D2: word;
  988. begin
  989. if (AThen>ANow) then
  990. begin
  991. DecodeDate(ANow,Y1,M1,D1);
  992. DecodeDate(AThen,Y2,M2,D2);
  993. end
  994. else
  995. begin
  996. DecodeDate(AThen,Y1,M1,D1);
  997. DecodeDate(ANow,Y2,M2,D2);
  998. end;
  999. Years:=Y2-Y1;
  1000. if (M1>M2) or ((M1=M2) and (D1>D2)) then Dec(Years);
  1001. if (M1>M2) then Inc(M2,12); //already adjusted Years in that case
  1002. Months:=M2-M1;
  1003. if (D2>=D1) then
  1004. Days:=D2-D1
  1005. else
  1006. begin
  1007. if (Months=0) then
  1008. Months:=11
  1009. else
  1010. Dec(Months);
  1011. Days:=(DaysInAMonth(Y1,M1)-D1)+D2;
  1012. end;
  1013. end;
  1014. { ---------------------------------------------------------------------
  1015. Timespan in xxx functions.
  1016. ---------------------------------------------------------------------}
  1017. Function YearSpan(const ANow, AThen: TDateTime): Double;
  1018. begin
  1019. Result:=Abs(DateTimeDiff(ANow,AThen))/ApproxDaysPerYear;
  1020. end;
  1021. Function MonthSpan(const ANow, AThen: TDateTime): Double;
  1022. begin
  1023. Result:=Abs(DateTimeDiff(ANow,AThen))/ApproxDaysPerMonth;
  1024. end;
  1025. Function WeekSpan(const ANow, AThen: TDateTime): Double;
  1026. begin
  1027. Result:=Abs(DateTimeDiff(ANow,AThen)) / 7
  1028. end;
  1029. Function DaySpan(const ANow, AThen: TDateTime): Double;
  1030. begin
  1031. Result:=Abs(DateTimeDiff(ANow,AThen));
  1032. end;
  1033. Function HourSpan(const ANow, AThen: TDateTime): Double;
  1034. begin
  1035. Result:=Abs(DateTimeDiff(ANow,AThen))*HoursPerDay;
  1036. end;
  1037. Function MinuteSpan(const ANow, AThen: TDateTime): Double;
  1038. begin
  1039. Result:=Abs(DateTimeDiff(ANow,AThen))*MinsPerDay;
  1040. end;
  1041. Function SecondSpan(const ANow, AThen: TDateTime): Double;
  1042. begin
  1043. Result:=Abs(DateTimeDiff(ANow,AThen))*SecsPerDay;
  1044. end;
  1045. Function MilliSecondSpan(const ANow, AThen: TDateTime): Double;
  1046. begin
  1047. Result:=Abs(DateTimeDiff(ANow,AThen))*MSecsPerDay;
  1048. end;
  1049. { ---------------------------------------------------------------------
  1050. Increment/decrement functions.
  1051. ---------------------------------------------------------------------}
  1052. { TDateTime is not defined in the interval [-1.0..0.0[. Additionally, when
  1053. negative the time part must be treated using its absolute value (0.25 always
  1054. means "6 a.m.") -> skip the gap and convert the time part when crossing the
  1055. gap -- and take care of rounding errors }
  1056. Procedure MaybeSkipTimeWarp(OldDate: TDateTime; var NewDate: TDateTime);
  1057. begin
  1058. if (OldDate>=0) and (NewDate<-TDateTimeEpsilon) then
  1059. NewDate:=int(NewDate-1.0+TDateTimeEpsilon)-frac(1.0+frac(NewDate))
  1060. else if (OldDate<=-1.0) and (NewDate>-1.0+TDateTimeEpsilon) then
  1061. NewDate:=int(NewDate+1.0-TDateTimeEpsilon)+frac(1.0-abs(frac(1.0+NewDate)));
  1062. end;
  1063. function IncNegativeTime(AValue, Addend: TDateTime): TDateTime;
  1064. var
  1065. newtime: tdatetime;
  1066. begin
  1067. newtime:=-frac(Avalue)+frac(Addend);
  1068. { handle rounding errors }
  1069. if SameValue(newtime,int(newtime)+1,TDateTimeEpsilon) then
  1070. newtime:=int(newtime)+1
  1071. else if SameValue(newtime,int(newtime),TDateTimeEpsilon) then
  1072. newtime:=int(newtime);
  1073. { time underflow -> previous day }
  1074. if newtime<-TDateTimeEpsilon then
  1075. begin
  1076. newtime:=1.0+newtime;
  1077. avalue:=int(avalue)-1;
  1078. end
  1079. { time overflow -> next day }
  1080. else if newtime>=1.0-TDateTimeEpsilon then
  1081. begin
  1082. newtime:=newtime-1.0;
  1083. avalue:=int(avalue)+1;
  1084. end;
  1085. Result:=int(AValue)+int(Addend)-newtime;
  1086. end;
  1087. Function IncYear(const AValue: TDateTime; const ANumberOfYears: Integer ): TDateTime;
  1088. Var
  1089. Y,M,D,H,N,S,MS : Word;
  1090. begin
  1091. DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
  1092. Y:=Y+ANumberOfYears;
  1093. If (M=2) and (D=29) And (Not IsLeapYear(Y)) then
  1094. D:=28;
  1095. Result:=EncodeDateTime(Y,M,D,H,N,S,MS);
  1096. end;
  1097. Function IncYear(const AValue: TDateTime): TDateTime; // ; const ANumberOfYears: Integer = 1)
  1098. begin
  1099. Result:=IncYear(Avalue,1);
  1100. end;
  1101. Function IncWeek(const AValue: TDateTime; const ANumberOfWeeks: Integer): TDateTime;
  1102. begin
  1103. Result:=AValue+ANumberOfWeeks*7;
  1104. MaybeSkipTimeWarp(AValue,Result);
  1105. end;
  1106. Function IncWeek(const AValue: TDateTime): TDateTime; // ; const ANumberOfWeeks: Integer = 1)
  1107. begin
  1108. Result:=IncWeek(Avalue,1);
  1109. end;
  1110. Function IncDay(const AValue: TDateTime; const ANumberOfDays: Integer): TDateTime;
  1111. begin
  1112. Result:=AValue+ANumberOfDays;
  1113. MaybeSkipTimeWarp(AValue,Result);
  1114. end;
  1115. Function IncDay(const AValue: TDateTime): TDateTime; //; const ANumberOfDays: Integer = 1)
  1116. begin
  1117. Result:=IncDay(Avalue,1);
  1118. end;
  1119. Function IncHour(const AValue: TDateTime; const ANumberOfHours: Int64): TDateTime;
  1120. begin
  1121. if AValue>=0 then
  1122. Result:=AValue+ANumberOfHours/HoursPerDay
  1123. else
  1124. Result:=IncNegativeTime(Avalue,ANumberOfHours/HoursPerDay);
  1125. MaybeSkipTimeWarp(AValue,Result);
  1126. end;
  1127. Function IncHour(const AValue: TDateTime): TDateTime; //; const ANumberOfHours: Int64 = 1
  1128. begin
  1129. Result:=IncHour(AValue,1);
  1130. end;
  1131. Function IncMinute(const AValue: TDateTime; const ANumberOfMinutes: Int64): TDateTime;
  1132. begin
  1133. if AValue>=0 then
  1134. Result:=AValue+ANumberOfMinutes/MinsPerDay
  1135. else
  1136. Result:=IncNegativeTime(Avalue,ANumberOfMinutes/MinsPerDay);
  1137. MaybeSkipTimeWarp(AValue,Result);
  1138. end;
  1139. Function IncMinute(const AValue: TDateTime): TDateTime; // ; const ANumberOfMinutes: Int64 = 1
  1140. begin
  1141. Result:=IncMinute(AValue,1);
  1142. end;
  1143. Function IncSecond(const AValue: TDateTime; const ANumberOfSeconds: Int64): TDateTime;
  1144. begin
  1145. if AValue>=0 then
  1146. Result:=AValue+ANumberOfSeconds/SecsPerDay
  1147. else
  1148. Result:=IncNegativeTime(Avalue,ANumberOfSeconds/SecsPerDay);
  1149. MaybeSkipTimeWarp(AValue,Result);
  1150. end;
  1151. Function IncSecond(const AValue: TDateTime): TDateTime; // ; const ANumberOfSeconds: Int64 = 1
  1152. begin
  1153. Result:=IncSecond(Avalue,1);
  1154. end;
  1155. Function IncMilliSecond(const AValue: TDateTime; const ANumberOfMilliSeconds: Int64): TDateTime;
  1156. begin
  1157. if Avalue>=0 then
  1158. Result:=AValue+ANumberOfMilliSeconds/MSecsPerDay
  1159. else
  1160. Result:=IncNegativeTime(Avalue,ANumberOfMilliSeconds/MSecsPerDay);
  1161. MaybeSkipTimeWarp(AValue,Result);
  1162. end;
  1163. Function IncMilliSecond(const AValue: TDateTime): TDateTime; // ; const ANumberOfMilliSeconds: Int64 = 1
  1164. begin
  1165. Result:=IncMilliSecond(AValue,1);
  1166. end;
  1167. { ---------------------------------------------------------------------
  1168. Encode/Decode of complete timestamp
  1169. ---------------------------------------------------------------------}
  1170. Function EncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime;
  1171. begin
  1172. If Not TryEncodeDateTime(AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond,Result) then
  1173. InvalidDateTimeError(AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond)
  1174. end;
  1175. Procedure DecodeDateTime(const AValue: TDateTime; out AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word);
  1176. begin
  1177. DecodeTime(AValue,AHour,AMinute,ASecond,AMilliSecond);
  1178. if AHour=24 then // can happen due rounding issues mantis 17123
  1179. begin
  1180. AHour:=0; // rest is already zero
  1181. DecodeDate(round(AValue),AYear,AMonth,ADay);
  1182. end
  1183. else
  1184. DecodeDate(AValue,AYear,AMonth,ADay);
  1185. end;
  1186. Function TryEncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; out AValue: TDateTime): Boolean;
  1187. Var
  1188. tmp : TDateTime;
  1189. begin
  1190. Result:=TryEncodeDate(AYear,AMonth,ADay,AValue);
  1191. Result:=Result and TryEncodeTime(AHour,AMinute,ASecond,Amillisecond,Tmp);
  1192. If Result then
  1193. Avalue:=ComposeDateTime(AValue,Tmp);
  1194. end;
  1195. { ---------------------------------------------------------------------
  1196. Encode/decode date, specifying week of year and day of week
  1197. ---------------------------------------------------------------------}
  1198. Function EncodeDateWeek(const AYear, AWeekOfYear: Word; const ADayOfWeek: Word): TDateTime;
  1199. begin
  1200. If Not TryEncodeDateWeek(AYear,AWeekOfYear,Result,ADayOfWeek) then
  1201. InvalidDateWeekError(AYear,AWeekOfYear,ADayOfWeek);
  1202. end;
  1203. Function EncodeDateWeek(const AYear, AWeekOfYear: Word): TDateTime; //; const ADayOfWeek: Word = 1
  1204. begin
  1205. Result := EncodeDateWeek(AYear,AWeekOfYear,1);
  1206. end;
  1207. Procedure DecodeDateWeek(const AValue: TDateTime; out AYear, AWeekOfYear, ADayOfWeek: Word);
  1208. var
  1209. DOY : Integer;
  1210. D: Word;
  1211. YS : TDateTime;
  1212. YSDOW, YEDOW: Word;
  1213. begin
  1214. AYear:=YearOf(AValue);
  1215. // Correct to ISO DOW
  1216. ADayOfWeek:=DayOfWeek(AValue)-1;
  1217. If ADAyOfWeek=0 then
  1218. ADayofweek:=7;
  1219. YS:=StartOfAYear(AYear);
  1220. DOY:=Trunc(AValue-YS)+1;
  1221. YSDOW:=DayOfTheWeek(YS);
  1222. // Correct week if later than wednesday. First week never starts later than wednesday
  1223. if (YSDOW<5) then
  1224. Inc(DOY,YSDOW-1)
  1225. else
  1226. Dec(DOY,8-YSDOW);
  1227. if (DOY<=0) then // Day is in last week of previous year.
  1228. DecodeDateWeek(YS-1,AYear,AWeekOfYear,D)
  1229. else
  1230. begin
  1231. AWeekOfYear:=DOY div 7;
  1232. if ((DOY mod 7)<>0) then
  1233. Inc(AWeekOfYear);
  1234. if (AWeekOfYear>52) then // Maybe in first week of next year ?
  1235. begin
  1236. YEDOW:=YSDOW;
  1237. if IsLeapYear(AYear) then
  1238. begin
  1239. Inc(YEDOW);
  1240. if (YEDOW>7) then
  1241. YEDOW:=1;
  1242. end;
  1243. if (YEDOW<4) then // Really next year.
  1244. begin
  1245. Inc(AYear);
  1246. AWeekOfYear:=1;
  1247. end;
  1248. end;
  1249. end;
  1250. end;
  1251. Function TryEncodeDateWeek(const AYear, AWeekOfYear: Word; out AValue: TDateTime; const ADayOfWeek: Word): Boolean;
  1252. Var
  1253. DOW : Word;
  1254. Rest : Integer;
  1255. begin
  1256. Result:=IsValidDateWeek(Ayear,AWeekOfYear,ADayOfWeek);
  1257. If Result then
  1258. begin
  1259. AValue:=EncodeDate(AYear,1,1)+(7*(AWeekOfYear-1));
  1260. DOW:=DayOfTheWeek(AValue);
  1261. Rest:=ADayOfWeek-DOW;
  1262. If (DOW>4) then
  1263. Inc(Rest,7);
  1264. AValue:=AValue+Rest;
  1265. end;
  1266. end;
  1267. Function TryEncodeDateWeek(const AYear, AWeekOfYear: Word; out AValue: TDateTime): Boolean; //; const ADayOfWeek: Word = 1
  1268. begin
  1269. Result:=TryEncodeDateWeek(AYear,AWeekOfYear,AValue,1);
  1270. end;
  1271. { ---------------------------------------------------------------------
  1272. Encode/decode date, specifying day of year
  1273. ---------------------------------------------------------------------}
  1274. Function EncodeDateDay(const AYear, ADayOfYear: Word): TDateTime;
  1275. begin
  1276. If Not TryEncodeDateDay(AYear,ADayOfYear,Result) then
  1277. InvalidDateDayError(AYear,ADayOfYear);
  1278. end;
  1279. Procedure DecodeDateDay(const AValue: TDateTime; out AYear, ADayOfYear: Word);
  1280. Var
  1281. M,D : Word;
  1282. begin
  1283. DecodeDate(AValue,AYear,M,D);
  1284. ADayOfyear:=Trunc(AValue-EncodeDate(AYear,1,1))+1;
  1285. end;
  1286. Function TryEncodeDateDay(const AYear, ADayOfYear: Word; out AValue: TDateTime): Boolean;
  1287. begin
  1288. Result:=(ADayOfYear<>0) and (ADayOfYear<=DaysPerYear [IsleapYear(AYear)]);
  1289. If Result then
  1290. AValue:=EncodeDate(AYear,1,1)+ADayOfYear-1;
  1291. end;
  1292. { ---------------------------------------------------------------------
  1293. Encode/decode date, specifying week of month
  1294. ---------------------------------------------------------------------}
  1295. Function EncodeDateMonthWeek(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word): TDateTime;
  1296. begin
  1297. If Not TryEncodeDateMonthWeek(Ayear,AMonth,AWeekOfMonth,ADayOfWeek,Result) then
  1298. InvalidDateMonthWeekError(AYear,AMonth,AWeekOfMonth,ADayOfWeek);
  1299. end;
  1300. Procedure DecodeDateMonthWeek(const AValue: TDateTime; out AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word);
  1301. Var
  1302. D,SDOM,EDOM : Word;
  1303. SOM : TdateTime;
  1304. DOM : Integer;
  1305. begin
  1306. DecodeDate(AValue,AYear,AMonth,D);
  1307. ADayOfWeek:=DayOfTheWeek(AValue);
  1308. SOM:=EncodeDate(Ayear,Amonth,1);
  1309. SDOM:=DayOfTheWeek(SOM);
  1310. DOM:=D-1+SDOM;
  1311. If SDOM>4 then
  1312. Dec(DOM,7);
  1313. // Too early in the month. First full week is next week, day is after thursday.
  1314. If DOM<=0 Then
  1315. DecodeDateMonthWeek(SOM-1,AYear,AMonth,AWeekOfMonth,D)
  1316. else
  1317. begin
  1318. AWeekOfMonth:=(DOM div 7)+Ord((DOM mod 7)<>0);
  1319. EDOM:=DayOfTheWeek(EndOfAMonth(Ayear,AMonth));
  1320. // In last days of last long week, so in next month...
  1321. If (EDOM<4) and ((DaysInAMonth(AYear,Amonth)-D)<EDOM) then
  1322. begin
  1323. AWeekOfMonth:=1;
  1324. Inc(AMonth);
  1325. If (AMonth=13) then
  1326. begin
  1327. AMonth:=1;
  1328. Inc(AYear);
  1329. end;
  1330. end;
  1331. end;
  1332. end;
  1333. Function TryEncodeDateMonthWeek(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word; out AValue: TDateTime): Boolean;
  1334. var
  1335. S : Word;
  1336. DOM : Integer;
  1337. begin
  1338. Result:=IsValidDateMonthWeek(AYear,AMonth,AWeekOfMonth,ADayOfWeek);
  1339. if Result then
  1340. begin
  1341. AValue:=EncodeDate(AYear,AMonth,1);
  1342. DOM:=(AWeekOfMonth-1)*7+ADayOfWeek-1;
  1343. { Correct for first week in last month.}
  1344. S:=DayOfTheWeek(AValue);
  1345. Dec(DOM,S-1);
  1346. if S in [DayFriday..DaySunday] then
  1347. Inc(DOM,7);
  1348. AValue:=AValue+DOM;
  1349. end;
  1350. end;
  1351. { ---------------------------------------------------------------------
  1352. Encode time interval, allowing hours>24
  1353. ---------------------------------------------------------------------}
  1354. function TryEncodeTimeInterval(Hour, Min, Sec, MSec: word; out Time: TDateTime): boolean;
  1355. begin
  1356. Result:= (Min<60) and (Sec<60) and (MSec<1000);
  1357. If Result then
  1358. Time:=TDateTime(cardinal(Hour)*3600000+cardinal(Min)*60000+cardinal(Sec)*1000+MSec)/MSecsPerDay;
  1359. end;
  1360. function EncodeTimeInterval(Hour, Minute, Second, MilliSecond: word): TDateTime;
  1361. begin
  1362. If not TryEncodeTimeInterval(Hour,Minute,Second,MilliSecond,Result) then
  1363. Raise EConvertError.CreateFmt(SerrInvalidHourMinuteSecMsec,
  1364. [Hour,Minute,Second,MilliSecond]);
  1365. end;
  1366. { ---------------------------------------------------------------------
  1367. Replace given element with supplied value.
  1368. ---------------------------------------------------------------------}
  1369. Const
  1370. LFAI = RecodeLeaveFieldAsIS; // Less typing, readable code
  1371. {
  1372. Note: We have little choice but to implement it like Borland did:
  1373. If AValue contains some 'wrong' value, it will throw an error.
  1374. To simulate this we'd have to check in each function whether
  1375. both arguments are correct. To avoid it, all is routed through
  1376. the 'central' RecodeDateTime function as in Borland's implementation.
  1377. }
  1378. Function RecodeYear(const AValue: TDateTime; const AYear: Word): TDateTime;
  1379. begin
  1380. Result := RecodeDateTime(AValue,AYear,LFAI,LFAI,LFAI,LFAI,LFAI,LFAI);
  1381. end;
  1382. Function RecodeMonth(const AValue: TDateTime; const AMonth: Word): TDateTime;
  1383. begin
  1384. Result := RecodeDateTime(AValue,LFAI,AMonth,LFAI,LFAI,LFAI,LFAI,LFAI);
  1385. end;
  1386. Function RecodeDay(const AValue: TDateTime; const ADay: Word): TDateTime;
  1387. begin
  1388. Result := RecodeDateTime(AValue,LFAI,LFAI,ADay,LFAI,LFAI,LFAI,LFAI);
  1389. end;
  1390. Function RecodeHour(const AValue: TDateTime; const AHour: Word): TDateTime;
  1391. begin
  1392. Result := RecodeDateTime(AValue,LFAI,LFAI,LFAI,AHour,LFAI,LFAI,LFAI);
  1393. end;
  1394. Function RecodeMinute(const AValue: TDateTime; const AMinute: Word): TDateTime;
  1395. begin
  1396. Result := RecodeDateTime(AValue,LFAI,LFAI,LFAI,LFAI,AMinute,LFAI,LFAI);
  1397. end;
  1398. Function RecodeSecond(const AValue: TDateTime; const ASecond: Word): TDateTime;
  1399. begin
  1400. Result := RecodeDateTime(AValue,LFAI,LFAI,LFAI,LFAI,LFAI,ASecond,LFAI);
  1401. end;
  1402. Function RecodeMilliSecond(const AValue: TDateTime; const AMilliSecond: Word): TDateTime;
  1403. begin
  1404. Result := RecodeDateTime(AValue,LFAI,LFAI,LFAI,LFAI,LFAI,LFAI,AMilliSecond);
  1405. end;
  1406. Function RecodeDate(const AValue: TDateTime; const AYear, AMonth, ADay: Word): TDateTime;
  1407. begin
  1408. Result := RecodeDateTime(AValue,AYear,AMonth,ADay,LFAI,LFAI,LFAI,LFAI);
  1409. end;
  1410. Function RecodeTime(const AValue: TDateTime; const AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime;
  1411. begin
  1412. Result := RecodeDateTime(AValue,LFAI,LFAI,LFAI,AHour,AMinute,ASecond,AMilliSecond);
  1413. end;
  1414. Function RecodeDateTime(const AValue: TDateTime; const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime;
  1415. begin
  1416. If Not TryRecodeDateTime(AValue,AYear,AMonth,ADay,AHour,AMinute,ASecond,AMilliSecond,Result) then
  1417. InvalidDateTimeError(AYear,AMonth,ADay,AHour,AMinute,ASecond,AMilliSecond,AValue);
  1418. end;
  1419. Function TryRecodeDateTime(const AValue: TDateTime; const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; out AResult: TDateTime): Boolean;
  1420. Procedure FV (Var AV : Word; Arg : Word);
  1421. begin
  1422. If (Arg<>LFAI) then
  1423. AV:=Arg;
  1424. end;
  1425. Var
  1426. Y,M,D,H,N,S,MS : Word;
  1427. begin
  1428. DecodeDateTime(AValue,Y,M,D,H,N,S,MS);
  1429. FV(Y,AYear);
  1430. FV(M,AMonth);
  1431. FV(D,ADay);
  1432. FV(H,AHour);
  1433. FV(N,AMinute);
  1434. FV(S,ASecond);
  1435. FV(MS,AMillisecond);
  1436. Result:=TryEncodeDateTime(Y,M,D,H,N,S,MS,AResult);
  1437. end;
  1438. { ---------------------------------------------------------------------
  1439. Comparision of date/time
  1440. ---------------------------------------------------------------------}
  1441. Function CompareDateTime(const A, B: TDateTime): TValueRelationship;
  1442. begin
  1443. If SameDateTime(A,B) then
  1444. Result:=EqualsValue
  1445. else if trunc(a)=trunc(b) then
  1446. begin
  1447. if abs(frac(a))>abs(frac(b)) then
  1448. result:=GreaterThanValue
  1449. else
  1450. result:=LessThanValue;
  1451. end
  1452. else
  1453. begin
  1454. if a>b then
  1455. result:=GreaterThanValue
  1456. else
  1457. result:=LessThanValue;
  1458. end;
  1459. end;
  1460. Function CompareDate(const A, B: TDateTime): TValueRelationship;
  1461. begin
  1462. If SameDate(A,B) then
  1463. Result:=EQualsValue
  1464. else if A<B then
  1465. Result:=LessThanValue
  1466. else
  1467. Result:=GreaterThanValue;
  1468. end;
  1469. Function CompareTime(const A, B: TDateTime): TValueRelationship;
  1470. begin
  1471. If SameTime(A,B) then
  1472. Result:=EQualsValue
  1473. else If Frac(A)<Frac(B) then
  1474. Result:=LessThanValue
  1475. else
  1476. Result:=GreaterThanValue;
  1477. end;
  1478. Function SameDateTime(const A, B: TDateTime): Boolean;
  1479. begin
  1480. Result:=Abs(A-B)<OneMilliSecond;
  1481. end;
  1482. Function SameDate(const A, B: TDateTime): Boolean; inline;
  1483. begin
  1484. Result:=Trunc(A)=Trunc(B);
  1485. end;
  1486. Function SameTime(const A, B: TDateTime): Boolean;
  1487. begin
  1488. Result:=Frac(Abs(A-B))<OneMilliSecond;
  1489. end;
  1490. Function InternalNthDayOfWeek(DoM : Word) : Word;
  1491. begin
  1492. Result:=(Dom-1) div 7 +1;
  1493. end;
  1494. Function NthDayOfWeek(const AValue: TDateTime): Word;
  1495. begin
  1496. Result:=InternalNthDayOfWeek(DayOfTheMonth(AValue));
  1497. end;
  1498. Procedure DecodeDayOfWeekInMonth(const AValue: TDateTime; out AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word);
  1499. var
  1500. D: Word;
  1501. begin
  1502. DecodeDate(AValue,AYear,AMonth,D);
  1503. ADayOfWeek:=DayOfTheWeek(AValue);
  1504. ANthDayOfWeek:=InternalNthDayOfWeek(D);
  1505. end;
  1506. Function EncodeDayOfWeekInMonth(const AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word): TDateTime;
  1507. begin
  1508. If Not TryEncodeDayOfWeekInMonth(AYear,AMonth,ANthDayOfWeek,ADayOfWeek,Result) then
  1509. InvalidDayOfWeekInMonthError(AYear,AMonth,ANthDayOfWeek,ADayOfWeek);
  1510. end;
  1511. Function TryEncodeDayOfWeekInMonth(const AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word; out AValue: TDateTime): Boolean;
  1512. Var
  1513. SOM,D : Word;
  1514. begin
  1515. SOM:=DayOfTheWeek(EncodeDate(Ayear,AMonth,1));
  1516. D:=1+ADayOfWeek-SOM+7*(ANthDayOfWeek-1);
  1517. If SOM>ADayOfWeek then
  1518. D:=D+7; // Clearer would have been Inc(ANthDayOfweek) but it's a const
  1519. Result:=TryEncodeDate(Ayear,AMonth,D,AValue);
  1520. end;
  1521. { ---------------------------------------------------------------------
  1522. Exception throwing routines
  1523. ---------------------------------------------------------------------}
  1524. Procedure InvalidDateTimeError(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; const ABaseDate: TDateTime);
  1525. Function DoField(Arg,Def : Word; Unknown: String) : String;
  1526. begin
  1527. If (Arg<>LFAI) then
  1528. Result:=Format('%.*d',[Length(Unknown),Arg])
  1529. else if (ABaseDate=0) then
  1530. Result:=Unknown
  1531. else
  1532. Result:=Format('%.*d',[Length(Unknown),Arg]);
  1533. end;
  1534. Var
  1535. Y,M,D,H,N,S,MS : Word;
  1536. Msg : String;
  1537. begin
  1538. DecodeDateTime(ABasedate,Y,M,D,H,N,S,MS);
  1539. Msg:=DoField(AYear,Y,'????');
  1540. Msg:=Msg+DefaultFormatSettings.DateSeparator+DoField(AMonth,M,'??');
  1541. Msg:=Msg+DefaultFormatSettings.DateSeparator+DoField(ADay,D,'??');
  1542. Msg:=Msg+' '+DoField(AHour,H,'??');
  1543. Msg:=Msg+DefaultFormatSettings.TimeSeparator+DoField(AMinute,N,'??');
  1544. Msg:=Msg+DefaultFormatSettings.TimeSeparator+Dofield(ASecond,S,'??');
  1545. Msg:=Msg+DefaultFormatSettings.DecimalSeparator+DoField(AMilliSecond,MS,'???');
  1546. Raise EConvertError.CreateFmt(SErrInvalidTimeStamp,[Msg]);
  1547. end;
  1548. Procedure InvalidDateTimeError(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word); // const ABaseDate: TDateTime = 0
  1549. begin
  1550. InvalidDateTimeError(AYear,AMonth,ADay,AHour,AMinute,ASecond,AMilliSecond,0);
  1551. end;
  1552. Procedure InvalidDateWeekError(const AYear, AWeekOfYear, ADayOfWeek: Word);
  1553. begin
  1554. Raise EConvertError.CreateFmt(SErrInvalidDateWeek,[AYear,AWeekOfYear,ADayOfWeek]);
  1555. end;
  1556. Procedure InvalidDateDayError(const AYear, ADayOfYear: Word);
  1557. begin
  1558. Raise EConvertError.CreateFmt(SErrInvalidDayOfYear,[AYear,ADayOfYear]);
  1559. end;
  1560. Procedure InvalidDateMonthWeekError(const AYear, AMonth, AWeekOfMonth, ADayOfWeek: Word);
  1561. begin
  1562. Raise EConvertError.CreateFmt(SErrInvalidDateMonthWeek,[Ayear,AMonth,AWeekOfMonth,ADayOfWeek]);
  1563. end;
  1564. Procedure InvalidDayOfWeekInMonthError(const AYear, AMonth, ANthDayOfWeek, ADayOfWeek: Word);
  1565. begin
  1566. Raise EConvertError.CreateFmt(SErrInvalidDayOfWeekInMonth,[AYear,AMonth,ANthDayOfWeek,ADayOfWeek]);
  1567. end;
  1568. { ---------------------------------------------------------------------
  1569. Julian and Modified Julian Date conversion support
  1570. ---------------------------------------------------------------------}
  1571. {$push}
  1572. {$R-}
  1573. {$Q-}
  1574. Function DateTimeToJulianDate(const AValue: TDateTime): Double;
  1575. var
  1576. day,month,year: word;
  1577. a,y,m: longint;
  1578. begin
  1579. DecodeDate ( AValue, year, month, day );
  1580. a := (14-month) div 12;
  1581. y := year + 4800 - a;
  1582. m := month + (12*a) - 3;
  1583. result := day + ((153*m+2) div 5) + (365*y)
  1584. + (y div 4) - (y div 100) + (y div 400) - 32045.5 + frac(avalue);
  1585. end;
  1586. Function JulianDateToDateTime(const AValue: Double): TDateTime;
  1587. begin
  1588. if not TryJulianDateToDateTime(AValue, Result) then
  1589. raise EConvertError.CreateFmt(SInvalidJulianDate, [AValue]);
  1590. end;
  1591. Function TryJulianDateToDateTime(const AValue: Double; out ADateTime: TDateTime): Boolean;
  1592. var
  1593. a,b,c,d,e,m:longint;
  1594. day,month,year: word;
  1595. begin
  1596. a := trunc(AValue + 32044.5);
  1597. b := (4*a + 3) div 146097;
  1598. c := a - (146097*b div 4);
  1599. d := (4*c + 3) div 1461;
  1600. e := c - (1461*d div 4);
  1601. m := (5*e+2) div 153;
  1602. day := e - ((153*m + 2) div 5) + 1;
  1603. month := m + 3 - 12 * ( m div 10 );
  1604. year := (100*b) + d - 4800 + ( m div 10 );
  1605. result := TryEncodeDate ( Year, Month, Day, ADateTime );
  1606. if Result then
  1607. // ADateTime:=IncMilliSecond(IncHour(ADateTime,-12),MillisecondOfTheDay(Abs(Frac(aValue))));
  1608. ADateTime:=ADateTime+frac(AValue-0.5);
  1609. end;
  1610. Function DateTimeToModifiedJulianDate(const AValue: TDateTime): Double;
  1611. begin
  1612. result := DateTimeToJulianDate(AValue) - 2400000.5;
  1613. end;
  1614. Function ModifiedJulianDateToDateTime(const AValue: Double): TDateTime;
  1615. begin
  1616. result := JulianDateToDateTime(AValue + 2400000.5);
  1617. end;
  1618. Function TryModifiedJulianDateToDateTime(const AValue: Double; out ADateTime: TDateTime): Boolean;
  1619. begin
  1620. Result:=TryJulianDateToDateTime(AValue + 2400000.5, ADateTime);
  1621. end;
  1622. {$pop}//{$R-}{$Q-} for Julian conversion functions
  1623. { ---------------------------------------------------------------------
  1624. Unix timestamp support.
  1625. ---------------------------------------------------------------------}
  1626. Function DateTimeToUnix(const AValue: TDateTime; AInputIsUTC: Boolean = True): Int64;
  1627. Var
  1628. T : TDateTime;
  1629. begin
  1630. T:=aValue;
  1631. if Not aInputisUTC then
  1632. T:=IncMinute(T,GetLocalTimeOffset);
  1633. Result:=Round(DateTimeDiff(RecodeMillisecond(T,0),UnixEpoch)*SecsPerDay);
  1634. end;
  1635. Function UnixToDateTime(const AValue: Int64; aReturnUTC : Boolean = true): TDateTime;
  1636. begin
  1637. Result:=IncSecond(UnixEpoch, AValue);
  1638. if Not aReturnUTC then
  1639. Result:=IncMinute(Result,-GetLocalTimeOffset);
  1640. end;
  1641. Function UnixTimeStampToMac(const AValue: Int64): Int64;
  1642. const
  1643. Epoch=24107 * 24 * 3600;
  1644. begin
  1645. Result:=AValue + Epoch;
  1646. end;
  1647. { ---------------------------------------------------------------------
  1648. Mac timestamp support.
  1649. ---------------------------------------------------------------------}
  1650. Function DateTimeToMac(const AValue: TDateTime): Int64;
  1651. var
  1652. Epoch:TDateTime;
  1653. begin
  1654. Epoch:=EncodeDateTime( 1904, 1, 1, 0, 0, 0, 0 );
  1655. Result:=SecondsBetween( Epoch, AValue );
  1656. end;
  1657. Function MacToDateTime(const AValue: Int64): TDateTime;
  1658. var
  1659. Epoch:TDateTime;
  1660. begin
  1661. Epoch:=EncodeDateTime( 1904, 1, 1, 0, 0, 0, 0 );
  1662. Result:=IncSecond( Epoch, AValue );
  1663. end;
  1664. Function MacTimeStampToUnix(const AValue: Int64): Int64;
  1665. const
  1666. Epoch=24107 * 24 * 3600;
  1667. begin
  1668. Result:=AValue - Epoch;
  1669. end;
  1670. Function DateTimeToDosDateTime(const AValue: TDateTime): longint;
  1671. var year,month,day,hour,min,sec,msec : word;
  1672. zs : longint;
  1673. begin
  1674. decodedatetime(avalue,year,month,day,hour,min,sec,msec);
  1675. result:=-1980;
  1676. result:=result+year and 127;
  1677. result:=result shl 4;
  1678. result:=result+month;
  1679. result:=result shl 5;
  1680. result:=result+day;
  1681. result:=result shl 16;
  1682. zs:=hour;
  1683. zs:=zs shl 6;
  1684. zs:=zs+min;
  1685. zs:=zs shl 5;
  1686. zs:=zs+sec div 2;
  1687. result:=result+(zs and $ffff);
  1688. end;
  1689. Function DosDateTimeToDateTime( AValue: longint): TDateTime;
  1690. var year,month,day,hour,min,sec : integer;
  1691. begin
  1692. sec:=(AValue and 31) * 2;
  1693. avalue:=AValue shr 5;
  1694. min:=AValue and 63;
  1695. avalue:=AValue shr 6;
  1696. hour:=AValue and 31;
  1697. avalue:=AValue shr 5;
  1698. day:=AValue and 31;
  1699. avalue:=AValue shr 5;
  1700. month:=AValue and 15;
  1701. avalue:=AValue shr 4;
  1702. year:=AValue+1980;
  1703. result:=EncodeDateTime(year,month,day,hour,min,sec,0);
  1704. end;
  1705. {
  1706. Inverse of formatdatetime, destined for the dateutils unit of FPC.
  1707. Limitations/implementation details:
  1708. - An inverse of FormatDateTime is not 100% an inverse, simply because one can put e.g. time tokens twice in the format string,
  1709. and scandatetime wouldn't know which time to pick.
  1710. - Strings like hn can't be reversed safely. E.g. 1:2 (2 minutes after 1) delivers 12 which is parsed as 12:00 and then
  1711. misses chars for the "n" part.
  1712. - trailing characters are ignored.
  1713. - no support for Eastern Asian formatting characters since they are windows only.
  1714. - no MBCS support.
  1715. Extensions
  1716. - #9 eats whitespace.
  1717. - whitespace at the end of a pattern is optional.
  1718. - ? matches any char.
  1719. - Quote the above chars to really match the char.
  1720. }
  1721. const whitespace = [' ',#13,#10];
  1722. hrfactor = 1/(24);
  1723. minfactor = 1/(24*60);
  1724. secfactor = 1/(24*60*60);
  1725. mssecfactor = 1/(24*60*60*1000);
  1726. const AMPMformatting : array[0..2] of string =('am/pm','a/p','ampm');
  1727. procedure raiseexception(const s:string);
  1728. begin
  1729. raise EConvertError.Create(s);
  1730. end;
  1731. function scandatetime(const pattern:string;const s:string;const fmt:TFormatSettings;startpos:integer=1) : tdatetime;
  1732. var len ,ind : integer;
  1733. yy,mm,dd : integer;
  1734. timeval : TDateTime;
  1735. activequote: char;
  1736. procedure intscandate(ptrn:pchar;plen:integer;poffs:integer);
  1737. // poffs is the offset to
  1738. var
  1739. pind : integer;
  1740. function findimatch(const mnts:array of string;p:pchar):integer;
  1741. var i : integer;
  1742. plen, findlen: integer;
  1743. begin
  1744. result:=-1;
  1745. i:=0;
  1746. plen := strlen(p);
  1747. while (i<=high(mnts)) and (result=-1) do
  1748. begin
  1749. findlen := length(mnts[i]);
  1750. if (findlen > 0) and (findlen <= plen) then // protect against buffer over-read
  1751. if AnsiStrLIComp(p,@(mnts[i][1]),findlen)=0 then
  1752. result:=i;
  1753. inc(i);
  1754. end;
  1755. end;
  1756. procedure arraymatcherror;
  1757. begin
  1758. raiseexception(format(SNoArrayMatch,[pind+1,ind]))
  1759. end;
  1760. function scanmatch(const mnts : array of string;p:pchar; patlen: integer):integer;
  1761. begin
  1762. result:=findimatch(mnts,p);
  1763. if result=-1 then
  1764. arraymatcherror
  1765. else
  1766. begin
  1767. inc(ind,length(mnts[result]));
  1768. inc(pind,patlen);
  1769. inc(result); // was 0 based.
  1770. end;
  1771. end;
  1772. var
  1773. pivot,
  1774. i : integer;
  1775. function scanfixedint(maxv:integer):integer;
  1776. var c : char;
  1777. oi:integer;
  1778. begin
  1779. result:=0;
  1780. oi:=ind;
  1781. c:=ptrn[pind];
  1782. while (pind<plen) and (ptrn[pind]=c) do inc(pind);
  1783. while (maxv>0) and (ind<=len) and (s[ind] IN ['0'..'9']) do
  1784. begin
  1785. result:=result*10+ord(s[ind])-48;
  1786. inc(ind);
  1787. dec(maxv);
  1788. end;
  1789. if oi=ind then
  1790. raiseexception(format(SPatternCharMismatch,[c,oi]));
  1791. end;
  1792. procedure matchchar(c:char);
  1793. begin
  1794. if (ind>len) or (s[ind]<>c) then
  1795. raiseexception(format(SNoCharMatch,[s[ind],c,pind+poffs+1,ind]));
  1796. inc(pind);
  1797. inc(ind);
  1798. end;
  1799. function scanpatlen:integer;
  1800. var c : char;
  1801. lind : Integer;
  1802. begin
  1803. result:=pind;
  1804. lind:=pind;
  1805. c:=ptrn[lind];
  1806. while (lind<=plen) and (ptrn[lind]=c) do
  1807. inc(lind);
  1808. result:=lind-result;
  1809. end;
  1810. procedure matchpattern(const lptr:string);
  1811. var len:integer;
  1812. begin
  1813. len:=length(lptr);
  1814. if len>0 then
  1815. intscandate(@lptr[1],len,pind+poffs);
  1816. end;
  1817. var lasttoken,lch : char;
  1818. begin
  1819. pind:=0; lasttoken:=' ';
  1820. while (ind<=len) and (pind<plen) do
  1821. begin
  1822. lch:=upcase(ptrn[pind]);
  1823. if activequote=#0 then
  1824. begin
  1825. if (lch='M') and (lasttoken='H') then
  1826. begin
  1827. i:=scanpatlen;
  1828. if i>2 then
  1829. raiseexception(format(Shhmmerror,[poffs+pind+1]));
  1830. timeval:=timeval+scanfixedint(2)* minfactor;
  1831. end
  1832. else
  1833. case lch of
  1834. 'H': timeval:=timeval+scanfixedint(2)* hrfactor;
  1835. 'D': begin
  1836. i:=scanpatlen;
  1837. case i of
  1838. 1,2 : dd:=scanfixedint(2);
  1839. 3 : dd:=scanmatch(fmt.shortDayNames,@s[ind],i);
  1840. 4 : dd:=scanmatch(fmt.longDayNames,@s[ind],i);
  1841. 5 :
  1842. begin
  1843. matchpattern(fmt.shortdateformat);
  1844. inc(pind, i);
  1845. end;
  1846. 6 :
  1847. begin
  1848. matchpattern(fmt.longdateformat);
  1849. inc(pind, i);
  1850. end;
  1851. end;
  1852. end;
  1853. 'N': timeval:=timeval+scanfixedint(2)* minfactor;
  1854. 'S': timeval:=timeval+scanfixedint(2)* secfactor;
  1855. 'Z': timeval:=timeval+scanfixedint(3)* mssecfactor;
  1856. 'Y': begin
  1857. i:=scanpatlen;
  1858. case i of
  1859. 1,2 : yy:=scanfixedint(2);
  1860. else yy:=scanfixedint(i);
  1861. end;
  1862. if i<=2 then
  1863. begin
  1864. pivot:=YearOf(now)-fmt.TwoDigitYearCenturyWindow;
  1865. inc(yy, pivot div 100 * 100);
  1866. if (fmt.TwoDigitYearCenturyWindow > 0) and (yy < pivot) then
  1867. inc(yy, 100);
  1868. end;
  1869. end;
  1870. 'M': begin
  1871. i:=scanpatlen;
  1872. case i of
  1873. 1,2: mm:=scanfixedint(2);
  1874. 3: mm:=scanmatch(fmt.ShortMonthNames,@s[ind],i);
  1875. 4: mm:=scanmatch(fmt.LongMonthNames,@s[ind],i);
  1876. end;
  1877. end;
  1878. 'T' : begin
  1879. i:=scanpatlen;
  1880. case i of
  1881. 1:
  1882. begin
  1883. matchpattern(fmt.shorttimeformat);
  1884. inc(pind, i);
  1885. end;
  1886. 2:
  1887. begin
  1888. matchpattern(fmt.longtimeformat);
  1889. inc(pind, i);
  1890. end;
  1891. end;
  1892. end;
  1893. 'A' : begin
  1894. i:=findimatch(AMPMformatting,@ptrn[pind]);
  1895. case i of
  1896. 0: begin
  1897. i:=findimatch(['AM','PM'],@s[ind]);
  1898. case i of
  1899. 0: ;
  1900. 1: timeval:=timeval+12*hrfactor;
  1901. else
  1902. arraymatcherror
  1903. end;
  1904. inc(pind,length(AMPMformatting[0]));
  1905. inc(ind,2);
  1906. end;
  1907. 1: begin
  1908. case upcase(s[ind]) of
  1909. 'A' : ;
  1910. 'P' : timeval:=timeval+12*hrfactor;
  1911. else
  1912. arraymatcherror
  1913. end;
  1914. inc(pind,length(AMPMformatting[1]));
  1915. inc(ind);
  1916. end;
  1917. 2: begin
  1918. i:=findimatch([fmt.timeamstring,fmt.timepmstring],@s[ind]);
  1919. case i of
  1920. 0: inc(ind,length(fmt.timeamstring));
  1921. 1: begin
  1922. timeval:=timeval+12*hrfactor;
  1923. inc(ind,length(fmt.timepmstring));
  1924. end;
  1925. else
  1926. arraymatcherror
  1927. end;
  1928. inc(pind,length(AMPMformatting[2]));
  1929. end;
  1930. else // no AM/PM match. Assume 'a' is simply a char
  1931. matchchar(ptrn[pind]);
  1932. end;
  1933. end;
  1934. '/' : matchchar(fmt.dateSeparator);
  1935. ':' : begin
  1936. matchchar(fmt.TimeSeparator);
  1937. lch:=lasttoken;
  1938. end;
  1939. #39,'"' : begin
  1940. activequote:=lch;
  1941. inc(pind);
  1942. end;
  1943. 'C' : begin
  1944. intscandate(@fmt.shortdateformat[1],length(fmt.ShortDateFormat),pind+poffs);
  1945. intscandate(@fmt.longtimeformat[1],length(fmt.longtimeformat),pind+poffs);
  1946. inc(pind);
  1947. end;
  1948. '?' : begin
  1949. inc(pind);
  1950. inc(ind);
  1951. end;
  1952. #9 : begin
  1953. while (ind<=len) and (s[ind] in whitespace) do
  1954. inc(ind);
  1955. inc(pind);
  1956. end;
  1957. else
  1958. matchchar(ptrn[pind]);
  1959. end; {case}
  1960. lasttoken:=lch;
  1961. end
  1962. else
  1963. begin
  1964. if activequote=lch then
  1965. begin
  1966. activequote:=#0;
  1967. inc(pind);
  1968. end
  1969. else
  1970. matchchar(ptrn[pind]);
  1971. end;
  1972. end;
  1973. if (pind<plen) and (plen>0) and not (ptrn[plen-1] in [#9, '"']) then // allow omission of trailing whitespace
  1974. RaiseException(format(SFullpattern,[poffs+pind+1]));
  1975. end;
  1976. var plen:integer;
  1977. begin
  1978. activequote:=#0;
  1979. yy:=0; mm:=0; dd:=0;
  1980. timeval:=0.0;
  1981. len:=length(s); ind:=startpos;
  1982. plen:=length(pattern);
  1983. intscandate(@pattern[1],plen,0);
  1984. result:=timeval;
  1985. if (yy>0) and (mm>0) and (dd>0) then
  1986. result:=result+encodedate(yy,mm,dd);
  1987. end;
  1988. function scandatetime(const pattern:string;const s:string;startpos:integer=1) : tdatetime; overload;
  1989. begin
  1990. result:=scandatetime(pattern,s,defaultformatsettings,startpos);
  1991. end;
  1992. { Conversion of UTC to local time and vice versa }
  1993. function UniversalTimeToLocal(UT: TDateTime): TDateTime;
  1994. begin
  1995. Result:=UniversalTimeToLocal(UT,-GetLocalTimeOffset);
  1996. end;
  1997. function UniversalTimeToLocal(UT: TDateTime; TZOffset : Integer): TDateTime;
  1998. begin
  1999. if (TZOffset > 0) then
  2000. Result := UT + EncodeTime(TZOffset div 60, TZOffset mod 60, 0, 0)
  2001. else if (TZOffset < 0) then
  2002. Result := UT - EncodeTime(Abs(TZOffset) div 60, Abs(TZOffset) mod 60, 0, 0)
  2003. else
  2004. Result := UT;
  2005. end;
  2006. Function LocalTimeToUniversal(LT: TDateTime): TDateTime;
  2007. begin
  2008. Result:=LocalTimeToUniversal(LT,-GetLocalTimeOffset);
  2009. end;
  2010. Function LocalTimeToUniversal(LT: TDateTime;TZOffset: Integer): TDateTime;
  2011. begin
  2012. if (TZOffset > 0) then
  2013. Result := LT - EncodeTime(TZOffset div 60, TZOffset mod 60, 0, 0)
  2014. else if (TZOffset < 0) then
  2015. Result := LT + EncodeTime(Abs(TZOffset) div 60, Abs(TZOffset) mod 60, 0, 0)
  2016. else
  2017. Result := LT;
  2018. end;
  2019. Const
  2020. FmtUTC = 'yyyy"-"mm"-"dd"T"hh":"nn":"ss"."zzz';
  2021. FmtUTCTZ = 'hh":"mm';
  2022. function DateToISO8601(const ADate: TDateTime; AInputIsUTC: Boolean = True): string;
  2023. const
  2024. FmtOffset: string = '%.02d:%.02d';
  2025. Sign: array[Boolean] of Char = ('+', '-');
  2026. var
  2027. Offset: Integer;
  2028. begin
  2029. Result := FormatDateTime(FmtUTC, ADate);
  2030. Offset := GetLocalTimeOffset;
  2031. if AInputIsUTC or (Offset=0) then
  2032. Result:=Result+'Z'
  2033. else
  2034. begin
  2035. Result:=Result+Sign[Offset>0];
  2036. Offset := Abs(Offset);
  2037. Result:= Result+Format(FmtOffset, [Offset div MinsPerHour, Offset mod MinsPerHour]);
  2038. end;
  2039. end;
  2040. Function TryISO8601ToDate(const DateString: string; ReturnUTC : Boolean;out ADateTime: TDateTime) : Boolean;
  2041. Var
  2042. S,TZ : String;
  2043. Offset,TZOffset : Integer;
  2044. DTTZ : TDateTime;
  2045. begin
  2046. S:=DateString;
  2047. If Length(S)>23 then
  2048. begin
  2049. S:=Copy(S,1,14);
  2050. TZ:=Copy(DateString,24);
  2051. end;
  2052. aDateTime:=ScanDatetime(fmtUTC,DateString);
  2053. Result:=aDateTime<>0;
  2054. if (not Result) then
  2055. Exit;
  2056. // Determine TZ offset. We're forgiving if no TZ info was present.
  2057. if (TZ='Z') or (TZ='') then
  2058. TZOffset:=0
  2059. else
  2060. begin
  2061. Result:=TZ[1] in ['+','-'];
  2062. if Not Result then
  2063. Exit;
  2064. DTTZ:=ScanDateTime(FmtUTCTZ,Copy(TZ,2,5));
  2065. TZOffset:=MinutesBetween(DTTZ,0);
  2066. if (TZ[1]='+') then
  2067. TZOffset:=-TZOffset;
  2068. end;
  2069. aDateTime:=IncMinute(aDateTime,TZOffSet);
  2070. // offset for UTC or not
  2071. if ReturnUTC then
  2072. Offset:=0
  2073. else
  2074. OffSet:=-GetLocalTimeOffset;
  2075. aDateTime:=IncMinute(aDateTime,Offset);
  2076. end;
  2077. Function ISO8601ToDate(const DateString: string; ReturnUTC : Boolean): TDateTime;
  2078. begin
  2079. if not TryISO8601ToDate(DateString,ReturnUTC,Result) then
  2080. Raise EConvertError.CreateFmt(SErrInvalidTimeStamp,[DateString]);
  2081. end;
  2082. Function ISO8601ToDateDef(const DateString: string; ReturnUTC : Boolean; aDefault : TDateTime): TDateTime;
  2083. begin
  2084. if not TryISO8601ToDate(DateString,ReturnUTC,Result) then
  2085. Result:=aDefault;
  2086. end;
  2087. {$else}
  2088. implementation
  2089. {$endif FPUNONE}
  2090. end.