You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

354 regels
8.2 KiB

  1. /*
  2. Expose functions.
  3. */
  4. module.exports =
  5. { toJalaali: toJalaali
  6. , toGregorian: toGregorian
  7. , isValidJalaaliDate: isValidJalaaliDate
  8. , isLeapJalaaliYear: isLeapJalaaliYear
  9. , jalaaliMonthLength: jalaaliMonthLength
  10. , jalCal: jalCal
  11. , j2d: j2d
  12. , d2j: d2j
  13. , g2d: g2d
  14. , d2g: d2g
  15. , jalaaliToDateObject: jalaaliToDateObject
  16. , jalaaliWeek: jalaaliWeek
  17. }
  18. /*
  19. Jalaali years starting the 33-year rule.
  20. */
  21. var breaks = [ -61, 9, 38, 199, 426, 686, 756, 818, 1111, 1181, 1210
  22. , 1635, 2060, 2097, 2192, 2262, 2324, 2394, 2456, 3178
  23. ]
  24. /*
  25. Converts a Gregorian date to Jalaali.
  26. */
  27. function toJalaali(gy, gm, gd) {
  28. if (Object.prototype.toString.call(gy) === '[object Date]') {
  29. gd = gy.getDate()
  30. gm = gy.getMonth() + 1
  31. gy = gy.getFullYear()
  32. }
  33. return d2j(g2d(gy, gm, gd))
  34. }
  35. /*
  36. Converts a Jalaali date to Gregorian.
  37. */
  38. function toGregorian(jy, jm, jd) {
  39. return d2g(j2d(jy, jm, jd))
  40. }
  41. /*
  42. Checks whether a Jalaali date is valid or not.
  43. */
  44. function isValidJalaaliDate(jy, jm, jd) {
  45. return jy >= -61 && jy <= 3177 &&
  46. jm >= 1 && jm <= 12 &&
  47. jd >= 1 && jd <= jalaaliMonthLength(jy, jm)
  48. }
  49. /*
  50. Is this a leap year or not?
  51. */
  52. function isLeapJalaaliYear(jy) {
  53. return jalCalLeap(jy) === 0
  54. }
  55. /*
  56. Number of days in a given month in a Jalaali year.
  57. */
  58. function jalaaliMonthLength(jy, jm) {
  59. if (jm <= 6) return 31
  60. if (jm <= 11) return 30
  61. if (isLeapJalaaliYear(jy)) return 30
  62. return 29
  63. }
  64. /*
  65. This function determines if the Jalaali (Persian) year is
  66. leap (366-day long) or is the common year (365 days)
  67. @param jy Jalaali calendar year (-61 to 3177)
  68. @returns number of years since the last leap year (0 to 4)
  69. */
  70. function jalCalLeap(jy) {
  71. var bl = breaks.length
  72. , jp = breaks[0]
  73. , jm
  74. , jump
  75. , leap
  76. , n
  77. , i
  78. if (jy < jp || jy >= breaks[bl - 1])
  79. throw new Error('Invalid Jalaali year ' + jy)
  80. for (i = 1; i < bl; i += 1) {
  81. jm = breaks[i]
  82. jump = jm - jp
  83. if (jy < jm)
  84. break
  85. jp = jm
  86. }
  87. n = jy - jp
  88. if (jump - n < 6)
  89. n = n - jump + div(jump + 4, 33) * 33
  90. leap = mod(mod(n + 1, 33) - 1, 4)
  91. if (leap === -1) {
  92. leap = 4
  93. }
  94. return leap
  95. }
  96. /*
  97. This function determines if the Jalaali (Persian) year is
  98. leap (366-day long) or is the common year (365 days), and
  99. finds the day in March (Gregorian calendar) of the first
  100. day of the Jalaali year (jy).
  101. @param jy Jalaali calendar year (-61 to 3177)
  102. @param withoutLeap when don't need leap (true or false) default is false
  103. @return
  104. leap: number of years since the last leap year (0 to 4)
  105. gy: Gregorian year of the beginning of Jalaali year
  106. march: the March day of Farvardin the 1st (1st day of jy)
  107. @see: http://www.astro.uni.torun.pl/~kb/Papers/EMP/PersianC-EMP.htm
  108. @see: http://www.fourmilab.ch/documents/calendar/
  109. */
  110. function jalCal(jy, withoutLeap) {
  111. var bl = breaks.length
  112. , gy = jy + 621
  113. , leapJ = -14
  114. , jp = breaks[0]
  115. , jm
  116. , jump
  117. , leap
  118. , leapG
  119. , march
  120. , n
  121. , i
  122. if (jy < jp || jy >= breaks[bl - 1])
  123. throw new Error('Invalid Jalaali year ' + jy)
  124. // Find the limiting years for the Jalaali year jy.
  125. for (i = 1; i < bl; i += 1) {
  126. jm = breaks[i]
  127. jump = jm - jp
  128. if (jy < jm)
  129. break
  130. leapJ = leapJ + div(jump, 33) * 8 + div(mod(jump, 33), 4)
  131. jp = jm
  132. }
  133. n = jy - jp
  134. // Find the number of leap years from AD 621 to the beginning
  135. // of the current Jalaali year in the Persian calendar.
  136. leapJ = leapJ + div(n, 33) * 8 + div(mod(n, 33) + 3, 4)
  137. if (mod(jump, 33) === 4 && jump - n === 4)
  138. leapJ += 1
  139. // And the same in the Gregorian calendar (until the year gy).
  140. leapG = div(gy, 4) - div((div(gy, 100) + 1) * 3, 4) - 150
  141. // Determine the Gregorian date of Farvardin the 1st.
  142. march = 20 + leapJ - leapG
  143. // return with gy and march when we don't need leap
  144. if (withoutLeap) return { gy: gy, march: march };
  145. // Find how many years have passed since the last leap year.
  146. if (jump - n < 6)
  147. n = n - jump + div(jump + 4, 33) * 33
  148. leap = mod(mod(n + 1, 33) - 1, 4)
  149. if (leap === -1) {
  150. leap = 4
  151. }
  152. return { leap: leap
  153. , gy: gy
  154. , march: march
  155. }
  156. }
  157. /*
  158. Converts a date of the Jalaali calendar to the Julian Day number.
  159. @param jy Jalaali year (1 to 3100)
  160. @param jm Jalaali month (1 to 12)
  161. @param jd Jalaali day (1 to 29/31)
  162. @return Julian Day number
  163. */
  164. function j2d(jy, jm, jd) {
  165. var r = jalCal(jy, true)
  166. return g2d(r.gy, 3, r.march) + (jm - 1) * 31 - div(jm, 7) * (jm - 7) + jd - 1
  167. }
  168. /*
  169. Converts the Julian Day number to a date in the Jalaali calendar.
  170. @param jdn Julian Day number
  171. @return
  172. jy: Jalaali year (1 to 3100)
  173. jm: Jalaali month (1 to 12)
  174. jd: Jalaali day (1 to 29/31)
  175. */
  176. function d2j(jdn) {
  177. var gy = d2g(jdn).gy // Calculate Gregorian year (gy).
  178. , jy = gy - 621
  179. , r = jalCal(jy, false)
  180. , jdn1f = g2d(gy, 3, r.march)
  181. , jd
  182. , jm
  183. , k
  184. // Find number of days that passed since 1 Farvardin.
  185. k = jdn - jdn1f
  186. if (k >= 0) {
  187. if (k <= 185) {
  188. // The first 6 months.
  189. jm = 1 + div(k, 31)
  190. jd = mod(k, 31) + 1
  191. return { jy: jy
  192. , jm: jm
  193. , jd: jd
  194. }
  195. } else {
  196. // The remaining months.
  197. k -= 186
  198. }
  199. } else {
  200. // Previous Jalaali year.
  201. jy -= 1
  202. k += 179
  203. if (r.leap === 1)
  204. k += 1
  205. }
  206. jm = 7 + div(k, 30)
  207. jd = mod(k, 30) + 1
  208. return { jy: jy
  209. , jm: jm
  210. , jd: jd
  211. }
  212. }
  213. /*
  214. Calculates the Julian Day number from Gregorian or Julian
  215. calendar dates. This integer number corresponds to the noon of
  216. the date (i.e. 12 hours of Universal Time).
  217. The procedure was tested to be good since 1 March, -100100 (of both
  218. calendars) up to a few million years into the future.
  219. @param gy Calendar year (years BC numbered 0, -1, -2, ...)
  220. @param gm Calendar month (1 to 12)
  221. @param gd Calendar day of the month (1 to 28/29/30/31)
  222. @return Julian Day number
  223. */
  224. function g2d(gy, gm, gd) {
  225. var d = div((gy + div(gm - 8, 6) + 100100) * 1461, 4)
  226. + div(153 * mod(gm + 9, 12) + 2, 5)
  227. + gd - 34840408
  228. d = d - div(div(gy + 100100 + div(gm - 8, 6), 100) * 3, 4) + 752
  229. return d
  230. }
  231. /*
  232. Calculates Gregorian and Julian calendar dates from the Julian Day number
  233. (jdn) for the period since jdn=-34839655 (i.e. the year -100100 of both
  234. calendars) to some millions years ahead of the present.
  235. @param jdn Julian Day number
  236. @return
  237. gy: Calendar year (years BC numbered 0, -1, -2, ...)
  238. gm: Calendar month (1 to 12)
  239. gd: Calendar day of the month M (1 to 28/29/30/31)
  240. */
  241. function d2g(jdn) {
  242. var j
  243. , i
  244. , gd
  245. , gm
  246. , gy
  247. j = 4 * jdn + 139361631
  248. j = j + div(div(4 * jdn + 183187720, 146097) * 3, 4) * 4 - 3908
  249. i = div(mod(j, 1461), 4) * 5 + 308
  250. gd = div(mod(i, 153), 5) + 1
  251. gm = mod(div(i, 153), 12) + 1
  252. gy = div(j, 1461) - 100100 + div(8 - gm, 6)
  253. return { gy: gy
  254. , gm: gm
  255. , gd: gd
  256. }
  257. }
  258. /**
  259. * Return Saturday and Friday day of current week(week start in Saturday)
  260. * @param {number} jy jalaali year
  261. * @param {number} jm jalaali month
  262. * @param {number} jd jalaali day
  263. * @returns Saturday and Friday of current week
  264. */
  265. function jalaaliWeek(jy, jm, jd) {
  266. var dayOfWeek = jalaaliToDateObject(jy, jm, jd).getDay();
  267. var startDayDifference = dayOfWeek == 6 ? 0 : -(dayOfWeek+1);
  268. var endDayDifference = 6+startDayDifference;
  269. return {
  270. saturday: d2j(j2d(jy, jm, jd+startDayDifference)),
  271. friday: d2j(j2d(jy, jm, jd+endDayDifference))
  272. }
  273. }
  274. /**
  275. * Convert Jalaali calendar dates to javascript Date object
  276. * @param {number} jy jalaali year
  277. * @param {number} jm jalaali month
  278. * @param {number} jd jalaali day
  279. * @param {number} [h] hours
  280. * @param {number} [m] minutes
  281. * @param {number} [s] seconds
  282. * @param {number} [ms] milliseconds
  283. * @returns Date object of the jalaali calendar dates
  284. */
  285. function jalaaliToDateObject(
  286. jy,
  287. jm,
  288. jd,
  289. h,
  290. m,
  291. s,
  292. ms
  293. ) {
  294. var gregorianCalenderDate = toGregorian(jy, jm, jd);
  295. return new Date(
  296. gregorianCalenderDate.gy,
  297. gregorianCalenderDate.gm - 1,
  298. gregorianCalenderDate.gd,
  299. h || 0,
  300. m || 0,
  301. s || 0,
  302. ms || 0
  303. );
  304. }
  305. /*
  306. Utility helper functions.
  307. */
  308. function div(a, b) {
  309. return ~~(a / b)
  310. }
  311. function mod(a, b) {
  312. return a - ~~(a / b) * b
  313. }