class Parsi::Date
Class representing a date.
See the documentation to the file parsi-date.rb for an overview.
Internally, the date is represented as an Astronomical Julian Day Number, ajd
. (There is also an offset
field for a time zone offset, but this is only for the use of the DateTime
subclass.)
A new Date
object is created using one of the object creation class methods named after the corresponding date format, and the arguments appropriate to that date format; for instance, Date::civil()
(aliased to Date::new()
) with year, month, and day-of-month, or Date::ordinal()
with year and day-of-year.
Date
objects are immutable once created.
Once a Date
has been created, date values can be retrieved for the different date formats supported using instance methods. For instance, mon()
gives the Civil month and yday()
gives the Ordinal day of the year. Date
values can be retrieved in any format, regardless of what format was used to create the Date
instance.
The Date
class includes the Comparable module, allowing date objects to be compared and sorted, ranges of dates to be created, and so forth.
Constants
- ABBR_DAYNAMES
Abbreviated day names, in Farsi.
- ABBR_EN_DAYNAMES
Abbreviated day names, in English.
- ABBR_MONTHNAMES
Abbreviated month names, in English.
We don’t have Farsi abbreviated month names, as they are not useful
- DAYNAMES
Full names of days of the week, in Farsi. Days of the week count from 0 to 6; a day’s numerical representation indexed into this array gives the name of that day.
- EN_DAYNAMES
Full names of days of the week, in English. Days of the week count from 0 to 6; a day’s numerical representation indexed into this array gives the name of that day.
- EN_MONTHNAMES
Full month names, in English. Months count from 1 to 12;
- MONTHNAMES
Full month names, in Farsi. Months count from 1 to 12; a month’s numerical representation indexed into this array gives the name of that month (hence the first element is nil).
- VERSION
Public Class Methods
Create a new Date
object for the Civil Date
specified by year
, month
, and day-of-month day
.
# File lib/parsi-date.rb, line 352 def civil year=1, month=1, day=1 raise ArgumentError, 'invalid date' unless jd = _valid_civil?(year, month, day) new! jd_to_ajd(jd, 0, 0), 0 end
Create a new Date
object from a Julian Day Number.
jday
is the Julian Day Number; if not specified, it defaults to 0.
examples:
Parsi::Date.jd 2456229 # => #<Parsi::Date: 1391-08-07> Parsi::Date.jd 2456230 # => #<Parsi::Date: 1391-08-08> Parsi::Date.jd # => #<Parsi::Date: -5335-09-01>
# File lib/parsi-date.rb, line 332 def jd jday=0 jd = _valid_jd? jday new! jd_to_ajd(jday, 0, 0), 0 end
Create a new Date
object.
ajd
is the Astronomical Julian Day Number. offset
is the offset from UTC as a fraction of a day.
# File lib/parsi-date.rb, line 394 def initialize ajd=0, offset=0 @ajd, @offset = ajd, offset end
Create a new Date
object from an Ordinal Date
, specified by year
and day-of-year yday
. yday
can be negative, in which it counts backwards from the end of the year.
examples:
Parsi::Date.ordinal 1390 # => #<Parsi::Date: 1390-01-01> Parsi::Date.ordinal 1391, 120 # => #<Parsi::Date: 1391-04-27> Parsi::Date.ordinal 1390, -1 # => #<Parsi::Date: 1389-12-29>
# File lib/parsi-date.rb, line 345 def ordinal year=0, yday=1 raise ArgumentError, 'invalid date' unless jd = _valid_ordinal?(year, yday) new! jd_to_ajd(jd, 0, 0), 0 end
Parses the given representation of date and time, and creates a date object.
If the optional second argument is true and the detected year is in the range “00” to “99”, considers the year a 2-digit form and makes it full.
For
# File lib/parsi-date.rb, line 364 def parse string, comp=true # TODO: Add more parse options, for example parse '۴ام فروردین ۱۳۹۱' m = string.match /(?<year>\d+)(\/|-| )(?<month>\d+)(\/|-| )(?<day>\d+)/ m ||= string.match /(?<year>\d+)(?<month>\d{2})(?<day>\d{2})/ if m.nil? raise ArgumentError.new 'invalid date' else year, month, day = m[:year].to_i, m[:month].to_i, m[:day].to_i if comp && m[:year].length == 2 centry = Date.today.year / 100 year = (m[:year].prepend centry.to_s).to_i end if jd = _valid_civil?(year, month, day) new! jd_to_ajd(jd, 0, 0), 0 else raise ArgumentError.new 'invalid date' end end end
# File lib/parsi-date.rb, line 316 def valid_civil? year, month, day !!_valid_civil?(year, month, day) end
# File lib/parsi-date.rb, line 308 def valid_jd? jd !!_valid_jd?(jd) end
# File lib/parsi-date.rb, line 312 def valid_ordinal? year, yday !!_valid_ordinal?(year, yday) end
Private Class Methods
Create a new Date
object representing today.
# File lib/parsi-date.rb, line 385 def today ::Date.today.to_parsi end
Public Instance Methods
Return a new Date
object that is n
days later than the current one.
n
may be a negative value, in which case the new Date
is earlier than the current one; however, -()
might be more intuitive.
If n
is not a Numeric, a TypeError will be thrown. In particular, two Dates cannot be added to each other.
# File lib/parsi-date.rb, line 485 def + n case n when Numeric return self.class.new!(ajd + n, offset) end raise TypeError, 'expected numeric' end
If x
is a Numeric value, create a new Date
object that is x
days earlier than the current one.
If x
is a Date
, return the number of days between the two dates; or, more precisely, how many days later the current date is than x
.
If x
is neither Numeric nor a Date
, a TypeError is raised.
# File lib/parsi-date.rb, line 500 def - x case x when Numeric return self.class.new!(ajd - x, offset) when Date return ajd - x.ajd end raise TypeError, 'expected numeric or date' end
Return a new Date
object that is n
months earlier than the current one.
If the day-of-the-month of the current Date
is greater than the last day of the target month, the day-of-the-month of the returned Date
will be the last day of the target month.
# File lib/parsi-date.rb, line 597 def << (n) self >> -n end
Compare this date with another date.
other
can also be a Numeric value, in which case it is interpreted as an Astronomical Julian Day Number.
Comparison is by Astronomical Julian Day Number, including fractional days. This means that both the time and the timezone offset are taken into account when comparing two DateTime
instances. When comparing a DateTime
instance with a Date
instance, the time of the latter will be considered as falling on midnight UTC.
# File lib/parsi-date.rb, line 520 def <=> other case other when Numeric return ajd <=> other when Date return ajd <=> other.ajd when ::Date return ajd <=> other.ajd else begin left, right = other.coerce(self) return left <=> right rescue NoMethodError end end nil end
The relationship operator for Date
.
Compares dates by Julian Day Number. When comparing two DateTime
instances, or a DateTime
with a Date
, the instances will be regarded as equivalent if they fall on the same date in local time.
# File lib/parsi-date.rb, line 543 def === (other) case other when Numeric return jd == other when Date; return jd == other.jd else begin l, r = other.coerce(self) return l === r rescue NoMethodError end end false end
Return a new Date
object that is n
months later than the current one.
If the day-of-the-month of the current Date
is greater than the last day of the target month, the day-of-the-month of the returned Date
will be the last day of the target month.
# File lib/parsi-date.rb, line 570 def >> n y, m = (year * 12 + (mon - 1) + n).divmod(12) m, = (m + 1) .divmod(1) d = mday until jd2 = _valid_civil?(y, m, d) d -= 1 raise ArgumentError, 'invalid date' unless d > 0 end self + (jd2 - jd) end
Do the year
and day-of-year yday
make a valid Ordinal Date
? Returns the corresponding Julian Day Number if they do, or nil if they don’t.
# File lib/parsi-date.rb, line 204 def _valid_ordinal? year, yday ordinal_to_jd year, yday end
Get the date as an Astronomical Julian Day Number.
# File lib/parsi-date.rb, line 399 def ajd @ajd end
Get the date as an Astronomical Modified Julian Day Number.
# File lib/parsi-date.rb, line 409 def amjd @amjd ||= ajd_to_amjd ajd end
Returns the day of calendar week (1-7, Monday is 1)
# File lib/parsi-date.rb, line 582 def cwday to_gregorian.cwday end
Returns the calendar week number (1-53).
# File lib/parsi-date.rb, line 587 def cweek start = Date.civil(year, 1, 1) ((jd - start.jd + start.cwday - 5) / 7.0).ceil end
Get any fractional day part of the date.
# File lib/parsi-date.rb, line 419 def day_fraction @day_fraction ||= ajd_to_jd(ajd, offset).last end
Step backward one day at a time until we reach min
(inclusive), yielding each date as we go.
# File lib/parsi-date.rb, line 708 def downto min, &block # :yield: date step min, -1, &block end
Calculate a hash value for this date.
# File lib/parsi-date.rb, line 718 def hash() ajd.hash end
Return internal object state as a programmer-readable string.
# File lib/parsi-date.rb, line 721 def inspect format('#<%s: %s (%s,%s)>', self.class, to_s, ajd, offset) end
Get the date as a Julian Day Number.
# File lib/parsi-date.rb, line 414 def jd @jd ||= ajd_to_jd(ajd, offset).first end
Convert a Julian Day Number to a Civil Date
. jday
is the Julian Day Number.
Returns the corresponding [year, month, day_of_month] as a three-element array.
# File lib/parsi-date.rb, line 171 def jd_to_civil jday depoch = (jday - first_day_of_year(475)) cycle, cyear = depoch.divmod 1029983 if cyear == 1029982 ycycle = 2820 else aux1, aux2 = cyear.divmod 366 ycycle = (2134 * aux1 + 2816 * aux2 + 2815) / 1028522 + aux1 + 1 end year = ycycle + 2820 * cycle + 474 yday = jday - first_day_of_year(year) + 1 month = ((yday <= 186) ? yday / 31.0 : (yday - 6) / 30.0).ceil day = (jday - first_day_of_month(year, month) + 1) [year, month, day] end
Get the date as the number of days since the Day of Calendar Reform (in Italy and the Catholic countries).
# File lib/parsi-date.rb, line 430 def ld @ld ||= jd_to_ld jd end
Returns true if the given year is a leap year of the calendar.
# File lib/parsi-date.rb, line 147 def leap? year ((((((year - ((year > 0) ? 474 : 473)) % 2820) + 474) + 38) * 682) % 2816) < 682 end
Dump to Marshal format.
# File lib/parsi-date.rb, line 731 def marshal_dump() [@ajd, @offset] end
Load from Marshal format.
# File lib/parsi-date.rb, line 734 def marshal_load(a) @ajd, @of, = a end
Get the day-of-the-month of this date.
# File lib/parsi-date.rb, line 456 def mday() civil[2] end
Get the date as a Modified Julian Day Number.
# File lib/parsi-date.rb, line 424 def mjd @mjd ||= jd_to_mjd jd end
Get the month of this date.
Farvardin is month 1.
# File lib/parsi-date.rb, line 452 def mon() civil[1] end
Return a new Date
one day after this one.
# File lib/parsi-date.rb, line 562 def next() next_day end
# File lib/parsi-date.rb, line 558 def next_day(n=1) self + n end
# File lib/parsi-date.rb, line 599 def next_month(n=1) self >> n end
# File lib/parsi-date.rb, line 602 def next_year(n=1) self >> n * 12 end
# File lib/parsi-date.rb, line 403 def offset @offset end
# File lib/parsi-date.rb, line 559 def prev_day(n=1) self - n end
# File lib/parsi-date.rb, line 600 def prev_month(n=1) self << n end
# File lib/parsi-date.rb, line 603 def prev_year(n=1) self << n * 12 end
Step the current date forward step
days at a time (or backward, if step
is negative) until we reach limit
(inclusive), yielding the resultant date at each step.
# File lib/parsi-date.rb, line 688 def step limit, step=1 return to_enum(:step, limit, step) unless block_given? date = self comp_op = %w(== <= >=)[step <=> 0] while date.send comp_op, limit yield date date += step end self end
Formats time according to the directives in the given format string. The directives begins with a percent (%) character. Any text not listed as a directive will be passed through to the output string.
The directive consists of a percent (%) character, zero or more flags, optional minimum field width, optional modifier and a conversion specifier as follows.
%<flags><width><modifier><conversion>
flags
and conversion
are as in Time
exept that the E
flag is not egnored any more, it forse useing English names
# File lib/parsi-date.rb, line 643 def strftime format='%Y/%m/%d' format. gsub('%%', 'PERCENT_SUBSTITUTION_MARKER'). gsub('%+', '%a %b %e %H:%M:%S %Z %Y'). gsub('%c', '%a %-d %B %Y'). gsub('%x', '%D'). gsub('%D', '%y/%m/%d'). gsub('%F', '%Y-%m-%d'). gsub('%v', '%e-%B-%Y'). gsub('%Y', year.to_s). gsub('%C', (year / 100).to_s). gsub('%y', (year % 100).to_s). gsub('%m', '%02d' % month). gsub('%_m', '%2d' % month). gsub('%-m', month.to_s). gsub('%^B', '%B'). gsub('%B', MONTHNAMES[month]). gsub('%E^B', '%^EB'). gsub('%^EB', EN_MONTHNAMES[month].capitalize). gsub('%EB', EN_MONTHNAMES[month]). gsub('%h', '%b'). gsub('%^h', '%^b'). gsub('%b', ABBR_MONTHNAMES[month]). gsub('%^b', ABBR_MONTHNAMES[month].capitalize). gsub('%d', '%02d' % day). gsub('%e', '%2d' % day). gsub('%-d', day.to_s). gsub('%j', '%03d' % yday.to_s). gsub('%A', DAYNAMES[wday]). gsub('%a', ABBR_DAYNAMES[wday]). gsub('%EA', EN_DAYNAMES[wday]). gsub('%Ea', ABBR_EN_DAYNAMES[wday]). gsub('%E^A', '%^EA'). gsub('%^EA', EN_DAYNAMES[wday].capitalize). gsub('%E^a', '%^Ea'). gsub('%^Ea', ABBR_EN_DAYNAMES[wday].capitalize). gsub('%w', wday.to_s). gsub('%n', "\n"). gsub('%t', "\t"). gsub('PERCENT_SUBSTITUTION_MARKER', '%') end
# File lib/parsi-date.rb, line 614 def to_date self end
# File lib/parsi-date.rb, line 618 def to_datetime DateTime.new! jd_to_ajd(jd, 0, 0), 0 end
# File lib/parsi-date.rb, line 605 def to_gregorian ::Date.jd jd end
# File lib/parsi-date.rb, line 622 def to_parsi self end
Return the date as a human-readable string.
The format used is YYYY-MM-DD.
# File lib/parsi-date.rb, line 728 def to_s() format('%.4d-%02d-%02d', year, mon, mday) end
# File lib/parsi-date.rb, line 610 def to_time gregorian.to_time end
Step forward one day at a time until we reach max
(inclusive), yielding each date as we go.
# File lib/parsi-date.rb, line 702 def upto max, &block # :yield: date step max, 1, &block end
Get the week day of this date. Sunday is day-of-week 0; Saturday is day-of-week 6.
# File lib/parsi-date.rb, line 466 def wday @wday ||= jd_to_wday jd end
Get the day-of-the-year of this date.
January 1 is day-of-the-year 1
# File lib/parsi-date.rb, line 462 def yday; ordinal[1] end
Get the year of this date.
# File lib/parsi-date.rb, line 447 def year() civil[0] end