ユリウス暦、グレゴリオ暦、協定世界時について

2000年頃に、暦と時刻についてまとめた資料です。
古いし、勘違いもあると思うので、誤り指摘願います。

## ユリウス暦(Julian calendar)
古い太陽暦。4で割り切れる年を閏年とする暦法。ローマのジュリアス・シーザー(Gaius Julius Caesar)が制定し、BC 46 から実施した。

## グレゴリオ暦(Gregorian calendar)
現行の太陽暦。400年間に97回の閏年がある。4で割り切れ、100で割り切れない年を閏年とし、2月29日を設ける。400で割り切れる年も閏年とする。ローマ教皇グレゴリウス13世が、ユリウス暦1582年10月4日の翌日を、グレゴリオ暦1582年10月15日として実施した。カトリック教国以外ではグレゴリオ暦の採用が遅れ、ドイツでは1700年、英国では1752年(9月2日の翌日を9月14日とした)。ロシアでは1918年から採用した。日本では、太陰太陽暦(天保暦)の明治5年12月2日の翌日を、グレゴリオ暦の明治6(1873)年1月1日として実施した。

## ユリウス通日(JD:Julian day)
ユリウス暦 BC.4713(-4712)年1月1日正午(世界時)から起算した通日(この日が第0日)。1582年にグレゴリオ暦へ改暦する際に導入された。年代学において、各種の暦に依存しないで日付を統一できて便利である。日の始まりが世界時の正午(日本時間の21時)であることに注意すること。正午以前はマイナス時間で計算するのが便利である。

### 例 (jxxxx=ユリウス暦, gxxxx=グレゴリオ暦)

日付 | ユリウス通日 | 曜日
----:|----------:|---
j-5000.1.1  12UT | -105192 | Fri (BC5001)
j-4712.1.1  12UT |       0 | Mon (BC4713)
j0000.1.1   12UT | 1721058 | Thr (BC0001)
j0001.1.1   12UT | 1721424 | Sat (AD0001)
j1582.10.5  12UT | 2299161 | Fri
g1582.10.15 12UT | 2299161 | Fri
g1858.11.17 12UT | 2400001 | Wed MDJ起点+0.5
g1900.1.1   12UT | 2415021 | Mon
g1970.1.1   12UT | 2440588 | Thr UNIX時間起点+0.5
g2000.1.1   12UT | 2451545 | Sat

## 修正ユリウス日(MJD:Modified Julian Date)
ユリウス通日(JD)から 2,400,000.5 を減じた値である。1858年11月17日正子UT が 0 となる。
ユリウス通日(JD)は正午起点のため世界時(UT)と日付が半日ずれるし、20世紀以後を扱うには桁数が大きく計算に不便なので、利便のために設けられた。

## ユリウス年
通常の暦では１年は365日または366日であり、これでは、年を単位とした計算が難しい。そこで、計算の便宜のため一年の長さを356.25日に固定したものをユリウス年と呼ぶ。

## ユリウス世紀
100ユリウス年。36525日のこと。

## グリニジ基準天体(平均太陽)
太陽は地球の公転運動により天球の黄道上を一年でほぼ一周する。天球は地球の自転運動により一日でほぼ一回転する。太陽の出没を生活時間の基準としている我々は、天球上の太陽が一回転するのに要する時間（南中してから次に南中するまで）を一日の長さとしているが、太陽は黄道上を進んでいるため、赤経方向でみると赤道と交わる春分秋分付近では進みが遅く、夏至冬至付近では進みが早くなる。つまり季節によって一日の長さが違ってしまう。
これでは不便なので、赤道上を一定速度で一年一周する仮想の太陽を、時刻の基準とする。この仮想太陽の赤経が「平均太陽」である。

## 世界時(UT0,UT1,UT2)
世界時は平均太陽の出没、つまり地球の自転を基準とした時間であり、天体観測を元にして決定される。天体位置観測から求めた地方平均恒星時と、観測地の採用経度λからUT0を得る。UT0に、極運動による観測地の経度変化の補正Δλを加えると、観測地によらない世界共通の UT1 となる。このUT1は天球の日周運動を最も忠実に反映した時刻であり、地上からの天体観測の基準になる時刻である。UT1に、地球の自転速度変化の季節変動（潮汐力による地球の変形や、偏西風の季節変化が原因とされる）ΔTsを加えて、秒の進みを平滑化した UT2 を得る。1960年にUTCが登場するまでは、このUT2が標準時として利用されていた。

λ = 観測地の採用天文緯度
φ = 観測地の採用天文緯度
 t = 年初からの経過時間[年単位]
 x,y = 極運動による極の位置
Δλ = -(x sinλ + y cosλ) tanφ  (およそ ±0.3″tanφ)
ΔTs = 0.022″sin2πt - 0.012″cos2πt - 0.006″sin4πt + 0.007″cos4πt
αm = グリニジ基準天体(平均太陽)
LMST = 地方平均恒星時
UT0 = LMST - λ - αm + 12h
UT1 = UT0 + Δλ
UT2 = UT1 + ΔTs

## 国際原子時(TAI:international atomic time)
原子が放射する電磁波の振動数を基にして一秒の長さを決め、その秒を積算して作り出した時間である。1967年10月にパリにおける国際度量衡総会において「１秒は、セシウム133の原子の基底状態における２つの超微細準位間の遷移に対応する放射の、9192631770周期の継続時間とする」と定められた。これが後にSI秒の定義となった。1958年1月1日0時0分0秒UT2を、同日同時刻のTAIと定めた。TAIは 86400 SI秒 を１日とする時刻であり、地球の自転とは無関係である。

## 協定世界時(UTC:coordinated universal time)
国際原子時(TAI)に閏秒を入れ、地球の自転に基づく世界時(UT1)との時刻差を ±0.9秒以内に管理した時間である。我々が日常使っている時間である。月末最終秒に閏秒(１秒)を追加するか抜くことで、時刻差を±0.9秒以内調整する。その時期は、国際地球回転観測事業(IERS)の中央局が決定する。1976～1997の20年間で、合計 15 秒の閏秒が挿入されている。
この方式は1972年からであり、それ以前はまったく別の方式でUTCが定められていた。また1972年から1974年末までは±0.7秒以内に調整されていた。

【追記】時刻に不連続をもたらす閏秒はデジタルネットワークシステムにとって扱いが難しく、特に2020年代には地球自転速度の変化により初の負の閏秒が必要になる可能性があるため、少なくとも1世紀間は閏秒調整不要となるように、2035年までにUT1との時刻差許容量(±0.9秒以内)を緩めることが [第27回CGPM決議4(2022年)](https://www.bipm.org/en/cgpm-2022/resolution-4) にて決定された。
### UTC - TAI の表
出典: http://jjy.nict.go.jp/QandA/data/leapsec.html

年月日 | UTC-TAI
------|--------
1972-01-01 | -10
1972-07-01 | -11
1973-01-01 | -12
1974-01-01 | -13
1975-01-01 | -14
1976-01-01 | -15
1977-01-01 | -16
1978-01-01 | -17
1979-01-01 | -18
1980-01-01 | -19
1981-07-01 | -20
1982-07-01 | -21
1983-07-01 | -22
1983-07-01 | -22
1985-07-01 | -23
1988-01-01 | -24
1990-01-01 | -25
1991-01-01 | -26
1992-07-01 | -27
1993-07-01 | -28
1994-07-01 | -29
1996-01-01 | -30
1997-07-01 | -31
1999-01-01 | -32
2006-01-01 | -33
2009-01-01 | -34
2012-07-01 | -35
2015-07-01 | -36
2017-01-01 | -37

1972年以前のUTCは、地球の自転速度に合うようにSI秒に調整値(周波数オフセット)を加えたものをUTCの１秒とし、さらにUT2との差が0.1秒を越えるときには段階的に0.1秒だけ修正（ステップ調整）する方式であった。この方式では閏秒が不要であるかわりに１秒の長さが変動することになり不便であったため、現在の方式に改められた。

## 国際地球回転観測事業(IERS:International Earth Rotation Service)
世界各地の天文台において、UTCを基準として恒星の位置観測を行い UT0 - UTC を求める。これをIERS中央局(フランス)が整理して、極位置(x,y)、UT1 - UTC、UTC - TAIの確定値、速報値、予測値を公表している。WWW上では http://hpiers.obspm.fr/ から公表値を入手できる。

## DUT1
各国の標準時電波報時(日本ではJJY)が報じる UT1 - UTC の予測値(0.1秒単位)のこと。UTCからUT1の予測値を求めるために使用する。

## 力学時(TD:Dynamical Time)
協定世界時(UTC)は予測不可能な閏秒の調整が入るため一様な時刻系ではなく、天体力学による惑星の軌道計算等で用いるには都合が悪い。そこで時間経過が一様な時刻系として用意されたのが力学時である。一般相対性理論から、空間位置によって時刻の進みは均質でないため、太陽系重心を基準とした惑星の位置計算には、「太陽系力学時(TDB:Barycentric Dynamical Time)」を用い、地心視位置の計算には、「地球力学時(TDT:Terrestrial Dynamical Time)」を用いる。TDTは国際原子時(TAI)に対して、TDT = TAI + 32.184秒 と定められている。TDTとTDBの差は ±0.002秒以内である。

## GPS時刻(GPST:Global Positioning System Time)
協定世界時(UTC)1980年1月6日0時0分0秒を基点する原子時刻。GPS時刻=TAI-19 である。
UTCのように閏秒を含まないが、GPS信号にはGPS時刻とUTCとの秒数差が含まれており、GPS時刻からUTCを算出できる。

## UNIX時間
協定世界時(UTC)1970年1月1日0時0分0秒からの秒数。閏秒は無視されており実際の経過秒数ではない。
OSがUNIX時間を「符号付き32bit整数」で扱う場合、2038年1月19日に上限に達する。 

| UNIX時間 | UTC | JST |
|--------:|:---:|:---:|
|-9,223,372,036,854,775,808 # INT64_MIN  | -292,271,021,076-08-26T08:29:52 | -2922億年(ビッグバン以前) |
|-2,147,483,648 # INT32_MIN | 1901-12-13T20:45:52 | 1901-12-14T05:45:52 | 
|-1,234,567,890 | 1930-11-18T00:28:30 | 1930-11-18T09:28:30
|-1,000,000,000 | 1938-04-24T22:13:20 | 1938-04-25T07:13:20
|-100,000,000 | 1966-10-31T14:13:20 | 1966-10-31T23:13:20
|0 | 1970-01-01T00:00:00 | 1970-01-01T09:00:00
|100,000,000 | 1973-03-03T09:46:40 | 1973-03-03T18:46:40
|1,000,000,000 | 2001-09-09T01:46:40 | 2001-09-09T10:46:40
|1,234,567,890 | 2009-02-13T23:31:30 | 2009-02-14T08:31:30
|2,147,483,647 # INT32_MAX | 2038-01-19T03:14:07 | 2038-01-19T12:14:07 | 
|4,294,967,295 # UINT32_MAX | 2106-02-07T06:28:15 | 2106-02-07T15:28:15 |
|9,223,372,036,854,775,807 # INT64_MAX  | 292,277,026,596-12-04T15:30:07 | 2922億年 |
|18,446,744,073,709,551,615 # UINT64_MAX | 584,554,051,223-11-09T07:00:15 | 5845億年 |

Unixtime相互変換ツール: https://keisan.casio.jp/exec/system/1526004418

## ソフトウェアにおける時刻データ形式

データ形式の一覧を以下に示し、以後の節でそれぞれを解説する。

| 種別 | 型 | 単位 | 0基点(epoch date) | 範囲 |
|------|----|------|------------------|------|
|time_t| INT32 | 1秒 | 1970-01-01T00:00:00 UTC | 1901-12-13T20:45:52 UTC から 2038-01-19T03:14:07 UTC |
|time_t| INT64 | 1秒 | 1970-01-01T00:00:00 UTC | ±2922億年 |
|struct timespec| time_t+long | 1ナノ秒 | 1970-01-01T00:00:00 UTC | time_tと同じ |
|struct timeval | time_t+int? | 1ミリ秒 | 1970-01-01T00:00:00 UTC | time_tと同じ |
|FAT date/time | UINT16,UINT16,(UINT8) | 2秒(10ミリ秒) | 1980年1月1日（ローカル時刻） | 1980年1月1日 から 2107年12月31日 |
|Windows FILETIME | UINT64 | 100ナノ秒 | 1601-01-01T00:00:00 UTC | 1601-01-01T00:00:00UTC から 60056-05-28 UTC |
|DotNet DateTimeOffset | INT64 | 100ナノ秒 | 0001-01-01T00:00:00 UTC | 0001-01-01(DateTimeOffset.MinValue) から 9999-12-31(DateTimeOffset.MaxValue) |

### time_t
https://ja.wikipedia.org/wiki/Time_t
C言語の時刻型。UNIX時間を秒単位で保持する符号付き整数値である。
古いシステムでは32bit整数型であり2038年問題を引き起こす。
21世紀以後の新しいシステムではだいたい64bit整数型である。

### struct timespec
UNIX時間 time_t に1秒以下の分解能(ナノ秒単位)を加えたISO-C11標準のデータ形式である。
https://ja.cppreference.com/w/c/chrono/timespec
https://ja.cppreference.com/w/c/chrono/timespec_get
https://manpages.ubuntu.com/manpages/precise/en/man2/clock_gettime.2.html

```c
/** C11: 時間を秒とナノ秒に分解して保持する構造体 */
struct timespec {
  time_t tv_sec; // 秒 (有効な値は ≧ 0)
  long tv_nsec;	 // ナノ秒 (有効な値は [0, 999999999])
};

/** C11 */
int timespec_get( struct timespec *ts, int base);

/** POSIX */
int clock_gettime(clockid_t clock_id, struct timespec *tp);
```

### struct timeval
UNIX時間 time_t に1秒以下の分解能(ミリ秒単位)を加えたBSD由来のデータ形式である。
※ C11で struct timespec が標準化されたので、今後は timespec へ代替すべきである。
https://manpages.ubuntu.com/manpages/bionic/en/man3/timeval.3bsd.html
https://manpages.ubuntu.com/manpages/precise/en/man2/gettimeofday.2.html

```c
/** BSD: 時間を秒とマイクロ秒に分解して保持する構造体 */
struct timeval {
  time_t          tv_sec;
  suseconds_t     tv_usec;
};

int gettimeofday(struct timeval *tv, struct timezone *tz);
```

### FAT date/time
MS-DOS FATファイルシステムで用いられる時刻形式。世界時(UTC)ではなくローカル時刻である。
範囲は 1980年1月1日 から 2107年12月31日 まで。精度は2秒単位である。
https://ja.wikipedia.org/wiki/File_Allocation_Table

2個の16bitデータで構成され、それぞれ日付と時刻を保持している。

#### DATE WORD
| feild | bits | value |
|-------|------|-------|
|year  | 7bit |0(1980年)..127(2107年)
|month | 4bit |1..12
|day   | 5bit |1..31

#### TIME WORD
| feild | bits | value |
|-------|------|-------|
|hour   | 5bit | 00..24
|minute | 6bit | 00..59
|second | 5bit | 00..29 (2秒単位)

#### 2SECONDS BYTE
https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#DIR_OFS_0Dh
> Create time, fine resolution: 10 ms units, values from 0 to 199 (since DOS 7.0 with VFAT).

DOS 7.0 以後のVFAT では、ファイル生成時刻に対して 2秒以下の精度を補うために10ミリ秒単位の値 0..199(1990ms) を、8bitデータに保持している。

#### FORMAT解説
https://devblogs.microsoft.com/oldnewthing/?p=42653

```
Number four: The DOS date/time format
The DOS date/time format is a bitmask:

               24                16                 8                 0
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|Y|Y|Y|Y|Y|Y|Y|M| |M|M|M|D|D|D|D|D| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s|
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
 \___________/\________/\_________/ \________/\____________/\_________/
     year        month       day      hour       minute        second
The year is stored as an offset from 1980. Seconds are stored in two-second increments.
(So if the “second” value is 15, it actually represents 30 seconds.)

These values are recorded in local time.

November 26, 2002 at 7:25p PST = 0x2D7A9B20.

To convert these values to something readable, convert it to a FILETIME via DosDateTimeToFileTime,
then convert the FILETIME to something readable.

```

### Windows FILETIME
Windows のファイル日付で用いられる時刻系.
下記引用にある通り 1601-01-01T00:00:00 UTC からの経過時間を100ナノ秒単位で表す「符号なし64bit整数値」である。
範囲は 1601-01-01T00:00:00UTC から 60056-05-28 UTC まで。

https://docs.microsoft.com/windows/win32/api/minwinbase/ns-minwinbase-filetime
> Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).

ただし WindowsAPI FileTimeToSystemTime() が処理できるのは、符号付き64bit整数値最大値となる 30828年 までである。

https://docs.microsoft.com/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime
> This value must be less than 0x8000000000000000. Otherwise, the function fails.

```sh:64,63,62bitで保持した100ナノ秒単位の経過時間の最大年数を計算する.
$ perl -E 'foreach (62..64) { $y=2**$_/10/1000/1000/3600/24/365.2425; say "$_ bits: $y years";}'
62 bits: 14613.8512313464 years
63 bits: 29227.7024626928 years
64 bits: 58455.4049253855 years
```

### DotNet DateTimeOffset
0001-01-01T00:00:00 UTC からの経過時間を100ナノ秒単位で表す「符号付き64bit整数値」である。
範囲は、0001-01-01(DateTimeOffset.MinValue) から 9999-12-31(DateTimeOffset.MaxValue) まで。

内部実装にて上位2bitをタイムゾーン用のフラグに使用しているので、残り62bitで経過時間を保持している。100ナノ秒単位で最大14613年間が保持できるが、切りの良いところで9999年を上限としているようだ。

https://docs.microsoft.com/dotnet/api/system.datetimeoffset
https://docs.microsoft.com/dotnet/api/system.datetimeoffset.utcticks
https://docs.microsoft.com/dotnet/api/system.datetimeoffset.minvalue
https://docs.microsoft.com/dotnet/api/system.datetimeoffset.maxvalue