parsi-date 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []