JalaliDate 0.1.0
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/CHANGELOG +8 -0
- data/MIT-LICENSE +21 -0
- data/README +1 -0
- data/TODO +1 -0
- data/lib/jalali_date.rb +273 -0
- data/test/test.rb +8 -0
- metadata +61 -0
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
== License
|
2
|
+
Copyright (c) 2008 Aziz Ashofte Bargi
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
=== README
|
data/lib/jalali_date.rb
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
# :title:JalaliDate
|
2
|
+
#
|
3
|
+
class JalaliDate
|
4
|
+
#:stopdoc:
|
5
|
+
$KCODE = 'u'
|
6
|
+
require 'jcode'
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
GDaysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
10
|
+
JDaysInMonth = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29]
|
11
|
+
PERSIAN_MONTH_NAMES = [nil, "فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور","مهر","آبان","آذر","دی","بهمن","اسفند"]
|
12
|
+
PERSIAN_WEEKDAY_NAMES = ["یکشنبه","دوشنبه","سهشنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"]
|
13
|
+
PERSIAN_ABBR_WEEKDAY_NAMES = ["۱ش","۲ش","۳ش","۴ش","۵ش","ج","ش"]
|
14
|
+
PERSIAN_MERIDIAN_INDICATOR = ["قبل از ظهر","بعد از ظهر"]
|
15
|
+
PERSIAN_ABBR_MERIDIAN_INDICATOR = ["ق.ظ","ب.ظ"]
|
16
|
+
#:startdoc:
|
17
|
+
|
18
|
+
attr_accessor :year,:month,:day
|
19
|
+
attr_reader :g_year, :g_month, :g_day
|
20
|
+
|
21
|
+
|
22
|
+
def initialize(year,month,day)
|
23
|
+
raise ArgumentError, "invalid arguments or invalid Jalali date" unless self.class.valid_date?(year,month,day)
|
24
|
+
@year = year
|
25
|
+
@month = month
|
26
|
+
@day = day
|
27
|
+
@g_year, @g_month, @g_day = self.class.jalali_to_gregorian(year,month,day)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return a new JalaliDate object but accept a Ruby Date object as the parameter,
|
31
|
+
# rather than 3 values for jalali year, jalali month and jalali date (which is what JalaliDate#new does).
|
32
|
+
def self.build(date)
|
33
|
+
g_to_j(date)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return a JalaliDate object representing today's date in calendar
|
37
|
+
def self.today(date = Time.now)
|
38
|
+
g_to_j(date)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return a JalaliDate object representing yesterday's date in calendar
|
42
|
+
def self.yesterday
|
43
|
+
tod = Time.now
|
44
|
+
today(Date.new(tod.year,tod.month,tod.day) - 1)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return a JalaliDate object representing tomorrow's date in calendar
|
48
|
+
def self.tomorrow
|
49
|
+
tod = Time.now
|
50
|
+
today(Date.new(tod.year,tod.month,tod.day) + 1)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Accpets a four digit number as the jalaliyear and returns true if that particular year
|
54
|
+
# is a leap year in jalali calendar otherwise it returns false.
|
55
|
+
def self.leap?(y)
|
56
|
+
[6,22,17,13,9,5,1,30].include?(y%33) ? true : false
|
57
|
+
end
|
58
|
+
|
59
|
+
# Accpets three numbers for year (4 digit), month and day in jalali calendar and checks if it's a
|
60
|
+
# valid date according to jalali calendar or not.
|
61
|
+
def self.valid_date?(y,m,d)
|
62
|
+
(y.class == Fixnum && y > 0 &&
|
63
|
+
m.class == Fixnum && (1..12).include?(m) &&
|
64
|
+
d.class == Fixnum && (((1..JDaysInMonth[m-1]).include?(d)) || (d == 30 && m == 12 && leap?(y) ))
|
65
|
+
) ? true : false
|
66
|
+
end
|
67
|
+
|
68
|
+
# Converts a JalaiDate object to Ruby Date object
|
69
|
+
def to_gregorian; Date.new(@g_year,@g_month,@g_day); end;
|
70
|
+
alias :to_g :to_gregorian
|
71
|
+
|
72
|
+
# Returns a string represtation of the JalaliDate object in format like this: y/m/d
|
73
|
+
def to_s() [@year,@month,@day].join("/") end
|
74
|
+
|
75
|
+
# Returns a hash in a format like this: {:year => @year, :month => @month, :day => @day}
|
76
|
+
def to_hash() {:year => @year, :month => @month, :day => @day} end
|
77
|
+
|
78
|
+
# Returns an array in a format like this: [y,m,d]
|
79
|
+
def to_a() [@year,@month,@day] end
|
80
|
+
|
81
|
+
# Adds n days to the current JalaliDate object
|
82
|
+
def +(days) self.class.g_to_j( to_g + days ) end
|
83
|
+
|
84
|
+
# Subtracts n days from the current JalaliDate object
|
85
|
+
def -(days) self.class.g_to_j( to_g - days ) end
|
86
|
+
|
87
|
+
# Compares two JalaliDate objects. acts like Date#<=>
|
88
|
+
def <=>(other) to_g <=> other.to_g end
|
89
|
+
|
90
|
+
# Move JalaliDate object forward by n months
|
91
|
+
def >>(months)
|
92
|
+
y, m = (@year * 12 + (@month - 1) + months).divmod(12)
|
93
|
+
m, = (m + 1) .divmod(1)
|
94
|
+
d = @day
|
95
|
+
d -= 1 until self.class.valid_date?(y, m, d)
|
96
|
+
self.class.new(y,m,d)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Move JalaliDate object backward by n months
|
100
|
+
def <<(months) self >> -months end
|
101
|
+
|
102
|
+
# Return the next day for the current JalaliDate object
|
103
|
+
def next(n=1) self + n end
|
104
|
+
|
105
|
+
# Return the previous day for the current JalaliDate object
|
106
|
+
def previous(n=-1) self - n end
|
107
|
+
alias :succ :next
|
108
|
+
|
109
|
+
# Step the current date forward step days at a time (or backward, if step is negative) until we reach
|
110
|
+
# limit (inclusive), yielding the resultant date at each step.
|
111
|
+
def step(limit, step=1)
|
112
|
+
da = self
|
113
|
+
op = %w(- <= >=)[step <=> 0]
|
114
|
+
while da.__send__(op, limit)
|
115
|
+
yield da
|
116
|
+
da += step
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
# Step forward one day at a time until we reach max (inclusive), yielding each date as we go.
|
122
|
+
def upto(max, &block) step(max, +1, &block) end
|
123
|
+
|
124
|
+
# Step backward one day at a time until we reach min (inclusive), yielding each date as we go.
|
125
|
+
def downto(min, &block) step(min, -1, &block) end
|
126
|
+
|
127
|
+
# Return internal object state as a programmer-readable string.
|
128
|
+
def inspect() "#<%#{self.class}: :year => #{@year} , :month => #{@month} , :day => #{@day} >" end
|
129
|
+
|
130
|
+
# Is this a leap year?
|
131
|
+
def leap?() self.class.leap?(@year) end
|
132
|
+
|
133
|
+
# Get the week day of this date. Sunday is day-of-week 0; Saturday is day-of-week 6.
|
134
|
+
def wday() to_g.wday end
|
135
|
+
|
136
|
+
# Get the day-of-the-year of this date.
|
137
|
+
# Farvardin 1 is day-of-the-year 1
|
138
|
+
def yday()
|
139
|
+
m = (@month-2 < 0) ? 0 : @month-2
|
140
|
+
(@month==1) ? @day : @day + JDaysInMonth[0..m].inject(0) {|sum, n| sum + n }
|
141
|
+
end
|
142
|
+
|
143
|
+
# Formats time according to the directives in the given format string. Any text not listed as a directive will be
|
144
|
+
# passed through to the output string.
|
145
|
+
#
|
146
|
+
# Format meaning:
|
147
|
+
#
|
148
|
+
#<tt>%a - The abbreviated weekday name (۳ش)</tt>
|
149
|
+
#<tt>%A - The full weekday name (یکشنبه)</tt>
|
150
|
+
#<tt>%b or %B - The month name (اردیبهشت)</tt>
|
151
|
+
#<tt>%d - Day of the month (1..31)</tt>
|
152
|
+
#<tt>%j - Day of the year (1..366)</tt>
|
153
|
+
#<tt>%m - Month of the year (1..12)</tt>
|
154
|
+
#<tt>%w - Day of the week (Sunday is 0, 0..6)</tt>
|
155
|
+
#<tt>%x - Preferred representation for the date alone, no time in format YY/M/D</tt>
|
156
|
+
#<tt>%y - Year without a century (00..99)</tt>
|
157
|
+
#<tt>%Y - Year with century</tt>
|
158
|
+
#<tt>%% - Literal %'' character</tt>
|
159
|
+
|
160
|
+
# d = JalaliDate.today
|
161
|
+
# d.strftime("Printed on %Y/%m/%d") #=> "Printed on 87/5/26
|
162
|
+
def strftime(format_str = '%Y/%m/%d')
|
163
|
+
clean_fmt = format_str.gsub(/%{2}/, "SUBSTITUTION_MARKER").
|
164
|
+
gsub(/%a/, PERSIAN_ABBR_WEEKDAY_NAMES[wday]).
|
165
|
+
gsub(/%A/, PERSIAN_WEEKDAY_NAMES[wday]).
|
166
|
+
gsub(/%b/, PERSIAN_MONTH_NAMES[@month]).
|
167
|
+
gsub(/%B/, PERSIAN_MONTH_NAMES[@month]).
|
168
|
+
gsub(/%d/, @day.to_s).
|
169
|
+
gsub(/%m/, @month.to_s).
|
170
|
+
gsub(/%Y/, @year.to_s).
|
171
|
+
gsub(/%y/, @year.to_s.slice(2,2)).
|
172
|
+
gsub(/%j/, yday.to_s).
|
173
|
+
gsub(/%w/, wday.to_s).
|
174
|
+
gsub(/%x/, [@year.to_s.slice(2,2),@month,@day].join("/")).
|
175
|
+
gsub(/#{"SUBSTITUTION_MARKER"}/, '%')
|
176
|
+
end
|
177
|
+
alias :format :strftime
|
178
|
+
|
179
|
+
private #-------------------------------------------------------------------------
|
180
|
+
def self.gregorian_to_jalali(year, month, day)
|
181
|
+
gy = year - 1600
|
182
|
+
gm = month - 1
|
183
|
+
gd = day - 1
|
184
|
+
g_day_no = 365*gy + (gy+3)/4 - (gy+99)/100 + (gy+399)/400
|
185
|
+
gm.times { |i| g_day_no += GDaysInMonth[i] }
|
186
|
+
g_day_no += 1 if gm > 1 && ((gy%4 == 0 && gy%100 != 0) || (gy%400 == 0))
|
187
|
+
g_day_no += gd
|
188
|
+
|
189
|
+
j_day_no = g_day_no-79
|
190
|
+
j_np = j_day_no/12053
|
191
|
+
j_day_no %= 12053
|
192
|
+
jy = 979 + 33 * j_np + 4*(j_day_no/1461)
|
193
|
+
j_day_no %= 1461
|
194
|
+
|
195
|
+
if (j_day_no >= 366)
|
196
|
+
jy += (j_day_no - 1)/365
|
197
|
+
j_day_no = (j_day_no - 1) % 365
|
198
|
+
end
|
199
|
+
|
200
|
+
11.times do |i|
|
201
|
+
if j_day_no >= JDaysInMonth[i]
|
202
|
+
j_day_no -= JDaysInMonth[i]
|
203
|
+
$j = i + 1
|
204
|
+
else
|
205
|
+
$j = i
|
206
|
+
break
|
207
|
+
end
|
208
|
+
end
|
209
|
+
jm = $j + 1
|
210
|
+
jd = j_day_no + 1
|
211
|
+
|
212
|
+
[jy, jm, jd]
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.jalali_to_gregorian(year,month,day)
|
216
|
+
jy = year - 979
|
217
|
+
jm = month - 1
|
218
|
+
jd = day - 1
|
219
|
+
j_day_no = 365*jy + (jy/33)*8 + (jy % 33 + 3)/4
|
220
|
+
jm.times { |i| j_day_no += JDaysInMonth[i] }
|
221
|
+
j_day_no += jd
|
222
|
+
|
223
|
+
g_day_no = j_day_no + 79
|
224
|
+
gy = 1600 + 400*(g_day_no/146097)
|
225
|
+
g_day_no %= 146097
|
226
|
+
|
227
|
+
leap = true
|
228
|
+
if g_day_no >= 36525
|
229
|
+
g_day_no -= 1
|
230
|
+
gy += 100 * (g_day_no/36524)
|
231
|
+
g_day_no %= 36524
|
232
|
+
(g_day_no >= 365) ? g_day_no += 1 : leap = false
|
233
|
+
end
|
234
|
+
|
235
|
+
gy += 4 * (g_day_no/1461)
|
236
|
+
g_day_no %= 1461
|
237
|
+
|
238
|
+
if g_day_no >= 366
|
239
|
+
leap = false
|
240
|
+
g_day_no -= 1
|
241
|
+
gy += g_day_no/365
|
242
|
+
g_day_no %= 365
|
243
|
+
end
|
244
|
+
|
245
|
+
11.times do |i|
|
246
|
+
leap_day = (i==1 && leap) ? 1 : 0
|
247
|
+
if g_day_no >= (GDaysInMonth[i] + leap_day )
|
248
|
+
g_day_no -= (GDaysInMonth[i] + leap_day )
|
249
|
+
$g = i + 1
|
250
|
+
else
|
251
|
+
$g = i
|
252
|
+
break
|
253
|
+
end
|
254
|
+
end
|
255
|
+
gm = $g + 1
|
256
|
+
gd = g_day_no + 1
|
257
|
+
|
258
|
+
[gy,gm,gd]
|
259
|
+
end
|
260
|
+
|
261
|
+
# Accept a Date or Time object and return a JalaliDate object
|
262
|
+
def self.g_to_j(date)
|
263
|
+
y,m,d = gregorian_to_jalali(date.year, date.month, date.day)
|
264
|
+
JalaliDate.new(y,m,d)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Accept a JalaliDate and return a Ruby Date object
|
268
|
+
def self.j_to_g(date)
|
269
|
+
y,m,d = jalali_to_gregorian(date.year, date.month, date.day)
|
270
|
+
Date.new(y,m,d)
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
data/test/test.rb
ADDED
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: JalaliDate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aziz Ashofte Bargi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-08-25 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: aziz.bargi@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
- CHANGELOG
|
25
|
+
- TODO
|
26
|
+
- MIT-LICENSE
|
27
|
+
files:
|
28
|
+
- lib/jalali_date.rb
|
29
|
+
- test/test.rb
|
30
|
+
- README
|
31
|
+
- CHANGELOG
|
32
|
+
- TODO
|
33
|
+
- MIT-LICENSE
|
34
|
+
has_rdoc: true
|
35
|
+
homepage: http://jalalidate.rubyforge.org
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project: JalaliDate
|
56
|
+
rubygems_version: 1.0.1
|
57
|
+
signing_key:
|
58
|
+
specification_version: 2
|
59
|
+
summary: A port of class Date in ruby that works based on Jalali Calendar (a.k.a Persian Calendar)
|
60
|
+
test_files:
|
61
|
+
- test/test.rb
|