Calendar với Reactjs Typescript sử dụng Moment
Hướng dẫn viết logic: trả ra kết quả như hình:
- Danh sách ngày tháng trước , trong tháng và tháng tới : item trả ra kèm theo loại ngày, ngày hiện tại
- Chi tiết ngày hiện tại
- Tạo Context API - để chia sẽ dữ liệu
'use client';
import { PropsWithChildren, createContext, useContext } from 'react';
import useLogic from './hook';
type Extra = {};
type ValueCtx = ReturnType<typeof useLogic> & Extra;
export const CalendarCtx = createContext({} as ValueCtx);
export const CalendarProvider = ({ ...props }: PropsWithChildren<Extra>) => {
const valueCtx = useLogic();
return (
<CalendarCtx.Provider value={{ ...valueCtx, ...props }}>
<>{props.children}</>
</CalendarCtx.Provider>
);
};
export const useCalendarCtx = () => useContext(CalendarCtx);
- Tạo Hook - để xử lý logic
'use client';
import moment from 'moment';
import { useState } from 'react';
import { calcDays, dataDate } from './data';
import { onSolar2Lunar } from './lunar';
const useLogic = () => {
const [date, setDate] = useState(moment());
const handleReload = () => {
setDate(moment());
};
const handlePrev = () => {
setDate((element: any) => moment(element).subtract(1, 'month'));
};
const handleNext = () => {
setDate((element: any) => moment(element).add(1, 'month'));
};
const currentLunar = () => {
const curdate = dataDate({ date: moment() });
const solar = onSolar2Lunar(
curdate.day,
curdate.curMonth,
curdate.year
);
return solar;
};
return {
date,
setDate,
listDay: calcDays({ ...dataDate({ date }) }),
current: dataDate({ date }),
handlePrev,
handleNext,
handleReload,
currentLunar,
};
};
export default useLogic;
- Method: Xử lý ngày tháng
import { range } from 'lodash';
import moment, { Moment } from 'moment';
import 'moment/locale/vi';
import { onSolar2Lunar } from './lunar';
import { CalcDays, ItemDay } from './types';
enum DefineDays {
DaysOut = 'DaysOut',
DaysIn = 'DaysIn',
}
export type DataDate = { date: Moment };
export const dataDate = ({ date }: DataDate) => {
const day = date.date();
const year = date.year(); // lấy năm hiện tại
const month = date.month(); // lấy tháng hiện tại
const daysInMonth = date.daysInMonth(); // lấy số ngày trong tháng , ví dụ như 30 ngày
const dayOfMonth = moment(date).subtract(1, 'months'); // lấy tháng vừa rồi
const dayOf = moment(`${year}-${month + 1}-1`); // tuần đầu tiền của tháng
const weekDayOf = dayOf.day(); // số ngày củ cua tháng rồi
const dayNew = moment(`${year}-${month + 1}-${daysInMonth}`); // tuần cuối cùng của tháng
const weekDayNew = dayNew.day(); // sô ngày mới của tháng tới
return {
day,
year,
month,
curMonth: month + 1,
daysInMonth,
dayOfMonth,
weekDayOf,
weekDayNew,
dayNew,
};
};
export const calcDays = ({
weekDayOf,
dayOfMonth,
weekDayNew,
daysInMonth,
dayNew,
year,
month,
}: CalcDays) => {
// lịch việt thì weekDayOf - 1 , weekDayOf + 1 ,
// lịch Quốc tế thì weekDayOf , weekDayOf + 1
const daysOld: ItemDay[] = range(weekDayOf - 1).map((item) => {
const iday = dayOfMonth.daysInMonth() - weekDayOf + 1 + item + 1;
return {
daysLunar: onSolar2Lunar(iday, month, year),
days: iday,
type: DefineDays.DaysOut,
daysSolar: {
day: iday,
month,
year,
ddmm: `${iday}/${month}`,
ddmmyyyy: `${iday}/${month}/${year}`,
},
};
});
const days: ItemDay[] = range(daysInMonth).map((item) => {
const isToday = moment(`${item + 1}/${month + 1}/${year}`).isSame(
moment(),
'day'
);
return {
daysLunar: onSolar2Lunar(item + 1, month + 1, year),
days: item + 1,
type: DefineDays.DaysIn,
isToday,
daysSolar: {
day: item + 1,
month: month + 1,
year,
ddmm: `${item + 1}/${month + 1}`,
ddmmyyyy: `${item + 1}/${month + 1}/${year}`,
} as const,
};
});
const daysNew: ItemDay[] = range(6 + 1 - weekDayNew).map(() => {
const iday = dayNew.add(1, 'day').date();
return {
daysLunar: onSolar2Lunar(iday, month + 2, year),
days: iday,
type: DefineDays.DaysOut,
daysSolar: {
day: iday,
month: month + 2,
year,
ddmm: `${iday}/${month + 2}`,
ddmmyyyy: `${iday}/${month + 2}/${year}`,
},
};
});
return daysOld.concat(days, daysNew);
};
- Định nghĩa Dữ liệu
import { Moment } from 'moment';
export type CalcDays = {
year: number;
month: number;
daysInMonth: any;
dayOfMonth: Moment;
weekDayOf: number;
weekDayNew: number;
dayNew: Moment;
};
export type ItemDay = {
daysLunar: LunarType;
days: number;
type: string;
isToday?: boolean;
daysSolar?: DaySolarType;
};
export interface LunarType {
dd: number;
mm: any;
yy: number;
ix: any;
LLLL: string;
DM: string;
}
export type DaySolarType = {
day: number;
month: number;
year: number;
ddmm?: string;
ddmmyyyy?: string;
};
- Tính lịch âm : Lunar
/* eslint-disable id-length */
/* eslint-disable prefer-const */
import { chunk, range } from 'lodash';
export const LOCAL_TIMEZONE = 7.0;
export const INT = (d = 0) => {
return Math.floor(d);
};
export const MOD = (x = 0, y = 0) => {
let z = x - y * Math.floor(x / y);
if (z === 0) {
z = y;
}
return z;
};
export const UniversalFromJD = (JD = 0) => {
let Z;
let A;
let B;
let C;
let D;
let E;
let F;
let alpha;
let dd;
let mm;
let yyyy;
Z = INT(JD + 0.5);
F = JD + 0.5 - Z;
if (Z < 2299161) {
A = Z;
} else {
alpha = INT((Z - 1867216.25) / 36524.25);
A = Z + 1 + alpha - INT(alpha / 4);
}
B = A + 1524;
C = INT((B - 122.1) / 365.25);
D = INT(365.25 * C);
E = INT((B - D) / 30.6001);
dd = INT(B - D - INT(30.6001 * E) + F);
if (E < 14) {
mm = E - 1;
} else {
mm = E - 13;
}
if (mm < 3) {
yyyy = C - 4715;
} else {
yyyy = C - 4716;
}
return { dd, mm, yyyy };
};
export const UniversalToJD = (D = 0, M = 0, Y = 0) => {
let JD;
if (
Y > 1582 ||
(Y === 1582 && M > 10) ||
(Y === 1582 && M === 10 && D > 14)
) {
JD =
367 * Y -
INT((7 * (Y + INT((M + 9) / 12))) / 4) -
INT((3 * (INT((Y + (M - 9) / 7) / 100) + 1)) / 4) +
INT((275 * M) / 9) +
D +
1721028.5;
} else {
JD =
367 * Y -
INT((7 * (Y + 5001 + INT((M - 9) / 7))) / 4) +
INT((275 * M) / 9) +
D +
1729776.5;
}
return JD;
};
export const LocalFromJD = (JD = 0) => {
return UniversalFromJD(JD + LOCAL_TIMEZONE / 24.0);
};
export const LocalToJD = (D = 0, M = 0, Y = 0) => {
return UniversalToJD(D, M, Y) - LOCAL_TIMEZONE / 24.0;
};
export const { PI } = Math;
export const NewMoon = (k = 0) => {
const T = k / 1236.85;
const T2 = T * T;
const T3 = T2 * T;
const dr = PI / 180;
let Jd1 =
2415020.75933 + 29.53058868 * k + 0.0001178 * T2 - 0.000000155 * T3;
Jd1 = Jd1 + 0.00033 * Math.sin((166.56 + 132.87 * T - 0.009173 * T2) * dr); // Mean new moon
const M = 359.2242 + 29.10535608 * k - 0.0000333 * T2 - 0.00000347 * T3; // Sun's mean anomaly
const Mpr = 306.0253 + 385.81691806 * k + 0.0107306 * T2 + 0.00001236 * T3; // Moon's mean anomaly
const F = 21.2964 + 390.67050646 * k - 0.0016528 * T2 - 0.00000239 * T3; // Moon's argument of latitude
let C1 =
(0.1734 - 0.000393 * T) * Math.sin(M * dr) +
0.0021 * Math.sin(2 * dr * M);
C1 = C1 - 0.4068 * Math.sin(Mpr * dr) + 0.0161 * Math.sin(dr * 2 * Mpr);
C1 = C1 - 0.0004 * Math.sin(dr * 3 * Mpr);
C1 = C1 + 0.0104 * Math.sin(dr * 2 * F) - 0.0051 * Math.sin(dr * (M + Mpr));
C1 =
C1 -
0.0074 * Math.sin(dr * (M - Mpr)) +
0.0004 * Math.sin(dr * (2 * F + M));
C1 =
C1 -
0.0004 * Math.sin(dr * (2 * F - M)) -
0.0006 * Math.sin(dr * (2 * F + Mpr));
C1 =
C1 +
0.001 * Math.sin(dr * (2 * F - Mpr)) +
0.0005 * Math.sin(dr * (2 * Mpr + M));
let deltat;
if (T < -11) {
deltat =
0.001 +
0.000839 * T +
0.0002261 * T2 -
0.00000845 * T3 -
0.000000081 * T * T3;
} else {
deltat = -0.000278 + 0.000265 * T + 0.000262 * T2;
}
const JdNew = Jd1 + C1 - deltat;
return JdNew;
};
export const SunLongitude = (jdn = 0) => {
const T = (jdn - 2451545.0) / 36525; // Time in Julian centuries from 2000-01-01 12:00:00 GMT
const T2 = T * T;
const dr = PI / 180; // degree to radian
const M = 357.5291 + 35999.0503 * T - 0.0001559 * T2 - 0.00000048 * T * T2; // mean anomaly, degree
const L0 = 280.46645 + 36000.76983 * T + 0.0003032 * T2; // mean longitude, degree
let DL = (1.9146 - 0.004817 * T - 0.000014 * T2) * Math.sin(dr * M);
DL =
DL +
(0.019993 - 0.000101 * T) * Math.sin(dr * 2 * M) +
0.00029 * Math.sin(dr * 3 * M);
let L = L0 + DL; // true longitude, degree
L = L * dr;
L = L - PI * 2 * INT(L / (PI * 2)); // Normalize to (0, 2*PI)
return L;
};
export const LunarMonth11 = (Y = 0) => {
const off = LocalToJD(31, 12, Y) - 2415021.076998695;
const k = INT(off / 29.530588853);
let jd = NewMoon(k);
const ret = LocalFromJD(jd);
const sunLong = SunLongitude(LocalToJD(ret.dd, ret.mm, ret.yyyy)); // sun longitude at local midnight
if (sunLong > (3 * PI) / 2) {
jd = NewMoon(k - 1);
}
return LocalFromJD(jd);
};
export const initLeapYear = (ret: any) => {
const sunLongitudes = [ret.length];
for (let index = 0; index < ret.length; index++) {
const a = ret[index];
const jdAtMonthBegin = LocalToJD(a[0], a[1], a[2]);
sunLongitudes[index] = SunLongitude(jdAtMonthBegin);
}
let found = false;
for (let index = 0; index < ret.length; index++) {
if (found) {
ret[index][3] = MOD(index + 10, 12);
continue;
}
const sl1 = sunLongitudes[index];
const sl2 = sunLongitudes[index + 1];
const hasMajorTerm =
Math.floor((sl1 / PI) * 6) !== Math.floor((sl2 / PI) * 6);
if (!hasMajorTerm) {
found = true;
ret[index][4] = 1;
ret[index][3] = MOD(index + 10, 12);
}
}
};
export const LunarYear = (Y = 0) => {
let ret: any = chunk(range(13 * 5), 5);
const month11A = LunarMonth11(Y - 1);
const jdMonth11A = LocalToJD(month11A.dd, month11A.mm, month11A.yyyy);
const k = Math.floor(0.5 + (jdMonth11A - 2415021.076998695) / 29.530588853);
const month11B = LunarMonth11(Y);
const off = LocalToJD(month11B.dd, month11B.mm, month11B.yyyy) - jdMonth11A;
const leap = off > 365.0;
if (!leap) {
ret = chunk(range(13 * 5), 5);
}
ret[0] = [month11A.dd, month11A.mm, month11A.yyyy, 0, 0];
ret[ret.length - 1] = [month11B.dd, month11B.mm, month11B.yyyy, 0, 0];
for (let index = 1; index < ret.length - 1; index++) {
const nm = NewMoon(k + index);
const a = LocalFromJD(nm);
ret[index] = [a.dd, a.mm, a.yyyy, 0, 0];
}
for (let index = 0; index < ret.length; index++) {
ret[index][3] = MOD(index + 11, 12);
}
if (leap) {
initLeapYear(ret);
}
return ret;
};
// dương sang âm
export const onSolar2Lunar = (D = 0, M = 0, Y = 0) => {
let yy = Y;
let ly = LunarYear(Y);
const month11 = ly[ly.length - 1];
const jdToday = LocalToJD(D, M, Y);
const jdMonth11 = LocalToJD(month11[0], month11[1], month11[2]);
if (jdToday >= jdMonth11) {
ly = LunarYear(Y + 1);
yy = Y + 1;
}
let index = Number(ly.length - 1);
while (jdToday < LocalToJD(ly[index][0], ly[index][1], ly[index][2])) {
index--;
}
const dd =
jdToday - LocalToJD(ly[index][0], ly[index][1], ly[index][2]) + 1;
const mm = ly[index][3];
if (mm >= 11) {
yy--;
}
const ix = ly[index][4];
return {
dd,
mm,
yy,
ix,
LLLL: `${dd}/${mm}/${yy}`,
DM: `${dd}/${mm}`,
};
};
// âm sang dương
export const Lunar2Solar = (D = 0, M = 0, Y = 0, leap = 0) => {
let yy = Y;
if (M >= 11) {
yy = Y + 1;
}
const lunarYear = LunarYear(yy);
let lunarMonth = null;
for (const index of lunarYear) {
const lm = lunarYear[index];
if (lm[3] === M && lm[4] === leap) {
lunarMonth = lm;
break;
}
}
if (lunarMonth != null) {
const jd = LocalToJD(lunarMonth[0], lunarMonth[1], lunarMonth[2]);
return LocalFromJD(jd + D - 1);
} else {
return null;
}
};
Rất đơn giản đúng hông, tính sẳn hết rùi.
Phần tiếp theo là Countdown
All rights reserved