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

civil(year=1, month=1, day=1) click to toggle source

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
Also aliased as: new
exist?(year, month, day)
Alias for: valid_civil?
jd(jday=0) click to toggle source

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
new(year=1, month=1, day=1)
Also aliased as: new!
Alias for: civil
new(ajd=0, offset=0) click to toggle source

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
new!(year=1, month=1, day=1)
Alias for: new
ordinal(year=0, yday=1) click to toggle source

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
parse(string, comp=true) click to toggle source

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
valid?(year, month, day)
Alias for: valid_civil?
valid_civil?(year, month, day) click to toggle source
# File lib/parsi-date.rb, line 316
def valid_civil? year, month, day
  !!_valid_civil?(year, month, day)
end
Also aliased as: valid_date?, valid?, exist?
valid_date?(year, month, day)
Alias for: valid_civil?
valid_jd?(jd) click to toggle source
# File lib/parsi-date.rb, line 308
def valid_jd? jd
  !!_valid_jd?(jd)
end
valid_ordinal?(year, yday) click to toggle source
# File lib/parsi-date.rb, line 312
def valid_ordinal? year, yday
  !!_valid_ordinal?(year, yday)
end

Private Class Methods

today() click to toggle source

Create a new Date object representing today.

# File lib/parsi-date.rb, line 385
def today
  ::Date.today.to_parsi
end

Public Instance Methods

+(n) click to toggle source

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
-(x) click to toggle source

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
<<(n) click to toggle source

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
<=>(other) click to toggle source

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
===(other) click to toggle source

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
>>(n) click to toggle source

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
_valid_ordinal?(year, yday) click to toggle source

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
ajd() click to toggle source

Get the date as an Astronomical Julian Day Number.

# File lib/parsi-date.rb, line 399
def ajd
  @ajd
end
amjd() click to toggle source

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
cwday() click to toggle source

Returns the day of calendar week (1-7, Monday is 1)

# File lib/parsi-date.rb, line 582
def cwday
  to_gregorian.cwday
end
cweek() click to toggle source

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
day()
Alias for: mday
day_fraction() click to toggle source

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
downto(min) { |date| ... } click to toggle source

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
eql?(other) click to toggle source

Is this Date equal to other?

other must both be a Date object, and represent the same date.

# File lib/parsi-date.rb, line 715
def eql? (other) self.class === other && self == other end
gregorian()
Alias for: to_gregorian
hash() click to toggle source

Calculate a hash value for this date.

# File lib/parsi-date.rb, line 718
def hash() ajd.hash end
inspect() click to toggle source

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
jalali()
Alias for: to_parsi
jd() click to toggle source

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
jd_to_civil(jday) click to toggle source

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
ld() click to toggle source

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
leap?(year) click to toggle source

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
marshal_dump() click to toggle source

Dump to Marshal format.

# File lib/parsi-date.rb, line 731
def marshal_dump() [@ajd, @offset] end
marshal_load(a) click to toggle source

Load from Marshal format.

# File lib/parsi-date.rb, line 734
def marshal_load(a) @ajd, @of, = a end
mday() click to toggle source

Get the day-of-the-month of this date.

# File lib/parsi-date.rb, line 456
def mday() civil[2] end
Also aliased as: day
mjd() click to toggle source

Get the date as a Modified Julian Day Number.

# File lib/parsi-date.rb, line 424
def mjd
  @mjd ||= jd_to_mjd jd
end
mon() click to toggle source

Get the month of this date.

Farvardin is month 1.

# File lib/parsi-date.rb, line 452
def mon() civil[1] end
Also aliased as: month
month()
Alias for: mon
next() click to toggle source

Return a new Date one day after this one.

# File lib/parsi-date.rb, line 562
def next() next_day end
Also aliased as: succ
next_day(n=1) click to toggle source
# File lib/parsi-date.rb, line 558
def next_day(n=1) self + n end
next_month(n=1) click to toggle source
# File lib/parsi-date.rb, line 599
def next_month(n=1) self >> n end
next_year(n=1) click to toggle source
# File lib/parsi-date.rb, line 602
def next_year(n=1) self >> n * 12 end
offset() click to toggle source
# File lib/parsi-date.rb, line 403
def offset
  @offset
end
prev_day(n=1) click to toggle source
# File lib/parsi-date.rb, line 559
def prev_day(n=1) self - n end
prev_month(n=1) click to toggle source
# File lib/parsi-date.rb, line 600
def prev_month(n=1) self << n end
prev_year(n=1) click to toggle source
# File lib/parsi-date.rb, line 603
def prev_year(n=1) self << n * 12 end
step(limit, step=1) { |date| ... } click to toggle source

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
strftime(format='%Y/%m/%d') click to toggle source

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
succ()
Alias for: next
to_date() click to toggle source
# File lib/parsi-date.rb, line 614
def to_date
  self
end
to_datetime() click to toggle source
# File lib/parsi-date.rb, line 618
def to_datetime
  DateTime.new! jd_to_ajd(jd, 0, 0), 0
end
to_gregorian() click to toggle source
# File lib/parsi-date.rb, line 605
def to_gregorian
  ::Date.jd jd
end
Also aliased as: gregorian
to_jalali()
Alias for: to_parsi
to_parsi() click to toggle source
# File lib/parsi-date.rb, line 622
def to_parsi
  self
end
Also aliased as: jalali, to_jalali, to_persian
to_persian()
Alias for: to_parsi
to_s() click to toggle source

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
to_time() click to toggle source
# File lib/parsi-date.rb, line 610
def to_time
  gregorian.to_time
end
upto(max) { |date| ... } click to toggle source

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
wday() click to toggle source

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
yday() click to toggle source

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
year() click to toggle source

Get the year of this date.

# File lib/parsi-date.rb, line 447
def year() civil[0] end