time-windowを使ってみた




# はじめに
みなさま，はじめまして．
[株式会社オプティマインド](http://www.optimind.tech)にて，GISエンジニアをしている@tkmbnです．
Qiitaへの初投稿です．

# 概要
時間枠（日時の組み合わせ，開始日時〜終了日時）のあれやこれをしてくれるライブラリ[time-window](https://github.com/EncodeGroup/time-window)の使い方を解説します．
![Screen Shot 2020-03-02 at 11.29.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285596/3059514a-0bce-b911-3c23-57c5e780b444.png)

複雑な時間枠をスッキリまとめてくれたりします！←このライブラリを使って一番やりたいこと．


# なぜ書いたか

![Screen Shot 2020-03-01 at 23.09.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285596/b7bd7cbd-6f4b-09c8-10d8-4e3d867e4968.png)
DBから時間枠つきのデータを取得する際に，時間枠に重なりがあればまとめてほしいという依頼がありました．
どうにかこうにか自力で実装しようとしたんですが，混乱しだして，「なんか良いツールないかなぁ．．．」と探していたところ[上記のライブラリ](https://github.com/EncodeGroup/time-window)に出会いました．
readme以外にもいろいろと便利そうな機能もあったので，自分への覚書も含め，記事にしました．

まるまる解説！というわけではないので，あしからず．．．

# 本題
### インストール方法
```zsh
pip install time-window
```

### 具体的な使い方
最初の方は，readmeの日本語訳的なものと思ってください笑

#### TimeWindow オブジェクト
##### 作り方

`2020年03月01日12:00:00` から `2020年03月01日15:00:00` のTimeWindowを作りたいときは．．．

```py
from datetime import datetime
from time_window import TimeWindow
t1 = datetime(2020, 3, 1, 12, 00, 00)
t2 = datetime(2020, 3, 1, 15, 00, 00)
tw1 = TimewWindow(t1, t2)
tw1

-> TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 15, 0))
```
このとき，時間枠は，`[t1, t2)` になることに注意！

また，timedeltaでも作成可能です．

```py
from datetime import datetime, timedelta
from time_window import TimeWindow
t1 = datetime(2020, 3, 1, 12, 00, 00)
delta = timedelta(hours=3)
tw2 = TimeWindow.from_timedelta(t1, delta)
tw2

-> TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 15, 0))
```
同じ結果が得られました．

##### delta, middle
時間枠の差分を知りたいときは，

```py
tw1.delta

-> datetime.timedelta(0, 10800)
```

時間枠の中間の時点を知りたいときは，

```py
tw1.middle

-> datetime.datetime(2020, 3, 1, 13, 30)
```

##### overlaps


```py
tw = TimeWindow(datetime(2020, 3, 1, 12, 0), datetime(2020, 3, 1, 15, 0))
tw2 = TimeWindow(datetime(2020, 3, 1, 13, 0), datetime(2020, 3, 1, 18, 0))
tw.overlaps(tw2)

-> True
```
重なっていれば，True, そうでなければ，False.

##### contains(readme未掲載)

```py
tw = TimeWindow(datetime(2020, 3, 1, 12, 0), datetime(2020, 3, 1, 15, 0))
tw4 = TimeWindow(datetime(2020, 3, 1, 13, 0), datetime(2020, 3, 1, 14, 0))
tw.contains(tw4)

-> True
```
含まれていれば，True, そうでなければ，False.

##### contiguous

```py
tw = TimeWindow(datetime(2020, 3, 1, 12, 0), datetime(2020, 3, 1, 15, 0))
tw3 = TimeWindow(datetime(2020, 3, 1, 15, 0), datetime(2020, 3, 1, 18, 0))
tw.contiguous(tw3)

-> [TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 15, 0)), 
TimeWindow(datetime.datetime(2020, 3, 1, 15, 0), datetime.datetime(2020, 3, 1, 18, 0))]
```
接している場合は，TimeWindowのリストが，接していない場合は，Falseが返ってくる．


##### set型みたいな事もできる

###### 積集合

```py
tw = TimeWindow(datetime(2020, 3, 1, 12, 0), datetime(2020, 3, 1, 15, 0))
tw2 = TimeWindow(datetime(2020, 3, 1, 13, 0), datetime(2020, 3, 1, 18, 0))
tw.intersection(tw2)

-> TimeWindow(datetime.datetime(2020, 3, 1, 13, 0), datetime.datetime(2020, 3, 1, 15, 0))
```


###### 和集合

```py
tw.union(tw2)

-> TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 18, 0))
```




#### TimeWindowsCollection オブジェクト （readme未掲載）
##### 作り方

```py
from datetime import datetime, timedelta
from time_window import TimeWindow, TimeWindowsCollection
tw = TimeWindow(datetime(2020, 3, 1, 13, 0), datetime(2020, 3, 1, 18, 0))
tw2 = TimeWindow(datetime(2020, 3, 1, 12, 0), datetime(2020, 3, 1, 15, 0))
twc = TimeWindowsCollection([tw, tw2])
twc

-> [TimeWindow(datetime.datetime(2020, 3, 1, 13, 0), datetime.datetime(2020, 3, 1, 18, 0)), 
TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 15, 0))]

##開始時刻で並べ替えたい！
twc.time_windows_sorted_by_since

-> [TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 15, 0)), 
TimeWindow(datetime.datetime(2020, 3, 1, 13, 0), datetime.datetime(2020, 3, 1, 18, 0))]
```

##### 複数の時間枠をまとめたい
これで自分のやりたいことは，完璧にできました．

```py
tw = TimeWindow(datetime(2020, 3, 1, 13, 0), datetime(2020, 3, 1, 18, 0))
tw2 = TimeWindow(datetime(2020, 3, 1, 12, 0), datetime(2020, 3, 1, 15, 0))
tw3 = TimeWindow(datetime(2020, 3, 1, 19, 0), datetime(2020, 3, 1, 21, 0))
twc = TimeWindowsCollection([tw, tw2, tw3])
twc

-> [TimeWindow(datetime.datetime(2020, 3, 1, 13, 0), datetime.datetime(2020, 3, 1, 18, 0)), 
TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 15, 0)), 
TimeWindow(datetime.datetime(2020, 3, 1, 19, 0), datetime.datetime(2020, 3, 1, 21, 0))]


twc.compressed()

-> [TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 18, 0)), 
TimeWindow(datetime.datetime(2020, 3, 1, 19, 0), datetime.datetime(2020, 3, 1, 21, 0))]
```

重なり合っている時間枠はまとまり，重なっていないものは別の時間枠となっています．

![Screen Shot 2020-03-01 at 23.41.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285596/bb6138d3-1b59-1d7c-1fce-52419bd976c8.png)

しかし，落とし穴がありました．
普通にインデックス指定して，TimeWindowを取り出そうとしたところ，

```py
twc.compressed()[0]   
           
-> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'TimeWindowsCollection' object does not support indexing
```

返ってくる形見ると，普通のリストなんですが，どうやら違うみたいです．．．
TimeWindowsCollectionオブジェクトをTimeWindowのリストに戻してやる必要があるので，

```py
twc.compressed().time_windows

-> [TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 18, 0)), 
TimeWindow(datetime.datetime(2020, 3, 1, 19, 0), datetime.datetime(2020, 3, 1, 21, 0))]

twc.compressed().time_windows[0]  
TimeWindow(datetime.datetime(2020, 3, 1, 12, 0), datetime.datetime(2020, 3, 1, 18, 0))
```
無事取り出せることができました！！




# 参考文献
- [time-window](https://github.com/EncodeGroup/time-window)
- [Qiitaで簡単に記事を書くためのフォーマット(@kenseitogari1 )](https://qiita.com/kenseitogari1/items/1dcf4328d54e9f2c087c)
- [いらすとや](https://www.irasutoya.com)

# 最後に
最後まで読んでいただきありがとうございます！
なにか疑問点，間違っている点，等ございましたら，コメントお願いいたします．