-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjavaneseDate.py
441 lines (349 loc) · 13.5 KB
/
javaneseDate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
###############
# javaneseDate.py
# ============
# Python script for handling and converting Javanese Date based on The Javanese calendar system.
# The Javanese calendar (Javanese: ꦥꦤꦁꦒꦭ꧀ꦭꦤ꧀ꦗꦮ, romanized: Pananggalan Jawa)
# is the calendar of the Javanese people which influenced by Hijri Calendar and Hindu Calendar.
# Reference : https://en.wikipedia.org/wiki/Javanese_calendar
###############
#
import re
from datetime import date, timedelta
class Sasi:
"""
A class used to represent month class (sasi) in javanese calendar system
...
Parameters
----------
index : int
"""
class SasiModel:
def __init__(self, name, index, days, alias=[]):
self.name = name
self.alias = alias
self.days = days
self.index = index
SASI_SURA = SasiModel(name="Sura", index=1, days=30)
SASI_SAPAR = SasiModel(name="Sapar", index=2, days=29)
SASI_MULUD = SasiModel(name="Mulud", alias=[
'Rabingulawal'], index=3, days=30)
SASI_BAKDAMULUD = SasiModel(name="Bakda Mulud", alias=[
'Rabingulakir'], index=4, days=29)
SASI_JUMADILAWAL = SasiModel(name="Jumadilawal", index=5, days=30)
SASI_JUMADILAKIR = SasiModel(name="Jumadilakir", index=6, days=29)
SASI_REJEB = SasiModel(name="Rejeb", index=7, days=30)
SASI_RUWAH = SasiModel(name="Ruwah", alias=[
'Arwah', 'Saban'], index=8, days=29)
SASI_PASA = SasiModel(name="Pasa", alias=[
'Puwasa', 'Siyam', 'Ramelan'], index=9, days=30)
SASI_SAWAL = SasiModel(name="Sawal", index=10, days=29)
SASI_SELA = SasiModel(name="Séla", alias=[
'Dulkangidah'], index=11, days=30)
SASI_BESAR = SasiModel(name="Besar", alias=[
'Dulkahijjah'], index=12, days=29)
values = (SASI_SURA, SASI_SAPAR, SASI_MULUD, SASI_BAKDAMULUD, SASI_JUMADILAWAL,
SASI_JUMADILAKIR, SASI_REJEB, SASI_RUWAH, SASI_PASA, SASI_SAWAL, SASI_SELA, SASI_BESAR)
def __init__(self, value=1):
self.value = value % len(Sasi.values)
model = Sasi.values[self.value - 1]
self.days = model.days
self.index = model.index
self.alias = model.alias
self.name = model.name
class Pasaran:
"""
A class used to represent market day (pasaran) in javanese calendar system
...
Parameters
----------
day : int
"""
class PasaranModel:
def __init__(self, index, name):
self.name = name
self.index = index
PASARAN_PON = PasaranModel(1, 'Pon')
PASARAN_WAGE = PasaranModel(2, 'Wage')
PASARAN_KLIWON = PasaranModel(3, 'Kliwon')
PASARAN_LEGI = PasaranModel(4, 'Legi')
PASARAN_PAHING = PasaranModel(5, 'Pahing')
values = (PASARAN_PON, PASARAN_WAGE, PASARAN_KLIWON,
PASARAN_LEGI, PASARAN_PAHING)
def __init__(self, day=1):
self.index = day % len(Pasaran.values)
model = Pasaran.values[self.index - 1]
self.name = model.name
self.index = model.index
class Dina:
"""
A class used to represent day (dina) in javanese calendar system
...
Parameters
----------
value : int
fromEpoch : int
"""
class DinaModel:
def __init__(self, index, name):
self.name = name
self.index = index
DINA_SELASA = DinaModel(1, 'Selasa')
DINA_REBO = DinaModel(2, 'Rebo')
DINA_KEMIS = DinaModel(3, 'Kemis')
DINA_JEMAH = DinaModel(4, 'Jemah')
DINA_SEBTU = DinaModel(5, 'Sebtu')
DINA_AKAD = DinaModel(6, 'Akad')
DINA_SENEN = DinaModel(7, 'Senen')
values = (DINA_SELASA, DINA_REBO, DINA_KEMIS,
DINA_JEMAH, DINA_SEBTU, DINA_AKAD, DINA_SENEN)
def __init__(self, value=1, fromEpoch=1):
self.value = value
self.fromEpoch = fromEpoch
self.index = self.fromEpoch % len(Dina.values)
model = Dina.values[self.index - 1]
self.name = model.name
self.pasaran = Pasaran(self.fromEpoch)
class Tahun:
"""
A class used to represent year (tahun) in javanese calendar system
...
Parameters
----------
value : int
"""
class TahunModel:
def __init__(self, index, name, days):
self.name = name
self.days = days
self.index = index
TAHUN_ALIP = TahunModel(1, 'Alip', 354)
TAHUN_EHE = TahunModel(2, 'Ehe', 355)
TAHUN_JIMAWAL = TahunModel(3, 'Jimawal', 354)
TAHUN_JE = TahunModel(4, 'Je', 354)
TAHUN_DAL = TahunModel(5, 'Dal', 355)
TAHUN_BE = TahunModel(6, 'Be', 354)
TAHUN_WAWU = TahunModel(7, 'Wawu', 354)
TAHUN_JIMAKIR = TahunModel(8, 'Jimakir', 355)
values = (TAHUN_ALIP, TAHUN_EHE, TAHUN_JIMAWAL, TAHUN_JE,
TAHUN_DAL, TAHUN_BE, TAHUN_WAWU, TAHUN_JIMAKIR)
def __init__(self, value=1):
self.value = value
self.index = (value - 1867) % len(Tahun.values)
model = Tahun.values[self.index]
self.days = model.days
self.name = model.name
class JavaneseDateDelta():
"""
A class used to represent date difference object for Javanese Date
...
Parameters
----------
tahun : int
tahun mean year in javanese
sasi : int
sasi mean month in javanese
dina : int
dina mean day in javanese
"""
def __init__(self, tahun=0, sasi=0, dina=0):
self.tahun = tahun
self.sasi = sasi
self.dina = dina
class JavaneseDate:
"""
A class used to represent a Javanese Date
...
Parameters
----------
tahun : int
tahun mean year in javanese
sasi : int
sasi mean month in javanese
dina : int
dina mean day in javanese
Methods
-------
getDaysFromEpoch(tahun, sasi, dina)
calculate number of days from epoch 1936-03-24
getDateFromEpoch(daysFromEpoch)
return JavaneseDate based on days calculated from epoch 1936-03-24
fromDate()
return javanese date converted from gregorian date
toDate()
return datetime from this JavaneseDate object
format()
return JavaneseDate based on customized string format
"""
DAYS_IN_WINDU = 2835
TAHUN_IN_WINDU = 8
GREGORIAN_EPOCH_DATE = date(1936, 3, 24)
EPOCH_YEAR = 1867
def __init__(self, tahun=1867, sasi=1, dina=1):
""" init javanese date. The default value is the first javanese date epoch (24 March 1936C or 1 Sura 1955J)
Parameters
----------
tahun : int, optional, tahun mean year in javanese
sasi : int, optional, sasi mean month in javanese
dina : int, optional, dina mean day in javanese
Returns
-------
JavaneseDate object
"""
if (tahun >= 1867 and sasi >= 1 and dina > -1):
self.tahun = Tahun(tahun)
self.sasi = Sasi(sasi)
self.daysFromEpoch = self.getDaysFromEpoch(tahun, sasi, dina)
self.dina = Dina(dina, self.daysFromEpoch)
else:
raise Exception(
"Sorry, year must be later than 1867")
def getDaysFromEpoch(self, tahun, sasi, dina):
""" This function calculate number of days from epoch 1936-03-24
Parameters
----------
tahun : int, required, tahun mean year in javanese
sasi : int, required, sasi mean month in javanese
dina : int, required, dina mean day in javanese
Returns
-------
JavaneseDate object
"""
winduCount = (
tahun - JavaneseDate.EPOCH_YEAR) // JavaneseDate.TAHUN_IN_WINDU
tahunCount = (
tahun - JavaneseDate.EPOCH_YEAR) % JavaneseDate.TAHUN_IN_WINDU
totalDays = sum([
winduCount * JavaneseDate.DAYS_IN_WINDU,
sum(map(lambda x: x.days, Tahun.values[:tahunCount])),
sum(map(lambda x: x.days, Sasi.values[:sasi - 1])),
dina
])
return totalDays
def getDateFromEpoch(self, daysFromEpoch):
""" This function return JavaneseDate based on days calculated from epoch 1936-03-24
Parameters
----------
daysFromEpoch : int, required, number of days from epoch
Returns
-------
JavaneseDate object
"""
if daysFromEpoch:
windu = daysFromEpoch // JavaneseDate.DAYS_IN_WINDU
tahun, sasi, days = 1, 1, daysFromEpoch % JavaneseDate.DAYS_IN_WINDU
while days > 355:
days, tahun = days - Tahun.values[tahun].days, tahun + 1
while days > 30:
days, sasi = days - Sasi.values[sasi].days, sasi + 1
tahun = Tahun(JavaneseDate.EPOCH_YEAR - 1 +
(windu * JavaneseDate.TAHUN_IN_WINDU + tahun))
sasi = Sasi(sasi)
pasaran = Pasaran(self.daysFromEpoch)
dina = Dina(days, self.daysFromEpoch)
return tahun, sasi, dina, pasaran
else:
raise Exception("Required argument was missing")
def __calculateDeltaDays(self, dt):
""" This function calculate days difference between this JavaneseDate with other JavaneseDate
Parameters
----------
dt : JavaneseDate, required, other JavaneseDate
Returns
-------
JavaneseDate object
"""
winduCount = (dt.tahun + dt.sasi //
12) // JavaneseDate.TAHUN_IN_WINDU
tahunCount = (dt.tahun + dt.sasi //
12) % JavaneseDate.TAHUN_IN_WINDU
totalDays = sum([
winduCount * JavaneseDate.DAYS_IN_WINDU,
sum(map(lambda x: x.days,
Tahun.values[self.tahun.value + 1:self.tahun.index + tahunCount + 1])),
sum(map(lambda x: x.days,
Sasi.values[self.sasi.index + 1: (self.sasi.index + 1) + (dt.sasi % 12)])),
dt.dina
])
return totalDays
def fromDate(self, year, month, day):
""" This function return javanese date converted from gregorian date
Parameters
----------
year : int, required
month : int, required
day : int, required
Returns
-------
JavaneseDate object
"""
if year and month and day:
dt0 = JavaneseDate.GREGORIAN_EPOCH_DATE
dt1 = date(year, month, day)
delta = dt1 - dt0
self.daysFromEpoch = delta.days + 1
self.tahun, self.sasi, self.dina, self.pasaran = self.getDateFromEpoch(
self.daysFromEpoch)
return self
else:
raise Exception("One or more required arguments was missing")
def toDate(self):
""" This function return datetime from this JavaneseDate object
Returns
-------
datetime
"""
dt0 = JavaneseDate.GREGORIAN_EPOCH_DATE
dt1 = dt0 + timedelta(days=self.daysFromEpoch - 1)
return dt1
def format(self, dateformat='%D %P, %d %M %Y'):
""" return JavaneseDate based on customized string format.
Parameters
----------
dateformat : string format, default is D P, d M Y. Example return Selasa Pon, 1 Sura 186
Format
----------
%d: javanese day (dina) value
%D: javanese day (dina) full name
%m: javanese month (sasi) value
%M: javanese month (sasi) full name
%y: 2-digits year (tahun)
%Y: 4-digits year (tahun)
%t: javanese year (tahun) name
%P: pasaran, daily javanese market name value
Returns
-------
str
"""
return re.sub(r'(%[a-zA-Z]{1})', '{\\1}', dateformat). \
replace('%', '').format(d=self.dina.value,
D=self.dina.name,
m=self.sasi.value,
M=self.sasi.name,
y=str(
self.tahun.value)[-2:],
Y=self.tahun.value,
t=self.tahun.name,
P=self.dina.pasaran.name
)
def __add__(self, other):
if (type(other) is JavaneseDateDelta):
totalDays = self.daysFromEpoch + self.__calculateDeltaDays(other)
self.daysFromEpoch = totalDays
self.tahun, self.sasi, self.dina, self.pasaran = self.getDateFromEpoch(
totalDays)
return self
else:
return self
def __sub__(self, other):
if (type(other) is JavaneseDateDelta):
totalDays = self.daysFromEpoch - self.__calculateDeltaDays(other)
if totalDays > 0:
self.daysFromEpoch = totalDays + 1
self.tahun, self.sasi, self.dina, self.pasaran = self.getDateFromEpoch(
totalDays)
else:
raise Exception(
"Cant be performed because the resulted date earlier than 24 March 1936")
return self
else:
return self