persncal.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. ******************************************************************************
  5. * Copyright (C) 2003-2013, International Business Machines Corporation
  6. * and others. All Rights Reserved.
  7. ******************************************************************************
  8. *
  9. * File PERSNCAL.CPP
  10. *
  11. * Modification History:
  12. *
  13. * Date Name Description
  14. * 9/23/2003 mehran posted to icu-design
  15. * 10/1/2012 roozbeh Fixed algorithm and heavily refactored and rewrote
  16. * based on the implementation of Gregorian
  17. *****************************************************************************
  18. */
  19. #include "persncal.h"
  20. #if !UCONFIG_NO_FORMATTING
  21. #include "umutex.h"
  22. #include "gregoimp.h" // Math
  23. #include <float.h>
  24. static const int16_t kPersianNumDays[]
  25. = {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year
  26. static const int8_t kPersianMonthLength[]
  27. = {31,31,31,31,31,31,30,30,30,30,30,29}; // 0-based
  28. static const int8_t kPersianLeapMonthLength[]
  29. = {31,31,31,31,31,31,30,30,30,30,30,30}; // 0-based
  30. static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = {
  31. // Minimum Greatest Least Maximum
  32. // Minimum Maximum
  33. { 0, 0, 0, 0}, // ERA
  34. { -5000000, -5000000, 5000000, 5000000}, // YEAR
  35. { 0, 0, 11, 11}, // MONTH
  36. { 1, 1, 52, 53}, // WEEK_OF_YEAR
  37. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
  38. { 1, 1, 29, 31}, // DAY_OF_MONTH
  39. { 1, 1, 365, 366}, // DAY_OF_YEAR
  40. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
  41. { 1, 1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
  42. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
  43. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
  44. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
  45. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
  46. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
  47. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
  48. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
  49. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
  50. { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
  51. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
  52. { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
  53. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
  54. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
  55. {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
  56. { 0, 0, 11, 11}, // ORDINAL_MONTH
  57. };
  58. U_NAMESPACE_BEGIN
  59. static const int32_t PERSIAN_EPOCH = 1948320;
  60. // Implementation of the PersianCalendar class
  61. //-------------------------------------------------------------------------
  62. // Constructors...
  63. //-------------------------------------------------------------------------
  64. const char *PersianCalendar::getType() const {
  65. return "persian";
  66. }
  67. PersianCalendar* PersianCalendar::clone() const {
  68. return new PersianCalendar(*this);
  69. }
  70. PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success)
  71. : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
  72. {
  73. setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
  74. }
  75. PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) {
  76. }
  77. PersianCalendar::~PersianCalendar()
  78. {
  79. }
  80. //-------------------------------------------------------------------------
  81. // Minimum / Maximum access functions
  82. //-------------------------------------------------------------------------
  83. int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
  84. return kPersianCalendarLimits[field][limitType];
  85. }
  86. //-------------------------------------------------------------------------
  87. // Assorted calculation utilities
  88. //
  89. /**
  90. * Determine whether a year is a leap year in the Persian calendar
  91. */
  92. UBool PersianCalendar::isLeapYear(int32_t year)
  93. {
  94. int32_t remainder;
  95. ClockMath::floorDivide(25 * year + 11, 33, &remainder);
  96. return (remainder < 8);
  97. }
  98. /**
  99. * Return the day # on which the given year starts. Days are counted
  100. * from the Persian epoch, origin 0.
  101. */
  102. int32_t PersianCalendar::yearStart(int32_t year) {
  103. return handleComputeMonthStart(year,0,false);
  104. }
  105. /**
  106. * Return the day # on which the given month starts. Days are counted
  107. * from the Persian epoch, origin 0.
  108. *
  109. * @param year The Persian year
  110. * @param year The Persian month, 0-based
  111. */
  112. int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const {
  113. return handleComputeMonthStart(year,month,true);
  114. }
  115. //----------------------------------------------------------------------
  116. // Calendar framework
  117. //----------------------------------------------------------------------
  118. /**
  119. * Return the length (in days) of the given month.
  120. *
  121. * @param year The Persian year
  122. * @param year The Persian month, 0-based
  123. */
  124. int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
  125. // If the month is out of range, adjust it into range, and
  126. // modify the extended year value accordingly.
  127. if (month < 0 || month > 11) {
  128. extendedYear += ClockMath::floorDivide(month, 12, &month);
  129. }
  130. return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month];
  131. }
  132. /**
  133. * Return the number of days in the given Persian year
  134. */
  135. int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const {
  136. return isLeapYear(extendedYear) ? 366 : 365;
  137. }
  138. //-------------------------------------------------------------------------
  139. // Functions for converting from field values to milliseconds....
  140. //-------------------------------------------------------------------------
  141. // Return JD of start of given month/year
  142. int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const {
  143. // If the month is out of range, adjust it into range, and
  144. // modify the extended year value accordingly.
  145. if (month < 0 || month > 11) {
  146. eyear += ClockMath::floorDivide(month, 12, &month);
  147. }
  148. int32_t julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + ClockMath::floorDivide(8 * eyear + 21, 33);
  149. if (month != 0) {
  150. julianDay += kPersianNumDays[month];
  151. }
  152. return julianDay;
  153. }
  154. //-------------------------------------------------------------------------
  155. // Functions for converting from milliseconds to field values
  156. //-------------------------------------------------------------------------
  157. int32_t PersianCalendar::handleGetExtendedYear() {
  158. int32_t year;
  159. if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
  160. year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
  161. } else {
  162. year = internalGet(UCAL_YEAR, 1); // Default to year 1
  163. }
  164. return year;
  165. }
  166. /**
  167. * Override Calendar to compute several fields specific to the Persian
  168. * calendar system. These are:
  169. *
  170. * <ul><li>ERA
  171. * <li>YEAR
  172. * <li>MONTH
  173. * <li>DAY_OF_MONTH
  174. * <li>DAY_OF_YEAR
  175. * <li>EXTENDED_YEAR</ul>
  176. *
  177. * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
  178. * method is called.
  179. */
  180. void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) {
  181. int32_t year, month, dayOfMonth, dayOfYear;
  182. int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH;
  183. year = 1 + (int32_t)ClockMath::floorDivide(33 * (int64_t)daysSinceEpoch + 3, (int64_t)12053);
  184. int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33);
  185. dayOfYear = (daysSinceEpoch - farvardin1); // 0-based
  186. if (dayOfYear < 216) { // Compute 0-based month
  187. month = dayOfYear / 31;
  188. } else {
  189. month = (dayOfYear - 6) / 30;
  190. }
  191. dayOfMonth = dayOfYear - kPersianNumDays[month] + 1;
  192. ++dayOfYear; // Make it 1-based now
  193. internalSet(UCAL_ERA, 0);
  194. internalSet(UCAL_YEAR, year);
  195. internalSet(UCAL_EXTENDED_YEAR, year);
  196. internalSet(UCAL_MONTH, month);
  197. internalSet(UCAL_ORDINAL_MONTH, month);
  198. internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
  199. internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
  200. }
  201. constexpr uint32_t kPersianRelatedYearDiff = 622;
  202. int32_t PersianCalendar::getRelatedYear(UErrorCode &status) const
  203. {
  204. int32_t year = get(UCAL_EXTENDED_YEAR, status);
  205. if (U_FAILURE(status)) {
  206. return 0;
  207. }
  208. return year + kPersianRelatedYearDiff;
  209. }
  210. void PersianCalendar::setRelatedYear(int32_t year)
  211. {
  212. // set extended year
  213. set(UCAL_EXTENDED_YEAR, year - kPersianRelatedYearDiff);
  214. }
  215. // default century
  216. static UDate gSystemDefaultCenturyStart = DBL_MIN;
  217. static int32_t gSystemDefaultCenturyStartYear = -1;
  218. static icu::UInitOnce gSystemDefaultCenturyInit {};
  219. UBool PersianCalendar::haveDefaultCentury() const
  220. {
  221. return true;
  222. }
  223. static void U_CALLCONV initializeSystemDefaultCentury() {
  224. // initialize systemDefaultCentury and systemDefaultCenturyYear based
  225. // on the current time. They'll be set to 80 years before
  226. // the current time.
  227. UErrorCode status = U_ZERO_ERROR;
  228. PersianCalendar calendar(Locale("@calendar=persian"),status);
  229. if (U_SUCCESS(status))
  230. {
  231. calendar.setTime(Calendar::getNow(), status);
  232. calendar.add(UCAL_YEAR, -80, status);
  233. gSystemDefaultCenturyStart = calendar.getTime(status);
  234. gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
  235. }
  236. // We have no recourse upon failure unless we want to propagate the failure
  237. // out.
  238. }
  239. UDate PersianCalendar::defaultCenturyStart() const {
  240. // lazy-evaluate systemDefaultCenturyStart
  241. umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
  242. return gSystemDefaultCenturyStart;
  243. }
  244. int32_t PersianCalendar::defaultCenturyStartYear() const {
  245. // lazy-evaluate systemDefaultCenturyStartYear
  246. umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
  247. return gSystemDefaultCenturyStartYear;
  248. }
  249. UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar)
  250. U_NAMESPACE_END
  251. #endif