dateutils.pas 77 KB

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