Class: KurdishDate::KurdishDate
- Inherits:
-
Object
- Object
- KurdishDate::KurdishDate
- Defined in:
- lib/kurdish_date.rb
Overview
Represents a date in the Kurdish (Sorani) calendar.
The Kurdish (Sorani) calendar uses the same astronomical year structure as the Solar Hijri (Jalali) calendar used in Iran — the first six months have 31 days, the next five 30 days, and the final month (Esme Xakêle / Reşemê / Esfand) has 29 days in a common year and 30 in a leap year.
The year numbering is the Madhi (Kurdish Median) era, counting from the traditional founding of the Median kingdom by Diako around 700 BCE. The offset is fixed at +1321 years from the Solar Hijri (Jalali) year, so a Hijri-Shamsi year of 1405 corresponds to the Kurdish Madhi year of 2726.
The day-by-day conversion uses Borkowski's analytic approximation (the same algorithm used by .NET PersianCalendar, jalaali-js, and the Java PersianDate library), accurate to within one day over a window of roughly 5,000 years.
Month and weekday names are available in two scripts:
:latin— Hawar-style Latin transliteration:sorani— the native Arabic-based Sorani script
Use KurdishDate.script=, KurdishDate.script, or the per-call
:script option on the formatting methods to choose.
Constant Summary collapse
- MONTHS =
Locale::MONTHS
- WEEKDAYS =
Locale::WEEKDAYS
- KURDISH_WEEK_START =
Ruby's Date#wday: 0=Sun, 1=Mon, ..., 5=Fri, 6=Sat.
6- CYCLE_DAYS =
Borkowski's reference constants. The Solar Hijri epoch (1 Farwardin 1 SH) corresponds to JDN 2,121,446.
1_029_983- CYCLE_YEARS =
days in a 2820-year super-cycle
2_820- PERSIAN_EPOCH =
2_121_446- YEAR_LENGTH =
365.24219858156028- LEAP_THRESHOLD =
0.24219858156028- MADHI_OFFSET =
Offset from Solar Hijri year to Kurdish Madhi (Diako) year.
1321
Class Attribute Summary collapse
-
.script ⇒ Object
Default script (:latin or :sorani) used by formatting helpers when no script is passed explicitly.
Instance Attribute Summary collapse
-
#day ⇒ Object
readonly
Returns the value of attribute day.
-
#month ⇒ Object
readonly
Returns the value of attribute month.
-
#year ⇒ Object
readonly
Returns the value of attribute year.
Class Method Summary collapse
-
.day_of_year(year, month, day) ⇒ Object
Day-of-year in the Solar Hijri / Kurdish calendar.
-
.from_gregorian(date) ⇒ Object
Build a KurdishDate from a Gregorian date (Date / DateTime / Time).
-
.from_kurdish(year, month, day) ⇒ Object
Build a KurdishDate from a Kurdish Madhi (year, month, day).
-
.jalali_from_jd(jd) ⇒ Object
JDN -> (Solar Hijri year, month, day).
-
.jd_from_jalali(year, month, day) ⇒ Object
(Solar Hijri year, month, day) -> JDN.
-
.leap?(solar_hijri_year) ⇒ Boolean
Note: leap year is a property of the solar year, which is shared between the Solar Hijri and Kurdish Madhi calendars.
- .month_days(solar_hijri_year, month) ⇒ Object
-
.now ⇒ Object
Now in the Kurdish calendar.
-
.today ⇒ Object
Today in the Kurdish calendar.
Instance Method Summary collapse
- #==(other) ⇒ Object (also: #eql?)
- #hash ⇒ Object
-
#initialize(year, month, day) ⇒ KurdishDate
constructor
A new instance of KurdishDate.
- #inspect ⇒ Object
- #leap? ⇒ Boolean
- #month_days ⇒ Object
-
#month_name(script = nil) ⇒ Object
Full Kurdish month name, e.g.
-
#strftime(format = "%Y-%m-%d", script: nil) ⇒ Object
Format the date.
- #to_a ⇒ Object (also: #deconstruct)
- #to_date ⇒ Object
- #to_datetime ⇒ Object
-
#to_gregorian ⇒ Object
---- accessors ------------------------------------------------------.
- #to_s(format = "%Y-%m-%d", **opts) ⇒ Object
- #to_time ⇒ Object
-
#weekday_index ⇒ Object
Returns the Kurdish weekday index, 0-based, starting at Saturday.
-
#weekday_name(script = nil) ⇒ Object
Full Kurdish weekday name, e.g.
Constructor Details
#initialize(year, month, day) ⇒ KurdishDate
Returns a new instance of KurdishDate.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/kurdish_date.rb', line 63 def initialize(year, month, day) unless year.is_a?(Integer) && month.is_a?(Integer) && day.is_a?(Integer) raise InvalidDateError, "year, month and day must be Integers" end unless month.between?(1, 12) raise InvalidDateError, "month must be between 1 and 12, got #{month}" end # Calendar rules (leap year, month lengths) are defined on the # Solar Hijri year; the Madhi year differs by a fixed offset. solar_year = year - MADHI_OFFSET max_day = self.class.month_days(solar_year, month) unless day.between?(1, max_day) raise InvalidDateError, "day must be between 1 and #{max_day} for month #{month} of year #{year}, got #{day}" end @year = year @month = month @day = day end |
Class Attribute Details
.script ⇒ Object
Default script (:latin or :sorani) used by formatting helpers when no script is passed explicitly.
58 59 60 |
# File 'lib/kurdish_date.rb', line 58 def script @script end |
Instance Attribute Details
#day ⇒ Object (readonly)
Returns the value of attribute day.
61 62 63 |
# File 'lib/kurdish_date.rb', line 61 def day @day end |
#month ⇒ Object (readonly)
Returns the value of attribute month.
61 62 63 |
# File 'lib/kurdish_date.rb', line 61 def month @month end |
#year ⇒ Object (readonly)
Returns the value of attribute year.
61 62 63 |
# File 'lib/kurdish_date.rb', line 61 def year @year end |
Class Method Details
.day_of_year(year, month, day) ⇒ Object
Day-of-year in the Solar Hijri / Kurdish calendar.
266 267 268 269 270 |
# File 'lib/kurdish_date.rb', line 266 def self.day_of_year(year, month, day) sum = 0 (1...month).each { |m| sum += month_days(year, m) } sum + day end |
.from_gregorian(date) ⇒ Object
Build a KurdishDate from a Gregorian date (Date / DateTime / Time).
89 90 91 92 93 |
# File 'lib/kurdish_date.rb', line 89 def self.from_gregorian(date) jd = date.respond_to?(:jd) ? date.jd : ::Date.parse(date.to_s).jd jy, jm, jd2 = jalali_from_jd(jd) new(jy + MADHI_OFFSET, jm, jd2) end |
.from_kurdish(year, month, day) ⇒ Object
Build a KurdishDate from a Kurdish Madhi (year, month, day).
96 97 98 |
# File 'lib/kurdish_date.rb', line 96 def self.from_kurdish(year, month, day) new(year, month, day) end |
.jalali_from_jd(jd) ⇒ Object
JDN -> (Solar Hijri year, month, day). Borkowski's approximation.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/kurdish_date.rb', line 233 def self.jalali_from_jd(jd) jd = jd.to_i offset = jd - PERSIAN_EPOCH cycle_no = offset / CYCLE_DAYS cycle_no -= 1 if offset < 0 cycle_start = PERSIAN_EPOCH + cycle_no * CYCLE_DAYS yc = ((jd - cycle_start) / YEAR_LENGTH).floor year = yc + 475 + cycle_no * CYCLE_YEARS lll = PERSIAN_EPOCH + cycle_no * CYCLE_DAYS + (yc * YEAR_LENGTH).floor day = jd - lll + 1 if day > (leap?(year) ? 366 : 365) year += 1 day = 1 end month = 1 d = day while month <= 12 && d > month_days(year, month) d -= month_days(year, month) month += 1 end [year, month, d] end |
.jd_from_jalali(year, month, day) ⇒ Object
(Solar Hijri year, month, day) -> JDN. Borkowski's reverse.
257 258 259 260 261 262 263 |
# File 'lib/kurdish_date.rb', line 257 def self.jd_from_jalali(year, month, day) era = (year - 475) / CYCLE_YEARS era -= 1 if (year - 475) < 0 y_c = (year - 475) - era * CYCLE_YEARS first_d = PERSIAN_EPOCH + era * CYCLE_DAYS + (y_c * YEAR_LENGTH).floor first_d + (day_of_year(year, month, day) - 1) end |
.leap?(solar_hijri_year) ⇒ Boolean
Note: leap year is a property of the solar year, which is shared between the Solar Hijri and Kurdish Madhi calendars.
218 219 220 |
# File 'lib/kurdish_date.rb', line 218 def self.leap?(solar_hijri_year) ((solar_hijri_year + 2346) * LEAP_THRESHOLD) % 1 < LEAP_THRESHOLD end |
.month_days(solar_hijri_year, month) ⇒ Object
222 223 224 225 226 |
# File 'lib/kurdish_date.rb', line 222 def self.month_days(solar_hijri_year, month) return 31 if month.between?(1, 6) return 30 if month.between?(7, 11) leap?(solar_hijri_year) ? 30 : 29 end |
.now ⇒ Object
Now in the Kurdish calendar.
106 107 108 |
# File 'lib/kurdish_date.rb', line 106 def self.now from_gregorian(::DateTime.now) end |
.today ⇒ Object
Today in the Kurdish calendar.
101 102 103 |
# File 'lib/kurdish_date.rb', line 101 def self.today from_gregorian(::Date.today) end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
199 200 201 202 |
# File 'lib/kurdish_date.rb', line 199 def ==(other) other.is_a?(KurdishDate) && year == other.year && month == other.month && day == other.day end |
#hash ⇒ Object
205 206 207 |
# File 'lib/kurdish_date.rb', line 205 def hash [@year, @month, @day].hash end |
#inspect ⇒ Object
195 196 197 |
# File 'lib/kurdish_date.rb', line 195 def inspect "#<KurdishDate #{strftime("%Y-%m-%d")} (#{weekday_name})>" end |
#leap? ⇒ Boolean
147 148 149 |
# File 'lib/kurdish_date.rb', line 147 def leap? self.class.leap?(@year - MADHI_OFFSET) end |
#month_days ⇒ Object
151 152 153 |
# File 'lib/kurdish_date.rb', line 151 def month_days self.class.month_days(@year - MADHI_OFFSET, @month) end |
#month_name(script = nil) ⇒ Object
Full Kurdish month name, e.g. "Gulan" (Latin) or
"گوڵان" (Sorani). script defaults to the class-level setting.
143 144 145 |
# File 'lib/kurdish_date.rb', line 143 def month_name(script = nil) Locale.month_name(@month, script || self.class.script) end |
#strftime(format = "%Y-%m-%d", script: nil) ⇒ Object
Format the date. Supported tokens:
%Y 4-digit year %y 2-digit year
%m zero-padded month %-m unpadded month
%d zero-padded day %-d unpadded day
%B full month name (Latin) %BB full month name (Sorani)
%b abbreviated month name (Latin)
%A full weekday name (Latin) %AA full weekday name (Sorani)
%a abbreviated weekday name (Latin) %aa abbreviated weekday name (Sorani)
The single-letter tokens (%B / %A / %a / %b) use the default
script set by KurdishDate.script=. The double-letter tokens
(%BB / %AA / %aa) always use Sorani so a mixed-language format
string is possible.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/kurdish_date.rb', line 170 def strftime(format = "%Y-%m-%d", script: nil) script = (script || self.class.script).to_sym format.gsub(/%-?Y|%-?y|%-?m|%-?d|%BB|%AA|%aa|%B|%b|%A|%a/) do |token| case token when "%Y" then @year.to_s.rjust(4, "0") when "%y" then (@year % 100).to_s.rjust(2, "0") when "%m" then @month.to_s.rjust(2, "0") when "%-m" then @month.to_s when "%d" then @day.to_s.rjust(2, "0") when "%-d" then @day.to_s when "%BB" then Locale.month_name(@month, :sorani) when "%AA" then Locale.weekday_name(weekday_index, :sorani) when "%aa" then Locale.weekday_name(weekday_index, :sorani)[0, 3] when "%B" then Locale.month_name(@month, script) when "%b" then Locale.month_name(@month, script)[0, 3] when "%A" then Locale.weekday_name(weekday_index, script) when "%a" then Locale.weekday_name(weekday_index, script)[0, 3] end end end |
#to_a ⇒ Object Also known as: deconstruct
209 210 211 |
# File 'lib/kurdish_date.rb', line 209 def to_a [@year, @month, @day] end |
#to_date ⇒ Object
117 118 119 |
# File 'lib/kurdish_date.rb', line 117 def to_date to_gregorian end |
#to_datetime ⇒ Object
125 126 127 128 |
# File 'lib/kurdish_date.rb', line 125 def to_datetime jd = self.class.jd_from_jalali(@year - MADHI_OFFSET, @month, @day) ::DateTime.jd(jd) end |
#to_gregorian ⇒ Object
---- accessors ------------------------------------------------------
112 113 114 115 |
# File 'lib/kurdish_date.rb', line 112 def to_gregorian jd = self.class.jd_from_jalali(@year - MADHI_OFFSET, @month, @day) ::Date.jd(jd) end |
#to_s(format = "%Y-%m-%d", **opts) ⇒ Object
191 192 193 |
# File 'lib/kurdish_date.rb', line 191 def to_s(format = "%Y-%m-%d", **opts) strftime(format, **opts) end |
#to_time ⇒ Object
121 122 123 |
# File 'lib/kurdish_date.rb', line 121 def to_time to_gregorian.to_time end |
#weekday_index ⇒ Object
Returns the Kurdish weekday index, 0-based, starting at Saturday.
131 132 133 |
# File 'lib/kurdish_date.rb', line 131 def weekday_index ((to_gregorian.wday - KURDISH_WEEK_START) % 7) end |
#weekday_name(script = nil) ⇒ Object
Full Kurdish weekday name, e.g. "Şemme" (Latin) or
"شەممە" (Sorani). script defaults to the class-level setting.
137 138 139 |
# File 'lib/kurdish_date.rb', line 137 def weekday_name(script = nil) Locale.weekday_name(weekday_index, script || self.class.script) end |