DateTimeモジュール で遊んでみたシリーズ⑥ -DateTime.now ~ DateTime.shift_zone! の紹介

こんにちは！
プログラミング未経験文系出身、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! の紹介](https://qiita.com/Alicesky2127/items/6ce1eb3071880db5dec2)
6.DateTimeモジュール で遊んでみたシリーズ⑥ -DateTime.now ~ DateTime.shift_zone! の紹介(本記事)
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.nowとは
`DateTime.now(time_zone, time_zone_database \\ Calendar.get_time_zone_database())`は`time_zone`の現在の日時を返します。
タイムゾーンはカレンダーデータベース（今回の場合はtzdata）と照合します。


## 例
```diff_elixir:iex
DateTime.now("Asia/Tokyo")
```
```
{:ok, #DateTime<2024-12-22 15:29:46.738383+09:00 JST Asia/Tokyo>}
```

`time_zone`が存在しない場合`{:error, :time_zone_not_found}`のタプルを返します。

```diff_elixir:iex
DateTime.now("bad timezone")
```
```
{:error, :time_zone_not_found}
```


# DateTime.now!とは
`DateTime.now!(time_zone, time_zone_database \\ Calendar.get_time_zone_database())`は`time_zone`の現在の日時を返します。
タイムゾーンはカレンダーデータベース（今回の場合はtzdata）と照合します。


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

```diff_elixir:iex
DateTime.now!("Asia/Tokyo")
```
```
#DateTime<2024-12-22 15:32:34.231983+09:00 JST Asia/Tokyo>
```

カレンダーデータベースにタイムゾーンが見つからない場合`ArgumentError`を返します。

```diff_elixir:iex
DateTime.now!("bad timezone")
```
```
** (ArgumentError) cannot get current datetime in "bad timezone" time zone, reason: :time_zone_not_found
    (elixir 1.17.3) lib/calendar/datetime.ex:867: DateTime.now!/2
    #cell:p746o2eoytx56zax:1: (file)
```

# DateTime.shiftとは
`DateTime.shift(datetime, duration, time_zone_database \\ Calendar.get_time_zone_database())`は`datetime`を`duration`分だけ調整します。このとき`datetime`に含まれるタイムゾーンの
この関数の目的はそのタイムゾーンにいる人の時計を合わせる目的です。
つまり、サマータイムの開始／終了時刻といったイベント発生時に時計の調整をかけることができます。

## 例
```diff_elixir:iex
dt = DateTime.new!(~D[2025-03-09], ~T[01:59:00], "Asia/Tokyo")
```
```
#DateTime<2025-03-09 01:59:00+09:00 JST Asia/Tokyo>
```

```diff_elixir:iex
DateTime.shift(dt, hour: 1)
```
```
#DateTime<2025-03-09 02:59:00+09:00 JST Asia/Tokyo>
```

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

```diff_elixir:iex
dt = DateTime.new!(~D[2025-03-09], ~T[01:59:00], "America/New_York")
```
```
#DateTime<2025-03-09 01:59:00-05:00 EST America/New_York>
```

```diff_elixir:iex
DateTime.shift(dt, hour: 1)
```
```
#DateTime<2025-03-09 03:59:00-04:00 EDT America/New_York>
```

# DateTime.shift_zoneとは
`DateTime.shift_zone(datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())`は`datetime`のタイムゾーンを変更します。
タイムゾーンはカレンダーデータベース（今回の場合はtzdata）と照合します。

## 例
```diff_elixir:iex
DateTime.shift_zone(~U[2018-07-16 10:00:00Z], "America/Los_Angeles", Tzdata.TimeZoneDatabase)
```
```
{:ok, #DateTime<2018-07-16 03:00:00-07:00 PDT America/Los_Angeles>}
```

カレンダーデータベースにタイムゾーンが見つからない場合`{:error, :time_zone_not_found}`のタプルを返します。
```diff_elixir:iex
DateTime.shift_zone(~U[2018-07-16 10:00:00Z], "bad timezone", Tzdata.TimeZoneDatabase)
```
```
{:error, :time_zone_not_found}
```


# DateTime.shift_zone!とは
`DateTime.shift_zone!(datetime, time_zone, time_zone_database \\ Calendar.get_time_zone_database())`は`datetime`のタイムゾーンを変更します。
タイムゾーンはカレンダーデータベース（今回の場合はtzdata）と照合します。

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

```diff_elixir:iex
DateTime.shift_zone!(~U[2018-07-16 10:00:00Z], "America/Los_Angeles", Tzdata.TimeZoneDatabase)
```
```
#DateTime<2018-07-16 03:00:00-07:00 PDT America/Los_Angeles>
```

カレンダーデータベースにタイムゾーンが見つからない場合`ArgumentError`を返します。

```diff_elixir:iex
DateTime.shift_zone!(~U[2018-07-16 10:00:00Z], "bad timezone", Tzdata.TimeZoneDatabase)
```
```
** (ArgumentError) cannot shift ~U[2018-07-16 10:00:00Z] to "bad timezone" time zone, reason: :time_zone_not_found
    (elixir 1.17.3) lib/calendar/datetime.ex:800: DateTime.shift_zone!/3
    #cell:azrdretu7avc4p3s: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


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


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