mohsin
mohsin

Reputation: 540

How to get all possible years from the given day, day of the week, and month?

I want to get the set of years which pertain to a specific date, week of the day and month. There are possibilities that I can get various years from this pattern. Suppose Date is 15, Month is August and Week of the day is Tuesday then, How would I find the years that are valid? Is there a formula for this?

Upvotes: 8

Views: 2485

Answers (5)

Iain Samuel McLean Elder
Iain Samuel McLean Elder

Reputation: 20984

To solve it in Python I use the rrule module, part of the dateutil module.

I assume you are looking for years between this year (2024) and the year 1900.

In the example:

  • freq=YEARLY because there is at most one date per year
  • byweekday=TU for your "day of the week is Tuesday"
  • bymonthday=15 for your "date is 15"
  • bymonth=8 for your "month is August" (January is 1, December is 12)
  • dtstart=date(year=1900, month=1, day=1) for my early limit year 1900
  • until=date(year=2024, month=12, day=31) for my late limit year 2024
from datetime import date
from dateutil.rrule import rrule, YEARLY, TU

rule=rrule(
    freq=YEARLY,
    byweekday=TU,
    bymonthday=15,
    bymonth=8,
    dtstart=date(year=1900, month=1, day=1),
    until=date(year=2024, month=12, day=31),
)

print(*[date.year for date in rule], sep=", ")

Answer: 1905, 1911, 1916, 1922, 1933, 1939, 1944, 1950, 1961, 1967, 1972, 1978, 1989, 1995, 2000, 2006, 2017, 2023.


Use an iCalendar recurrence rule to solve the problem in any programming language that has a recurrence rule resolver package.

The iCalendar standard is a data format for representing and exchanging calendaring and scheduling information. The Internet Engineering Task Force maintains the standard in a document called RFC5545, and it has a Wikipedia page.

The recurrence rule expression for my interpretation of your problem is:

DTSTART:19000101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20241231T000000Z;BYDAY=TU;BYMONTH=8;BYMONTHDAY=15

Edouard Courty's article "The best way to programmatically handle recurrence" recommends more libraries for popular programming languages:

  • Python: dateutil, rrule
  • Java/Kotlin: ical4j, biweekly
  • JavaScript / TypeScript: rrule, rrule.js
  • PHP: RRule, php-rrule
  • C#: NCalendar, DDay.iCal
  • Ruby: Ice_cube, recurrent
  • Go: rrule-go, RRULE
  • Swift: DateTools, RRule

Some web applications let you check the answers from your own code because they answer the question for all dates.

Use the 10000 year calendar website to identify all years that match the specified day, day of the week, and month.

Screenshot of 1000 year calendar's answer

Use the rrule.js demo to tabulate answers, generate iCalendar recurrence rule syntax, and generate code for Jakub Roztocil's rrule JavaScript library.

Screenshot of rrule.js demo's answer

Use David Bal's RRULE Recurrence Calculator to tabulate recurrence rule expressions.

Screenshot of RRULE Recurrence Calculator's solution

I credit the 10000-year-calendar discovery to users こもりのり and V. Atkin. Their answers got deleted because they shared no code.

Upvotes: 0

Matthew Anderson
Matthew Anderson

Reputation: 26

Here is a way in Java 8 using the LocalDate parser to do the work for you. I used a modified version of this when I had a bunch of dates that had no year but were either the current year or the next one.

    String dateString = "Mon Dec 23";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM d yyyy");
    Integer startYear = 2000;
    Integer endYear = 3000;

    List<Integer> validYears = new ArrayList<>();
    for(int i = startYear; i <= endYear; i++){
      try{
        LocalDate localDate = LocalDate.parse(dateString + " " + i, formatter);
        validYears.add(localDate.getYear());
      } catch (Exception e){
        //Years that are not valid
      }
    }

    for(Integer year: validYears){
      System.out.println(year);
    }

Upvotes: 0

griFlo
griFlo

Reputation: 2164

Would this fully solve your problem?

The idea behind this:

  • Start from the specific date (August 15th) from year 0
  • if the date is on the specified weekday --> add it to your list
  • add 1 year
  • back to square 1

(till your specified MAX_DATE is reached)

List<Integer> validYears = new ArrayList<>();
    LocalDate date = LocalDate.of(0, Month.AUGUST, 15);
    while (date.getYear() < 2015) {
        if (date.getDayOfWeek() == DayOfWeek.TUESDAY) {
            validYears.add(date.getYear());
        }
        date = date.plusYears(1);
    }
    validYears.forEach(year -> System.out.println(year));

Edit: if your date is February 29th you should add following code (Because if you add 1 year to 0000-02-29 it will give you 0001-02-28, and from there on you will stay at the 28th)

if (date.isLeapYear()) { //And date was feb. 29
            date = date.withDayOfMonth(29);
        }

Heres a version with streams:

int day = 29;
Month month = Month.FEBRUARY;
List<LocalDate> collect = IntStream.range(0, 2016).filter(year -> day <= LocalDate.of(year, month, 1).lengthOfMonth())
            .mapToObj(year -> LocalDate.of(year, month, day)).filter(date -> date.getDayOfWeek() == DayOfWeek.TUESDAY)
            .collect(Collectors.toList());

collect.forEach((year -> System.out.println(year)));

Upvotes: 4

Arvind Sharma
Arvind Sharma

Reputation: 77

This question relates to the classical Dommsday algo.
A simple method to calculate is Kraitchik's method.

Kraitchik method -
w = d + m + c + y mod 7,
where w is the day of the week (counting upwards from 1 on Sunday instead of 0 in Gauss's version);
and d, m, c and y are numbers depending on the day, month, and year as in the following tables:

Month   1   2   3   4   5   6   7   8   9   10  11  12  
m       1   4   3   6   1   4   6   2   5   0   3   5   

For the Gregorian calendar, take the century of the year (ex, the year 1986 would be 1900, 2014 would be 2000).
[Century/100] mod 4: 0 1 2 3
c: 0 5 3 1

For the Julian calendar,
[Century/100] mod 7: 0 1 2 3 4 5 6
c: 5 4 3 2 1 0 6

Finally, the year number is obtained from this table (with 1 being subtracted from dates in January or February):
Last two digits of the year y
00 06 17 23 28 34 45 51 56 62 73 79 84 90 0 01 07 12 18 29 35 40 46 57 63 68 74 85 91 96 1 02 13 19 24 30 41 47 52 58 69 75 80 86 97 2 03 08 14 25 31 36 42 53 59 64 70 81 87 92 98 3 09 15 20 26 37 43 48 54 65 71 76 82 93 99 4 04 10 21 27 32 38 49 55 60 66 77 83 88 94 5 05 11 16 22 33 39 44 50 61 67 72 78 89 95 6

That's it. You may obtain more background detailed info from wikipedia

Upvotes: 1

Dejan Dular
Dejan Dular

Reputation: 373

Here is a simple formula you can do even in your head.You can find more info here: How to determine the day of the week, given the month, day and year

  1. Take the last two digits of the year.
  2. Divide by 4, discarding any fraction.
  3. Add the day of the month.
  4. Add the month's key value: JFM AMJ JAS OND 144 025 036 146
  5. Subtract 1 for January or February of a leap year.
  6. For a Gregorian date, add 0 for 1900's, 6 for 2000's, 4 for 1700's, 2 for 1800's; for other years, add or subtract multiples of 400.
  7. For a Julian date, add 1 for 1700's, and 1 for every additional century you go back.
  8. Add the last two digits of the year.
  9. Divide by 7 and take the remainder.

Upvotes: 1

Related Questions