過去のdateオブジェクトを検証する機会があり(明治の元号計算が合っていない件)、いろいろ検証していたところ、1867/1/1から1887/12/1までのdateオブジェクトからyear,month,dayを個別に取り出すと、dayが-1されるという現象が観測されました。
西暦から和暦に変換する処理で、明治の年が合わないという(自分のプログラム由来の)問題を検討していました。この問題の洗い出しのため、「日」単位で順次和暦変換していたら、そのちょうど(明治→大正など)改元の境目の日付で(原因不明の)計算ミスが起こっていました。
1868/1/25からは明治時代、という判定は行えても、その1868/1/25というdateオブジェクトからYear, Month, Dayの各要素を取り出すと1868, 1, 24という結果に。
以前から「大昔の日付を扱うと問題がありそう」だとは思っていましたが、このように具体的な現象として観測したのは(個人的には)初めてでした(単に覚えていないだけかも)。
確認したのはmacOS 10.14.6とmacOS 10.15.3beta、macOS 10.13.6上ですが、どれも同じ結果になるようです。
# 追試で、OS X 10.7.5でも試してみたところ、同じ結果になりました
# OS X 10.6.8では確認されませんでした
未来のdateオブジェクトは2050年ぐらいまでチェックしてみたものの、問題はありませんでした。
過去に遡ってうるう日が設定されたとかいう話なんでしょうか? そういう話は聞いたことがないのですが、、、
日本においては太陽暦(グレゴリオ暦)を導入したのが明治元年(1868/1/25〜)なので、それ以前の日付をグレゴリオ歴で求めてもいまひとつ実用性がない(?) ともいえますが、少なくとも1868年から1887年までの間のdateオブジェクトから各種の値の取り出しが期待どおりに行われないのは問題といえるでしょう。
多分、もっと昔の日付も同様にdateオブジェクトからの値取り出しを行うと問題が出ると思われますが、前述のような理由からそこまでの日付を計算させることもないだろうかと。
# ふだん使っているgetMLenInternationalに問題があるのかと考えて、昔使っていたgetMLenを引っ張り出してきましたが、こちらはずいぶんと記法がお可愛らしい感じで、、、、
ただ、この件は気がつかなかっただけで、ずいぶん昔から存在している話なのかも????
▲Classic Mac OS 8.6では確認できませんでした(SheepShaver上で動作)この時代にはmonthをas numberで数値にcastできませんでした
AppleScript名:dateTest.scptd |
— – Created by: Takaaki Naganoya – Created on: 2020/01/22 — – Copyright © 2020 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" use scripting additions use framework "Foundation" script spd property mList : {} end script set (mList of spd) to {} repeat with y from 1867 to 1888 repeat with m from 1 to 12 set aLen to getMlen(y, m) of me repeat with d from 1 to aLen set aTmpStr to (y as string) & "/" & (m as string) & "/" & (d as string) set aTmpD to date aTmpStr set tmpY to year of aTmpD set tmpM to month of aTmpD as number set tmpD to day of aTmpD if (tmpY is not equal to y) or (tmpM is not equal to m) or (tmpD is not equal to d) then set the end of (mList of spd) to {aTmpD, tmpY, tmpM, tmpD} end if end repeat end repeat end repeat return (mList of spd) –> {{date "1867年1月1日 火曜日 0:00:00", 1866, 12, 31}, …… {date "1887年12月31日 土曜日 0:00:00", 1887, 12, 30}} –現在のカレンダーで指定年月の日数を返す on getMlenInternational(aYear as integer, aMonth as integer) set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier: set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0 set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate return |length| of theResult end getMlenInternational –指定年の指定月の日数を求める on getMlen(aYear, aMonth) set aYear to aYear as number set aMonth to aMonth as number set aDat to (aYear as text) & "/" & (aMonth as text) & "/1" if aMonth is 12 then set eDat to ((aYear + 1) as text) & "/" & (1 as text) & "/1" else set eDat to ((aYear as text) & "/" & (aMonth + 1) as text) & "/1" end if set eDat to date eDat set eDat to eDat – 1 set mLen to day of eDat return mLen end getMlen |
元号変換v42 – AppleScriptの穴 says:
[…] これさえ行わなければ、とくにdateオブジェクトまわりの問題(before1887)は発生しません。dateオブジェクトとして解釈を行わず、単なる文字列を年、月、日に分解して、大小判定するだ […]