nager / Nager.Date

Worldwide holidays (REST API), NuGet or docker container :earth_americas:
https://date.nager.at
MIT License
1.06k stars 171 forks source link

Turkey - Eid al-Fitr First wrong date #611

Open ramazancivak opened 3 months ago

ramazancivak commented 3 months ago

Affected country

Türkiye

Affected public holiday

Eid al-Fitr First

Source of the information

https://tr.wikipedia.org/wiki/Ramazan_Bayram%C4%B1

More information

https://date.nager.at/Api/v2/PublicHolidays/2024/TR

2024.03.30 instead of 2024.04.10

tinohager commented 3 months ago

please take note of the information WebApi V1 and V2 deprecated

LayerCakeMakes commented 3 months ago

This is wrong in WebApi V3 too for all three days. These are correct: https://www.timeanddate.de/feiertage/tuerkei/ These are not (returned by V3):

  {
    "date": "2024-03-30",
    "localName": "Ramazan Bayramı 1. Gün",
    "name": "Eid al-Fitr First Day",
    "countryCode": "TR",
    "fixed": false,
    "global": true,
    "counties": null,
    "launchYear": null,
    "types": [
      "Public"
    ]
  },
  {
    "date": "2024-03-31",
    "localName": "Ramazan Bayramı 2. Gün",
    "name": "Eid al-Fitr Second Day",
    "countryCode": "TR",
    "fixed": false,
    "global": true,
    "counties": null,
    "launchYear": null,
    "types": [
      "Public"
    ]
  },
  {
    "date": "2024-04-01",
    "localName": "Ramazan Bayramı 3. Gün",
    "name": "Eid al-Fitr Third Day",
    "countryCode": "TR",
    "fixed": false,
    "global": true,
    "counties": null,
    "launchYear": null,
    "types": [
      "Public"
    ]
  },
tinohager commented 3 months ago

Where can I find out the exact date for Ramadan? It is set every year by a church leader. Is there a government source here?

LayerCakeMakes commented 3 months ago

As far as I understand it, it is roughly based on a lunar calendar but it depends on when the cresent moon is actually visible/seen so it's a tentative date until just a few days before the actual date when it will be announced to the public, but who announces it varies from country to country and not all countries might have it start on the exact same date.

tinohager commented 3 months ago

I am aware of that. But where do I get the real date? And does the date only apply to Turkey or also to other countries?

LayerCakeMakes commented 3 months ago

It's different for each country. For turkey it seems to be Directorate of Religious Affairs also known as Diyanet. From the articels I've found it seems they announced it on Instagram??? Unfortunately I don't speak the language so I couldn't verify that.

LayerCakeMakes commented 3 months ago

Maybe for V4 these kinds of holidays need a marker for tentative dates that might still change.

I just noticed that your calculation for the tentative date you supply seems to be off by 1 year. The tentative date for 2024 is 2024-04-01 but the api returned 2024-03-30 which matches the tentative date for 2025 which is 2025-03-30. This seem to continue on to other future years.

LayerCakeMakes commented 3 months ago

There is another issue that might not exactly belong in this report but it is related. Some holidays in Turkey start half a day early at 13:00 like Eid al-Fitr First, so this might be a marker in V4 too or half day support.

tinohager commented 3 months ago

I have temporarily deactivated the faulty holidays. Until there is a sensible solution for the future.

LayerCakeMakes commented 3 months ago

Thank you for the quick update.

I think tentative dates would be fine if marked as such in the name for example but there is a general issue with the calculation.

I've checked the code and found that the calculation of the hijri year in the TurkeyHolidayProvider is the source of half the problem.

The problem is calculating the hijri year requires the full gregorian date as the years have different lengths and are not aligned so in 2024 there is 1445 during roughly the first half of 2024 and 1446 for the second half.

So the holidays that depend on it are currently returned wrongly for next year this affects not just Ramadan but also the days of Eid al-Adha (Feast Of Sacrifice) Holidays.

hasankaplan-github commented 2 months ago

According to Wikipedia one single Gregorian year can match with three Hijri years. So my solution is returning multiple Hijri years instead of one Hijri year (calculated Hijri year, minus 1, and plus 1.)

private int[] GetPossibleHijriYears(int year)
{
    var diff = year - 621;
    var hijriYear = Convert.ToInt32(Math.Round(diff + decimal.Divide(diff, 33)));
    return [hijriYear - 1, hijriYear, hijriYear + 1];
}

Then calculate multiple holidays according to multiple Hijri years and just select the dates which has correct year.

public IEnumerable<SpecialDay> RamadanFirstDay(int year)
{
    var hijriCalendar = new UmAlQuraCalendar();
    var possibleHijriYears = GetPossibleHijriYears(year);

    foreach (var hijriYear in possibleHijriYears)
    {
        var dateTime = hijriCalendar.ToDateTime(hijriYear, 10, 1, 0, 0, 0, 0);
        if (dateTime.Year != year)
        {
            continue;
        }

        var date = DateOnly.FromDateTime(dateTime);

        yield return new SpecialDay(
            id: Guid.Empty,
            isHoliday: true,
            date: date,
            name: "Ramazan Bayramı 1. Gün",
            isFixed: false);
    }
}

Also in one Gregorian year, 2000 for example, multiple Ramadan holidays can exist. This solution also covers this case.

hasankaplan-github commented 2 months ago

I want to update my previous GetPossibleHijriYears method. I think GetHijriYears method below is more accurate then the previous GetPossibleHijriYears method:

private IEnumerable<int> GetHijriYears(int year)
{
    var hijriCalendar = new UmAlQuraCalendar();

    var minHijriYear = hijriCalendar.GetYear(new DateTime(year, 1, 1));
    var maxHijriYear = hijriCalendar.GetYear(new DateTime(year, 12, 31));

    return Enumerable.Range(minHijriYear, maxHijriYear - minHijriYear + 1);
}

EDIT :

One Gregorian year may match with three Hijri years, but third year maps just the beginning of the Hijri year which contains no Ramadan Holiday. So only taking only two Hijri years is enough. Below code may be more performant than calculating min-max Hijri year.

private IEnumerable<int> GetHijriYears(int year)
{
    var diff = year - 621;
    var hijriYear = Convert.ToInt32(Math.Round(diff + decimal.Divide(diff, 33), MidpointRounding.ToZero));
    return [hijriYear, hijriYear + 1];
}