DateTimeモジュール で遊んでみたシリーズ⑤ -DateTime.from_unix ~ DateTime.new! の紹介

こんにちは！
プログラミング未経験文系出身、Elixirの国に迷い込んだ？！見習いアルケミストのaliceと申します。
今回はDateTimeモジュールについて学んだことをまとめます。

# 目次
1.[DateTimeモジュール で遊んでみたシリーズ① -事前準備編](https://qiita.com/Alicesky2127/items/71d6905684f6f0f936c2)
2.[DateTimeモジュール で遊んでみたシリーズ② -DateTime.add ~ DateTime.compare の紹介](https://qiita.com/Alicesky2127/items/971a013f34f0f1fb7c90)
3.[DateTimeモジュール で遊んでみたシリーズ③ -DateTime.convert ~ DateTime.from_gregorian_seconds の紹介](https://qiita.com/Alicesky2127/items/74e646264c7a55ea6f81)
4.[DateTimeモジュール で遊んでみたシリーズ④ -DateTime.from_iso8601 ~ DateTime.from_naive! の紹介](https://qiita.com/Alicesky2127/items/79869efcf8bb5cee9d82)
5.DateTimeモジュール で遊んでみたシリーズ⑤ -DateTime.from_unix ~ DateTime.new! の紹介(本記事)
6.[DateTimeモジュール で遊んでみたシリーズ⑥ -DateTime.now ~ DateTime.shift_zone! の紹介](https://qiita.com/Alicesky2127/items/b706801a887ceb5427ee)
7.[DateTimeモジュール で遊んでみたシリーズ⑦ -DateTime.to_date ~ DateTime.to_string の紹介](https://qiita.com/Alicesky2127/items/a3fcf13bd2577d57b431)
8.[DateTimeモジュール で遊んでみたシリーズ⑧ -DateTime.to_time ~ DateTime.utc_now の紹介](https://qiita.com/Alicesky2127/items/ebd5b063c041adf2d1d0)
9.[DateTimeモジュール で遊んでみたシリーズ番外編 -Livebookでtzdata導入](https://qiita.com/Alicesky2127/items/4ad2072cbca04193a3d4)


# 目的 
DateTimeモジュールに含まれる関数を触って機能を理解したい

https://hexdocs.pm/elixir/DateTime.html

## 実行環境
Windows 11 ＋ WSL2 ＋ Ubuntu 22.04
Elixir v1.17.3
Erlang v27.0

# 前提
本記事を手元検証するには、前提としてtzdataの導入が必要です。
まだの場合は下記1.または2.で準備をしてください。

1.local環境でtzdataの導入

https://qiita.com/Alicesky2127/items/71d6905684f6f0f936c2

2.Livebookでtzdataの導入

https://qiita.com/Alicesky2127/items/4ad2072cbca04193a3d4



# DateTime.from_unixとは
`DateTime.from_unix(integer, unit \\ :second, calendar \\ Calendar.ISO)`は`integer`で示したUNIX時間[^1]を、Datetime構造体に変換します。
この関数のタイムゾーンはUTCで返されます（UNIX時間にはタイムゾーン情報が含まれないため）

この関数の逆は`DateTime.to_unix(datetime, unit \\ :second)`です。

## 例
```diff_elixir:iex
DateTime.from_unix(1_464_096_368)
```
```
{:ok, ~U[2016-05-24 13:26:08Z]}
```

`integer`は最大253,402,300,799秒がサポートされます。
これを超える場合`{:error, :invalid_unix_time}`のタプルが返されます。
```diff_elixir:iex
DateTime.from_unix(253_402_300_799)
```
```
{:ok, ~U[9999-12-31 23:59:59Z]}
```

```diff_elixir:iex
DateTime.from_unix(253_402_300_800)
```
```
{:error, :invalid_unix_time}
```

`unit`は`System.time_unit()`でサポートされているものが使用できます。
```diff_elixir:iex
DateTime.from_unix(1_432_560_368_868_569, :microsecond)
```
```
{:ok, ~U[2015-05-25 13:26:08.868569Z]}
```



# DateTime.from_unix!とは
`DateTime.from_unix!(integer, unit \\ :second, calendar \\ Calendar.ISO)`は`integer`で示したUNIX時間[^1]を、Datetime構造体に変換します。
この関数のタイムゾーンはUTCで返されます（UNIX時間にはタイムゾーン情報が含まれないため）



## 例
```diff_elixir:iex
DateTime.from_unix!(1_464_096_368)
```
```
~U[2016-05-24 13:26:08Z]
```

変換に成功した場合`DateTime.from_unix`は `{:ok, Datetime構造体}`の***タプル*** が返されるが、`DateTime.from_unix!`は ***Datetime構造体*** が返されるので注意。

`integer`は最大253,402,300,799秒がサポートされます。
これを超える場合`ArgumentError`が返されます。
```diff_elixir:iex
DateTime.from_unix!(253_402_300_799)
```
```
~U[9999-12-31 23:59:59Z]
```

```diff_elixir:iex
DateTime.from_unix!(253_402_300_800)
```
```
** (ArgumentError) invalid Unix time 253402300800
    (elixir 1.17.3) lib/calendar/datetime.ex:475: DateTime.from_unix!/3
    #cell:6bybjar5k2gh7jdi:1: (file)
```

`unit`は`System.time_unit()`でサポートされているものが使用できます。
```diff_elixir:iex
DateTime.from_unix!(1_432_560_368_868_569, :microsecond)
```
```
~U[2015-05-25 13:26:08.868569Z]
```

`integer`に`0`を渡すと簡単にUNIXエポックを取得できます。
```diff_elixir:iex
DateTime.from_unix!(0)
```
```
~U[1970-01-01 00:00:00Z]
```



# DateTime.newとは
`DateTime.new(date, time, time_zone \\ "Etc/UTC", time_zone_database \\ Calendar.get_time_zone_database())`はDate構造体とTime構造体を合わせてDatetime構造体に変換します。
タイムゾーンはカレンダーデータベース（今回の場合はtzdata）と照合します。

## 例
```diff_elixir:iex
DateTime.new(~D[2016-05-24], ~T[13:26:08.003])
```
```
{:ok, ~U[2016-05-24 13:26:08.003Z]}
```

### サマータイム開始／終了時刻をまたぐ場合
米国ニューヨークの次のサマータイム開始時刻[^1]は、現地時刻の3/9の02:00です。
このタイミングで時計が1時間進みます[^2]
これをまたいで使用する場合`{:gap, gap直前の有効な日時, gap直後の有効な日時}`のタプルが返されます。

```diff_elixir:iex
DateTime.new(~D[2025-03-09], ~T[02:00:00.003], "America/New_York")
```
```
{:gap, #DateTime<2025-03-09 01:59:59.999999-05:00 EST America/New_York>,
 #DateTime<2025-03-09 03:00:00-04:00 EDT America/New_York>}
```


# DateTime.new!とは
`DateTime.new!(date, time, time_zone \\ "Etc/UTC", time_zone_database \\ Calendar.get_time_zone_database())`はDate構造体とTime構造体を合わせてDatetime構造体に変換します。
タイムゾーンはカレンダーデータベース（今回の場合はtzdata）と照合します。

## 例
```diff_elixir:iex
DateTime.new!(~D[2016-05-24], ~T[13:26:08.003])
```
```
~U[2016-05-24 13:26:08.003Z]
```

変換に成功した場合`DateTime.new`は `{:ok, Datetime構造体}`の***タプル*** が返されるが、`DateTime.new!`は ***Datetime構造体*** が返されるので注意。


### サマータイム開始／終了時刻をまたぐ場合
米国ニューヨークの次のサマータイム開始時刻は、現地時刻の3/9の02:00です。
このタイミングで時計が1時間進みます
これをまたいで使用する場合`ArgumentError`を返します。

```diff_elixir:iex
DateTime.new!(~D[2025-03-09], ~T[02:00:00.003], "America/New_York")
```
```
** (ArgumentError) cannot build datetime with ~D[2025-03-09] and ~T[02:00:00.003] because such instant does not exist in time zone America/New_York as there is a gap between #DateTime<2025-03-09 01:59:59.999999-05:00 EST America/New_York> and #DateTime<2025-03-09 03:00:00-04:00 EDT America/New_York>
    (elixir 1.17.3) lib/calendar/datetime.ex:363: DateTime.new!/4
    #cell:nnmq33fxksvkuzxj:1: (file)
```



# ～Elixirの国のご案内～
↓Elixirって何ぞや？と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

https://qiita.com/piacerex/items/09876caa1e17169ec5e1

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ！私もエンジニア未経験から学習中です。

https://elixir-lang.info/topics/entry_column


↓[We Are The Alchemists, my friends!:bouquet:](https://qiita.com/torifukukaiou/items/6bcd5bbb80df9e594e9e "Awesome-san's Awesome article")[^10]
Elixirコミュニティは本当に優しくて温かい人たちばかり！
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。[^11]


https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian

[^1]:起点が1970年1月1日00:00:00(UTC)にある。起点からの経過秒数で特定の日時を表現する https://wa3.i-3-i.info/word18474.html

[^10]: @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。


[^11]: @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中！