動機
fx-9750GIII や fx-CG50 には Finance アプリがあり、この中に日付・曜日計算機能がある。
一方、 Graph Math+ には今のところ Finance アプリが搭載されていない(2025年のアップデートで搭載予定とする情報もあるが)。
そこで MicroPython で曜日を求めるスクリプトを入れておこうと思い立った。
曜日判定はツェラーの公式を使う
Graph Math+ に搭載されている MicroPython では datetime モジュールなどを利用することができない。
そこでツェラーの公式を使って計算で求めるのが一般的な解決法。実際に Python コードを公開している人がいた。
そこでこれを電卓で入力しやすい形に微修正して…
def zeller(year, month, day):
if month <= 2:
year -= 1
month += 10
else:
month -= 2
w = day + int((13 * month - 1) / 5) + year + int(year / 4) - int(year / 100) + int(year / 400)
x = w % 7
return x
ws = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
date = input('YYYYMMDD? ')
year = int(date[0:4])
month = int(date[4:6])
day = int(date[6:8])
x = zeller(year, month, day)
print('%d/%d/%d(%s)' % (year, month, day, ws[x]))
こんな感じに実行できる。
YYYYMMDD? 20241201
2024/12/1(Sun)
>>>
日数計算
下記ブログ記事にあった「VB6のプログラムにあった計算方法」の式を使ってみる。
このやり方が秀逸なのは、3月を起点にした月数に30.59を掛けて整数化しているところ。
3月から前月までの累積日数と30違いで一致する。
def datediff(y1, m1, d1, y2, m2, d2):
if m1 == 1 or m1 == 2:
m1 = m1 + 12
y1 = y1 - 1
if m2 == 1 or m2 == 2:
m2 = m2 + 12
y2 = y2 - 1
days1 = int(365.25 * y1) + int(y1 / 400) - int(y1 / 100) + int(30.59 * (m1 - 2)) + d1
days2 = int(365.25 * y2) + int(y2 / 400) - int(y2 / 100) + int(30.59 * (m2 - 2)) + d2
days = days2 - days1
return days
こんな感じで日数が求められる。
YYYYMMDD? 20241127
2024/11/27(Wed)
Target? 20250101
20241127Wed-20250101Wed
Diff: 35
何日後・何日前も求めよう
上記の整数化された年月日は、0年3月1日を31としたシリアル値となっている。
このシリアル値から日付を求めることができれば、何日後・何日前の日付を求めることが可能だ。
400年、100年、4年、年、月、日と順にたどっていけば復元できるはず。
…が、簡単にはいかない。
かなりトリッキーに処理をした。もっとよい方法があると思う。
完成品
- Graph Math+ が未着の段階で作成したので、 Google Colab と fx-CG50 のエミュレータで動作確認をした。 fx-CG50 でも使えるが、文字サイズが大きいため、一部の文字ははみ出して表示される。
- エラー処理はしていない。
- グレゴリオ暦以前(1582年以前)の日付は正しくない。
def ymd(date):
year = int(date[0:4])
month = int(date[4:6])
day = int(date[6:8])
return year, month, day
def zeller(year, month, day):
if month <= 2:
year -= 1
month += 10
else:
month -= 2
w = day + int((13 * month - 1) / 5) + year + int(year / 4) - int(year / 100) + int(year / 400)
x = w % 7
return x
def date_to_serial(year, month, day):
if month == 1 or month == 2:
month = month + 12
year = year - 1
s_day = int(365.25 * year) + int(year / 400) - int(year / 100) + int(30.59 * (month - 2)) + day
return s_day
def serial_to_date(s_day):
s_day = s_day - 31
y400 = int(s_day / 146097)
s_day = s_day - y400 * 146097
y100 = int(s_day / 36524)
if y100 == 4:
y100 = 3; y4 = 0; y1 = 99; month = 11; day = 29
else:
s_day = s_day - y100 * 36524
y4 = int(s_day / 1461)
s_day = s_day - y4 * 1461
y1 = int(s_day / 365)
if y1 == 4:
y1 = 3; month = 11; day = 29
else:
s_day = s_day - y1 * 365
for i in range(12):
if s_day < round((i+1)*30.6):
month = i ; day = s_day - round(i*30.6) + 1 ; break
year = y400*400 + y100*100 + y4*4 + y1
month = month + 3
if month > 12:
year = year + 1
month = month - 12
return year, month, day
ws = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
while True:
date = input('YYYYMMDD? ')
if len(date) != 8:
break
else:
year, month, day = ymd(date)
x = zeller(year, month, day)
s_day1 = date_to_serial(year, month, day)
print('{:04}/{:02}/{:02}({})'.format(year, month, day, ws[x]))
date2 = input('Target/Diff? ')
if len(date2) == 8:
year_t, month_t, day_t = ymd(date2)
y = zeller(year_t, month_t, day_t)
s_day2 = date_to_serial(year_t, month_t, day_t)
days = s_day2 - s_day1
print('{:04}{:02}{:02}{}-{:04}{:02}{:02}{}'.format(year, month, day, ws[x], year_t, month_t, day_t, ws[y]))
print('Diff: %d' % days)
else:
days = int(date2)
s_day2 = s_day1 + days
year_t, month_t, day_t = serial_to_date(s_day2)
y = zeller(year_t, month_t, day_t)
print('{:04}{:02}{:02}{}[{:+}d]'.format(year, month, day, ws[x], days))
print('{:04}/{:02}/{:02}({})'.format(year_t, month_t, day_t, ws[y]))