TimeZone.cs 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707
  1. using System;
  2. using System.IO;
  3. using System.Runtime.InteropServices;
  4. using System.Text;
  5. using Ryujinx.Common;
  6. using Ryujinx.HLE.Utilities;
  7. using static Ryujinx.HLE.HOS.Services.Time.TimeZoneRule;
  8. namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
  9. {
  10. public class TimeZone
  11. {
  12. private const int TimeTypeSize = 8;
  13. private const int EpochYear = 1970;
  14. private const int YearBase = 1900;
  15. private const int EpochWeekDay = 4;
  16. private const int SecondsPerMinute = 60;
  17. private const int MinutesPerHour = 60;
  18. private const int HoursPerDays = 24;
  19. private const int DaysPerWekk = 7;
  20. private const int DaysPerNYear = 365;
  21. private const int DaysPerLYear = 366;
  22. private const int MonthsPerYear = 12;
  23. private const int SecondsPerHour = SecondsPerMinute * MinutesPerHour;
  24. private const int SecondsPerDay = SecondsPerHour * HoursPerDays;
  25. private const int YearsPerRepeat = 400;
  26. private const long AverageSecondsPerYear = 31556952;
  27. private const long SecondsPerRepeat = YearsPerRepeat * AverageSecondsPerYear;
  28. private static readonly int[] YearLengths = { DaysPerNYear, DaysPerLYear };
  29. private static readonly int[][] MonthsLengths = new int[][]
  30. {
  31. new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  32. new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
  33. };
  34. private const string TimeZoneDefaultRule = ",M4.1.0,M10.5.0";
  35. [StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)]
  36. private struct CalendarTimeInternal
  37. {
  38. // NOTE: On the IPC side this is supposed to be a 16 bits value but internally this need to be a 64 bits value for ToPosixTime.
  39. public long Year;
  40. public sbyte Month;
  41. public sbyte Day;
  42. public sbyte Hour;
  43. public sbyte Minute;
  44. public sbyte Second;
  45. public int CompareTo(CalendarTimeInternal other)
  46. {
  47. if (Year != other.Year)
  48. {
  49. if (Year < other.Year)
  50. {
  51. return -1;
  52. }
  53. return 1;
  54. }
  55. if (Month != other.Month)
  56. {
  57. return Month - other.Month;
  58. }
  59. if (Day != other.Day)
  60. {
  61. return Day - other.Day;
  62. }
  63. if (Hour != other.Hour)
  64. {
  65. return Hour - other.Hour;
  66. }
  67. if (Minute != other.Minute)
  68. {
  69. return Minute - other.Minute;
  70. }
  71. if (Second != other.Second)
  72. {
  73. return Second - other.Second;
  74. }
  75. return 0;
  76. }
  77. }
  78. private enum RuleType
  79. {
  80. JulianDay,
  81. DayOfYear,
  82. MonthNthDayOfWeek
  83. }
  84. private struct Rule
  85. {
  86. public RuleType Type;
  87. public int Day;
  88. public int Week;
  89. public int Month;
  90. public int TransitionTime;
  91. }
  92. private static int Detzcode32(byte[] bytes)
  93. {
  94. if (BitConverter.IsLittleEndian)
  95. {
  96. Array.Reverse(bytes, 0, bytes.Length);
  97. }
  98. return BitConverter.ToInt32(bytes, 0);
  99. }
  100. private static unsafe int Detzcode32(int* data)
  101. {
  102. int result = *data;
  103. if (BitConverter.IsLittleEndian)
  104. {
  105. byte[] bytes = BitConverter.GetBytes(result);
  106. Array.Reverse(bytes, 0, bytes.Length);
  107. result = BitConverter.ToInt32(bytes, 0);
  108. }
  109. return result;
  110. }
  111. private static unsafe long Detzcode64(long* data)
  112. {
  113. long result = *data;
  114. if (BitConverter.IsLittleEndian)
  115. {
  116. byte[] bytes = BitConverter.GetBytes(result);
  117. Array.Reverse(bytes, 0, bytes.Length);
  118. result = BitConverter.ToInt64(bytes, 0);
  119. }
  120. return result;
  121. }
  122. private static bool DifferByRepeat(long t1, long t0)
  123. {
  124. return (t1 - t0) == SecondsPerRepeat;
  125. }
  126. private static unsafe bool TimeTypeEquals(TimeZoneRule outRules, byte aIndex, byte bIndex)
  127. {
  128. if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount)
  129. {
  130. return false;
  131. }
  132. TimeTypeInfo a = outRules.Ttis[aIndex];
  133. TimeTypeInfo b = outRules.Ttis[bIndex];
  134. fixed (char* chars = outRules.Chars)
  135. {
  136. return a.GmtOffset == b.GmtOffset &&
  137. a.IsDaySavingTime == b.IsDaySavingTime &&
  138. a.IsStandardTimeDaylight == b.IsStandardTimeDaylight &&
  139. a.IsGMT == b.IsGMT &&
  140. StringUtils.CompareCStr(chars + a.AbbreviationListIndex, chars + b.AbbreviationListIndex) == 0;
  141. }
  142. }
  143. private static int GetQZName(char[] name, int namePosition, char delimiter)
  144. {
  145. int i = namePosition;
  146. while (name[i] != '\0' && name[i] != delimiter)
  147. {
  148. i++;
  149. }
  150. return i;
  151. }
  152. private static int GetTZName(char[] name, int namePosition)
  153. {
  154. int i = namePosition;
  155. char c = name[i];
  156. while (c != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
  157. {
  158. c = name[i];
  159. i++;
  160. }
  161. return i;
  162. }
  163. private static bool GetNum(char[] name, ref int namePosition, out int num, int min, int max)
  164. {
  165. num = 0;
  166. char c = name[namePosition];
  167. if (!char.IsDigit(c))
  168. {
  169. return false;
  170. }
  171. do
  172. {
  173. num = num * 10 + (c - '0');
  174. if (num > max)
  175. {
  176. return false;
  177. }
  178. c = name[++namePosition];
  179. }
  180. while (char.IsDigit(c));
  181. if (num < min)
  182. {
  183. return false;
  184. }
  185. return true;
  186. }
  187. private static bool GetSeconds(char[] name, ref int namePosition, out int seconds)
  188. {
  189. seconds = 0;
  190. int num;
  191. bool isValid = GetNum(name, ref namePosition, out num, 0, HoursPerDays * DaysPerWekk - 1);
  192. if (!isValid)
  193. {
  194. return false;
  195. }
  196. seconds = num * SecondsPerHour;
  197. if (name[namePosition] == ':')
  198. {
  199. namePosition++;
  200. isValid = GetNum(name, ref namePosition, out num, 0, MinutesPerHour - 1);
  201. if (!isValid)
  202. {
  203. return false;
  204. }
  205. seconds += num * SecondsPerMinute;
  206. if (name[namePosition] == ':')
  207. {
  208. namePosition++;
  209. isValid = GetNum(name, ref namePosition, out num, 0, SecondsPerMinute);
  210. if (!isValid)
  211. {
  212. return false;
  213. }
  214. seconds += num;
  215. }
  216. }
  217. return true;
  218. }
  219. private static bool GetOffset(char[] name, ref int namePosition, ref int offset)
  220. {
  221. bool isNegative = false;
  222. if (name[namePosition] == '-')
  223. {
  224. isNegative = true;
  225. namePosition++;
  226. }
  227. else if (name[namePosition] == '+')
  228. {
  229. namePosition++;
  230. }
  231. bool isValid = GetSeconds(name, ref namePosition, out offset);
  232. if (!isValid)
  233. {
  234. return false;
  235. }
  236. if (isNegative)
  237. {
  238. offset = -offset;
  239. }
  240. return true;
  241. }
  242. private static bool GetRule(char[] name, ref int namePosition, out Rule rule)
  243. {
  244. rule = new Rule();
  245. bool isValid = false;
  246. if (name[namePosition] == 'J')
  247. {
  248. namePosition++;
  249. rule.Type = RuleType.JulianDay;
  250. isValid = GetNum(name, ref namePosition, out rule.Day, 1, DaysPerNYear);
  251. }
  252. else if (name[namePosition] == 'M')
  253. {
  254. namePosition++;
  255. rule.Type = RuleType.MonthNthDayOfWeek;
  256. isValid = GetNum(name, ref namePosition, out rule.Month, 1, MonthsPerYear);
  257. if (!isValid)
  258. {
  259. return false;
  260. }
  261. if (name[namePosition++] != '.')
  262. {
  263. return false;
  264. }
  265. isValid = GetNum(name, ref namePosition, out rule.Week, 1, 5);
  266. if (!isValid)
  267. {
  268. return false;
  269. }
  270. if (name[namePosition++] != '.')
  271. {
  272. return false;
  273. }
  274. isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1);
  275. }
  276. else if (char.IsDigit(name[namePosition]))
  277. {
  278. rule.Type = RuleType.DayOfYear;
  279. isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1);
  280. }
  281. else
  282. {
  283. return false;
  284. }
  285. if (!isValid)
  286. {
  287. return false;
  288. }
  289. if (name[namePosition] == '/')
  290. {
  291. namePosition++;
  292. return GetOffset(name, ref namePosition, ref rule.TransitionTime);
  293. }
  294. else
  295. {
  296. rule.TransitionTime = 2 * SecondsPerHour;
  297. }
  298. return true;
  299. }
  300. private static int IsLeap(int year)
  301. {
  302. if (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0))
  303. {
  304. return 1;
  305. }
  306. return 0;
  307. }
  308. private static bool ParsePosixName(Span<char> name, out TimeZoneRule outRules, bool lastDitch)
  309. {
  310. outRules = new TimeZoneRule
  311. {
  312. Ats = new long[TzMaxTimes],
  313. Types = new byte[TzMaxTimes],
  314. Ttis = new TimeTypeInfo[TzMaxTypes],
  315. Chars = new char[TzCharsArraySize]
  316. };
  317. int stdLen;
  318. Span<char> stdName = name;
  319. int namePosition = 0;
  320. int stdOffset = 0;
  321. if (lastDitch)
  322. {
  323. stdLen = 3;
  324. namePosition += stdLen;
  325. }
  326. else
  327. {
  328. if (name[namePosition] == '<')
  329. {
  330. namePosition++;
  331. stdName = name.Slice(namePosition);
  332. int stdNamePosition = namePosition;
  333. namePosition = GetQZName(name.ToArray(), namePosition, '>');
  334. if (name[namePosition] != '>')
  335. {
  336. return false;
  337. }
  338. stdLen = namePosition - stdNamePosition;
  339. namePosition++;
  340. }
  341. else
  342. {
  343. namePosition = GetTZName(name.ToArray(), namePosition);
  344. stdLen = namePosition;
  345. }
  346. if (stdLen == 0)
  347. {
  348. return false;
  349. }
  350. bool isValid = GetOffset(name.ToArray(), ref namePosition, ref stdOffset);
  351. if (!isValid)
  352. {
  353. return false;
  354. }
  355. }
  356. int charCount = stdLen + 1;
  357. int destLen = 0;
  358. int dstOffset = 0;
  359. Span<char> destName = name.Slice(namePosition);
  360. if (TzCharsArraySize < charCount)
  361. {
  362. return false;
  363. }
  364. if (name[namePosition] != '\0')
  365. {
  366. if (name[namePosition] == '<')
  367. {
  368. destName = name.Slice(++namePosition);
  369. int destNamePosition = namePosition;
  370. namePosition = GetQZName(name.ToArray(), namePosition, '>');
  371. if (name[namePosition] != '>')
  372. {
  373. return false;
  374. }
  375. destLen = namePosition - destNamePosition;
  376. namePosition++;
  377. }
  378. else
  379. {
  380. destName = name.Slice(namePosition);
  381. namePosition = GetTZName(name.ToArray(), namePosition);
  382. destLen = namePosition;
  383. }
  384. if (destLen == 0)
  385. {
  386. return false;
  387. }
  388. charCount += destLen + 1;
  389. if (TzCharsArraySize < charCount)
  390. {
  391. return false;
  392. }
  393. if (name[namePosition] != '\0' && name[namePosition] != ',' && name[namePosition] != ';')
  394. {
  395. bool isValid = GetOffset(name.ToArray(), ref namePosition, ref dstOffset);
  396. if (!isValid)
  397. {
  398. return false;
  399. }
  400. }
  401. else
  402. {
  403. dstOffset = stdOffset - SecondsPerHour;
  404. }
  405. if (name[namePosition] == '\0')
  406. {
  407. name = TimeZoneDefaultRule.ToCharArray();
  408. namePosition = 0;
  409. }
  410. if (name[namePosition] == ',' || name[namePosition] == ';')
  411. {
  412. namePosition++;
  413. bool IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule start);
  414. if (!IsRuleValid)
  415. {
  416. return false;
  417. }
  418. if (name[namePosition++] != ',')
  419. {
  420. return false;
  421. }
  422. IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule end);
  423. if (!IsRuleValid)
  424. {
  425. return false;
  426. }
  427. if (name[namePosition] != '\0')
  428. {
  429. return false;
  430. }
  431. outRules.TypeCount = 2;
  432. outRules.Ttis[0] = new TimeTypeInfo
  433. {
  434. GmtOffset = -dstOffset,
  435. IsDaySavingTime = true,
  436. AbbreviationListIndex = stdLen + 1
  437. };
  438. outRules.Ttis[1] = new TimeTypeInfo
  439. {
  440. GmtOffset = -stdOffset,
  441. IsDaySavingTime = false,
  442. AbbreviationListIndex = 0
  443. };
  444. outRules.DefaultType = 0;
  445. int timeCount = 0;
  446. long janFirst = 0;
  447. int janOffset = 0;
  448. int yearBegining = EpochYear;
  449. do
  450. {
  451. int yearSeconds = YearLengths[IsLeap(yearBegining - 1)] * SecondsPerDay;
  452. yearBegining--;
  453. if (IncrementOverflow64(ref janFirst, -yearSeconds))
  454. {
  455. janOffset = -yearSeconds;
  456. break;
  457. }
  458. }
  459. while (EpochYear - YearsPerRepeat / 2 < yearBegining);
  460. int yearLimit = yearBegining + YearsPerRepeat + 1;
  461. int year;
  462. for (year = yearBegining; year < yearLimit; year++)
  463. {
  464. int startTime = TransitionTime(year, start, stdOffset);
  465. int endTime = TransitionTime(year, end, dstOffset);
  466. int yearSeconds = YearLengths[IsLeap(year)] * SecondsPerDay;
  467. bool isReversed = endTime < startTime;
  468. if (isReversed)
  469. {
  470. int swap = startTime;
  471. startTime = endTime;
  472. endTime = swap;
  473. }
  474. if (isReversed || (startTime < endTime && (endTime - startTime < (yearSeconds + (stdOffset - dstOffset)))))
  475. {
  476. if (TzMaxTimes - 2 < timeCount)
  477. {
  478. break;
  479. }
  480. outRules.Ats[timeCount] = janFirst;
  481. if (!IncrementOverflow64(ref outRules.Ats[timeCount], janOffset + startTime))
  482. {
  483. outRules.Types[timeCount++] = isReversed ? (byte)1 : (byte)0;
  484. }
  485. else if (janOffset != 0)
  486. {
  487. outRules.DefaultType = isReversed ? 1 : 0;
  488. }
  489. outRules.Ats[timeCount] = janFirst;
  490. if (!IncrementOverflow64(ref outRules.Ats[timeCount], janOffset + endTime))
  491. {
  492. outRules.Types[timeCount++] = isReversed ? (byte)0 : (byte)1;
  493. yearLimit = year + YearsPerRepeat + 1;
  494. }
  495. else if (janOffset != 0)
  496. {
  497. outRules.DefaultType = isReversed ? 0 : 1;
  498. }
  499. }
  500. if (IncrementOverflow64(ref janFirst, janOffset + yearSeconds))
  501. {
  502. break;
  503. }
  504. janOffset = 0;
  505. }
  506. outRules.TimeCount = timeCount;
  507. // There is no time variation, this is then a perpetual DST rule
  508. if (timeCount == 0)
  509. {
  510. outRules.TypeCount = 1;
  511. }
  512. else if (YearsPerRepeat < year - yearBegining)
  513. {
  514. outRules.GoBack = true;
  515. outRules.GoAhead = true;
  516. }
  517. }
  518. else
  519. {
  520. if (name[namePosition] == '\0')
  521. {
  522. return false;
  523. }
  524. long theirStdOffset = 0;
  525. for (int i = 0; i < outRules.TimeCount; i++)
  526. {
  527. int j = outRules.Types[i];
  528. if (outRules.Ttis[j].IsStandardTimeDaylight)
  529. {
  530. theirStdOffset = -outRules.Ttis[j].GmtOffset;
  531. }
  532. }
  533. long theirDstOffset = 0;
  534. for (int i = 0; i < outRules.TimeCount; i++)
  535. {
  536. int j = outRules.Types[i];
  537. if (outRules.Ttis[j].IsDaySavingTime)
  538. {
  539. theirDstOffset = -outRules.Ttis[j].GmtOffset;
  540. }
  541. }
  542. bool isDaySavingTime = false;
  543. long theirOffset = theirStdOffset;
  544. for (int i = 0; i < outRules.TimeCount; i++)
  545. {
  546. int j = outRules.Types[i];
  547. outRules.Types[i] = outRules.Ttis[j].IsDaySavingTime ? (byte)1 : (byte)0;
  548. if (!outRules.Ttis[j].IsGMT)
  549. {
  550. if (isDaySavingTime && !outRules.Ttis[j].IsStandardTimeDaylight)
  551. {
  552. outRules.Ats[i] += dstOffset - theirStdOffset;
  553. }
  554. else
  555. {
  556. outRules.Ats[i] += stdOffset - theirStdOffset;
  557. }
  558. }
  559. theirOffset = -outRules.Ttis[j].GmtOffset;
  560. if (outRules.Ttis[j].IsDaySavingTime)
  561. {
  562. theirDstOffset = theirOffset;
  563. }
  564. else
  565. {
  566. theirStdOffset = theirOffset;
  567. }
  568. }
  569. outRules.Ttis[0] = new TimeTypeInfo
  570. {
  571. GmtOffset = -stdOffset,
  572. IsDaySavingTime = false,
  573. AbbreviationListIndex = 0
  574. };
  575. outRules.Ttis[1] = new TimeTypeInfo
  576. {
  577. GmtOffset = -dstOffset,
  578. IsDaySavingTime = true,
  579. AbbreviationListIndex = stdLen + 1
  580. };
  581. outRules.TypeCount = 2;
  582. outRules.DefaultType = 0;
  583. }
  584. }
  585. else
  586. {
  587. // default is perpetual standard time
  588. outRules.TypeCount = 1;
  589. outRules.TimeCount = 0;
  590. outRules.DefaultType = 0;
  591. outRules.Ttis[0] = new TimeTypeInfo
  592. {
  593. GmtOffset = -stdOffset,
  594. IsDaySavingTime = false,
  595. AbbreviationListIndex = 0
  596. };
  597. }
  598. outRules.CharCount = charCount;
  599. int charsPosition = 0;
  600. for (int i = 0; i < stdLen; i++)
  601. {
  602. outRules.Chars[i] = stdName[i];
  603. }
  604. charsPosition += stdLen;
  605. outRules.Chars[charsPosition++] = '\0';
  606. if (destLen != 0)
  607. {
  608. for (int i = 0; i < destLen; i++)
  609. {
  610. outRules.Chars[charsPosition + i] = destName[i];
  611. }
  612. outRules.Chars[charsPosition + destLen] = '\0';
  613. }
  614. return true;
  615. }
  616. private static int TransitionTime(int year, Rule rule, int offset)
  617. {
  618. int leapYear = IsLeap(year);
  619. int value;
  620. switch (rule.Type)
  621. {
  622. case RuleType.JulianDay:
  623. value = (rule.Day - 1) * SecondsPerDay;
  624. if (leapYear == 1 && rule.Day >= 60)
  625. {
  626. value += SecondsPerDay;
  627. }
  628. break;
  629. case RuleType.DayOfYear:
  630. value = rule.Day * SecondsPerDay;
  631. break;
  632. case RuleType.MonthNthDayOfWeek:
  633. // Here we use Zeller's Congruence to get the day of week of the first month.
  634. int m1 = (rule.Month + 9) % 12 + 1;
  635. int yy0 = (rule.Month <= 2) ? (year - 1) : year;
  636. int yy1 = yy0 / 100;
  637. int yy2 = yy0 % 100;
  638. int dayOfWeek = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
  639. if (dayOfWeek < 0)
  640. {
  641. dayOfWeek += DaysPerWekk;
  642. }
  643. // Get the zero origin
  644. int d = rule.Day - dayOfWeek;
  645. if (d < 0)
  646. {
  647. d += DaysPerWekk;
  648. }
  649. for (int i = 1; i < rule.Week; i++)
  650. {
  651. if (d + DaysPerWekk >= MonthsLengths[leapYear][rule.Month - 1])
  652. {
  653. break;
  654. }
  655. d += DaysPerWekk;
  656. }
  657. value = d * SecondsPerDay;
  658. for (int i = 0; i < rule.Month - 1; i++)
  659. {
  660. value += MonthsLengths[leapYear][i] * SecondsPerDay;
  661. }
  662. break;
  663. default:
  664. throw new NotImplementedException("Unknown time transition!");
  665. }
  666. return value + rule.TransitionTime + offset;
  667. }
  668. private static bool NormalizeOverflow32(ref int ip, ref int unit, int baseValue)
  669. {
  670. int delta;
  671. if (unit >= 0)
  672. {
  673. delta = unit / baseValue;
  674. }
  675. else
  676. {
  677. delta = -1 - (-1 - unit) / baseValue;
  678. }
  679. unit -= delta * baseValue;
  680. return IncrementOverflow32(ref ip, delta);
  681. }
  682. private static bool NormalizeOverflow64(ref long ip, ref long unit, long baseValue)
  683. {
  684. long delta;
  685. if (unit >= 0)
  686. {
  687. delta = unit / baseValue;
  688. }
  689. else
  690. {
  691. delta = -1 - (-1 - unit) / baseValue;
  692. }
  693. unit -= delta * baseValue;
  694. return IncrementOverflow64(ref ip, delta);
  695. }
  696. private static bool IncrementOverflow32(ref int time, int j)
  697. {
  698. try
  699. {
  700. time = checked(time + j);
  701. return false;
  702. }
  703. catch (OverflowException)
  704. {
  705. return true;
  706. }
  707. }
  708. private static bool IncrementOverflow64(ref long time, long j)
  709. {
  710. try
  711. {
  712. time = checked(time + j);
  713. return false;
  714. }
  715. catch (OverflowException)
  716. {
  717. return true;
  718. }
  719. }
  720. internal static bool ParsePosixName(string name, out TimeZoneRule outRules)
  721. {
  722. return ParsePosixName(name.ToCharArray(), out outRules, false);
  723. }
  724. internal static unsafe bool LoadTimeZoneRules(out TimeZoneRule outRules, Stream inputData)
  725. {
  726. outRules = new TimeZoneRule
  727. {
  728. Ats = new long[TzMaxTimes],
  729. Types = new byte[TzMaxTimes],
  730. Ttis = new TimeTypeInfo[TzMaxTypes],
  731. Chars = new char[TzCharsArraySize]
  732. };
  733. BinaryReader reader = new BinaryReader(inputData);
  734. long streamLength = reader.BaseStream.Length;
  735. if (streamLength < Marshal.SizeOf<TzifHeader>())
  736. {
  737. return false;
  738. }
  739. TzifHeader header = reader.ReadStruct<TzifHeader>();
  740. streamLength -= Marshal.SizeOf<TzifHeader>();
  741. int ttisGMTCount = Detzcode32(header.TtisGMTCount);
  742. int ttisSTDCount = Detzcode32(header.TtisSTDCount);
  743. int leapCount = Detzcode32(header.LeapCount);
  744. int timeCount = Detzcode32(header.TimeCount);
  745. int typeCount = Detzcode32(header.TypeCount);
  746. int charCount = Detzcode32(header.CharCount);
  747. if (!(0 <= leapCount
  748. && leapCount < TzMaxLeaps
  749. && 0 < typeCount
  750. && typeCount < TzMaxTypes
  751. && 0 <= timeCount
  752. && timeCount < TzMaxTimes
  753. && 0 <= charCount
  754. && charCount < TzMaxChars
  755. && (ttisSTDCount == typeCount || ttisSTDCount == 0)
  756. && (ttisGMTCount == typeCount || ttisGMTCount == 0)))
  757. {
  758. return false;
  759. }
  760. if (streamLength < (timeCount * TimeTypeSize
  761. + timeCount
  762. + typeCount * 6
  763. + charCount
  764. + leapCount * (TimeTypeSize + 4)
  765. + ttisSTDCount
  766. + ttisGMTCount))
  767. {
  768. return false;
  769. }
  770. outRules.TimeCount = timeCount;
  771. outRules.TypeCount = typeCount;
  772. outRules.CharCount = charCount;
  773. byte[] workBuffer = StreamUtils.StreamToBytes(inputData);
  774. timeCount = 0;
  775. fixed (byte* workBufferPtrStart = workBuffer)
  776. {
  777. byte* p = workBufferPtrStart;
  778. for (int i = 0; i < outRules.TimeCount; i++)
  779. {
  780. long at = Detzcode64((long*)p);
  781. outRules.Types[i] = 1;
  782. if (timeCount != 0 && at <= outRules.Ats[timeCount - 1])
  783. {
  784. if (at < outRules.Ats[timeCount - 1])
  785. {
  786. return false;
  787. }
  788. outRules.Types[i - 1] = 0;
  789. timeCount--;
  790. }
  791. outRules.Ats[timeCount++] = at;
  792. p += TimeTypeSize;
  793. }
  794. timeCount = 0;
  795. for (int i = 0; i < outRules.TimeCount; i++)
  796. {
  797. byte type = *p++;
  798. if (outRules.TypeCount <= type)
  799. {
  800. return false;
  801. }
  802. if (outRules.Types[i] != 0)
  803. {
  804. outRules.Types[timeCount++] = type;
  805. }
  806. }
  807. outRules.TimeCount = timeCount;
  808. for (int i = 0; i < outRules.TypeCount; i++)
  809. {
  810. TimeTypeInfo ttis = outRules.Ttis[i];
  811. ttis.GmtOffset = Detzcode32((int*)p);
  812. p += 4;
  813. if (*p >= 2)
  814. {
  815. return false;
  816. }
  817. ttis.IsDaySavingTime = *p != 0;
  818. p++;
  819. int abbreviationListIndex = *p++;
  820. if (abbreviationListIndex >= outRules.CharCount)
  821. {
  822. return false;
  823. }
  824. ttis.AbbreviationListIndex = abbreviationListIndex;
  825. outRules.Ttis[i] = ttis;
  826. }
  827. fixed (char* chars = outRules.Chars)
  828. {
  829. Encoding.ASCII.GetChars(p, outRules.CharCount, chars, outRules.CharCount);
  830. }
  831. p += outRules.CharCount;
  832. outRules.Chars[outRules.CharCount] = '\0';
  833. for (int i = 0; i < outRules.TypeCount; i++)
  834. {
  835. if (ttisSTDCount == 0)
  836. {
  837. outRules.Ttis[i].IsStandardTimeDaylight = false;
  838. }
  839. else
  840. {
  841. if (*p >= 2)
  842. {
  843. return false;
  844. }
  845. outRules.Ttis[i].IsStandardTimeDaylight = *p++ != 0;
  846. }
  847. }
  848. for (int i = 0; i < outRules.TypeCount; i++)
  849. {
  850. if (ttisSTDCount == 0)
  851. {
  852. outRules.Ttis[i].IsGMT = false;
  853. }
  854. else
  855. {
  856. if (*p >= 2)
  857. {
  858. return false;
  859. }
  860. outRules.Ttis[i].IsGMT = *p++ != 0;
  861. }
  862. }
  863. long position = (p - workBufferPtrStart);
  864. long nRead = streamLength - position;
  865. if (nRead < 0)
  866. {
  867. return false;
  868. }
  869. // Nintendo abort in case of a TzIf file with a POSIX TZ Name too long to fit inside a TimeZoneRule.
  870. // As it's impossible in normal usage to achive this, we also force a crash.
  871. if (nRead > (TzNameMax + 1))
  872. {
  873. throw new InvalidOperationException();
  874. }
  875. char[] tempName = new char[TzNameMax + 1];
  876. Array.Copy(workBuffer, position, tempName, 0, nRead);
  877. if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes)
  878. {
  879. tempName[nRead - 1] = '\0';
  880. char[] name = new char[TzNameMax];
  881. Array.Copy(tempName, 1, name, 0, nRead - 1);
  882. if (ParsePosixName(name, out TimeZoneRule tempRules, false))
  883. {
  884. int abbreviationCount = 0;
  885. charCount = outRules.CharCount;
  886. fixed (char* chars = outRules.Chars)
  887. {
  888. for (int i = 0; i < tempRules.TypeCount; i++)
  889. {
  890. fixed (char* tempChars = tempRules.Chars)
  891. {
  892. char* tempAbbreviation = tempChars + tempRules.Ttis[i].AbbreviationListIndex;
  893. int j;
  894. for (j = 0; j < charCount; j++)
  895. {
  896. if (StringUtils.CompareCStr(chars + j, tempAbbreviation) == 0)
  897. {
  898. tempRules.Ttis[i].AbbreviationListIndex = j;
  899. abbreviationCount++;
  900. break;
  901. }
  902. }
  903. if (j >= charCount)
  904. {
  905. int abbreviationLength = StringUtils.LengthCstr(tempAbbreviation);
  906. if (j + abbreviationLength < TzMaxChars)
  907. {
  908. for (int x = 0; x < abbreviationLength; x++)
  909. {
  910. chars[j + x] = tempAbbreviation[x];
  911. }
  912. charCount = j + abbreviationLength + 1;
  913. tempRules.Ttis[i].AbbreviationListIndex = j;
  914. abbreviationCount++;
  915. }
  916. }
  917. }
  918. }
  919. if (abbreviationCount == tempRules.TypeCount)
  920. {
  921. outRules.CharCount = charCount;
  922. // Remove trailing
  923. while (1 < outRules.TimeCount && (outRules.Types[outRules.TimeCount - 1] == outRules.Types[outRules.TimeCount - 2]))
  924. {
  925. outRules.TimeCount--;
  926. }
  927. int i;
  928. for (i = 0; i < tempRules.TimeCount; i++)
  929. {
  930. if (outRules.TimeCount == 0 || outRules.Ats[outRules.TimeCount - 1] < tempRules.Ats[i])
  931. {
  932. break;
  933. }
  934. }
  935. while (i < tempRules.TimeCount && outRules.TimeCount < TzMaxTimes)
  936. {
  937. outRules.Ats[outRules.TimeCount] = tempRules.Ats[i];
  938. outRules.Types[outRules.TimeCount] = (byte)(outRules.TypeCount + (byte)tempRules.Types[i]);
  939. outRules.TimeCount++;
  940. i++;
  941. }
  942. for (i = 0; i < tempRules.TypeCount; i++)
  943. {
  944. outRules.Ttis[outRules.TypeCount++] = tempRules.Ttis[i];
  945. }
  946. }
  947. }
  948. }
  949. }
  950. if (outRules.TypeCount == 0)
  951. {
  952. return false;
  953. }
  954. if (outRules.TimeCount > 1)
  955. {
  956. for (int i = 1; i < outRules.TimeCount; i++)
  957. {
  958. if (TimeTypeEquals(outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
  959. {
  960. outRules.GoBack = true;
  961. break;
  962. }
  963. }
  964. for (int i = outRules.TimeCount - 2; i >= 0; i--)
  965. {
  966. if (TimeTypeEquals(outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
  967. {
  968. outRules.GoAhead = true;
  969. break;
  970. }
  971. }
  972. }
  973. int defaultType;
  974. for (defaultType = 0; defaultType < outRules.TimeCount; defaultType++)
  975. {
  976. if (outRules.Types[defaultType] == 0)
  977. {
  978. break;
  979. }
  980. }
  981. defaultType = defaultType < outRules.TimeCount ? -1 : 0;
  982. if (defaultType < 0 && outRules.TimeCount > 0 && outRules.Ttis[outRules.Types[0]].IsDaySavingTime)
  983. {
  984. defaultType = outRules.Types[0];
  985. while (--defaultType >= 0)
  986. {
  987. if (!outRules.Ttis[defaultType].IsDaySavingTime)
  988. {
  989. break;
  990. }
  991. }
  992. }
  993. if (defaultType < 0)
  994. {
  995. defaultType = 0;
  996. while (outRules.Ttis[defaultType].IsDaySavingTime)
  997. {
  998. if (++defaultType >= outRules.TypeCount)
  999. {
  1000. defaultType = 0;
  1001. break;
  1002. }
  1003. }
  1004. }
  1005. outRules.DefaultType = defaultType;
  1006. }
  1007. return true;
  1008. }
  1009. private static long GetLeapDaysNotNeg(long year)
  1010. {
  1011. return year / 4 - year / 100 + year / 400;
  1012. }
  1013. private static long GetLeapDays(long year)
  1014. {
  1015. if (year < 0)
  1016. {
  1017. return -1 - GetLeapDaysNotNeg(-1 - year);
  1018. }
  1019. else
  1020. {
  1021. return GetLeapDaysNotNeg(year);
  1022. }
  1023. }
  1024. private static ResultCode CreateCalendarTime(long time, int gmtOffset, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
  1025. {
  1026. long year = EpochYear;
  1027. long timeDays = time / SecondsPerDay;
  1028. long remainingSeconds = time % SecondsPerDay;
  1029. calendarTime = new CalendarTimeInternal();
  1030. calendarAdditionalInfo = new CalendarAdditionalInfo()
  1031. {
  1032. TimezoneName = new char[8]
  1033. };
  1034. while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)])
  1035. {
  1036. long timeDelta = timeDays / DaysPerLYear;
  1037. long delta = timeDelta;
  1038. if (delta == 0)
  1039. {
  1040. delta = timeDays < 0 ? -1 : 1;
  1041. }
  1042. long newYear = year;
  1043. if (IncrementOverflow64(ref newYear, delta))
  1044. {
  1045. return ResultCode.OutOfRange;
  1046. }
  1047. long leapDays = GetLeapDays(newYear - 1) - GetLeapDays(year - 1);
  1048. timeDays -= (newYear - year) * DaysPerNYear;
  1049. timeDays -= leapDays;
  1050. year = newYear;
  1051. }
  1052. long dayOfYear = timeDays;
  1053. remainingSeconds += gmtOffset;
  1054. while (remainingSeconds < 0)
  1055. {
  1056. remainingSeconds += SecondsPerDay;
  1057. dayOfYear -= 1;
  1058. }
  1059. while (remainingSeconds >= SecondsPerDay)
  1060. {
  1061. remainingSeconds -= SecondsPerDay;
  1062. dayOfYear += 1;
  1063. }
  1064. while (dayOfYear < 0)
  1065. {
  1066. if (IncrementOverflow64(ref year, -1))
  1067. {
  1068. return ResultCode.OutOfRange;
  1069. }
  1070. dayOfYear += YearLengths[IsLeap((int)year)];
  1071. }
  1072. while (dayOfYear >= YearLengths[IsLeap((int)year)])
  1073. {
  1074. dayOfYear -= YearLengths[IsLeap((int)year)];
  1075. if (IncrementOverflow64(ref year, 1))
  1076. {
  1077. return ResultCode.OutOfRange;
  1078. }
  1079. }
  1080. calendarTime.Year = year;
  1081. calendarAdditionalInfo.DayOfYear = (uint)dayOfYear;
  1082. long dayOfWeek = (EpochWeekDay + ((year - EpochYear) % DaysPerWekk) * (DaysPerNYear % DaysPerWekk) + GetLeapDays(year - 1) - GetLeapDays(EpochYear - 1) + dayOfYear) % DaysPerWekk;
  1083. if (dayOfWeek < 0)
  1084. {
  1085. dayOfWeek += DaysPerWekk;
  1086. }
  1087. calendarAdditionalInfo.DayOfWeek = (uint)dayOfWeek;
  1088. calendarTime.Hour = (sbyte)((remainingSeconds / SecondsPerHour) % SecondsPerHour);
  1089. remainingSeconds %= SecondsPerHour;
  1090. calendarTime.Minute = (sbyte)(remainingSeconds / SecondsPerMinute);
  1091. calendarTime.Second = (sbyte)(remainingSeconds % SecondsPerMinute);
  1092. int[] ip = MonthsLengths[IsLeap((int)year)];
  1093. while (dayOfYear >= ip[calendarTime.Month])
  1094. {
  1095. calendarTime.Month += 1;
  1096. dayOfYear -= ip[calendarTime.Month];
  1097. }
  1098. calendarTime.Day = (sbyte)(dayOfYear + 1);
  1099. calendarAdditionalInfo.IsDaySavingTime = false;
  1100. calendarAdditionalInfo.GmtOffset = gmtOffset;
  1101. return 0;
  1102. }
  1103. private static ResultCode ToCalendarTimeInternal(TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
  1104. {
  1105. calendarTime = new CalendarTimeInternal();
  1106. calendarAdditionalInfo = new CalendarAdditionalInfo()
  1107. {
  1108. TimezoneName = new char[8]
  1109. };
  1110. ResultCode result;
  1111. if ((rules.GoAhead && time < rules.Ats[0]) || (rules.GoBack && time > rules.Ats[rules.TimeCount - 1]))
  1112. {
  1113. long newTime = time;
  1114. long seconds;
  1115. long years;
  1116. if (time < rules.Ats[0])
  1117. {
  1118. seconds = rules.Ats[0] - time;
  1119. }
  1120. else
  1121. {
  1122. seconds = time - rules.Ats[rules.TimeCount - 1];
  1123. }
  1124. seconds -= 1;
  1125. years = (seconds / SecondsPerRepeat + 1) * YearsPerRepeat;
  1126. seconds = years * AverageSecondsPerYear;
  1127. if (time < rules.Ats[0])
  1128. {
  1129. newTime += seconds;
  1130. }
  1131. else
  1132. {
  1133. newTime -= seconds;
  1134. }
  1135. if (newTime < rules.Ats[0] && newTime > rules.Ats[rules.TimeCount - 1])
  1136. {
  1137. return ResultCode.TimeNotFound;
  1138. }
  1139. result = ToCalendarTimeInternal(rules, newTime, out calendarTime, out calendarAdditionalInfo);
  1140. if (result != 0)
  1141. {
  1142. return result;
  1143. }
  1144. if (time < rules.Ats[0])
  1145. {
  1146. calendarTime.Year -= years;
  1147. }
  1148. else
  1149. {
  1150. calendarTime.Year += years;
  1151. }
  1152. return ResultCode.Success;
  1153. }
  1154. int ttiIndex;
  1155. if (rules.TimeCount == 0 || time < rules.Ats[0])
  1156. {
  1157. ttiIndex = rules.DefaultType;
  1158. }
  1159. else
  1160. {
  1161. int low = 1;
  1162. int high = rules.TimeCount;
  1163. while (low < high)
  1164. {
  1165. int mid = (low + high) >> 1;
  1166. if (time < rules.Ats[mid])
  1167. {
  1168. high = mid;
  1169. }
  1170. else
  1171. {
  1172. low = mid + 1;
  1173. }
  1174. }
  1175. ttiIndex = rules.Types[low - 1];
  1176. }
  1177. result = CreateCalendarTime(time, rules.Ttis[ttiIndex].GmtOffset, out calendarTime, out calendarAdditionalInfo);
  1178. if (result == 0)
  1179. {
  1180. calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime;
  1181. unsafe
  1182. {
  1183. fixed (char* timeZoneAbbreviation = &rules.Chars[rules.Ttis[ttiIndex].AbbreviationListIndex])
  1184. {
  1185. int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8);
  1186. for (int i = 0; i < timeZoneSize; i++)
  1187. {
  1188. calendarAdditionalInfo.TimezoneName[i] = timeZoneAbbreviation[i];
  1189. }
  1190. }
  1191. }
  1192. }
  1193. return result;
  1194. }
  1195. private static ResultCode ToPosixTimeInternal(TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
  1196. {
  1197. posixTime = 0;
  1198. int hour = calendarTime.Hour;
  1199. int minute = calendarTime.Minute;
  1200. if (NormalizeOverflow32(ref hour, ref minute, MinutesPerHour))
  1201. {
  1202. return ResultCode.Overflow;
  1203. }
  1204. calendarTime.Minute = (sbyte)minute;
  1205. int day = calendarTime.Day;
  1206. if (NormalizeOverflow32(ref day, ref hour, HoursPerDays))
  1207. {
  1208. return ResultCode.Overflow;
  1209. }
  1210. calendarTime.Day = (sbyte)day;
  1211. calendarTime.Hour = (sbyte)hour;
  1212. long year = calendarTime.Year;
  1213. long month = calendarTime.Month;
  1214. if (NormalizeOverflow64(ref year, ref month, MonthsPerYear))
  1215. {
  1216. return ResultCode.Overflow;
  1217. }
  1218. calendarTime.Month = (sbyte)month;
  1219. if (IncrementOverflow64(ref year, YearBase))
  1220. {
  1221. return ResultCode.Overflow;
  1222. }
  1223. while (day <= 0)
  1224. {
  1225. if (IncrementOverflow64(ref year, -1))
  1226. {
  1227. return ResultCode.Overflow;
  1228. }
  1229. long li = year;
  1230. if (1 < calendarTime.Month)
  1231. {
  1232. li++;
  1233. }
  1234. day += YearLengths[IsLeap((int)li)];
  1235. }
  1236. while (day > DaysPerLYear)
  1237. {
  1238. long li = year;
  1239. if (1 < calendarTime.Month)
  1240. {
  1241. li++;
  1242. }
  1243. day -= YearLengths[IsLeap((int)li)];
  1244. if (IncrementOverflow64(ref year, 1))
  1245. {
  1246. return ResultCode.Overflow;
  1247. }
  1248. }
  1249. while (true)
  1250. {
  1251. int i = MonthsLengths[IsLeap((int)year)][calendarTime.Month];
  1252. if (day <= i)
  1253. {
  1254. break;
  1255. }
  1256. day -= i;
  1257. calendarTime.Month += 1;
  1258. if (calendarTime.Month >= MonthsPerYear)
  1259. {
  1260. calendarTime.Month = 0;
  1261. if (IncrementOverflow64(ref year, 1))
  1262. {
  1263. return ResultCode.Overflow;
  1264. }
  1265. }
  1266. }
  1267. calendarTime.Day = (sbyte)day;
  1268. if (IncrementOverflow64(ref year, -YearBase))
  1269. {
  1270. return ResultCode.Overflow;
  1271. }
  1272. calendarTime.Year = year;
  1273. int savedSeconds;
  1274. if (calendarTime.Second >= 0 && calendarTime.Second < SecondsPerMinute)
  1275. {
  1276. savedSeconds = 0;
  1277. }
  1278. else if (year + YearBase < EpochYear)
  1279. {
  1280. int second = calendarTime.Second;
  1281. if (IncrementOverflow32(ref second, 1 - SecondsPerMinute))
  1282. {
  1283. return ResultCode.Overflow;
  1284. }
  1285. savedSeconds = second;
  1286. calendarTime.Second = 1 - SecondsPerMinute;
  1287. }
  1288. else
  1289. {
  1290. savedSeconds = calendarTime.Second;
  1291. calendarTime.Second = 0;
  1292. }
  1293. long low = long.MinValue;
  1294. long high = long.MaxValue;
  1295. while (true)
  1296. {
  1297. long pivot = low / 2 + high / 2;
  1298. if (pivot < low)
  1299. {
  1300. pivot = low;
  1301. }
  1302. else if (pivot > high)
  1303. {
  1304. pivot = high;
  1305. }
  1306. int direction;
  1307. ResultCode result = ToCalendarTimeInternal(rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
  1308. if (result != 0)
  1309. {
  1310. if (pivot > 0)
  1311. {
  1312. direction = 1;
  1313. }
  1314. else
  1315. {
  1316. direction = -1;
  1317. }
  1318. }
  1319. else
  1320. {
  1321. direction = candidateCalendarTime.CompareTo(calendarTime);
  1322. }
  1323. if (direction == 0)
  1324. {
  1325. long timeResult = pivot + savedSeconds;
  1326. if ((timeResult < pivot) != (savedSeconds < 0))
  1327. {
  1328. return ResultCode.Overflow;
  1329. }
  1330. posixTime = timeResult;
  1331. break;
  1332. }
  1333. else
  1334. {
  1335. if (pivot == low)
  1336. {
  1337. if (pivot == long.MaxValue)
  1338. {
  1339. return ResultCode.TimeNotFound;
  1340. }
  1341. pivot += 1;
  1342. low += 1;
  1343. }
  1344. else if (pivot == high)
  1345. {
  1346. if (pivot == long.MinValue)
  1347. {
  1348. return ResultCode.TimeNotFound;
  1349. }
  1350. pivot -= 1;
  1351. high -= 1;
  1352. }
  1353. if (low > high)
  1354. {
  1355. return ResultCode.TimeNotFound;
  1356. }
  1357. if (direction > 0)
  1358. {
  1359. high = pivot;
  1360. }
  1361. else
  1362. {
  1363. low = pivot;
  1364. }
  1365. }
  1366. }
  1367. return ResultCode.Success;
  1368. }
  1369. internal static ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
  1370. {
  1371. ResultCode result = ToCalendarTimeInternal(rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
  1372. calendar = new CalendarInfo()
  1373. {
  1374. Time = new CalendarTime()
  1375. {
  1376. Year = (short)calendarTime.Year,
  1377. Month = calendarTime.Month,
  1378. Day = calendarTime.Day,
  1379. Hour = calendarTime.Hour,
  1380. Minute = calendarTime.Minute,
  1381. Second = calendarTime.Second
  1382. },
  1383. AdditionalInfo = calendarAdditionalInfo
  1384. };
  1385. return result;
  1386. }
  1387. internal static ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
  1388. {
  1389. CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal()
  1390. {
  1391. Year = calendarTime.Year,
  1392. Month = calendarTime.Month,
  1393. Day = calendarTime.Day,
  1394. Hour = calendarTime.Hour,
  1395. Minute = calendarTime.Minute,
  1396. Second = calendarTime.Second
  1397. };
  1398. return ToPosixTimeInternal(rules, calendarTimeInternal, out posixTime);
  1399. }
  1400. }
  1401. }