Algorithm to calculate the number fortnightly occurring events in a given calendar month - algorithm

I'm looking for the cleverest algorithm for determining the number of fortnightly occurring events in a given calendar month, within a specific series.
i.e. Given the series is 'Every 2nd Thursday from 7 October 2010' the "events" are falling on (7 Oct 2010, 21 Oct, 4 Nov, 18 Nov, 2 Dec, 16 Dec, 30 Dec, ...)
So what I am after is a function
function(seriesDefinition, month) -> integer
where:
- seriesDefinition is some date that is a valid date in the series,
- month indicates a month and a year
such that it accurately yeilds: numberFortnightlyEventsInSeriesThatFallInCalendarMonth
Examples:
NumberFortnightlyEventsInMonth('7 Oct 2010, 'Oct 2010') -> 2
NumberFortnightlyEventsInMonth('7 Oct 2010, 'Nov2010') -> 2
NumberFortnightlyEventsInMonth('7 Oct 2010, 'Dec 2010') -> 3
Note that October has 2 events, November has 2 events, but December has 3 events.
Psuedocode preferred.
I don't want to rely on lookup tables or web service calls or any other external resources other than potentially universal libraries. For example, I think we can safely assume that most programming languages will have some date manipulation functions available.

There is no "clever" algorithm when handling dates, there is only the tedious one. That is, you have to specifically list how many days are in each month, handle leap years (every four years, except every 100 years, except every 400 years), etc.

Well, for the algorithm you are talking about the usual solution is to calculate the day number starting from some fixed date. (Number of day plus cumulated number of days in prev months plus number of years * 365 minus (number of year / 4) plus (number of year / 100) minus (number of year / 400))
Having this, you can easily implement what you need to. You need to calculate which day of week was the 1 January 1. Then you can easily see what is the number of "every second thursdays" from that day to 1 Oct 2010 and 1 Dec 2010. their difference is the value you are looking for.

My solution ...
Public Function NumberFortnightlyEventsInMonth(seriesDefinition As Date, month As String) As Integer
Dim monthBeginDate As Date
monthBeginDate = DateValue("1 " + month)
Dim lastDateOfMonth As Date
lastDateOfMonth = DateAdd("d", -1, DateAdd("m", 1, monthBeginDate))
' Step 1 - How many days between seriesDefinition and the 1st of [month]
Dim daysToMonthBegin As Integer
daysToMonthBegin = DateDiff("d", seriesDefinition, monthBeginDate)
' Step 2 - How many fortnights (14 days) fit into the number from Step 1? Round up to the nearest whole number.
Dim numberFortnightsToFirstOccurenceOfSeriesInMonth As Integer
numberFortnightsToFirstOccurenceOfSeriesInMonth = (daysToMonthBegin \ 14) + IIf(daysToMonthBegin Mod 14 > 0, 1, 0)
' Step 3 - The date of the first date of this series inside that month is seriesDefinition + the number of fortnights from Step 2
Dim firstDateOfSeriesInMonth As Date
firstDateOfSeriesInMonth = DateAdd("d", (14 * numberFortnightsToFirstOccurenceOfSeriesInMonth), seriesDefinition)
' Step 4 - How many fortnights fit between the date from Step 3 and the last date of the [month]?
NumberFortnightlyEventsInMonth = 1 + (DateDiff("d", firstDateOfSeriesInMonth, lastDateOfMonth) \ 14)
End Function

Related

Calculating which day of the week a date falls on using Gauss's algorithm, ordinal date and modulo arithmetic

After calculating which day of the week the 1st of January falls on using Gauss's algorithm, as well as calculating the ordinal date for a given calendar date, how can the day of the week of the latter date be calculated?
For example, Gauss's algorithm can tell us that, this year, the 1st of January fell on a Sunday, the 7th day of the week. Today is the 22nd of October, with an ordinal day of 295. How can this information be used to calculate that today is a Sunday?
For common years (= non-leap years), 1st of January and 1st of October are on the same day of the week:
Jan 31
Feb 28
Mar 31
Apr 30
May 31
Jun 30
Jul 31
Aug 30
Sep 31
Sum 273 = 39 x 7
See Wikipedia
22nd October is exactly three weeks later than 1st of October.
An approach I've found, which I haven't tested extensively, but seems to work with the dates I've thrown at it, is...
(ordinal day + day of 1st of January - 1) % 7
Where Mon = 1, Tue = 2,..., Sat = 6, Sun = 0.
In the example mentioned in the question:
(295 + 0 - 1) % 7 = 0 (Sunday)

Find the time period with the maximum number of overlapping intervals

There is one very famous problem. I am asking the same here.
There is number of elephants time span given, here time span means, year of birth to year of death.
You have to calculate the period where maximum number of elephants are alive.
Example:
1990 - 2013
1995 - 2000
2010 - 2020
1992 - 1999
Answer is 1995 - 1999
I tried hard to solve this, but I am unable to do so.
How can I solve this problem?
I got approach for when a user asks to find the number of elephants in any year. I solved that by using segment tree, whenever any elephants time span given, increase every year of that time span by 1. We can solve that in this way. Can this be used to solve the above problem?
For above question, I only need the high-level approach, I will code it myself.
Split each date range into start date and end date.
Sort the dates. If a start date and an end date are the same, put the end date first (otherwise you could get an empty date range as the best).
Start with a count of 0.
Iterate through the dates using a sweep-line algorithm:
If you get a start date:
Increment the count.
If the current count is higher than the last best count, set the count, store this start date and set a flag.
If you get an end date:
If the flag is set, store the stored start date and this end date with the count as the best interval so far.
Reset the flag.
Decrement the count.
Example:
For input:
1990 - 2013
1995 - 2000
2010 - 2020
1992 - 1999
Split and sorted: (S = start, E = end)
1990 S, 1992 S, 1995 S, 1999 E, 2000 E, 2010 S, 2013 E, 2020 E
Iterating through them:
count = 0
lastStart = N/A
1990: count = 1
count = 1 > 0, so set flag
and lastStart = 1990
1992: count = 2
count = 2 > 0, so set flag
and lastStart = 1992
1995: count = 3
count = 3 > 0, so set flag
and lastStart = 1995
1999: flag is set, so
record [lastStart (= 1995), 1999] with a count of 3
reset flag
count = 2
2000: flag is not set
reset flag
count = 1
2010: count = 2
since count = 2 < 3, don't set flag
2013: flag is not set
reset flag
count = 1
2020: flag is not set
reset flag
count = 0
How about this?
Say I have all the above data stored in a file. Read it into two arrays separated by the " - ".
Hence, now I have birthYear[] which contains all the birth years and deathYear[] containing all the death years.
so birthYear[] = [1990, 1995, 2010, 1992]
deathYear[] = [2013, 2000, 2020, 1999]
Get the min birth year and the max death year. Create a Hashtable with the Key as a year, and the Value as the count.
Hence,
HashTable<String, Integer> numOfElephantsAlive = new HashTable<String, Integer>();
Now, from the min(BirthYear) to the max(BirthYear), do the following :
Iterate through the Birth Year Array and do an add to the HashTable all the years in between the BirthYear and Corresponding DeathYear with the count being 1. If the key already exists, add 1 to it. Hence, for the last case :
1992 - 1999
HashTable.put(1992, 1)
HashTable.put(1993, 1)
and so on for every year.
Say, for example, you have a Hashtable that looks like this at the end of it:
Key Value
1995 3
1996 3
1992 2
1993 1
1994 3
1998 1
1997 2
1999 2
Now, you need the range of the Years when the number of elephants were maximum. Hence, let's iterate and find the year with the max value. This is pretty easy. Iterate over the keySet() and get the year.
Now, you need a contiguous range of years. You can either do this in two ways:
Do Collections.sort() over the keySet() and when you hit the max value, save all contiguous locations.
Hence, on hitting 3 for our example at 1994, we would check for all the following years with a 3. This will return you your range which is the min-year, max-year combo.
One approach maybe:
Iterate through the periods. Keep track of a list of periods up to now. Note: At each step, the number of periods increases by 2 (or 1 if there is no overlap with the existing list of periods).
For example
1990 - 2013
Period List contains 1 period { (1990,2013) }
Count List contains 1 entry { 1 }
1995 - 2000
Period List contains 3 periods { (1990,1995), (1995,2000), (2000,2013) }
Count List contains 3 entries { 1, 2, 1 }
2010 - 2020
Period List contains 5 periods { (1990,1995), (1995,2000), (2000,2010), (2010, 2013), (2013, 2020) }
Count List contains 5 entries { 1, 2, 1, 2, 1 }
1992 - 1999
Period List contains 7 periods { (1990,1992), (1992,1995), (1995,1999), (1999,2000), (2000,2010), (2010, 2013), (2013, 2020) }
Count List contains 7 entries { 1, 2, 3, 2, 1, 2, 1 }
1) arrange in assending order year wise starting from the largest series.
2) count the years for largest series for whole data set
3) then identify the largest count.
4) the largest count is your answer for years... this can be done in Algo.

Calculating total days minus weekend

I have a requirement to obtain number of days passed since creation date. This number would need to minus the weekends. I have only some functions : JulianDay, JulianWeek, JulianYear to get Julian date values, I also have Today which returns the date of today, time stamp which returns date and time. I have manage to get the difference of today-creation date by using: JulianDay(today)-JulianDay(creation date) but I still can't wrap my head around subtracting the weekends
Not completely sure what the functions you cited in your question do, however, you seem to be comfortable with
doing the basic date arithmetic to determine the number of days between two given dates. The hard part seems
to be figuring out how may days to subtract for weekends.
I think you can accomplish this with two functions:
Given two dates, return the number of days between them. Call this DAYS(date-1, date-2)
Given a date, return the day of the week (where 1 = Monday ... 7 = Sunday). Call this DAY-OF-WEEK(date)
Having these functions you can then do the following:
Calculate full weeks in the date range: WEEKS = DAYS(date-1, date2) mod 7
Calculate days not parts of full weeks: DAYS-LEFT = DAYS(date-1, date-2) - (WEEKS * 7)
Determine which day of the week the last day falls on: LAST-DAY = DAY-OF-WEEK(date-2)
Adjust the number of DAYS-LEFT from the partial week as follows:
if DAYS-LEFT > 0 then
case LAST-DAY
when 6 then /* Saturday */
DAYS-LEFT = DAYS-LEFT - 1
when 7 then /* Sunday */
if DAYS-LEFT = 1 then
DAYS-LEFT = 0
else
DAYS-LEFT = DAYS-LEFT - 2
end-if
when other /* Monday through Friday */
case DAYS-LEFT - LAST-DAY
when > 1 then
DAYS-LEFT = DAYS-LEFT - 2
when = 1 then
DAYS-LEFT = DAYS-LEFT - 1
when other
DAYS-LEFT = DAYS-LEFT /* no adjustment */
end-case
end-case
end-if
DAYS-EXCLUDING-WEEKENDS = DAYS(date-1, date-2) - (WEEKS * 2) + DAYS-LEFT
I assume you have, or can build, a DAYS(date-1, date-2) function. The next bit is to determine what day of the week
a given date falls on. The algorithm to do this is called Zeller's congruence. I won't
repeat the algorithm here since Wikipedia does a fine job of describing it.
Hope this gets you on your way...
Your JulianDay(y,m,d) function returns a serial number for each date; let's say for the sake of discussion that JulianDay(2013,7,4) returns 2456478. The next day will be 2456479, then 2456480, and so on. And let's say that the difference of two days is diff.
The number of full weeks in diff, each containing 5 weekdays, is diff // 7 (that's integer division, so it rounds down). Thus if diff is 25, there will be 25 // 7 = 3 full weeks plus an extra diff % 7 = 4 days. The 3 full weeks contain 15 weekdays; it doesn't matter which day of the week you start from. So you only need to consider the 4 extra days to see how may are weekdays.
The number that the JulianDay function returns can be taken modulo 7 to calculate the day of the week; on my JulianDay function, modulo 5 represents Saturday and modulo 6 represents Sunday. You can take the 4 extra days to be either the 4 days at the beginning of the period or the 4 days at the end; it doesn't matter because all the other days are part of a period of consecutive full weeks that each have 5 weekdays. Say you pick the first 4 days. Then take the JulianDay of the first day modulo 7, then the JulianDay of the first day plus 1 modulo 7, then the JulianDay of the first day plus 2 modulo 7, then the JulianDay of the first day plus 3 modulo 7, determine how many of them are weekdays, and add that to the number of weekdays in full weeks.
All you need is a JulianDay function.
This code should do what you want:
Date fromDate = new Date(System.currentTimeMillis()-(30L*24*60*60*1000)); // 30 days ago
Date toDate = new Date(System.currentTimeMillis()); // now
Calendar cal = Calendar.getInstance();
cal.setTime(fromDate);
int countDays = 0;
while (toDate.compareTo(cal.getTime()) > 0) {
if (cal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY)
countDays++;
cal.add(Calendar.DATE, 1);
}
System.out.println(countDays);

Algorithm to find nth working day [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have deviced a procedure to find nth working day without using loops.
Please bring around your suggesstions over this -
Algorithm to manipulate working days -
Problem: Find the date of nth working day from any particular day.
Solution:
Normalize to closest Monday -
If today(or the initial day) happens to be something other than monday, bring the day to the closest monday by simple addition or subtraction.
eg: Initial Day - 17, Oct. This happens to be wednesday. So normalize this no monday by going 2 dates down.
Now name this 2 dates, the initial normalization factor.
Add the number of working days + week ends that fall in these weeks.
eg: to add 10 working days, we need to add 12 days. Since 10 days has 1 week that includes only 1 saturday and 1 sunday.
this is because, we are normalizing to nearest monday.
Amortizing back -
Now from the end date add the initial normalization factor (for negative initial normalization) and another constant factor (say, k).
Or add 1 if the initial normalization is obtained from a Friday, which happens to be +3.
If start date falls on Saturday and sunday , treat as monday. so no amortization required at this step.
eg: Say if initial normalization is from wednesday, the intial normalization factor is -2. Hence add 2 to the end date and a constant k.
The constant k is either 2 or 0.
Constant definition -
If initial normalization factor is -3, then add 2 to the resulting date if the day before amortization is (wed,thu,fri)
If initial normalization factor is -2, then add 2 to the resulting date if the day before amortization is (thu,fri)
If initial normalization factor is -1, then add 2 to the resulting date if the day before amortization is (fri)
Example -
Find the 15th working day from Oct,17 (wednesday).
Step 1 -
initial normalization = -2
now start date is Oct,15 (monday).
Step 2 -
add 15 working days -
15 days => 2 weeks
weekends = 2 (2 sat, 2 sun)
so add 15 + 4 = 19 days to Oct, 15 monday.
end_date = 2, nov, Friday
Step 3a -
end_date = end_date + initial normalization = 4, nov sunday
Step 3b -
end_date = end_date + constant_factor = 4, nov, sunday + 2 = 6, nov (Tuesday)
Cross Verfication -
Add 15th working day to Oct, 17 wednesday
Oct,17 + 3 (Oct 17,18,19) + 5 (Oct 22-26) + 5 (Oct 29 - Nov 2) + 2 (Nov 5, Nov 6)
Now the answer is 6, Nov, Tuesday.
I have verified with a few cases. Please share your suggesstions.
Larsen.
To start with, its a nice algorithm, i have doubts about boundary conditions though: for example, what if i need to find the 0th working day from today's date:
Step 1 -
initial normalization = -2 now start date is Oct,15 (monday).
Step 2 -
add 0 working days -
0 days => 0 weeks
weekends = 0
so add 0 + 0 = 0 days to Oct, 15 monday.
end_date = 15, oct, monday
Step 3a -
end_date = end_date + initial normalization = 17, oct wednesday
Step 3b -
end_date = end_date + constant_factor = 17, Oct wednesday or 19,oct friday based on whether constant factor is 0 or 2 as it be only one of these values.
Now lets repeat the steps for finding the 1st working day from today:
Step 1 -
initial normalization = -2 now start date is Oct,15 (monday).
Step 2 -
add 1 working days -
1 days => 0 weeks
weekends = 0
so add 1 + 0 = 1 days to Oct, 15 monday.
end_date = 15, oct, monday
Step 3a -
end_date = end_date + initial normalization = 17, oct wednesday
Step 3b -
end_date = end_date + constant_factor = 17, Oct wednesday or 19,oct friday based on whether constant factor is 0 or 2 as it be only one of these values.
Did you notice, algorithm gives the same end result for 0 and 1. May be thats not an issue if t defined beforehand that 0 working days and 1 working days are considered as same scenario, but ideally they should be giving different results.
I would also suggest you to consider the negative test cases, like what if i need to find -6th working day from today, will your alforithm give me a date in past rightfully?
Lets consider 0th working day from today (17/10, wed).
Step 1 -
start_date = 17/10 wed
normalized date = 15/10 mon
Step 2 -
end_date = normalized date + working days
= 15/10 mon + 0 = 15/10 mon
Step 3 -
amortized_back = end_date_before_amortization + normalization factor
= 15/10 + (+2) = 17/10 wed
since the end_date_before_amortization falls on monday and initial normalization is 2, constant factor = 0.
hence, end_date = 17/10 wed.
now case 2, 1st working day from today.
Step 1 -
start_date = 17/10 wed
normalized date = 15/10 mon
Step 2 -
end_date = normalized date + working days
= 15/10 mon + 1 = 16/10 tue
Step 3 -
amortized_back = end_date_before_amortization + normalization factor
= 16/10 + (+2) = 18/10 thu.
since the end_date_before_amortization falls on tuesday and initial normalization is 2, constant factor = 0.
hence, end_date = 18/10 thu.
Looks to be working for 0th and 1st WD.

Leap year calculation

In order to find leap years, why must the year be indivisible by 100 and divisible by 400?
I understand why it must be divisible by 4. Please explain the algorithm.
The length of a year is (more or less) 365.242196 days.
So we have to subtract, more or less, a quarter of a day to make it fit :
365.242196 - 0.25 = 364.992196 (by adding 1 day in 4 years) : but oops, now it's too small!! lets add a hundreth of a day (by not adding that day once in a hundred year :-))
364.992196 + 0,01 = 365.002196 (oops, a bit too big, let's add that day anyway one time in about 400 years)
365.002196 - 1/400 = 364.999696
Almost there now, just play with leapseconds now and then, and you're set.
(Note : the reason no more corrections are applied after this step is because a year also CHANGES IN LENGTH!!, that's why leapseconds are the most flexible solution, see for examlple here)
That's why i guess
There's an algorithm on wikipedia to determine leap years:
function isLeapYear (year):
if ((year modulo 4 is 0) and (year modulo 100 is not 0))
or (year modulo 400 is 0)
then true
else false
There's a lot of information about this topic on the wikipedia page about leap years, inclusive information about different calendars.
In general terms the algorithm for calculating a leap year is as follows...
A year will be a leap year if it is divisible by 4 but not by 100. If a year is divisible by 4 and by 100, it is not a leap year unless it is also divisible by 400.
Thus years such as 1996, 1992, 1988 and so on are leap years because they are divisible by 4 but not by 100. For century years, the 400 rule is important. Thus, century years 1900, 1800 and 1700 while all still divisible by 4 are also exactly divisible by 100. As they are not further divisible by 400, they are not leap years
this is enough to check if a year is a leap year.
if( (year%400==0 || year%100!=0) &&(year%4==0))
cout<<"It is a leap year";
else
cout<<"It is not a leap year";
a) The year is 365.242199 days.
b) If every year was 365 days, in 100 years we would lose 24.2199 days.
That's why we add 24 days per century (every 4 years EXCEPT when divisible by 100)
c) But still we lose 0.21299 days/century. So in 4 centuries we lose 0.8796 days.
That's why we add 1 day per 4 centuries (every fourth century we DO count a leap year).
d) But that means we lose -0.1204 days (we go forward) per quadricentennial (4 centuries). So in 8 quadricentennial (3200 years) we DO NOT count a leap year.
e) But that means we lose 0.0368 days per 3200 years. So in 24x3200 years (=76800years) we lose 0.8832 days. That's why we DO count a leap year.
and so on... (by then we will have destroyed the planet, so it doesn't matter)
What I cannot understand though, is why we don't count a leap year every 500 years instead of 400. In that way we would converge more rapidly to the correct time (we would lose 2.3 hours/500 years).
I'm sure Wikipedia can explain it better than I can, but it is basically to do with the fact that if you added an extra day every four years we'd get ahead of the sun as its time to orbit the sun is less than 365.25 days so we compensate for this by not adding leap days on years that are not divisible by 400 eg 1900.
Hope that helps
Here is a simple implementation of the wikipedia algorithm, using the javascript ternary operator:
isLeapYear = (year % 100 === 0) ? (year % 400 === 0) : (year % 4 === 0);
Return true if the input year is a leap year
Basic modern day code:
If year mod 4 = 0, then leap year
if year mod 100 then normal year
if year mod 400 then leap year
else normal year
Todays rule started 1582 AD
Julian calendar rule with every 4th year started 46BC but is not coherent before 10 AD as declared by Cesar.
They did however add some leap years every 3rd year now and then in the years before:
Leap years were therefore 45 BC, 42 BC, 39 BC, 36 BC, 33 BC, 30 BC, 27 BC, 24 BC, 21 BC, 18 BC, 15 BC, 12 BC, 9 BC, 8 AD, 12 AD
Before year 45BC leap year was not added.
The year 0 do not exist as it is ...2BC 1BC 1AD 2AD... for some calculation this can be an issue.
function isLeapYear(year: Integer): Boolean;
begin
result := false;
if year > 1582 then // Todays calendar rule was started in year 1582
result := ((year mod 4 = 0) and (not(year mod 100 = 0))) or (year mod 400 = 0)
else if year > 10 then // Between year 10 and year 1582 every 4th year was a leap year
result := year mod 4 = 0
else //Between year -45 and year 10 only certain years was leap year, every 3rd year but the entire time
case year of
-45, -42, -39, -36, -33, -30, -27, -24, -21, -18, -15, -12, -9:
result := true;
end;
end;
You really should try to google first.
Wikipedia has a explanation of leap years. The algorithm your describing is for the Proleptic Gregorian calendar.
More about the math around it can be found in the article Calendar Algorithms (PDF).
Will it not be much better if we make one step further.
Assuming every 3200 year as no leap year,
the length of the year will come
364.999696 + 1/3200 = 364.999696 + .0003125 = 365.0000085
and after this the adjustment will be required after around 120000 years.
In Java Below code calculates leap year count between two given year. Determine starting and ending point of the loop.
Then if parameter modulo 4 is equal 0 and parameter modulo 100 not equal 0 or parameter modulo 400 equal zero then it is leap year and increase counter.
static int calculateLeapYearCount(int year, int startingYear) {
int min = Math.min(year, startingYear);
int max = Math.max(year, startingYear);
int counter = 0;
for (int i = min; i < max; i++) {
if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) {
counter = counter + 1;
}
}
return counter;
}
PHP:
// is number of days in the year 366? (php days of year is 0 based)
return ((int)date('z', strtotime('Dec 31')) === 365);
Leap years are arbitrary, and the system used to describe them is a man made construct. There is no why.
What I mean is there could have been a leap year every 28 years and we would have an extra week in those leap years ... but the powers that be decided to make it a day every 4 years to catch up.
It also has to do with the earth taking a pesky 365.25 days to go round the sun etc. Of course it isn't really 365.25 is it slightly less (365.242222...), so to correct for this discrepancy they decided drop the leap years that are divisible by 100.
If you're interested in the reasons for these rules, it's because the time it takes the earth to make exactly one orbit around the sun is a long imprecise decimal value. It's not exactly 365.25. It's slightly less than 365.25, so every 100 years, one leap day must be eliminated (365.25 - 0.01 = 365.24). But that's not exactly correct either. The value is slightly larger than 365.24. So only 3 out of 4 times will the 100 year rule apply (or in other words, add back in 1 day every 400 years; 365.25 - 0.01 + 0.0025 = 365.2425).
There are on average, roughly 365.2425 days in a year at the moment (the Earth is slowing down but let's ignore that for now).
The reason we have leap years every 4 years is because that gets us to 365.25 on average [(365+365+365+366) / 4 = 365.25, 1461 days in 4 years].
The reason we don't have leap years on the 100-multiples is to get us to 365.24 `[(1461 x 25 - 1) / 100 = 365.24, 36,524 days in 100 years.
Then the reason we once again have a leap year on 400-multiples is to get us to 365.2425 [(36,524 x 4 + 1) / 400 = 365.2425, 146,097 days in 400 years].
I believe there may be another rule at 3600-multiples but I've never coded for it (Y2K was one thing but planning for one and a half thousand years into the future is not necessary in my opinion - keep in mind I've been wrong before).
So, the rules are, in decreasing priority:
multiple of 400 is a leap year.
multiple of 100 is not a leap year.
multiple of 4 is a leap year.
anything else is not a leap year.
Here comes a rather obsqure idea.
When every year dividable with 100 gets 365 days, what shall be done at this time? In the far future, when even years dividable with 400 only can get 365 days.
Then there is a possibility or reason to make corrections in years dividable with 80.
Normal years will have 365 day and those dividable with 400 can get 366 days. Or is this a loose-loose situation.
You could just check if the Year number is divisible by both 4 and 400. You dont really need to check if it is indivisible by 100. The reason 400 comes into question is because according to the Gregorian Calendar, our "day length" is slightly off, and thus to compensate that, we have 303 regular years (365 days each) and 97 leap years (366 days each). The difference of those 3 extra years that are not leap years is to stay in cycle with the Gregorian calendar, which repeats every 400 years. Look up Christian Zeller's congruence equation. It will help understanding the real reason. Hope this helps :)
In the Gregorian calendar 3 criteria must be taken into account to identify leap years:
The year is evenly divisible by 4;
If the year can be evenly divided by 100, it is NOT a leap year, unless;
The year is also evenly divisible by 400. Then it is a leap year. Why the year divided by 100 is not leap year
Python 3.5
def is_leap_baby(year):
if ((year % 4 is 0) and (year % 100 is not 0)) or (year % 400 is 0):
return "{0}, {1} is a leap year".format(True, year)
return "{0} is not a leap year".format(year)
print(is_leap_baby(2014))
print(is_leap_baby(2012))
Simply
Because year 2000 is a leap year and it is divisible by 100 and dividable by 4.
SO to guarantee it is correct leap we need to ensure it is divisible by 400.
2000 % 4 = 0
2000 % 100 = 0
According to algorithm it's not leap, but it is dividable by 400
2000 % 400 = 0
so it is leap.
I found this problem in the book "Illustrated Guide to Python 3". It was in a very early chapter that only discussed the math operations, no loops, no comparisons, no conditionals. How can you tell if a given year is a leap year?
Below is what I came up with:
y = y % 400
a = y % 4
b = y % 100
c = y // 100
ly = (0**a) * ((1-(0**b)) + 0**c) # ly is not zero for leap years, else 0
This is the most efficient way, I think.
Python:
def leap(n):
if n % 100 == 0:
n = n / 100
return n % 4 == 0
C# implementation
public bool LeapYear()
{
int year = 2016;
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0 ;
}
From 1700 to 1917, official calendar was the Julian calendar. Since then they we use the Gregorian calendar system. The transition from the Julian to Gregorian calendar system occurred in 1918, when the next day after January 31st was February 14th. This means that 32nd day in 1918, was the February 14th.
In both calendar systems, February is the only month with a variable amount of days, it has 29 days during a leap year, and 28 days during all other years. In the Julian calendar, leap years are divisible by 4 while in the Gregorian calendar, leap years are either of the following:
Divisible by 400.
Divisible by 4 and not divisible by 100.
So the program for leap year will be:
Python:
def leap_notleap(year):
yr = ''
if year <= 1917:
if year % 4 == 0:
yr = 'leap'
else:
yr = 'not leap'
elif year >= 1919:
if (year % 400 == 0) or (year % 4 == 0 and year % 100 != 0):
yr = 'leap'
else:
yr = 'not leap'
else:
yr = 'none actually, since feb had only 14 days'
return yr
In shell you can use cal -j YYYY which prints the julian day of the year, If the last julian day is 366, then it is a leap year.
$ function check_leap_year
{
year=$1
if [ `cal -j $year | awk 'NF>0' | awk 'END { print $NF } '` -eq 366 ];
then
echo "$year -> Leap Year";
else
echo "$year -> Normal Year" ;
fi
}
$ check_leap_year 1900
1900 -> Normal Year
$ check_leap_year 2000
2000 -> Leap Year
$ check_leap_year 2001
2001 -> Normal Year
$ check_leap_year 2020
2020 -> Leap Year
$
Using awk, you can do
$ awk -v year=1900 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
365
$ awk -v year=2000 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
366
$ awk -v year=2001 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
365
$ awk -v year=2020 ' BEGIN { jul=strftime("%j",mktime(year " 12 31 0 0 0 ")); print jul } '
366
$
BIS will be 1 if the year is leap, otherwise 0 in this boolean logic:
BIS = A MOD 4=0 - (A MOD 100=0 AND A>1600) + (A MOD 400=0 AND A>1600)
just wrote this in Coffee-Script:
is_leap_year = ( year ) ->
assert isa_integer year
return true if year % 400 == 0
return false if year % 100 == 0
return true if year % 4 == 0
return false
# parseInt? that's not even a word.
# Let's rewrite that using real language:
integer = parseInt
isa_number = ( x ) ->
return Object.prototype.toString.call( x ) == '[object Number]' and not isNaN( x )
isa_integer = ( x ) ->
return ( isa_number x ) and ( x == integer( x ) )
of course, the validity checking done here goes a little further than what was asked for, but i find it a necessary thing to do in good programming.
note that the return values of this function indicate leap years in the so-called proleptic gregorian calendar, so for the year 1400 it indicates false, whereas in fact that year was a leap year, according to the then-used julian calendar. i will still leave it as such in the datetime library i'm writing because writing correct code to deal with dates quickly gets surprisingly involved, so i will only ever support the gregorian calendar (or get paid for another one).

Resources