Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
12 KiB

  1. #include "ctlspriv.h"
  2. #include "scdttime.h"
  3. // BUGBUG? remove references to 1750 and 1752 as they
  4. // are not needed -- the minimal year we allow is 1753
  5. // to avoid such problems. (We don't care about
  6. // pre-revised-Gregorian dates! If you want to develop
  7. // a history application, then you can deal with these problems)
  8. int mpcdymoAccum[13] =
  9. { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
  10. /*
  11. - LIncrWord
  12. -
  13. * Purpose:
  14. * Increment (or decrement) an integer by a specified amount,
  15. * given the constraints nMic and nMac.
  16. * Returns the amount of carry into the following (or preceding)
  17. * field, or zero if none.
  18. *
  19. * Intended for use with incrementing date/times.
  20. *
  21. * Arguments:
  22. * pn Pointer to integer to be modified.
  23. * nDelta Amount by which to modify *pn; may be positive,
  24. * negative or zero.
  25. * nMic Minimum value for *pn; if decrementing below this,
  26. * a carry is performed.
  27. * nMac Maximum value for *pn; if incrementing above this,
  28. * a carry is performed.
  29. *
  30. * Returns:
  31. * Zero if modification done within constraints, otherwise the
  32. * amount of carry (positive in incrementing, negative if
  33. * decrementing).
  34. *
  35. */
  36. LONG LIncrWord(WORD *pn, LONG nDelta, int nMic, int nMac)
  37. {
  38. LONG lNew, lIncr;
  39. lIncr = 0;
  40. lNew = *pn + nDelta;
  41. while (lNew >= nMac)
  42. {
  43. lNew -= nMac - nMic;
  44. lIncr++;
  45. }
  46. if (!lIncr)
  47. {
  48. while (lNew < nMic)
  49. {
  50. lNew += nMac - nMic;
  51. lIncr--;
  52. }
  53. }
  54. *pn = (WORD)lNew;
  55. return(lIncr);
  56. }
  57. void IncrSystemTime(SYSTEMTIME *pstSrc, SYSTEMTIME *pstDest, LONG nDelta, LONG flag)
  58. {
  59. int cdyMon;
  60. if (pstSrc != pstDest)
  61. *pstDest = *pstSrc;
  62. switch (flag)
  63. {
  64. case INCRSYS_SECOND:
  65. if (!(nDelta = LIncrWord(&pstDest->wSecond, nDelta, 0, 60)))
  66. break;
  67. case INCRSYS_MINUTE:
  68. if (!(nDelta = LIncrWord(&pstDest->wMinute, nDelta, 0, 60)))
  69. break;
  70. case INCRSYS_HOUR:
  71. if (!(nDelta = LIncrWord(&pstDest->wHour, nDelta, 0, 24)))
  72. break;
  73. case INCRSYS_DAY:
  74. IDTday:
  75. if (nDelta >= 0)
  76. {
  77. cdyMon = GetDaysForMonth(pstDest->wYear, pstDest->wMonth);
  78. while (pstDest->wDay + nDelta > cdyMon)
  79. {
  80. nDelta -= cdyMon + 1 - pstDest->wDay;
  81. pstDest->wDay = 1;
  82. IncrSystemTime(pstDest, pstDest, 1, INCRSYS_MONTH);
  83. cdyMon = GetDaysForMonth(pstDest->wYear, pstDest->wMonth);
  84. }
  85. }
  86. else
  87. {
  88. while (pstDest->wDay <= -nDelta)
  89. {
  90. nDelta += pstDest->wDay;
  91. IncrSystemTime(pstDest, pstDest, -1, INCRSYS_MONTH);
  92. cdyMon = GetDaysForMonth(pstDest->wYear, pstDest->wMonth);
  93. pstDest->wDay = (WORD) cdyMon;
  94. }
  95. }
  96. pstDest->wDay += (WORD)nDelta;
  97. break;
  98. case INCRSYS_MONTH:
  99. if (!(nDelta = LIncrWord(&pstDest->wMonth, nDelta, 1, 13)))
  100. {
  101. cdyMon = GetDaysForMonth(pstDest->wYear, pstDest->wMonth);
  102. if (pstDest->wDay > cdyMon)
  103. pstDest->wDay = (WORD) cdyMon;
  104. break;
  105. }
  106. case INCRSYS_YEAR:
  107. pstDest->wYear += (WORD)nDelta;
  108. cdyMon = GetDaysForMonth(pstDest->wYear, pstDest->wMonth);
  109. if (pstDest->wDay > cdyMon)
  110. pstDest->wDay = (WORD) cdyMon;
  111. break;
  112. case INCRSYS_WEEK:
  113. nDelta *= 7;
  114. goto IDTday;
  115. break;
  116. }
  117. }
  118. CmpDate(const SYSTEMTIME *pst1, const SYSTEMTIME *pst2)
  119. {
  120. int iRet;
  121. if (pst1->wYear < pst2->wYear)
  122. iRet = -1;
  123. else if (pst1->wYear > pst2->wYear)
  124. iRet = 1;
  125. else if (pst1->wMonth < pst2->wMonth)
  126. iRet = -1;
  127. else if (pst1->wMonth > pst2->wMonth)
  128. iRet = 1;
  129. else if (pst1->wDay < pst2->wDay)
  130. iRet = -1;
  131. else if (pst1->wDay > pst2->wDay)
  132. iRet = 1;
  133. else
  134. iRet = 0;
  135. return(iRet);
  136. }
  137. CmpSystemtime(const SYSTEMTIME *pst1, const SYSTEMTIME *pst2)
  138. {
  139. int iRet;
  140. if (pst1->wYear < pst2->wYear)
  141. iRet = -1;
  142. else if (pst1->wYear > pst2->wYear)
  143. iRet = 1;
  144. else if (pst1->wMonth < pst2->wMonth)
  145. iRet = -1;
  146. else if (pst1->wMonth > pst2->wMonth)
  147. iRet = 1;
  148. else if (pst1->wDay < pst2->wDay)
  149. iRet = -1;
  150. else if (pst1->wDay > pst2->wDay)
  151. iRet = 1;
  152. else if (pst1->wHour < pst2->wHour)
  153. iRet = -1;
  154. else if (pst1->wHour > pst2->wHour)
  155. iRet = 1;
  156. else if (pst1->wMinute < pst2->wMinute)
  157. iRet = -1;
  158. else if (pst1->wMinute > pst2->wMinute)
  159. iRet = 1;
  160. else if (pst1->wSecond < pst2->wSecond)
  161. iRet = -1;
  162. else if (pst1->wSecond > pst2->wSecond)
  163. iRet = 1;
  164. else
  165. iRet = 0;
  166. return(iRet);
  167. }
  168. /*
  169. - CdyBetweenYmd
  170. -
  171. * Purpose:
  172. * Calculate the number of days between two dates as expressed
  173. * in YMD's.
  174. *
  175. * Parameters:
  176. * pymdStart start day of range.
  177. * pymdEnd end day of range.
  178. *
  179. * Returns:
  180. * Number of days between two dates. The number
  181. * of days does not include the starting day, but does include
  182. * the last day. ie 1/24/1990-1/25/1990 = 1 day.
  183. */
  184. DWORD DaysBetweenDates(const SYSTEMTIME *pstStart, const SYSTEMTIME *pstEnd)
  185. {
  186. DWORD cday;
  187. WORD yr;
  188. // Calculate number of days between the start month/day and the
  189. // end month/day as if they were in the same year - since cday
  190. // is unsigned, cday could be really large if the end month/day
  191. // is before the start month.day.
  192. // This will be cleared up when we account for the days between
  193. // the years.
  194. ASSERT(pstEnd->wMonth >= 1 && pstEnd->wMonth <= 12);
  195. cday = mpcdymoAccum[pstEnd->wMonth - 1] - mpcdymoAccum[pstStart->wMonth - 1] +
  196. pstEnd->wDay - pstStart->wDay;
  197. yr = pstStart->wYear;
  198. // Check to see if the start year is before the end year,
  199. // and if the end month is after February and
  200. // if the end year is a leap year, then add an extra day
  201. // for to account for Feb. 29 in the end year.
  202. if ( ((yr < pstEnd->wYear) || (pstStart->wMonth <= 2)) &&
  203. pstEnd->wMonth > 2 &&
  204. (pstEnd->wYear & 03) == 0 &&
  205. (pstEnd->wYear <= 1750 || pstEnd->wYear % 100 != 0 || pstEnd->wYear % 400 == 0))
  206. {
  207. cday++;
  208. }
  209. // Now account for the leap years in between the start and end dates
  210. // as well as accounting for the days in each year.
  211. if (yr < pstEnd->wYear)
  212. {
  213. // If the start date is before march and the start year is
  214. // a leap year then add an extra day to account for Feb. 29.
  215. if ( pstStart->wMonth <= 2 &&
  216. (yr & 03) == 0 &&
  217. (yr <= 1750 || yr % 100 != 0 || yr % 400 == 0))
  218. {
  219. cday++;
  220. }
  221. // Account for the days in each year (disregarding leap years).
  222. cday += 365;
  223. yr++;
  224. // Keep on accounting for the days in each year including leap
  225. // years until we reach the end year.
  226. while (yr < pstEnd->wYear)
  227. {
  228. cday += 365;
  229. if ((yr & 03) == 0 && (yr <= 1750 || yr % 100 != 0 || yr % 400 == 0))
  230. cday++;
  231. yr++;
  232. }
  233. }
  234. return(cday);
  235. }
  236. /*
  237. - DowStartOfYrMo
  238. -
  239. * Purpose:
  240. * Find the day of the week the indicated month begins on
  241. *
  242. * Parameters:
  243. * yr year, must be > 0
  244. * mo month, number 1-12
  245. *
  246. * Returns:
  247. * day of the week (0-6) on which the month begins
  248. * (0 = Sunday, 1 = Monday etc.)
  249. */
  250. int GetStartDowForMonth(int yr, int mo)
  251. {
  252. int dow;
  253. // we want monday = 0, sunday = 6
  254. // dow = 6 + (yr - 1) + ((yr - 1) >> 2);
  255. dow = 5 + (yr - 1) + ((yr - 1) >> 2);
  256. if (yr > 1752)
  257. dow += ((yr - 1) - 1600) / 400 - ((yr - 1) - 1700) / 100 - 11;
  258. else if (yr == 1752 && mo > 9)
  259. dow -= 11;
  260. dow += mpcdymoAccum[mo - 1];
  261. if (mo > 2 && (yr & 03) == 0 && (yr <= 1750 || yr % 100 != 0 || yr % 400 == 0))
  262. dow++;
  263. dow %= 7;
  264. return(dow);
  265. }
  266. int DowFromDate(const SYSTEMTIME *pst)
  267. {
  268. int dow;
  269. dow = GetStartDowForMonth(pst->wYear, pst->wMonth);
  270. dow = (dow + pst->wDay - 1) % 7;
  271. return(dow);
  272. }
  273. int GetDaysForMonth(int yr, int mo)
  274. {
  275. int cdy;
  276. if (yr == 1752 && mo == 9)
  277. return(19);
  278. cdy = mpcdymoAccum[mo] - mpcdymoAccum[mo - 1];
  279. if (mo == 2 && (yr & 03) == 0 && (yr <= 1750 || yr % 100 != 0 || yr % 400 == 0))
  280. cdy++;
  281. return(cdy);
  282. }
  283. /*
  284. - NweekNumber
  285. -
  286. * Purpose:
  287. * Calculates week number in which a given date occurs, based
  288. * on a specified start-day of week.
  289. * Adjusts based on how a calendar would show this week
  290. * (ie. week 53 is probably week 1 on the calendar).
  291. *
  292. * Arguments:
  293. * pdtm Pointer to date in question
  294. * dowStartWeek Day-of-week on which weeks starts (0 - 6).
  295. *
  296. * Returns:
  297. * Week number of the year, in which *pdtr occurs.
  298. *
  299. */
  300. // TODO: this currently ignores woyFirst
  301. // it uses the 1st week containing 4+ days as the first week (woyFirst = 2)
  302. // need to make appropriate changes so it handles woyFirst = 0 and = 1...
  303. int GetWeekNumber(const SYSTEMTIME *pst, int dowFirst, int woyFirst)
  304. {
  305. int day, ddow, ddowT, nweek;
  306. SYSTEMTIME st;
  307. st.wYear = pst->wYear;
  308. st.wMonth = 1;
  309. st.wDay = 1;
  310. ddow = GetStartDowForMonth(st.wYear, st.wMonth) - dowFirst;
  311. if (ddow < 0)
  312. ddow += 7;
  313. if (pst->wMonth == 1 && pst->wDay < 8 - ddow)
  314. {
  315. nweek = 0;
  316. }
  317. else
  318. {
  319. if (ddow)
  320. st.wDay = 8 - ddow;
  321. nweek = (DaysBetweenDates(&st, pst) / 7) + 1;
  322. }
  323. if (ddow && ddow <= 3)
  324. nweek++;
  325. // adjust if necessary for calendar
  326. if (!nweek)
  327. {
  328. if (!ddow)
  329. return(1);
  330. // check what week Dec 31 is on
  331. st.wYear--;
  332. st.wMonth = 12;
  333. st.wDay = 31;
  334. return(GetWeekNumber(&st, dowFirst, woyFirst));
  335. }
  336. else if (nweek >= 52)
  337. {
  338. ddowT = (GetStartDowForMonth(pst->wYear, pst->wMonth) +
  339. pst->wDay - 1 + 7 - dowFirst) % 7;
  340. day = pst->wDay + (7 - ddowT);
  341. if (day > 31 + 4)
  342. nweek = 1;
  343. }
  344. return(nweek);
  345. }
  346. // ignores day of week and time-related fields...
  347. // BUGBUG also validate years in range
  348. BOOL IsValidDate(const SYSTEMTIME *pst)
  349. {
  350. int cDay;
  351. if (pst && pst->wMonth >= 1 && pst->wMonth <= 12)
  352. {
  353. cDay = GetDaysForMonth(pst->wYear, pst->wMonth);
  354. if (pst->wDay >= 1 && pst->wDay <= cDay)
  355. return(TRUE);
  356. }
  357. return(FALSE);
  358. }
  359. // ignores milliseconds and date-related fields...
  360. BOOL IsValidTime(const SYSTEMTIME *pst)
  361. {
  362. return(pst->wHour <= 23 &&
  363. pst->wMinute <= 59 &&
  364. pst->wSecond <= 59);
  365. }
  366. // ignores day of week
  367. BOOL IsValidSystemtime(const SYSTEMTIME *pst)
  368. {
  369. if (pst && pst->wMonth >= 1 && pst->wMonth <= 12)
  370. {
  371. int cDay = GetDaysForMonth(pst->wYear, pst->wMonth);
  372. if (pst->wDay >= 1 &&
  373. pst->wDay <= cDay &&
  374. pst->wHour <= 23 &&
  375. pst->wMinute <= 59 &&
  376. pst->wSecond <= 59 &&
  377. pst->wMilliseconds < 1000)
  378. return(TRUE);
  379. }
  380. return(FALSE);
  381. }