parsi-date 0.1 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  *.gem
2
+ doc/
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- parsi-date (0.1.pre)
4
+ parsi-date (0.2)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/lib/parsi-date.rb CHANGED
@@ -1,67 +1,352 @@
1
1
  # encoding: utf-8
2
+ #
3
+ # parsi-date.rb
4
+ #
5
+ # Author: Hassan Zamani 2012
6
+ #
7
+ # == Overview
8
+ #
9
+ # Date class represent a date in persian (jalali) calendar
10
+ #
11
+ # === Ways of calculating the date.
12
+ #
13
+ # In common usage, the date is reckoned in years since or before the Common Era
14
+ # (Hegira of Muhammad from Mecca to Medina in 622 CE), then as a month and
15
+ # day-of-the-month within the current year. This is known as the *Civil* *Date*,
16
+ # and abbreviated as +civil+ in the Date class.
17
+ #
18
+ # Instead of year, month-of-the-year, and day-of-the-month, the date can also
19
+ # be reckoned in terms of year and day-of-the-year. This is known as the *Ordinal*
20
+ # *Date*, and is abbreviated as +ordinal+ in the Date class. (Note that referring
21
+ # to this as the Julian date is incorrect.)
22
+ #
23
+ # For scientific purposes, it is convenient to refer to a date simply as a day
24
+ # count, counting from an arbitrary initial day. The date first chosen for this
25
+ # was January 1, 4713 BCE (-5335/9/1 in hijri solar calendar). A count of days from
26
+ # this date is the *Julian* *Day* *Number* or *Julian* *Date*, which is abbreviated
27
+ # as +jd+ in the Date class. This is in local time, and counts from midnight on the
28
+ # initial day. The stricter usage is in UTC, and counts from midday on the initial
29
+ # day. This is referred to in the Date class as the *Astronomical* *Julian* *Day*
30
+ # *Number*, and abbreviated as +ajd+. In the Date class, the Astronomical Julian
31
+ # Day Number includes fractional days.
32
+ #
33
+ # Another absolute day count is the *Modified* *Julian* *Day* *Number*, which
34
+ # takes November 17, 1858 (1237/08/26 in hijri solar calendar) as its initial day.
35
+ # This is abbreviated as +mjd+ in the Date class. There is also an *Astronomical*
36
+ # *Modified* *Julian* *Day* *Number*, which is in UTC and includes fractional days.
37
+ # This is abbreviated as +amjd+ in the Date class. Like the Modified Julian Day
38
+ # Number (and unlike the Astronomical Julian Day Number), it counts from midnight.
39
+ #
40
+ # === Time Zones
41
+ #
42
+ # DateTime objects support a simple representation of time zones. Time zones are
43
+ # represented as an offset from UTC, as a fraction of a day. This offset is the
44
+ # how much local time is later (or earlier) than UTC. UTC offset 0 is centred on
45
+ # England (also known as GMT). As you travel east, the offset increases until you
46
+ # reach the dateline in the middle of the Pacific Ocean; as you travel west, the
47
+ # offset decreases. This offset is abbreviated as +offset+ in the DateTime class.
48
+ #
49
+ # This simple representation of time zones does not take into account the common
50
+ # practice of Daylight Savings Time or Summer Time.
51
+ #
52
+ # Most DateTime methods return the date and the time in local time. The two
53
+ # exceptions are #ajd() and #amjd(), which return the date and time in UTC time,
54
+ # including fractional days.
55
+ #
56
+ # The Date class does not support time zone offsets, in that there is no way to
57
+ # create a Date object with a time zone. However, methods of the Date class when
58
+ # used by a DateTime instance will use the time zone offset of this instance.
59
+ #
2
60
 
3
61
  require 'date'
4
62
 
5
63
  module Parsi
6
- MONTHNAMES = [nil] + %w(فروردین اردیبهشت خرداد تیر مرداد شهریور مهر آبان آذر دی بهمن اسفند)
7
- ABBR_MONTHNAMES = [nil] + %w(Far Ord Kho Tir Mor Sha Meh Abn Azr Dey Bah Esf)
8
- DAYNAMES = %w(شنده یک‌شنده دوشنده سه‌شنده چهارشنده چنج‌شنده جمعه)
9
- ABBR_DAYNAMES = %w(ش ۱ش ۲ش ۳ش ۴ش ۵ش ج)
10
- [MONTHNAMES, ABBR_MONTHNAMES, DAYNAMES, ABBR_DAYNAMES].each &:freeze
11
-
64
+ # Class representing a date.
65
+ #
66
+ # See the documentation to the file parsi-date.rb for an overview.
67
+ #
68
+ # Internally, the date is represented as an Astronomical Julian Day Number, +ajd+.
69
+ # (There is also an +offset+ field for a time zone offset, but this is only for
70
+ # the use of the DateTime subclass.)
71
+ #
72
+ # A new Date object is created using one of the object creation class methods
73
+ # named after the corresponding date format, and the arguments appropriate to
74
+ # that date format; for instance, Date::civil() (aliased to Date::new()) with
75
+ # year, month, and day-of-month, or Date::ordinal() with year and day-of-year.
76
+ #
77
+ # Date objects are immutable once created.
78
+ #
79
+ # Once a Date has been created, date values can be retrieved for the different
80
+ # date formats supported using instance methods. For instance, #mon() gives the
81
+ # Civil month and #yday() gives the Ordinal day of the year. Date values can be
82
+ # retrieved in any format, regardless of what format was used to create the
83
+ # Date instance.
84
+ #
85
+ # The Date class includes the Comparable module, allowing
86
+ # date objects to be compared and sorted, ranges of dates
87
+ # to be created, and so forth.
12
88
  class Date
89
+
13
90
  include Comparable
14
91
 
15
- attr_reader :year, :month, :day
92
+ # Full month names, in Farsi. Months count from 1 to 12; a
93
+ # month's numerical representation indexed into this array
94
+ # gives the name of that month (hence the first element is nil).
95
+ MONTHNAMES = [nil] + %w(فروردین اردیبهشت خرداد تیر مرداد شهریور مهر آبان آذر دی بهمن اسفند)
16
96
 
17
- DAYS_TO_FIRST_OF_MONTH = [nil, 0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336]
18
- DAYS_IN_MONTH = [nil, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29]
19
- JALALI_EPOCH = 1948320.5
97
+ # Full names of days of the week, in Farsi. Days of the week
98
+ # count from 0 to 6 (except in the commercial week); a day's numerical
99
+ # representation indexed into this array gives the name of that day.
100
+ DAYNAMES = %w(یک‌شنده دوشنده سه‌شنده چهارشنده چنج‌شنده جمعه شنده)
20
101
 
21
- class << self
102
+ # Abbreviated month names, in English.
103
+ ABBR_MONTHNAMES = [nil] + %w(Far Ord Kho Tir Mor Sha Meh Abn Azr Dey Bah Esf)
22
104
 
105
+ # Abbreviated day names, in Farsi.
106
+ ABBR_DAYNAMES = %w(۱ش ۲ش ۳ش ۴ش ۵ش ج ش)
107
+
108
+ [MONTHNAMES, ABBR_MONTHNAMES, DAYNAMES, ABBR_DAYNAMES].each do |xs|
109
+ xs.each{|x| x.freeze unless x.nil?}.freeze
110
+ end
111
+
112
+ HALF_DAYS_IN_DAY = Rational(1, 2) # :nodoc:
113
+ HOURS_IN_DAY = Rational(1, 24) # :nodoc:
114
+ MINUTES_IN_DAY = Rational(1, 1440) # :nodoc:
115
+ SECONDS_IN_DAY = Rational(1, 86400) # :nodoc:
116
+ MILLISECONDS_IN_DAY = Rational(1, 86400*10**3) # :nodoc:
117
+ NANOSECONDS_IN_DAY = Rational(1, 86400*10**9) # :nodoc:
118
+ MILLISECONDS_IN_SECOND = Rational(1, 10**3) # :nodoc:
119
+ NANOSECONDS_IN_SECOND = Rational(1, 10**9) # :nodoc:
120
+
121
+ JALALI_EPOCH_IN_AJD = Rational(3896641, 2) # :nodoc:
122
+ MJD_EPOCH_IN_AJD = Rational(4800001, 2) # 1858-11-17 # :nodoc:
123
+ UNIX_EPOCH_IN_AJD = Rational(4881175, 2) # 1970-01-01 # :nodoc:
124
+ JALALI_EPOCH_IN_CJD = 1948321 # :nodoc:
125
+ MJD_EPOCH_IN_CJD = 2400001 # :nodoc:
126
+ UNIX_EPOCH_IN_CJD = 2440588 # :nodoc:
127
+ LD_EPOCH_IN_CJD = 2299160 # :nodoc:
128
+ DAYS_IN_MONTH = [nil, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29] # :nodoc:
129
+
130
+ shared_methods = Module.new do
131
+
132
+ # Returns true if the given year is a leap year of the calendar.
23
133
  def leap? year
24
134
  ((((((year - ((year > 0) ? 474 : 473)) % 2820) + 474) + 38) * 682) % 2816) < 682
25
135
  end
26
- alias :exist? :leap?
136
+ alias_method :jalali_leap?, :leap?
27
137
 
28
- def valid? year, month, day
29
- return false unless year.is_a?(Fixnum) && month.is_a?(Fixnum) && day.is_a?(Fixnum)
30
- return true if leap?(year) && month == 12 && day == 30
138
+ private
31
139
 
32
- 1 <= month && month <= 12 && 1 <= day && day <= DAYS_IN_MONTH[month]
33
- end
140
+ DAYS_TO_FIRST_OF_MONTH = [nil, 0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336] # :nodoc:
34
141
 
35
- def jd jday=0
36
- jday = jday.floor
142
+ # Convert a Civil Date to a Julian Day Number and returns the corresponding Julian Day Number.
143
+ def civil_to_jd year, month, day # :nodoc:
144
+ epbase = year - 474
145
+ epyear = 474 + (epbase % 2820)
37
146
 
38
- depoch = (jday - Date.new(475, 1, 1).jd).floor
39
- cycle = depoch / 1029983
40
- cyear = depoch % 1029983
147
+ day + DAYS_TO_FIRST_OF_MONTH[month] +
148
+ (epyear * 682 - 110) / 2816 +
149
+ (epyear - 1) * 365 +
150
+ (epbase / 2820 * 1029983) +
151
+ (JALALI_EPOCH_IN_CJD - 1)
152
+ end
153
+
154
+ # Convert a Julian Day Number to a Civil Date. +jday+ is the Julian Day Number.
155
+ #
156
+ # Returns the corresponding [year, month, day_of_month] as a three-element array.
157
+ def jd_to_civil jday
158
+ depoch = (jday - first_day_of_year(475))
159
+ cycle, cyear = depoch.divmod 1029983
41
160
 
42
161
  if cyear == 1029982
43
- ycycle = 2820
162
+ ycycle = 2820
163
+ else
164
+ aux1, aux2 = cyear.divmod 366
165
+ ycycle = (2134 * aux1 + 2816 * aux2 + 2815) / 1028522 + aux1 + 1
166
+ end
167
+ year = ycycle + 2820 * cycle + 474
168
+ yday = jday - first_day_of_year(year) + 1
169
+ month = ((yday <= 186) ? yday / 31.0 : (yday - 6) / 30.0).ceil
170
+ day = (jday - first_day_of_month(year, month) + 1)
171
+ [year, month, day]
172
+ end
173
+
174
+ # Do +year+, +month+, and day-of-month +day+ make a valid Civil Date?
175
+ # Returns the corresponding Julian Day Number if they do, nil if they don't.
176
+ # Invalid values cause an ArgumentError to be raised.
177
+ def _valid_civil? year, month, day # :nodoc:
178
+ return unless year.is_a?(Fixnum) && month.is_a?(Fixnum) && day.is_a?(Fixnum)
179
+ return civil_to_jd(year, 12, 30) if leap?(year) && month == 12 && day == 30
180
+
181
+ if 1 <= month && month <= 12 && 1 <= day && day <= DAYS_IN_MONTH[month]
182
+ return civil_to_jd year, month, day
44
183
  else
45
- aux1 = cyear / 366
46
- aux2 = cyear % 366
47
- ycycle = (2134 * aux1 + 2816 * aux2 + 2815) / 1028522 + aux1 + 1
184
+ nil
48
185
  end
49
- year = ycycle + 2820 * cycle + 474
50
- year -= 1 if year <= 0
186
+ end
51
187
 
52
- yday = jday - Date.new(year, 1, 1).jd + 1
53
- month = (yday <= 186) ? (yday / 31.0).ceil : ((yday - 6) / 30.0).ceil
54
- day = (jday - Date.new(year, month, 1).jd + 1).floor
55
- Date.new year, month, day
188
+ # Do the +year+ and day-of-year +yday+ make a valid Ordinal Date?
189
+ # Returns the corresponding Julian Day Number if they do, or nil if they don't.
190
+ def _valid_ordinal? year, yday
191
+ ordinal_to_jd year, yday
56
192
  end
57
193
 
58
- alias :civil :new
194
+ def first_day_of_year year # :nodoc:
195
+ civil_to_jd year, 1, 1
196
+ end
59
197
 
60
- def ordinal year, ydays=1
61
- jd = Date.new(year, 1, 1).jd + ydays - 1
62
- Date.jd jd
198
+ def last_day_of_year year # :nodoc:
199
+ _valid_civil?(year, 12, 30) || civil_to_jd(year, 12, 29)
63
200
  end
64
201
 
202
+ def first_day_of_month year, month # :nodoc:
203
+ civil_to_jd year, month, 1
204
+ end
205
+
206
+ def last_day_of_month year, month # :nodoc:
207
+ _valid_civil?(year, month, 31) || _valid_civil?(year, month, 30) || _valid_civil?(year, month, 29)
208
+ end
209
+
210
+ # Convert an Ordinal Date to a Julian Day Number.
211
+ #
212
+ # +year+ and +yday+ are the year and day-of-year to convert.
213
+ #
214
+ # Returns the corresponding Julian Day Number.
215
+ def ordinal_to_jd year, yday # :nodoc:
216
+ first_day_of_year(year) + yday - 1
217
+ end
218
+
219
+ # Convert a Julian Day Number to an Ordinal Date.
220
+ #
221
+ # +jday+ is the Julian Day Number to convert.
222
+ #
223
+ # Returns the corresponding Ordinal Date as [year, day_of_year]
224
+ def jd_to_ordinal jday # :nodoc:
225
+ year = jd_to_civil(jd).first
226
+ yday = jday - first_day_of_year(year) + 1
227
+ [year, yday]
228
+ end
229
+
230
+ # Convert an Astronomical Julian Day Number to a (civil) Julian Day Number.
231
+ #
232
+ # +ajd+ is the Astronomical Julian Day Number to convert. +offset+ is the offset from
233
+ # UTC as a fraction of a day (defaults to 0).
234
+ #
235
+ # Returns the (civil) Julian Day Number as [day_number, fraction] where +fraction+ is
236
+ # always 1/2.
237
+ def ajd_to_jd ajd, offset=0 # :nodoc:
238
+ (ajd + offset + HALF_DAYS_IN_DAY).divmod(1)
239
+ end
240
+
241
+ # Convert a (civil) Julian Day Number to an Astronomical Julian Day Number.
242
+ #
243
+ # +jd+ is the Julian Day Number to convert, and +fraction+ is a fraction of day.
244
+ # +offset+ is the offset from UTC as a fraction of a day (defaults to 0).
245
+ #
246
+ # Returns the Astronomical Julian Day Number as a single numeric value.
247
+ def jd_to_ajd jd, fraction, offset=0 # :nodoc:
248
+ jd + fraction - offset - HALF_DAYS_IN_DAY
249
+ end
250
+
251
+ # Convert an +hour+, +minute+, +second+s period to a fractional day.
252
+ def time_to_day_fraction hour, minute, second # :nodoc:
253
+ Rational(hour * 3600 + minute * 60 + second, 86400)
254
+ end
255
+
256
+ # Convert an Astronomical Modified Julian Day Number to an Astronomical Julian Day Number.
257
+ def amjd_to_ajd(amjd) amjd + MJD_EPOCH_IN_AJD end # :nodoc:
258
+
259
+ # Convert an Astronomical Julian Day Number to an Astronomical Modified Julian Day Number.
260
+ def ajd_to_amjd(ajd) ajd - MJD_EPOCH_IN_AJD end # :nodoc:
261
+
262
+ # Convert a Modified Julian Day Number to a Julian Day Number.
263
+ def mjd_to_jd(mjd) mjd + MJD_EPOCH_IN_CJD end # :nodoc:
264
+
265
+ # Convert a Julian Day Number to a Modified Julian Day Number.
266
+ def jd_to_mjd(jd) jd - MJD_EPOCH_IN_CJD end # :nodoc:
267
+
268
+ # Convert a count of the number of days since the adoption of the Gregorian Calendar
269
+ # (in Italy) to a Julian Day Number.
270
+ def ld_to_jd(ld) ld + LD_EPOCH_IN_CJD end # :nodoc:
271
+
272
+ # Convert a Julian Day Number to the number of days since the adoption of the
273
+ # Gregorian Calendar (in Italy).
274
+ def jd_to_ld(jd) jd - LD_EPOCH_IN_CJD end # :nodoc:
275
+
276
+ # Convert a Julian Day Number to the day of the week.
277
+ #
278
+ # Sunday is day-of-week 0; Saturday is day-of-week 6.
279
+ def jd_to_wday(jd) (jd + 1) % 7 end # :nodoc:
280
+
281
+ # Is +jd+ a valid Julian Day Number?
282
+ #
283
+ # If it is, returns it. In fact, any value is treated as a valid Julian Day Number.
284
+ def _valid_jd?(jd) jd end # :nodoc:
285
+ end
286
+
287
+ extend shared_methods
288
+ include shared_methods
289
+
290
+ class << self
291
+
292
+ alias_method :new!, :new
293
+
294
+ def valid_jd? jd
295
+ !!_valid_jd?(jd)
296
+ end
297
+
298
+ def valid_ordinal? year, yday
299
+ !!_valid_ordinal?(year, yday)
300
+ end
301
+
302
+ def valid_civil? year, month, day
303
+ !!_valid_civil?(year, month, day)
304
+ end
305
+ alias :valid_date? :valid_civil?
306
+ alias :valid? :valid_civil?
307
+ alias :exist? :valid_civil?
308
+
309
+ # Create a new Date object from a Julian Day Number.
310
+ #
311
+ # +jday+ is the Julian Day Number; if not specified, it defaults to 0.
312
+ #
313
+ # examples:
314
+ # Parsi::Date.jd 2456229 # => #<Parsi::Date: 1391-08-07>
315
+ # Parsi::Date.jd 2456230 # => #<Parsi::Date: 1391-08-08>
316
+ # Parsi::Date.jd # => #<Parsi::Date: -5335-09-01>
317
+ #
318
+ def jd jday=0
319
+ jd = _valid_jd? jday
320
+ new! jd_to_ajd(jday, 0, 0), 0
321
+ end
322
+
323
+ # Create a new Date object from an Ordinal Date, specified by +year+ and day-of-year +yday+.
324
+ # +yday+ can be negative, in which it counts backwards from the end of the year.
325
+ #
326
+ # examples:
327
+ # Parsi::Date.ordinal 1390 # => #<Parsi::Date: 1390-01-01>
328
+ # Parsi::Date.ordinal 1391, 120 # => #<Parsi::Date: 1391-04-27>
329
+ # Parsi::Date.ordinal 1390, -1 # => #<Parsi::Date: 1389-12-29>
330
+ #
331
+ def ordinal year=0, yday=1
332
+ raise ArgumentError, 'invalid date' unless jd = _valid_ordinal?(year, yday)
333
+ new! jd_to_ajd(jd, 0, 0), 0
334
+ end
335
+
336
+ # Create a new Date object for the Civil Date specified by +year+, +month+, and
337
+ # day-of-month +day+.
338
+ def civil year=1, month=1, day=1
339
+ raise ArgumentError, 'invalid date' unless jd = _valid_civil?(year, month, day)
340
+ new! jd_to_ajd(jd, 0, 0), 0
341
+ end
342
+ alias_method :new, :civil
343
+
344
+ # Parses the given representation of date and time, and creates a date object.
345
+ #
346
+ # If the optional second argument is true and the detected year is in the range “00” to “99”,
347
+ # considers the year a 2-digit form and makes it full.
348
+ #
349
+ # For
65
350
  def parse string, comp=true
66
351
  # TODO: Add more parse options, for example parse '۴ام فروردین ۱۳۹۱'
67
352
  m = string.match /(?<year>\d+)(\/|-| )(?<month>\d+)(\/|-| )(?<day>\d+)/
@@ -74,77 +359,237 @@ module Parsi
74
359
  centry = Date.today.year / 100
75
360
  year = (m[:year].prepend centry.to_s).to_i
76
361
  end
77
- if Date.valid? year, month, day
78
- Date.new year, month, day
362
+ if jd = _valid_civil?(year, month, day)
363
+ new! jd_to_ajd(jd, 0, 0), 0
79
364
  else
80
365
  raise ArgumentError.new 'invalid date'
81
366
  end
82
367
  end
83
368
  end
84
369
 
370
+ # Create a new Date object representing today.
85
371
  def today
86
- Date.jd ::Date.today.jd
372
+ ::Date.today.to_parsi
87
373
  end
88
-
89
- def tomorrow ; today + 1 end
90
- def yesterday; today - 1 end
91
374
  end
92
375
 
93
- def initialize year=0, month=1, day=1
94
- raise ArgumentError.new 'invalid date' unless Date.valid? year, month, day
95
- @year, @month, @day = year, month, day
376
+ # Create a new Date object.
377
+ #
378
+ # +ajd+ is the Astronomical Julian Day Number.
379
+ # +offset+ is the offset from UTC as a fraction of a day.
380
+ def initialize ajd=0, offset=0
381
+ @ajd, @offset = ajd, offset
96
382
  end
97
383
 
98
- alias :mon :month
99
- alias :mday :day
384
+ # Get the date as an Astronomical Julian Day Number.
385
+ def ajd
386
+ @ajd
387
+ end
100
388
 
101
- def to_s sep='/'
102
- "%d%s%02d%s%02d" % [year, sep, month, sep, day]
389
+ def offset
390
+ @offset
103
391
  end
392
+ private :offset
104
393
 
105
- def inspect
106
- "#<#{self.class}: #{to_s('-')}>"
394
+ # Get the date as an Astronomical Modified Julian Day Number.
395
+ def amjd
396
+ @amjd ||= ajd_to_amjd ajd
107
397
  end
108
398
 
399
+ # Get the date as a Julian Day Number.
109
400
  def jd
110
- @jd ||= begin
111
- epbase = year - ((year >= 0) ? 474 : 473)
112
- epyear = 474 + epbase % 2820
401
+ @jd ||= ajd_to_jd(ajd, offset).first
402
+ end
113
403
 
114
- day + DAYS_TO_FIRST_OF_MONTH[month] +
115
- (epyear * 682 - 110) / 2816 +
116
- (epyear - 1) * 365 +
117
- (epbase / 2820 * 1029983) +
118
- (JALALI_EPOCH - 1) + 0.5
119
- end
404
+ # Get any fractional day part of the date.
405
+ def day_fraction
406
+ @day_fraction ||= ajd_to_jd(ajd, offset).last
407
+ end
408
+
409
+ # Get the date as a Modified Julian Day Number.
410
+ def mjd
411
+ @mjd ||= jd_to_mjd jd
412
+ end
413
+
414
+ # Get the date as the number of days since the Day of Calendar
415
+ # Reform (in Italy and the Catholic countries).
416
+ def ld
417
+ @ld ||= jd_to_ld jd
418
+ end
419
+
420
+ # Get the date as a Civil Date, [year, month, day_of_month]
421
+ def civil # :nodoc:
422
+ @civil ||= jd_to_civil jd
423
+ end
424
+
425
+ # Get the date as an Ordinal Date, [year, day_of_year]
426
+ def ordinal # :nodoc:
427
+ @ordinal ||= jd_to_ordinal jd
120
428
  end
121
429
 
122
- def mjd ; (jd - 2400001).floor end
123
- def ld ; (jd - 2299160).floor end
124
- def ajd ; gregorian.ajd end
125
- def amjd ; gregorian.amjd end
430
+ private :civil, :ordinal
126
431
 
432
+ # Get the year of this date.
433
+ def year() civil[0] end
434
+
435
+ # Get the month of this date.
436
+ #
437
+ # Farvardin is month 1.
438
+ def mon() civil[1] end
439
+ alias_method :month, :mon
440
+
441
+ # Get the day-of-the-month of this date.
442
+ def mday() civil[2] end
443
+ alias_method :day, :mday
444
+
445
+ # Get the day-of-the-year of this date.
446
+ #
447
+ # January 1 is day-of-the-year 1
448
+ def yday; ordinal[1] end
449
+
450
+ # Get the week day of this date. Sunday is day-of-week 0;
451
+ # Saturday is day-of-week 6.
127
452
  def wday
128
- (gregorian.wday + 1) % 7
453
+ @wday ||= jd_to_wday jd
454
+ end
455
+
456
+ ::Date::DAYNAMES.each_with_index do |n, i|
457
+ define_method(n.downcase + '?'){ wday == i }
458
+ end
459
+
460
+ # Return a new Date object that is +n+ days later than the current one.
461
+ #
462
+ # +n+ may be a negative value, in which case the new Date is earlier
463
+ # than the current one; however, #-() might be more intuitive.
464
+ #
465
+ # If +n+ is not a Numeric, a TypeError will be thrown. In particular,
466
+ # two Dates cannot be added to each other.
467
+ def + n
468
+ case n
469
+ when Numeric
470
+ return self.class.new!(ajd + n, offset)
471
+ end
472
+ raise TypeError, 'expected numeric'
129
473
  end
130
474
 
131
- def cwday
132
- wday + 1
475
+ # If +x+ is a Numeric value, create a new Date object that is +x+ days
476
+ # earlier than the current one.
477
+ #
478
+ # If +x+ is a Date, return the number of days between the two dates; or,
479
+ # more precisely, how many days later the current date is than +x+.
480
+ #
481
+ # If +x+ is neither Numeric nor a Date, a TypeError is raised.
482
+ def - x
483
+ case x
484
+ when Numeric
485
+ return self.class.new!(ajd - x, offset)
486
+ when Date
487
+ return ajd - x.ajd
488
+ end
489
+ raise TypeError, 'expected numeric or date'
133
490
  end
134
491
 
135
- def yday
136
- (jd - first_of_year.jd + 1).to_i
492
+ # Compare this date with another date.
493
+ #
494
+ # +other+ can also be a Numeric value, in which case it is interpreted as an
495
+ # Astronomical Julian Day Number.
496
+ #
497
+ # Comparison is by Astronomical Julian Day Number, including fractional days.
498
+ # This means that both the time and the timezone offset are taken into account
499
+ # when comparing two DateTime instances. When comparing a DateTime instance
500
+ # with a Date instance, the time of the latter will be considered as falling
501
+ # on midnight UTC.
502
+ def <=> other
503
+ case other
504
+ when Numeric
505
+ return ajd <=> other
506
+ when Date
507
+ return ajd <=> other.ajd
508
+ when ::Date
509
+ return ajd <=> other.ajd
510
+ else
511
+ begin
512
+ left, right = other.coerce(self)
513
+ return left <=> right
514
+ rescue NoMethodError
515
+ end
516
+ end
517
+ nil
137
518
  end
138
519
 
139
- def leap?
140
- Date.leap? year
520
+ # The relationship operator for Date.
521
+ #
522
+ # Compares dates by Julian Day Number. When comparing two DateTime instances,
523
+ # or a DateTime with a Date, the instances will be regarded as equivalent if
524
+ # they fall on the same date in local time.
525
+ def === (other)
526
+ case other
527
+ when Numeric
528
+ return jd == other
529
+ when Date; return jd == other.jd
530
+ else
531
+ begin
532
+ l, r = other.coerce(self)
533
+ return l === r
534
+ rescue NoMethodError
535
+ end
536
+ end
537
+ false
141
538
  end
142
539
 
540
+ def next_day(n=1) self + n end
541
+ def prev_day(n=1) self - n end
542
+
543
+ # Return a new Date one day after this one.
544
+ def next() next_day end
545
+ alias_method :succ, :next
546
+
547
+ # Return a new Date object that is +n+ months later than the current one.
548
+ #
549
+ # If the day-of-the-month of the current Date is greater than the last day of
550
+ # the target month, the day-of-the-month of the returned Date will be the last
551
+ # day of the target month.
552
+ def >> n
553
+ y, m = (year * 12 + (mon - 1) + n).divmod(12)
554
+ m, = (m + 1) .divmod(1)
555
+ d = mday
556
+ until jd2 = _valid_civil?(y, m, d)
557
+ d -= 1
558
+ raise ArgumentError, 'invalid date' unless d > 0
559
+ end
560
+ self + (jd2 - jd)
561
+ end
562
+
563
+ # Return a new Date object that is +n+ months earlier than the current one.
564
+ #
565
+ # If the day-of-the-month of the current Date is greater than the last day of
566
+ # the target month, the day-of-the-month of the returned Date will be the last
567
+ # day of the target month.
568
+ def << (n) self >> -n end
569
+
570
+ def next_month(n=1) self >> n end
571
+ def prev_month(n=1) self << n end
572
+
573
+ def next_year(n=1) self >> n * 12 end
574
+ def prev_year(n=1) self << n * 12 end
575
+
143
576
  def to_gregorian
144
577
  ::Date.jd jd
145
578
  end
146
579
  alias :gregorian :to_gregorian
147
580
 
581
+ def to_time
582
+ gregorian.to_time
583
+ end
584
+
585
+ def to_date
586
+ self
587
+ end
588
+
589
+ def to_datetime
590
+ DateTime.new! jd_to_ajd(jd, 0, 0), 0
591
+ end
592
+
148
593
  def strftime format='%Y/%m/%d'
149
594
  format.
150
595
  gsub('%%', 'PERCENT_SUBSTITUTION_MARKER').
@@ -161,125 +606,90 @@ module Parsi
161
606
  gsub('%_m', '% 2d' % month).
162
607
  gsub('%-m', month.to_s).
163
608
  gsub('%^B', '%B').
164
- gsub('%B', Parsi::MONTHNAMES[month]).
609
+ gsub('%B', MONTHNAMES[month]).
165
610
  gsub('%h', '%b').
166
- gsub('%b', Parsi::ABBR_MONTHNAMES[month]).
167
- gsub('%^b', Parsi::ABBR_MONTHNAMES[month].capitalize).
611
+ gsub('%b', ABBR_MONTHNAMES[month]).
612
+ gsub('%^b', ABBR_MONTHNAMES[month].capitalize).
168
613
  gsub('%d', '%02d' % day).
169
614
  gsub('%e', '% 2d' % day).
170
615
  gsub('%-d', day.to_s).
171
616
  gsub('%j', '%03d' % yday.to_s).
172
- gsub('%A', Parsi::DAYNAMES[wday]).
173
- gsub('%a', Parsi::ABBR_DAYNAMES[wday]).
174
- gsub('%u', cwday.to_s).
617
+ gsub('%A', DAYNAMES[wday]).
618
+ gsub('%a', ABBR_DAYNAMES[wday]).
175
619
  gsub('%w', wday.to_s).
176
620
  gsub('%n', "\n").
177
621
  gsub('%t', "\t").
178
622
  gsub('PERCENT_SUBSTITUTION_MARKER', '%')
179
623
  end
180
624
 
181
- def + days
182
- raise TypeError.new 'expected numeric' unless days.is_a? Numeric
183
- Date.jd jd + days
184
- end
185
-
186
- def - days
187
- self + -days
188
- end
189
625
 
190
- def >> monthes
191
- raise TypeError.new 'expected numeric' unless monthes.is_a? Numeric
192
- monthes = year * 12 + month + monthes
193
- y = monthes / 12
194
- m = monthes % 12
195
- y -= 1 and m = 12 if m == 0
196
- d = day
197
- d -= 1 until Date.valid? y, m, d
198
- Date.new y, m, d
199
- end
626
+ # Step the current date forward +step+ days at a time (or backward, if +step+ is
627
+ # negative) until we reach +limit+ (inclusive), yielding the resultant date at each step.
628
+ def step limit, step=1
629
+ return to_enum(:step, limit, step) unless block_given?
200
630
 
201
- def << monthes
202
- self >> -monthes
203
- end
204
-
205
- def <=> other
206
- if other.respond_to? :jd
207
- jd <=> other.jd
208
- elsif other.is_a? Numeric
209
- jd <=> other
210
- else
211
- raise ArgumentError.new "comparison of #{self.class} with #{other.class} failed"
212
- end
213
- end
214
-
215
- def step limit, by=1
216
631
  date = self
217
- comp_op = %w(== <= >=)[by <=> 0]
632
+ comp_op = %w(== <= >=)[step <=> 0]
218
633
  while date.send comp_op, limit
219
634
  yield date
220
- date += by
635
+ date += step
221
636
  end
222
637
  self
223
638
  end
224
639
 
225
- def upto max, &block
640
+ # Step forward one day at a time until we reach +max+
641
+ # (inclusive), yielding each date as we go.
642
+ def upto max, &block # :yield: date
226
643
  step max, 1, &block
227
644
  end
228
645
 
229
- def downto min, &block
646
+ # Step backward one day at a time until we reach +min+
647
+ # (inclusive), yielding each date as we go.
648
+ def downto min, &block # :yield: date
230
649
  step min, -1, &block
231
650
  end
232
651
 
233
- def next
234
- self + 1
235
- end
236
- alias :succ :next
652
+ # Is this Date equal to +other+?
653
+ #
654
+ # +other+ must both be a Date object, and represent the same date.
655
+ def eql? (other) self.class === other && self == other end
237
656
 
238
- def next_day n=1
239
- self + n
240
- end
241
-
242
- def next_month n=1
243
- self >> n
244
- end
245
-
246
- def next_year n=1
247
- self >> (n * 12)
248
- end
657
+ # Calculate a hash value for this date.
658
+ def hash() ajd.hash end
249
659
 
250
- def prev_day n=1
251
- self - n
660
+ # Return internal object state as a programmer-readable string.
661
+ def inspect
662
+ format('#<%s: %s (%s,%s)>', self.class, to_s, ajd, offset)
252
663
  end
253
664
 
254
- def prev_month n=1
255
- self << n
256
- end
665
+ # Return the date as a human-readable string.
666
+ #
667
+ # The format used is YYYY-MM-DD.
668
+ def to_s() format('%.4d-%02d-%02d', year, mon, mday) end
257
669
 
258
- def prev_year n=1
259
- self << (n * 12)
260
- end
670
+ # Dump to Marshal format.
671
+ def marshal_dump() [@ajd, @offset] end
261
672
 
262
- def shanbe? ; wday == 0 end
263
- def yekshanbe? ; wday == 1 end
264
- def doshanbe? ; wday == 2 end
265
- def seshanbe? ; wday == 3 end
266
- def chaharshanbe? ; wday == 4 end
267
- def panjshanbe? ; wday == 5 end
268
- def jomee? ; wday == 6 end
269
-
270
- private
271
- def first_of_year
272
- @first_of_year ||= Date.ordinal year
273
- end
673
+ # Load from Marshal format.
674
+ def marshal_load(a) @ajd, @of, = a end
274
675
  end
275
676
  end
276
677
 
277
678
  class Date
679
+ class << self
680
+ # Creates a Date object corresponding to the specified Jalali Date +year+, +month+ and
681
+ # +day+.
682
+ def parsi year=0, month=1, day=1
683
+ Parsi::Date.civil year, month, day
684
+ end
685
+ alias :jalali :parsi
686
+ end
687
+
688
+ # Returns a Parsi::Date object representing same date in Jalali calendar
278
689
  def to_parsi
279
- Parsi::Date.jd jd
690
+ Parsi::Date.new! ajd, offset
280
691
  end
281
- alias :to_persian :to_parsi
282
- alias :to_jalali :to_parsi
283
- alias :parsi :to_parsi
284
692
  alias :jalali :to_parsi
693
+ alias :to_jalali :to_parsi
694
+ alias :to_persian :to_parsi
285
695
  end
@@ -1,101 +1,202 @@
1
+ # encoding: utf-8
2
+ #
3
+ # parsi-date.rb
4
+ #
5
+ # Author: Hassan Zamani 2012
6
+
1
7
  module Parsi
8
+ # Class representing a date and time.
9
+ #
10
+ # See the documentation to the file parsi-date.rb for an overview.
11
+ #
12
+ # DateTime objects are immutable once created.
2
13
  class DateTime < Date
3
- include Comparable
4
14
 
5
- attr_reader :hour, :minute, :second, :offset
15
+ shared_methods = Module.new do
16
+
17
+ def zone_to_offset zone
18
+ m = zone.match /(?<sign>\+|-)?(?<hour>\d{1,2}):?(?<minute>\d{,2})/
19
+ return 0 if m.nil?
20
+ offset = Rational(m[:hour].to_i, 24) + Rational(m[:minute].to_i, 1440)
21
+ m[:sign] == '-' ? -offset : offset
22
+ end
23
+ private :zone_to_offset
24
+
25
+ # Convert a fractional day +fraction+ to [hours, minutes, seconds, fraction_of_a_second]
26
+ def day_fraction_to_time fraction # :nodoc:
27
+ seconds, fraction = fraction.divmod SECONDS_IN_DAY
28
+ hours, seconds = seconds.divmod 3600
29
+ minutes, seconds = seconds.divmod 60
30
+ [hours, minutes, seconds, fraction * 86400]
31
+ end
32
+
33
+ # Do +hour+, +minute+, and +second+ constitute a valid time?
34
+ #
35
+ # If they do, returns their value as a fraction of a day. If not, returns nil.
36
+ #
37
+ # The 24-hour clock is used. Negative values of +hour+, +minute+, and +second+ are treating
38
+ # as counting backwards from the end of the next larger unit (e.g. a +minute+ of -2 is
39
+ # treated as 58). No wraparound is performed.
40
+ def _valid_time? hour, minute, second
41
+ hour += 24 if hour < 0
42
+ minute += 60 if minute < 0
43
+ seconds += 60 if second < 0
44
+ return unless ((0...24) === hour && (0...60) === minute && (0...60) === second) ||
45
+ (24 == hour && 0 == minute && 0 == second)
46
+ time_to_day_fraction hour, minute, second
47
+ end
48
+ end
49
+
50
+ extend shared_methods
51
+ include shared_methods
6
52
 
7
53
  class << self
8
- def valid_time? hour=0, minute=0, second=0, offset=0
9
- if 0 <= hour && hour <= 23 &&
10
- 0 <= minute && minute <= 59 &&
11
- 0 <= second && second <= 59
12
- #TODO: Add offset validation
13
- true
14
- else
15
- false
16
- end
54
+
55
+ def valid_time? hour, min, sec
56
+ !!_valid_time?(hour, min, sec)
57
+ end
58
+ private :valid_time?
59
+
60
+ # Create a new DateTime object corresponding to the specified Julian Day Number +jd+
61
+ # and +hour+, +minute+, +second+.
62
+ #
63
+ # The 24-hour clock is used. If an invalid time portion is specified, an ArgumentError
64
+ # is raised.
65
+ def jd jd=0, hour=0, minute=0, second=0, zone="00:00"
66
+ fraction = _valid_time? hour, minute, second
67
+ raise ArgumentError, 'invalid time' if fraction.nil?
68
+
69
+ offset = zone_to_offset zone
70
+
71
+ new! jd_to_ajd(jd, fraction, offset), offset
72
+ end
73
+
74
+ # Create a new DateTime object corresponding to the specified Astronomical Julian Day
75
+ # Number +ajd+ in given +offset+.
76
+ def ajd ajd=0, zone="00:00"
77
+ new! ajd, zone_to_offset(zone)
17
78
  end
18
79
 
19
- def jd jd=0
20
- date = Date.jd jd.to_i
21
- h = (jd - jd.to_i) * 24
22
- m = (h - h.to_i) * 60
23
- s = (m - m.to_i) * 60
24
- DateTime.new date.year, date.month, date.day, h.to_i, m.to_i, s.to_i
80
+ # Create a new DateTime object corresponding to the specified Ordinal Date and +hour+,
81
+ # +minute+, +second+.
82
+ #
83
+ # The 24-hour clock is used. If an invalid time portion is specified, an ArgumentError
84
+ # is raised.
85
+ def ordinal year=0, yday=1, hour=0, minute=0, second=0, zone="00:00"
86
+ jd = _valid_ordinal? year, yday
87
+ fraction = _valid_time?(hour, minute, second)
88
+ raise ArgumentError, 'invalid date' if jd.nil? or fraction.nil?
89
+
90
+ offset = zone_to_offset zone
91
+
92
+ new! jd_to_ajd(jd, fr, offset), offset
25
93
  end
26
94
 
95
+ # Create a new DateTime object corresponding to the specified Civil Date and +hour+,
96
+ # +minute+, +second+.
97
+ #
98
+ # The 24-hour clock is used. If an invalid time portion is specified, an ArgumentError is
99
+ # raised.
100
+ #
101
+ # +offset+ is the offset from UTC as a fraction of a day (defaults to 0).
102
+ def civil year=0, month=1, day=1, hour=0, minute=0, second=0, zone="00:00"
103
+ jd = _valid_civil? year, month, day
104
+ fraction = _valid_time? hour, minute, second
105
+ raise ArgumentError, 'invalid date' if jd.nil? or fraction.nil?
106
+
107
+ offset = zone_to_offset zone
108
+
109
+ new! jd_to_ajd(jd, fraction, offset), offset
110
+ end
111
+ alias_method :new, :civil
112
+
113
+ # Create a new DateTime object representing the current time.
27
114
  def now
28
- now = ::DateTime.now
29
- today = now.to_date.to_parsi
30
- DateTime.new today.year, today.month, today.day, now.hour, now.minute, now.second, now.offset
115
+ ::DateTime.now.to_parsi
31
116
  end
32
- end
33
117
 
34
- def initialize year=0, month=1, day=1, hour=0, minute=0, second=0, offset=0
35
- raise ArgumentError.new 'invalid time' unless
36
- DateTime.valid_time? hour, minute, second
118
+ private :today
119
+ end
37
120
 
38
- super year, month, day
39
- @hour, @minute, @second, @offset = hour, minute, second, offset
121
+ # Get the time of this date as [hours, minutes, seconds,
122
+ # fraction_of_a_second]
123
+ def time # :nodoc:
124
+ @time ||= day_fraction_to_time day_fraction
40
125
  end
126
+ private :time
127
+
128
+ # Get the hour of this date.
129
+ def hour() time[0] end
130
+
131
+ # Get the minute of this date.
132
+ def min() time[1] end
133
+
134
+ # Get the second of this date.
135
+ def sec() time[2] end
136
+
137
+ # Get the fraction-of-a-second of this date.
138
+ def sec_fraction() time[3] end
41
139
 
42
- def zone sep=':'
43
- f = offset * 24.0
44
- "%s%02d%s%02d" % [(f >= 0 ? '+' : '-'), f.floor, sep, (f % 1) * 60]
140
+ alias_method :minute, :min
141
+ alias_method :second, :sec
142
+ alias_method :second_fraction, :sec_fraction
143
+
144
+ def to_s
145
+ format('%.4d-%02d-%02dT%02d:%02d:%02d%s', year, mon, mday, hour, min, sec, zone)
45
146
  end
46
147
 
47
- def to_s sep='/'
48
- "%sT%02d:%02d:%02d%s" % [super(sep), hour, minute, second, zone]
148
+ public :offset
149
+
150
+ def new_offset zone
151
+ offset = zone_to_offset zone
152
+ self.class.new! ajd, offset
49
153
  end
50
154
 
51
- def inspect
52
- "#<#{self.class}: #{to_s('-')}>"
155
+ def zone
156
+ o = offset * 24
157
+ format("%s%02d:%02d", (o >= 0 ? '+' : '-'), o.to_i, (o - o.to_i) * 60)
53
158
  end
54
159
 
55
160
  def strftime format='%Y/%m/%d %H:%M:%S'
56
161
  gregorian.strftime super format
57
162
  end
58
163
 
59
- def to_date
60
- Date.new year, month, day
61
- end
62
-
63
- def to_gregorian
164
+ def gregorian
64
165
  @gregorian ||= begin
65
- ::DateTime.new super.year, super.month, super.day, hour, minute, second, zone
166
+ ::DateTime.jd jd, hour, minute, second, zone
66
167
  end
67
168
  end
68
- alias :gregorian :to_gregorian
169
+ alias :to_gregorian :gregorian
69
170
 
70
- def + days
71
- date = super
72
- DateTime.new date.year, date.month, date.day, hour, minute, second, offset
171
+ def to_time
172
+ gregorian.to_time
73
173
  end
74
174
 
75
- def >> monthes
76
- date = super
77
- DateTime.new date.year, date.month, date.day, hour, minute, second, offset
175
+ def to_date
176
+ Date.new! jd_to_ajd(jd, 0, 0), 0
78
177
  end
79
178
 
80
- def <=> other
81
- if other.is_a? Date
82
- to_gregorian <=> other.to_gregorian
83
- elsif other.is_a? ::Date
84
- to_gregorian <=> other
85
- else
86
- raise ArgumentError.new "comparison of #{self.class} with #{other.class} failed"
87
- end
179
+ def to_datetime
180
+ self
88
181
  end
89
182
  end
90
183
  end
91
184
 
92
185
  class DateTime
186
+ class << self
187
+ # Creates a DateTime object corresponding to the specified Jalali Date (+year+, +month+ and
188
+ # +day+) and time (+hour+, +minute+ and +second+) in given +zone+.
189
+ def parsi year=0, month=1, day=1, hour=0, minute=0, second=0, zone="00:00"
190
+ Parsi::DateTime.civil year, month, day, hour, minute, second, zone
191
+ end
192
+ alias :jalali :parsi
193
+ end
194
+
195
+ # Returns a Parsi::DateTime object representing same date in Jalali calendar
93
196
  def to_parsi
94
- date = super
95
- Parsi::DateTime.new date.year, date.month, date.day, hour, minute, second, offset
197
+ Parsi::DateTime.new! ajd, offset
96
198
  end
97
- alias :to_persian :to_parsi
98
- alias :to_jalali :to_parsi
99
- alias :parsi :to_parsi
100
199
  alias :jalali :to_parsi
200
+ alias :to_jalali :to_parsi
201
+ alias :to_persian :to_parsi
101
202
  end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Parsi
2
2
  class Date
3
- VERSION = "0.1"
3
+ VERSION = "0.2"
4
4
  end
5
5
  end
data/parsi-date.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.email = 'hsn.zamani@gmail.com'
12
12
  s.homepage = 'http://github.com/hzamani/parsi-date'
13
13
  s.summary = 'Solar Hijri (Jalali, Persian, Parsi) date library for Ruby'
14
- s.description = "A date library for Ruby based on Iran's standard calandar, whitch provides much of Ruby's built-in date class"
14
+ s.description = "A Solar Hijri (Jalali) date library for Ruby, whitch provides much of Ruby's built-in date class"
15
15
 
16
16
 
17
17
  # Load Paths...
@@ -53,14 +53,7 @@ end
53
53
 
54
54
  describe "Parsi::Date#wday" do
55
55
  it "determines the week day" do
56
- Parsi::Date.civil(1391, 1, 17).wday.should == 5
57
- Parsi::Date.civil(1391, 8, 6).wday.should == 0
56
+ Parsi::Date.civil(1391, 1, 17).wday.should == 4
57
+ Parsi::Date.civil(1391, 8, 6).wday.should == 6
58
58
  end
59
59
  end
60
-
61
- describe "Parsi::Date#cwday" do
62
- it "determines the commercial week day" do
63
- Parsi::Date.civil(1391, 1, 17).cwday.should == 6
64
- Parsi::Date.civil(1391, 8, 6).cwday.should == 1
65
- end
66
- end
@@ -18,14 +18,14 @@ describe "Parsi::Date#<=>" do
18
18
  end
19
19
 
20
20
  it "returns 0 when self is equal to a Numeric" do
21
- (Parsi::Date.civil(1391, 4, 6) <=> 2456105).should == 0
21
+ (Parsi::Date.civil(1391, 4, 6) <=> Rational(4912209,2)).should == 0
22
22
  end
23
23
 
24
24
  it "returns -1 when self is less than a Numeric" do
25
- (Parsi::Date.civil(1391, 4, 6) <=> 2456106).should == -1
25
+ (Parsi::Date.civil(1391, 4, 6) <=> Rational(4912210,2)).should == -1
26
26
  end
27
27
 
28
28
  it "returns 1 when self is greater than a Numeric" do
29
- (Parsi::Date.civil(1391, 4, 6) <=> 2456104).should == 1
29
+ (Parsi::Date.civil(1391, 4, 6) <=> Rational(4912208,2)).should == 1
30
30
  end
31
31
  end
@@ -3,23 +3,23 @@ require File.expand_path('../../spec_helper', __FILE__)
3
3
 
4
4
  describe "Date constants" do
5
5
  it "defines MONTHNAMES" do
6
- Parsi::MONTHNAMES.should == [nil] + %w(فروردین اردیبهشت خرداد تیر مرداد شهریور مهر آبان آذر دی بهمن اسفند)
6
+ Parsi::Date::MONTHNAMES.should == [nil] + %w(فروردین اردیبهشت خرداد تیر مرداد شهریور مهر آبان آذر دی بهمن اسفند)
7
7
  end
8
8
 
9
9
  it "defines ABBR_MONTHNAMES" do
10
- Parsi::ABBR_MONTHNAMES.should == [nil] + %w(Far Ord Kho Tir Mor Sha Meh Abn Azr Dey Bah Esf)
10
+ Parsi::Date::ABBR_MONTHNAMES.should == [nil] + %w(Far Ord Kho Tir Mor Sha Meh Abn Azr Dey Bah Esf)
11
11
  end
12
12
 
13
13
  it "defines DAYNAMES" do
14
- Parsi::DAYNAMES.should == %w(شنده یک‌شنده دوشنده سه‌شنده چهارشنده چنج‌شنده جمعه)
14
+ Parsi::Date::DAYNAMES.should == %w(یک‌شنده دوشنده سه‌شنده چهارشنده چنج‌شنده جمعه شنده )
15
15
  end
16
16
 
17
17
  it "defines ABBR_DAYNAMES" do
18
- Parsi::ABBR_DAYNAMES.should == %w(ش ۱ش ۲ش ۳ش ۴ش ۵ش ج)
18
+ Parsi::Date::ABBR_DAYNAMES.should == %w(۱ش ۲ش ۳ش ۴ش ۵ش ج ش)
19
19
  end
20
20
 
21
21
  it "freezes MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYSNAMES" do
22
- [Parsi::MONTHNAMES, Parsi::DAYNAMES, Parsi::ABBR_MONTHNAMES, Parsi::ABBR_DAYNAMES].each do |ary|
22
+ [Parsi::Date::MONTHNAMES, Parsi::Date::DAYNAMES, Parsi::Date::ABBR_MONTHNAMES, Parsi::Date::ABBR_DAYNAMES].each do |ary|
23
23
  ary.should be_frozen
24
24
  end
25
25
  end
@@ -21,9 +21,9 @@ describe Parsi::Date do
21
21
  expect { Parsi::Date.civil 1391, '1', 9 }.to raise_error(ArgumentError, 'invalid date')
22
22
  end
23
23
 
24
- it "constructs a Date for 0/1/1 by default" do
24
+ it "constructs a Date for 1/1/1 by default" do
25
25
  date = Parsi::Date.civil
26
- date.year.should == 0
26
+ date.year.should == 1
27
27
  date.month.should == 1
28
28
  date.day.should == 1
29
29
  end
@@ -57,12 +57,4 @@ describe Parsi::Date do
57
57
  expect { date = Parsi::Date.parse '12-30-1390' }.to raise_error(ArgumentError)
58
58
  end
59
59
  end
60
-
61
- context "#today" do
62
- it "initialize a date object with today's attributes" do
63
- Date.stub(:today) { Date.new 2012, 10, 26 }
64
- date = Parsi::Date.today
65
- [date.year, date.month, date.day].should == [1391, 8, 5]
66
- end
67
- end
68
60
  end
@@ -7,11 +7,11 @@ describe "Parsi::Date.jd" do
7
7
  end
8
8
 
9
9
  it "returns a Date object representing Julian day 0 if no arguments passed"do
10
- Parsi::Date.jd.should == Parsi::Date.civil(-5335, 9, 1)
10
+ Parsi::Date.jd.should == Parsi::Date.civil(-5334, 9, 1)
11
11
  end
12
12
 
13
13
  it "constructs a Date object if passed a negative number" do
14
- Parsi::Date.jd(-1).should == Parsi::Date.civil(-5335, 8, 30)
14
+ Parsi::Date.jd(-1).should == Parsi::Date.civil(-5334, 8, 30)
15
15
  end
16
16
  end
17
17
 
@@ -5,7 +5,6 @@ describe "Parsi::Date#strftime" do
5
5
 
6
6
  it "should be able to print the date" do
7
7
  Parsi::Date.civil(1390, 4, 6).strftime.should == "1390/04/06"
8
- Parsi::Date.civil(1390, 4, 6).strftime.should == Parsi::Date.civil(1390, 4, 6).to_s
9
8
  end
10
9
 
11
10
  it "should be able to print the full day name" do
@@ -50,14 +49,9 @@ describe "Parsi::Date#strftime" do
50
49
  Parsi::Date.civil(1390, 4, 6).strftime("%t").should == "\t"
51
50
  end
52
51
 
53
- it "should be able to show the commercial week day" do
54
- Parsi::Date.civil(1390, 4, 10).strftime("%u").should == "7"
55
- Parsi::Date.civil(1390, 4, 11).strftime("%u").should == "1"
56
- end
57
-
58
52
  it "should be able to show the week day" do
59
- Parsi::Date.civil(1390, 4, 10).strftime("%w").should == "6"
60
- Parsi::Date.civil(1390, 4, 11).strftime("%w").should == "0"
53
+ Parsi::Date.civil(1390, 4, 11).strftime("%w").should == "6"
54
+ Parsi::Date.civil(1390, 4, 12).strftime("%w").should == "0"
61
55
  end
62
56
 
63
57
  it "should be able to show the year in YYYY format" do
data/spec/spec_helper.rb CHANGED
@@ -9,5 +9,5 @@ RSpec.configure do |config|
9
9
  # order dependency and want to debug it, you can fix the order by providing
10
10
  # the seed, which is printed after each run.
11
11
  # --seed 1234
12
- config.order = 'random'
12
+ # config.order = 'random'
13
13
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parsi-date
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-27 00:00:00.000000000 Z
12
+ date: 2012-10-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -59,8 +59,8 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
- description: A date library for Ruby based on Iran's standard calandar, whitch provides
63
- much of Ruby's built-in date class
62
+ description: A Solar Hijri (Jalali) date library for Ruby, whitch provides much of
63
+ Ruby's built-in date class
64
64
  email: hsn.zamani@gmail.com
65
65
  executables: []
66
66
  extensions: []