ahmedoid / hijri_date

Convert between Hijri Calendar and Gregorian Calendar date.
Other
18 stars 29 forks source link

There should be offset option #6

Open shakirkasmani opened 3 years ago

shakirkasmani commented 3 years ago

There should be offset option as hijri dates depends on visibility of moon. There should be offset of -2 to 2.

aarif commented 3 years ago

Offset is not sufficient as it doesn't affect the number of days in a given month as defined in the table. Applying an offset close to the beginning or the end of the month could possibly derive the incorrect month.

eg. If today is 30th of Jumada II but table has defined Jumada II as having only 29 days, applying offset of +1, will incorrectly derive date to 1st of Rajab.

Am I understanding your issue correctly?

moosalim commented 3 years ago

Thanks for this plugin, Jazak Allah Khair

I fully agree with Shakir, there should be a way to correct the date if there is a need to so so , based on moon sighting. please do something.

based on your example, if today is 30th of Jumada|| , but the table defined Jumada || as only 29 days, then we should apply -1 which will correct it.

I suggest adding method similar to the flutter standard one : DateTime.now().add(Duration(days: -1))

for for hijri it maybe like this HijriCalendar.now().add(Duration(days: -1))

May Allah reward your work.

aarif commented 3 years ago

You can do something like this already...

HijriCalendar.fromDate(DateTime.now().add(Duration(days: -1)));

moosalim commented 3 years ago

Thanks @aarif , it works,

I didn't know that it works like this.

but I question here, why offset is needed then ?

aarif commented 3 years ago

@moosalim as stated earlier, simply using this offset is not sufficient.

This is my understanding.

See the array ummAlquraDateArray defined in lib/hijri_array.dart. This array essentially defines the Julian Day (minus 2400000) for every month starting with Muharram 1355. The first 2 values are 28607 and 28636. Subtracting the 1st from the second yields 29. This means that Muharram 1355 had 29 days and started on Sunday March 14th, 1937.

On Monday April 12th, 1937 when this algorithm would have showed the 1st of Safar 1355 it was determined that Muharram that year had 30 days instead of 29, simply applying the offset above (days: +1 or days: -1) and without modifying the underlying table there would be no way for the algorithm to show the 30th of Muharram 1355.

See PR #4 which introduced a mechanism to modify the underlying table to make the algorithm correctly produce 30th Muharram 1355 by doing the following:

HijriCalendar cal = new HijriCalendar();
cal.setAdjustments({ 16261: 28637 });  // To set Safar 1355 to start 1 day later: 16261 = (1355 * 12) + 1 (year * 12 + (month - 1)
alasami commented 3 years ago

@moosalim as stated earlier, simply using this offset is not sufficient.

This is my understanding.

See the array ummAlquraDateArray defined in lib/hijri_array.dart. This array essentially defines the Julian Day (minus 2400000) for every month starting with Muharram 1355. The first 2 values are 28607 and 28636. Subtracting the 1st from the second yields 29. This means that Muharram 1355 had 29 days and started on Sunday March 14th, 1937.

On Monday April 12th, 1937 when this algorithm would have showed the 1st of Safar 1355 it was determined that Muharram that year had 30 days instead of 29, simply applying the offset above (days: +1 or days: -1) and without modifying the underlying table there would be no way for the algorithm to show the 30th of Muharram 1355.

See PR #4 which introduced a mechanism to modify the underlying table to make the algorithm correctly produce 30th Muharram 1355 by doing the following:

HijriCalendar cal = new HijriCalendar();
cal.setAdjustments({ 16261: 28637 });  // To set Safar 1355 to start 1 day later: 16261 = (1355 * 12) + 1 (year * 12 + (month - 1)

Could you please explain the numbers I couldn’t figure out any of them

Best regards

aarif commented 3 years ago

@alasami I apologize for the confusing example. I will try my best to explain.

This mechanism allows for correcting the day that a Hijri month starts on. The Key of the map parameter being passed into the setAdjustments method represents the Hijri Month for which you would like to correct and the Value represents the correct Julian Day that the month should start on.

The first element in the ummAlquraDateArray array is 28607. When we add 2400000 we get 2428607, which is the Julian day for Sunday March 14th, 1937 (https://core2.gsfc.nasa.gov/time/julian.html). Since the array starts from Muharram 1355, this means that the 1st of Muharram 1355 was on Julian Day 28607. The second element in the array defines the Julian Date for the 1st of Safar 1355, which is 28636, and so on.

In the following example, I will show how to change the date that Muharram 1355 started on from March 14th, 1937 to March 13th, 1937.

To calculate the Key, use the following algorithm:

key = (hijri_year * 12) + zero_based_month_index
key = (1355 * 12) + 0
key = 16260

The Value of the Map should be the Julian Day of March 13th, 1937. Using the link above we can see that this yields 2428606. Subtracting 2400000 gives 28606. If we pass the following map to the setAdjustments method, it effectively changes the start date of Muharram 1355 to March 13th, 1937.

HijriCalendar cal = new HijriCalendar();
cal.setAdjustments({ 16260: 28606 });

Here is an algorithm someone posted on Stack Overflow (https://stackoverflow.com/questions/52382097/how-to-convert-date-to-julian-date-in-dart-or-flutter).

I hope this explanation is a little more clear. Please let me know if you need any clarification.

alasami commented 3 years ago

@alasami I apologize for the confusing example. I will try my best to explain.

This mechanism allows for correcting the day that a Hijri month starts on. The Key of the map parameter being passed into the setAdjustments method represents the Hijri Month for which you would like to correct and the Value represents the correct Julian Day that the month should start on.

The first element in the ummAlquraDateArray array is 28607. When we add 2400000 we get 2428607, which is the Julian day for Sunday March 14th, 1937 (https://core2.gsfc.nasa.gov/time/julian.html). Since the array starts from Muharram 1355, this means that the 1st of Muharram 1355 was on Julian Day 28607. The second element in the array defines the Julian Date for the 1st of Safar 1355, which is 28636, and so on.

In the following example, I will show how to change the date that Muharram 1355 started on from March 14th, 1937 to March 13th, 1937.

To calculate the Key, use the following algorithm:

key = (hijri_year * 12) + zero_based_month_index
key = (1355 * 12) + 0
key = 16260

The Value of the Map should be the Julian Day of March 13th, 1937. Using the link above we can see that this yields 2428606. Subtracting 2400000 gives 28606. If we pass the following map to the setAdjustments method, it effectively changes the start date of Muharram 1355 to March 13th, 1937.

HijriCalendar cal = new HijriCalendar();
cal.setAdjustments({ 16260: 28606 });

Here is an algorithm someone posted on Stack Overflow (https://stackoverflow.com/questions/52382097/how-to-convert-date-to-julian-date-in-dart-or-flutter).

I hope this explanation is a little more clear. Please let me know if you need any clarification.

Thanks you did a great job explain everything

May Allah bless you

Update

I did try your way but I get wrong number also I tried this format and it is working for the key ((year in Hijri - 1) * 12 + month in Hijri) =

Thanks

jainharsh21 commented 2 years ago

Hello @aarif , current islamic year is 1444, and Mahurram should be for 30 days instead of 29, I have spent the last 2-3 hours trying to make an adjustment, used all the formulas in the thread, could you please assist me with this, thank you

aarif commented 2 years ago

Hello @jainharsh21,

There are two ways to do this. You can either start Muharram 1444 one day earlier, on July 29th instead of July 30th, or you can start Safar 1444 one day later August 29th instead of August 28th.

Here are the calculations for both:

The julian day for July 29th is 2459790. Subtracting 2400000 gives us 59790. This should be the new value for the start of Muharram 1444.

To calculate the adjustment key for the Muharram 1444, we use the algorithm corrected by @alasami above:

((1444 - 1) * 12) + 0 = 17316

So the adjustment to change Muharram 1444 to start on July 29th, 2022 would be:

HijriCalendar cal = new HijriCalendar();
cal.setAdjustments({ 17316: 59790 });

The julian day for August 29th is 2459821. Subtracting 2400000 gives us 59821. This should be the new value for the start of Safar 1444.

To calculate the adjustment key for the Safar 1444, we use the same algorithm:

((1444 - 1) * 12) + 1 = 17317

So the adjustment to change Safar 1444 to start on August 29th, 2022 would be:

HijriCalendar cal = new HijriCalendar();
cal.setAdjustments({ 17317: 59821 });
jainharsh21 commented 2 years ago

Thank you for replying, I was using the same formula, apparently we have to make a gregorianToHijri() call after setting adjustments for it to work, just presetting the calendar from date doesn't seem to work, also in the thread above the formula mentioned for calculating key is incorrect, a '-1' was missing from year, thanks for the assistance!

Ruaa-Ali commented 2 years ago

Hello @aarif

I tried to apply the offset as described here and it worked but there is a problem. When I want to reduce the month by a day, I start the next month one day earlier than its actual start. Like if Ramadan in the calendar has 30 days but it appears that it is only for 29, I set Shawwal to start a day earlier. But that way if Shawwal has 29 days, it'll become 30 days. Or if it is 30 days, it'll become 31 days.

How can I fix this, in other words, how can I shift the whole calendar so that the months still have their original number of days but start/finish in different dates according to that offset?

aarif commented 2 years ago

@Ruaa-Ali,

This is a limitation with the current implementation, although I would argue that the need to do this should be quite low. In my experience, the array which represents the starting day of each month is quite accurate and aligns in most cases.

I will describe how we use this library and perhaps it will allay your concerns.

We have a community site built with WordPress and use a Plugin called WP-Hijri. It can be configured to use the Umm al-Qura algorithm, as this library does, and has an almost identical pre-configured array. When date adjustments need to be made, they are made on the site using the WP-Hijri configuration page and are persisted within WordPress. WP-Hijri already handles your particular issue by limiting the adjustments in order to maintain either 29 or 30 days months.

We also have a mobile application using the hijri_date library. When this application launches, it makes a request to the main site. A purpose built service endpoint gathers all the relevant adjustments and returns them to the mobile app. This is essentially and array of adjustments which looks like this:

"hijriAdjustments": { "17312": 59673, "17314": 59732, "17315": 59762, "17317": 59821, "17345": 60647, "17817": 74585 },

The mobile app turns this into an array and feeds it to the setAdjustments method so that a Hijri date matching the community site can be rendered.

I realize that your use case may not be anything close to this and I apologize if this wasn't helpful.

I invite you to submit changes to this repository to support your own use case. Who knows, it may help others.

Ruaa-Ali commented 2 years ago

Thank you for replying. If I understood you correctly, I should apply the offset to the target month and all the months after to maintain their number of days? If this is not what you mean would you briefly explain to me how maintaining days to 29 or 30 works, or maybe refer me to a source that does so.

One thing to mention here is that the offset is only valid for one year. It is only for Ramadan because it is strictly according to the moon. Other than that I don't imagine adjustments are necessary in my case.

aarif commented 2 years ago

Yes, that is correct. If you need to maintain the number of days for all months following a certain month, you would need to apply an adjustment to all months individually.

If you examine the ummAlquraDateArray array, you will note that each element corresponds to a particular month in a particular year. Any adjustment you make for a particular element will not affect the following year.

Ruaa-Ali commented 2 years ago

Okey I tried to adjust the calendar from Sha'aban to Dhu Al-Hijjah but this way I'll have to keep adjusting until the end of the array!

I made Ramadan to start a day earlier to make Sha'aban 29 days, but there will always be one more day that'll be added to the next month. No matter how long you go with the adjustments there will always be one more day.

Is there anything to do with this? or am I getting something wrong about the adjustments?

aarif commented 2 years ago

@Ruaa-Ali,

I fail to understand why you would need to maintain the number of days in subsequent months. I believe your understanding of the Hijri calendar is incorrect.

The Hijri calendar is not like the Gregorian calendar, where each month must have a fixed number of days. It is based on the lunar cycle which is 29.5 days long.

If you adjust the start date of Ramadhan to make Sha'aban 29 days, this effectively makes Ramadhan 30 days long. The "one more day" will now be part of Ramadhan, which I am suggesting to you is likely correct.

If you can elaborate on your use-case, perhaps I can assist you further, but I believe you are trying to use the library incorrectly.

Ruaa-Ali commented 2 years ago

Actually yes, the whole idea of maintaining the number of days in each months was incorrect. Thank you for pointing that.

In my use case the only change I want to apply is regarding the start and end of Ramadan. How to deal with the calendar if Ramadan is one more or one less day than what it is in the calendar?

The one more day will add up to the next month right? But what if the next month is already 30 days, should I move the one more day to the nearest month that has only 29 days? Same for one less day, should it be taken from the nearest 30 days month?

Please help with that and correct me if I am understanding it the wrong way.

aarif commented 2 years ago

I'm not sure I can advise you on this as I have never encountered it. I can however share some characteristics I have gleaned about Hijri months that may help you determine the correct course of action.

I hope that helps. I pray you find a solution.