0

I have a problem of finding a future year based upon a fixed past year. I can come to the correct answer if I use an if statement, but I feel this should be accomplished with one formula.

For example, let $$\text{CurrentYear} = 2018,\quad \text{cycleYears} = 3,\quad \text{baseYear} = 2015$$

With these values I can simply say $$\text{(mod(CurrentYear $-$ baseYear), cycleYears) $+$ CurrentYear}$$

and I get the expected answer of $2018$. Now, let $\text{baseYear} = 2014$ and get a value of $2019$, off by one. Simple, factor in the remainder to the above formula.

$$ \text{(mod(CurrentYear $-$ baseYear), cycleYears) $+$ CurrentYear $+$ mod(CurrentYear $-$ baseYear)} $$

Now I get the expected answer of $2020$, and this tweak works for the base year of $2015$ as the modulus is zero.

Now, let $\text{baseYear} = 2016$ and the year with the first formula is 2020 (off by one) and with the second one is 2022 (skipped the current year in favor of the next cycle). The correct answer is 2019.

I can get this to work with an if of some sort, checking the modulus value, but I feel this should work without a logic branch.

Thoughts?

Update/Clarifications

I am trying to compute a series of years based upon an base year and how many years this date cycles.

A real-world example would be election years. For a US President, the baseYear could be any leap year, let's say 1976. We also know that that office is elected every 4 years. So, I could use this formula and find out that the next presidential election is in 2020. Like wise, a US Senator is on a 6 year cycle. Some senators could have a base year of 1974, so their next election would be this year in 2018.

Jim
  • 111
  • Just drop the $mod$ – Old Peter Aug 22 '18 at 19:03
  • @OldPeter, OK, I tried it, but still not quite. It's fine for the 2018 example, but it doesn't work for the other 2.

    Plus, without using mod, it is even more off if say the base year is 2012 instead of 2015.

    – Jim Aug 22 '18 at 22:02
  • 2
    It is not completely clear what you are trying to compute. Is it the function that can be defined as follows: "If baseYear is greater than or equal to currentYear return the base year; else add the minimum integer multiple of cycleYears to baseYear such that the sum is greater than or equal to currentYear, and return the sum"? – David K Aug 22 '18 at 22:37
  • Or if I say currentYear = 2018, baseYear = 2030, cycleYears = 5 should I get the result 2020? – David K Aug 22 '18 at 22:38
  • I'm not quite clear on what you're trying to compute either. "... and I get the expected answer of 2018." Why was 2018 your expected answer from the first example? Why is 2018 still the expected output even if you change the $\mathrm{baseYear}$? – Mike Pierce Aug 23 '18 at 03:27
  • @MikePierce. I have updated the question with a real-world example. I hope that helps to clarify what I am trying to accomplish. – Jim Aug 23 '18 at 12:58
  • @DavidK I have updated the question with a real-world example. I hope that helps to clarify what I am trying to accomplish. For me, the baseYear would never be in the future though. That said, I don't know why it should work... – Jim Aug 23 '18 at 14:44

1 Answers1

3

To rephrase the question, you want to find the least $y$ satisfying $y \ge y_0$ and $y \equiv y_{base} \pmod \omega$. Here $y_0$ is the current year, $y_{base}$ is the base year, and $\omega$ is the cycle length.

Unfortunately this is hard to do with a conventional modulo operator and no case splits. The related problem of finding the least $y'$ such that $y' > y_0$ and $y' \equiv y_{base} \pmod \omega$ is solved with $$y' = y_0 + \omega - ((y_0 - y_{base}) \bmod \omega)$$ but if your modulo operator gives $0$ as a result instead of $\omega$ then it's arguable that the simplest expression for $y$ is $$y = \begin{cases}y_0 & \text{when } y_0 \equiv y_{base} \pmod \omega \\ y_0 + \omega - ((y_0 - y_{base}) \bmod \omega) & \text{otherwise} \end{cases}$$

I suppose that if you really want to avoid case splits you have $$y = y_0 + ((\omega - ((y_0 - y_{base}) \bmod \omega)) \bmod \omega)$$

Peter Taylor
  • 13,425
  • Yes, that seems to do the job. And yes, I was looking to avoid case splits. I am looking to add this to a SQL table as a calculated field along with the base year. – Jim Aug 23 '18 at 15:19
  • Then I'll mention an implementation note. Many (not all - don't know about SQL variants) programming languages implement the modulo operation in a way which is inconsistent with the way it's used in number theory: specifically, they can give a negative result if one of the operands is negative. The double-mod here will be robust against that even in the case $y_{base} < y_0$, so that's a useful side-effect of using the double-mod instead of the case split. – Peter Taylor Aug 23 '18 at 15:31
  • Good to know. And yes, Microsoft SQL Server can do that. I just ran Select 10 % 4, -10 % 4, 10 % -4 , -10 % -4 and the second and fourth one, with the first operand being negative gave a negative result. If only the second operand is negative, then the result is positive. I will check if this can happen in my actual use or if I need to wrap an abs() around it. – Jim Aug 23 '18 at 16:16
  • Don't use abs! The correct way to bring a negative % result into range is to add the second operand. But it's not necessary anyway if you use the double-mod: I apologise if my comment has brought confusion where there was none (and see now that I typed $<$ where I should have typed $>$). – Peter Taylor Aug 23 '18 at 16:30