Reputation: 39
I'm trying to implement a non-gregorian calendar which has 13 months. And there were two functions I was using to do that. One of them
generateCalendar is used to create a matrix of calendar for each month indexed with the week days
function generateCalendar(type = 'en') {
var [etYear, etMonth, etDate] = Ethiopic.toEthiopian(
state.activeDate.getFullYear(),
state.activeDate.getMonth() + 1,
state.activeDate.getDate(),
);
console.log('Month: ', etMonth);
var enYear = state.activeDate.getFullYear();
var enMonth = state.activeDate.getMonth();
var matrix = [];
if (type == 'en') {
matrix = [];
var firstDay = new Date(enYear, enMonth, 1).getDay();
var maxDays = enCalendar.en_days[enMonth];
if (enMonth == 1) {
if ((enYear % 4 == 0 && enYear % 100 != 0) || enYear % 400 == 0) {
maxDays += 1;
}
}
matrix[0] = enCalendar.en_weekDays;
var counter = 1;
for (var row = 1; row < 7; row++) {
matrix[row] = [];
for (var col = 0; col < 7; col++) {
matrix[row][col] = -1;
if (row == 1 && col >= firstDay) {
matrix[row][col] = counter++;
} else if (row > 1 && counter <= maxDays) {
matrix[row][col] = counter++;
}
}
}
} else if (type == 'et') {
matrix = [];
var startDayOfYear = Ethiopic.startDayOfEthiopian(etYear);
// var firstDay = startOfYear + (30 % startDayOfYear);
var firstDayOfYear = new Date(enYear, 8, startDayOfYear).getDay();
var firstDay =
(etMonth - 1) * 2 + firstDayOfYear > 7
? ((etMonth - 1) * 2 + firstDayOfYear) % 7
: (etMonth - 1) * 2 + firstDayOfYear;
var maxDays = etCalendar.et_days[etMonth - 1];
console.log(maxDays);
if (etMonth == 13) {
if (etYear % 4 == 3) {
maxDays += 1;
}
}
matrix[0] = etCalendar.et_weekDays;
var counter = 1;
for (var row = 1; row < 7; row++) {
matrix[row] = [];
for (var col = 0; col < 7; col++) {
matrix[row][col] = -1;
if (row == 1 && col >= firstDay) {
matrix[row][col] = counter++;
} else if (row > 1 && counter <= maxDays) {
matrix[row][col] = counter++;
}
}
}
}
return matrix;
}
And the other is changeMonth which adds a month and changes the state thus creating a new matrix.
function changeMonth(n) {
setState({
activeDate: new Date(state.activeDate.setMonth(state.activeDate.getMonth() + n))
});
}
The problem here is the ethiopian calendar has 13 months so I can't use setmonth and increment or decrement that and convert back to ethiopian because the activeDate determines the number of month which is 12 for gregorian. So setmonth resets after 11 and only works for 12 months
But since every month in ethiopian calendar is 30 days except the last month which has 5 or 6 days, I wanted to make it so that it cycles through the months when incrementing or decrementing months and maybe then I can convert back to gregorian and set the activeDate.
Upvotes: 1
Views: 279
Reputation: 147483
You need to explain what the rules are for adding a Coptic month.
For example, adding a month to dates in months 1 to 11 can be just adding 30 days. For dates in month 12, adding 30 days may push the date beyond the 13th month so into the first month of the following year. In this case, should the date be limited to the last day of month 13? So that adding 1 month to 30/12/1737 (5 Sep 2021) goes to 5/13/1737 rather than 25/01/1738 (30 days later).
Similarly, should adding one month to 1/13/1737 be 1/01/1738 (i.e. same day number in the following month) or 26/01/1738 (30 days later)?
Given a native ECMAScript Date, you can add a Coptic month by:
E.g.
// Given a date, return an object with era, year, month
// and day properties and values for the equivalent
// Coptic date
function getCopticDate(date = new Date()) {
return new Intl.DateTimeFormat(
'en-u-ca-coptic', {
year : 'numeric',
month: '2-digit',
day : '2-digit'
}).formatToParts(date).reduce((acc, part) => {
if (part.type != 'literal') {
acc[part.type] = part.value;
}
return acc;
}, Object.create(null));
}
// Given a Date, return a string for the equivalent
// Coptic date
function printCopticDate(date = new Date()) {
let {era, year, month, day} = getCopticDate(date);
return `${day}/${month}/${year} ${era}`;
}
// Given a Date, return a Date with one Coptic month
// added to to the equivalent Coptic date
function addCopticMonth(date) {
let oDate = new Date(date);
let d = getCopticDate(oDate);
// Add 30 days, then deal with months 12 and 13
oDate.setDate(oDate.getDate() + 30)
// If month was 12 and is now not 13, set to
// last day of 13
let e = getCopticDate(oDate);
if (d.month == '12' && e.month != '13') {
oDate.setDate(oDate.getDate() - e.day);
}
// If month was 13, set to same day in next month
if (d.month == '13') {
oDate.setDate(oDate.getDate() - e.day + +d.day)
}
return oDate;
}
// Some tests
// Add 1 month to today
let d = new Date();
console.log('Today is: ' + printCopticDate(d) +
' (' + d.toDateString() + ')');
let e = addCopticMonth(d);
console.log('+1 month: ' + printCopticDate(e) +
' (' + e.toDateString() + ')');
// Add 1 month to 20/12/1737 -> last day of 13th month
let f = new Date(2021, 7, 26);
let g = addCopticMonth(f);
console.log(printCopticDate(f) + ' (' +
f.toDateString() + ') +1 month is\n' +
printCopticDate(g) + ' (' + g.toDateString() + ')');
// Add 1 month to 05/13/1737 -> same day in first month of next year
let h = addCopticMonth(g);
console.log(printCopticDate(g) + ' (' +
g.toDateString() + ') +1 month is\n' +
printCopticDate(h) + ' (' + h.toDateString() + ')');
The above leverages Intl.DateTimeFormat and covers adding a single month, if you want to add more than one or add years, there's more work to do… It's really just to demonstrate an algorithm rather than provide a robust function (e.g. getCopticDate should probably be called getCopticDateParts and the values should be numbers not strings, etc.).
Upvotes: 1