OmniAICreator/Qiita-1.07M huggingface dataset:
WEBアプリ セキュリティ 簡易まとめ

# この記事は何？
オライリーから出版されている[リアルワールドバグハンティング](https://www.oreilly.co.jp/books/9784873119212/)を読み、
再度セキュリティに関して確認しようと思い作成したもの。
また事例の調査も行った。
[リアルワールドバグハンティング](https://www.oreilly.co.jp/books/9784873119212/)はバグハンティングプログラムを利用した、脆弱性の報告からバグを見つける手法・考え方を解説する本です。セキュリティ観点としてだけでなく、読み物としても面白いためオススメです。

# SQLインジェクション
## 概要
悪意あるリクエストにより、データベースへの不正利用がおこなわれる
## 脅威
- データベースの非公開情報の閲覧
- データベースの情報の改竄・消去
- 認証回避による不正ログイン

## 被害事例
[価格.com事件](https://xtech.nikkei.com/it/free/ITPro/Security/20050602/162004/)
SQLインジェクションにより、価格.comのOZmallが不正アクセスを受けた。
それにより、メールアドレスなどの個人情報が二万件超流出する被害にあい、サービスは一時休止する事態となった。

[SQLインジェクション対策漏れの責任を開発会社に問う判決](https://blog.tokumaru.org/2015/01/sql.html)
SQLインジェクションの対策漏れが原因で発生した、クレジットカード情報漏洩事件。
詳しくは記事を読んだほうがいいです。

## 対策
- SQL文の組み立てはすべてプレースホルダで実装する
- SQL文の組み立てはエスケープ処理をおこない、リテラルを正しく構成する

# OSコマンド・インジェクション
## 概要
WEBサーバのOSコマンドを不正に実行することにより起こされる攻撃

## 脅威
- サーバ内ファイル閲覧・改竄・削除
- 不正なシステム操作
- 不正なプログラムのダウンロード・実行
- 他のシステムへの攻撃の踏み台

## 被害事例
[日テレ個人情報漏洩](https://cybersecurity-jp.com/security-incident-case/12081)
日本テレビのWebモバイルサイトで利用しているソフトが不正アクセスによる攻撃を受け、個人情報４３万件が流出したとされる事件。遠隔操作プログラムが設置され、それを利用して、内部情報を捜索され、流出につながった。

## 対策
- シェルを起動できる言語機能の利用を避ける

# ディレクトリ・トラバーサル
## 概要
プログラムへ指定するパラメータに対して、本来想定していないファイルを指定して、公開をしていないファイルへのアクセスを許してしまったり、意図しない処理をさせられる

## 脅威
- ファイルの閲覧・改竄・消去

## 被害事例
[ウィルスバスター 脆弱性](https://success.trendmicro.com/jp/solution/1122253)
ウィルスバスターの上記脆弱性をつかれ、攻撃者がウイルスバスター Corp. サーバ、もしくはビジネスセキュリティサーバの任意のファイルを変更することができた。

## 対策
- 外部からのパラメータでウェブサーバ内のファイル名を直接指定する実装を避ける
- アクセス権限の設定を正しく管理する

# セッション管理の不備
## 概要
セッションIDを不正に取得（推測、盗む）され、なりすましてアクセスされてしまう

## 脅威
対象の利用者になりすまし、その利用者の個人情報の漏洩や、利用者の意図しないプログラムの処理をおこなう

## 被害事例
[Apacheのサーバへの攻撃](https://scan.netsecurity.ne.jp/article/2010/04/15/25107.html)
XSSによってセッションを含むクッキー情報を盗むコードが仕込まれていた。
そのため、セッションハイジャックされ、侵入を許すこととなった。

## 対策
- セッションIDを推測困難な文字列とする
- セッションIDをURLパラメータに格納しない
- 既存のセッション ID とは別に秘密情報を発行し、ページの遷移ごとにその値を確認

# クロスサイトスクリプティング
## 概要
WEBアプリの入力欄に対してスクリプトなどを埋め込み、クッキー情報の漏洩やサイトに偽のページを表示させてしまう

## 脅威
- クッキー情報の漏洩
- 偽情報の流布

## 被害事例
[Apacheのサーバへの攻撃](https://scan.netsecurity.ne.jp/article/2010/04/15/25107.html)

## 対策
- エスケープ処理をほどこす
- 入力値の内容チェックを行う
- HTTP レスポンスヘッダのContent-Type フィールドに文字コード（charset）を指定する。

# CSRF(クロスサイト・リクエスト・フォージェリ)
## 概要
Webアプリ利用者自身が意図しない処理が実行されてしまう

## 脅威
- 利用者のみが利用可能なサービスの悪用
- 利用者のみが編集可能な情報の改竄

## 被害事例
[twitter CSRF脆弱性](https://www.itmedia.co.jp/enterprise/articles/1311/07/news039.html)
Twitterにモバイル端末を登録してSMSでアカウントを管理できる機能に存在していた脆弱性。
攻撃者の携帯端末とネットワークを被害者のアカウントに登録させたりすることができた。

## 対策
- 処理を実行するページを POST メソッドでアクセスするようにし、その「hidden パラメータ」に秘密情報が挿入されるよう、前のページを自動生成して、実行ページではその値が正しい場合のみ処理を実行する。
- Referer が正しいリンク元かを確認し、正しい場合のみ処理を実行する。
- 処理を実行する直前のページで再度パスワードの入力を求め、実行ページでは、再度入力されたパスワードが正しい場合のみ処理を実行する。

# 参考
[安全なウェブサイトの作り方](https://www.ipa.go.jp/files/000017316.pdf)
[安全なSQLの呼び方](https://www.ipa.go.jp/files/000017320.pdf)
[リアルワールドバグハンティング](https://www.oreilly.co.jp/books/9784873119212/)
【TypeScript】インターフェースの使い方

#はじめに
前回は[【TypeScript】基本的な型](https://qiita.com/t_t238/private/30c28817b980258885ff)についてアウトプットしました。
今回はTypeScriptの**``Interface``**についてアウトプットします。

#インターフェースとは？
TypeScriptのインターフェースは、あるインスタンスがどのようなプロパティ、メソッドを持っているかの仕様を定義します。

#使い方
インターフェースを実装する時にはインターフェースで定義されているすべてのプロパティに値を設定する必要があります。

```javascript

interface Person{
    name:string;
    age:number;
}

class Taro implements Person{
    name:string;
    age:number;
}
```

また、インターフェースのメンバー全てがpublicとなる為、
アクセス修飾子をprivateにするとコンパイルエラーとなります。

```javascript

interface Person{
    name:string;
    age:number;
}

class Taro implements Person{
    private name:string; //コンパイルエラー
    age:number;
}
```

#任意のプロパティ
必ずしもすべてのプロパティが必須とは限らないため、そのようなプロパティを表すにはプロパティ名の末尾に ``?`` を付けます。
以下の``Person``インターフェースの``age``プロパティはオプショナルになっているため、``Person``オブジェクトを生成するときに``age``プロパティの値を省略することができます。

```javascript

interface Person {
  name: string;
  age?: number;  // age プロパティはオプショナル
}

const p1: Person = { name: 'Taro', age: 100 };  // OK
const p2: Person = { name: 'Jiro' };  // OK
console.log(p1.age);  //=> 100
console.log(p2.age);  //=> undefined
}
```

#読み取り専用プロパティ
読み取り専用にしたいプロパティはプロパティ名の前に``readonly``を付けます。


```javascript

interface Person {
  name: string;
  readOnly age: number; 
}

const p1: Person = { name: 'Taro', age: 20 };  // オブジェクト作成

p1.age = 30; // エラー: Cannot assign to 'age' because it is a read-only property.
```

#関数の型定義
インタフェースは特定のプロパティを持つオブジェクトを表す以外に、メソッドの型を表すこともできます。
引数と戻り値だけを持つ関数宣言のように表現し、引数リストには名前と型が必須です。

```javascript
// Grocery インタフェースを定義
interface Grocery {
  name: string;
  price: number;
  calcTotal(quantity: number): number;
}
```


```javascript
// Grocery 型のオブジェクトを生成
const grocery: Grocery = {
  name: 'Apple',
  price: 150,
  calcTotal(quantity: number): number {
    return this.price * quantity;
  }
};

console.log(grocery.calcTotal(10));  //=> 1500
```

#インデックスシグネチャ
プロパティへの添え字アクセスに対する型情報を定義します。

```javascript
interface numInSpanish {
    [key: number]: string;
}
```

```javascript
//初期化
const esp: numInSpanish = {
    1: "uno",
    2: "dos",
    3: "tres";
}
//更新
esp[10] = "diez";

//取得
console.log(esp[1]); //=> uno
console.log(esp[2]); //=> dos
console.log(esp[3]); //=> tres
console.log(esp[10]); //=> diez
```

#継承
継承は``extends``を使います。

```javascript
interface Sports {
    name:string;
}

interface Soccer extends Sports{
    numOfPlayer:number;
}

class Jleague implements Soccer {
    name = 'Soccer';
    numOfPlayer: 11;
}
```
インターフェースは多重継承が可能です。

```javascript
interface Sports {
    name:string;
}

interface Soccer {
    numOfPlayer:number;
}

class Jleague implements Sports,Soccer {
    name = 'Soccer';
    numOfPlayer: 11;
}
```

#参照
[TypeScript Handbook : Interfaces](https://www.typescriptlang.org/docs/handbook/interfaces.html)
[Typescriptのinterfaceの使い方](https://qiita.com/nogson/items/86b47ee6947f505f6a7b)
SaaSインサイドセールス、プログラミングの達成感を知る

#はじめに

・プログラミング初心者の気持ちが知りたい方
・やりたいと思いつつ先延ばしにしている初学者
・オンラインでプログラミング学ぶってどんな感じか知りたい方


に合う内容です。技術的にあれ？と思う


#私のスペック
こんにちは。先日、ジーズアカデミーのスタッフとなりました梅川です。
前職では、チャットボットのインサイドセールスとして3年間ほど働いていました。
クラウドサービスの営業といえども、エンジニアではなかったので、何か手を動かして作るということはしたことがありませんでした。

私のプログラミング学習戦歴は…


###ドンっ！


####\\\\「いちばんやさしいHTML5＆CSS3の教本」////
webサイトを作ろうと思い立ち、なんとなーく書いてみるも、サーバーにつなぐところで、上手くいかず断念。

####\\\\ progate ////
HTMLとCSSの基礎を隙間時間にやっていたが、「わからない→スライドに戻る→打つ」がだんだん煩わしくなってきて断念。

####\\\\ ディズニーのプログラミング教材 - テクノロジア魔法学校////
キングダムハーツ風の無料体験を転職活動中にやった。結構楽しくjavascriptまで触れることができたが、収入０の状態だったので断念。


と、まあ、「HTMLがサイトの骨組みで、CSSは装飾で、javascriptで動きの指示を出せるんだー」という浅い理解をしている状態でした。
クラウドサービスを売っている身として、それがどうやって作られているか理解すべきだとは常々思っていたものの、恥ずかしながら営業の実務に追われているというのが実情でした。

###あと、やっぱり独学だと続かない！
わからないことにぶち当たって、検索してもそれにたどりつけないと積んでしまい、結局やらなくなる。そもそもモチベーションが続かない。ということで、ダイエットのように、やってはやめ、やってはやめ、を繰り返していました。


#『G’s CAMP』に参加する


そんな中、ジーズアカデミーのスタッフとして入社しました。（入社経緯はまたの機会に）そこで、研修を兼ね、近畿大学の学生さんと一緒にCAMPに参加させてもらうことになりました。

『G’s CAMP』は「はじめてのプログラミング」を無理なくスタートするための短期集中CAMPです。

講義はすべてオンラインでした。
まずは、セットアップ作業を行いました。そもそもこのセットアップから躓いていたので、それができただけでも感動…。

▼VScodesetupとビュー。
土台となるコードは先生にいただき、小菅先生の画面を見ながら、スタート。
このビューに映すことすらできなかったが、2秒でできた。
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/901306/b027c697-7806-0822-0286-6071e944e160.png)

#API連携に挑戦！


HTML/CSS/javascriptを用いておみくじアプリやじゃんけんアプリなどを作成したのですが、一番感動したのが、「API連携」でした。
実は、前職のチャットボットがAPI連携機能を搭載しており、「APIを公開しているサービスから情報を引っ張れる」ということは認識してました。しかし、その裏側はぼんやりとしか理解できていなかったからです。


▼小菅先生のイラストがかわいい。
![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/901306/6cc05e78-2a89-544c-9b97-29016344d43d.png)


課題は、曲あてクイズを作ること。
これも先生の書くコード写経しているのですが、なぜかエラーが発生します。


▼「正解です！」が全く表示されず、ここのコードをこねくり回す
![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/901306/fa32eb36-14e5-9e0c-95de-62068ef4cf31.png)



▼文字の意味は何となくしか分からないけど、「正解です！」が出ないのはこのコードにも原因があるかも？？
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/901306/e998501c-4060-0a7a-c6b7-ce60836e2dd9.png)



このあたりはエラーが何度も出てしまって、「意味が定義されてないんじゃない？」「スペルミスない？大文字小文字あってる？」
とチューターさんのお力を借りながら、うんうん唸ってカタカタやっていました。


そして、きちんと表示された時の**「おおーー！！」と歓声が上がる快感！**


プログラミングというと、独りでカタカタとパソコンに向かい合っているイメージだったけど、**実はチームプレイなんだ。**と、この時初めて実感しました。



#CAMPを終えて
これで私も、レベルはさておき、「知っている」から「できる」人に一歩近づいたと思います！Zoomで講義、Slackで習熟度の確認を行いつつ進んだので、置いてけぼりになることはありませんでした。また、チューターさんがいてくれたので、都度個別で質問に対応してくれるので至れり尽くせりですね。小菅先生、チューターの方々、本当にありがとうございました！

明日は、このキャンプの立役者、すがやさんが担当します！


#余談
この記事を書くにあたり、完ぺきに動作した曲あてクイズを載せてやろうと思っていました。
が、一週間ぶりに開くと、エラーが発生していました…。悔しいので、エラーの原因を探っているところです。
ついでに、じゃんけんアプリももっといいものにしたい！そういや、SlackのAPI使って効率化もしたい！
こうしてプログラミングにずぶずぶハマっていくのでした…。
5000兆回LGTMしたい！無限にLGTMできるネタアプリを作った

# こんなやつ
**5000兆円欲しい！**という古のネタに触発された。（PCのみ対応）

URL: https://i-want-to-5senchou-lgtm.netlify.app/
GitHub: https://github.com/crypt0box/i_want_to_5senchou_lgtm
![takusan_lgtm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/612354/a3cbd85e-f8a2-d220-5721-ce17315a8efb.gif)

#作った理由
**1. 5000兆回いいねしたかった**
Qiitaに溢れる幾多の良記事に1LGTMしかできないのって欠陥じゃない！？！？！？！？
何時間もハマったエラーを解決できたときに、

**オラァッ！！！！**
![5000chou.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/612354/aaf77a6a-424f-02ad-7633-295b714c9319.jpeg)
と**感謝の5000兆LGTM**をしたいがために作りました。


**2.CSSの勉強**
CSSフレームワークに頼ってばかりだったのでCSSだけで実装してみたかった

**3.TypeScriptの勉強**
TypeScript+Vueの書き方を学びたかった

**4.Vue.js単体テストの勉強（これから）**
テストの書き方がいまだにわかっていないのでシンプルなアプリで試してみたかった

#使用技術
* TypeScript 3.9.3
* Vue 2.6.11
* axios 0.21.0
* Netlify

Qiitaからトレンドを持ってくるAPIは@HelloRusk様の素晴らしいAPIを使わせて頂きましたm(_ _)m
[Netlify Functions を使って Qiita のトレンド API を非公式で作ってみた](https://qiita.com/HelloRusk/items/803f9599cde72810f1a8)

#おわりに
これからは下記の対応をしていこうと思います。

1. テストコードの実装
2. レスポンシブ対応
3. LGTMボタンのチェックボックス化

ちなみにページをリロードするとLGTMは初期化されます。
![takusan_lgtm_rest.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/612354/b50a9e80-b632-fc21-70dd-16317c505e6b.gif)
**全ては儚い夢だったのだ。**
テスト21

```vb

'フォームAからフォームBを表示して文字列を入力、
'一旦非表示にしてフォームCからフォームBの情報を利用するコード

'フォーム2

Private Sub CommandButton1_Click()

Me.Hide

End Sub

'---------------------------------
'フォーム3

Private Sub CommandButton1_Click()

MsgBox UserForm2.TextBox1.Value & UserForm2.TextBox2.Value

End Sub

Private Sub CommandButton2_Click()

Unload Me

End Sub
'-----------------------------------
'フォーム4

Private Sub CommandButton1_Click()

UserForm2.Show vbModeless

End Sub

Private Sub CommandButton2_Click()

UserForm3.Show vbModeless

End Sub

Private Sub CommandButton3_Click()

Unload UserForm2
Unload Me

End Sub
```
Cloud9(Amazon Linux+Rails5.2+MySQL5.7)

#1 現状のMySQLバージョンを確認

```
$ mysql --version
```

#2 旧バージョン(5.5)パッケージ関連を削除

```
$ sudo yum -y remove mysql-config mysql55-server mysql55-libs mysql55
```

#3 新バージョン(5.7)パッケージ関連をインストール

```
sudo yum -y install mysql57-server mysql57
```

#4 日本語文字化け対策を実施
```
$ sed -e "/utf8/d" -e "/client/d" -e "/^\[mysqld_safe\]$/i character-set-server=utf8\n\n[client]\ndefault-character-set=utf8" /etc/my.cnf |sudo tee /etc/my.cnf
```

#5 MySQL確認&起動

```
$ mysql --version
$ sudo service mysqld start
```
#6 MySQL Account を作る

```
$ sudo mysql -u root   #rootユーザーでログイン
$ mysql> create user 'ユーザー名' identified by 'パスワード';  #ユーザー作成
$ mysql> grant all on *.* to 'ユーザー名';  #ALL権限付与

#作成したユーザーの確認は下記コマンド
$ mysql> select User,Host from mysql.user;
```

#7 順番にライブラリをインストール&mysql2(gem)のインストール

```
$　sudo yum install mysql57-devel
$　sudo yum -y install ruby-devel
$　sudo yum groupinstall "Development Tools"

$　gem install mysql2
```

#8 Railsをインストール

```
$ gem install rails -v 5.2.1
```

#9 プロジェクトを作成

```
$ rails new projectname --database=mysql
```

#10 config/database.ymlの編集

```
default: &default
  adapter: mysql2
  encoding: unicode
  pool: 5
  # 以下、3行追加
  username: <username> # 設定したMySQL Accountと同一のもの
  password: <password> # 設定したMySQL Accountと同一のもの
  host: localhost

development:
  <<: *default
  database: appname_development # appnameのところは、rails new時のappnameになっているはずです。
test:
  <<: *default
  database: appname_test # appnameのところは、rails new時のappnameになっているはずです。
```

#11 rails db:createでデータベースを作成

```
$ cd appname
$ rails db:create

#rails5の場合は不要
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)
$ yarn install --check-files
$ rails webpacker:install

#config/enviroments/development.rb
config.hosts.clear
```
AWSのEC2にOracle19cをインストールしてDBを作成する

Oracle Bronzeの資格学習のため、AWSにOracleDB環境を構築する事にしたが、とても苦戦したのでメモ。
→結論：インストール可能。しかし、Oracle19cはRedHat8には対応してなさそう。(java関連のエラーが発生した)
勉強に使う分には問題無い思う！

→追記:2020/12/1 RedHat7だと正常にインストールされた。RedHat7の場合はcompat-libcap1-1.10-7.el7.x86_64.rpmのインストールは不要。

# 環境
EC2のOS : Red Hat Enterprise Linux 8
Oracle : Oracle Database 19c

# 準備作業
・OSがRedHatのEC2作成
・Oracleアカウントの作成
・[自身のPCにOracle Database 19cのLinux x86-64のRPMをダウンロード](https://www.oracle.com/database/technologies/oracle-database-software-downloads.html?xd_co_f=NjhiOGFlMzItNGJiNi00M2ZhLWE0ZjUtYzljNmU3NWYyNTEz)
・EBSを30Gに拡張しておく。（無料枠のマックス）

# Oracle 19cをEC2に配置
scpを使って、ダウンロードしたRPMをEC2にアップロードする。
PEM_FILE : 鍵のパス
INSTANCE_IP : 接続先IP

~~~bash:アップロード
sudo scp -i PEM_FILE oracle-database-ee-19c-1.0-1.x86_64.rpm ec2-user@INSTANCE_IP:/home/ec2-user/
~~~

# 前提パッケージをインストール 
以下のパッケージがインストールされていないと、Oracle19cのインストールに失敗する。

~~~bash:ダウンロード
# EC2ログイン後 /home/ec2-user/配下
curl -o compat-libcap1-1.10-7.el7.x86_64.rpm http://mirror.centos.org/centos/7/os/x86_64/Packages/compat-libcap1-1.10-7.el7.x86_64.rpm
curl -o compat-libstdc++-33-3.2.3-72.el7.x86_64.rpm http://mirror.centos.org/centos/7/os/x86_64/Packages/compat-libstdc++-33-3.2.3-72.el7.x86_64.rpm
curl -o oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm https://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm
~~~

~~~bash:インストール
sudo yum -y localinstall compat-libcap1-1.10-7.el7.x86_64.rpm
sudo yum -y localinstall compat-libstdc++-33-3.2.3-72.el7.x86_64.rpm
sudo yum -y localinstall oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm
~~~

# Oracle19cのインストール

~~~bash
sudo yum -y localinstall oracle-database-ee-19c-1.0-1.x86_64.rpm
~~~

Java関連のエラーが大量に出るが、インストール出来ているっぽい。

# DB作成&接続
[このサイト](https://www.fuku.tokyo/2019/09/oracle19c-install/)からほぼコピペ。

~~~bash:DB作成
/etc/init.d/oracledb_ORCLCDB-19c configure
~~~

環境変数の設定。再度ログイン時も保持したいので、`/etc/profile.d/oracle.sh`を作成する。

~~~bash
sudo vi /etc/profile.d/oracle.sh
~~~

~~~bash:/etc/profile.d/oracle.sh
export ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
export ORACLE_SID=ORCLCDB
export NLS_LANG=Japanese_Japan.AL32UTF8
export PATH=$ORACLE_HOME/bin:$PATH
~~~

~~~bash:接続
source /etc/profile.d/oracle.sh
sudo su -
su - oracle
sqlplus / as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on 日 11月 29 08:02:07 2020
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

アイドル・インスタンスに接続しました。

SQL> 
~~~

# 終わりに
大分時間かかったけど、CUIで簡単にインストールする方法を見つけることが出来たので満足！
GUIでインストールするよりも圧倒的に楽なのでお薦め。
ようやくBronzeの勉強に移れるぞ〜
競プロでc++のpowを使ったときの話

#表示形式ミス

c++で実装されているpowは引数としてint, intをとったときdouble型で計算することになっており

```cpp
int a = 1125;
cout << pow(a, 2) << endl;
```
としたときの出力は

```
1.26562e+06
```
となる。
しかし、一般にこの形式で答えを出力させることはないのでint型へキャストする必要がある。

```cpp
int a = 1125;
cout << static_cast<int>(pow(a, 2)) << endl;
```
このときの出力は期待通り

```
1265625
```

となる。
rubyzip でZIPを作るときに秒がズレないように日時を入れる方法

`Zip::Entry.new` の後ろの方に日時を渡すのではなく、インスタンスの `time=` に渡すと秒がズレないようです

```ruby
entry = Zip::Entry.new(zos, "foo1.txt", nil, nil, nil, nil, nil, nil, dos_time)
```
↓

```ruby
entry = Zip::Entry.new(zos, "foo2.txt")
entry.time = dos_time
```

下のが検証したコードです

```ruby
require "zip"
require "zip/version"
require "time"

Zip::VERSION                    # => "2.3.0"

created_at = Time.parse("2020-01-01 00:00:05") # => 2020-01-01 00:00:05 +0900
dos_time = Zip::DOSTime.from_time(created_at)  # => 2020-01-01 00:00:05 +0900

io = Zip::OutputStream.write_buffer do |zos|
  # ズレる方法
  entry = Zip::Entry.new(zos, "foo1.txt", nil, nil, nil, nil, nil, nil, dos_time)
  zos.put_next_entry(entry)

  # ズレない方法
  entry = Zip::Entry.new(zos, "foo2.txt")
  entry.time = dos_time
  zos.put_next_entry(entry)
end
File.write("foo.zip", io.string)
`unzip -o foo.zip`
puts `ls -go --full-time foo*.txt`
# >> -rw-r--r-- 1 0 2020-01-01 00:00:04.000000000 +0900 foo1.txt
# >> -rw-r--r-- 1 0 2020-01-01 00:00:05.000000000 +0900 foo2.txt
```

# 参考

rubyzipを使ってファイル圧縮する際に任意の変更日付をいれてテストしてみたら展開ファイルが1秒ズレてハマったという話。
https://qiita.com/kitaindia/items/894a3a600e0892dc0197
Laravel 5.5 Socialiteのインストールでエラーが出た

<h2>環境</h2>
Laravel バージョン 5.5

<h2>Socialiteをインストール</h2>

LaravelでSNSログインする際にいろいろやってくれるSocialite。

公式
https://readouble.com/laravel/5.5/ja/socialite.html

composerでインストールします。

~~~:ターミナル
composer require laravel/socialite
~~~

順調に行くかと思われたその時、

~~~
Installation failed, reverting ./composer.json to its original content.
~~~

むむ。
怒られました。


<h2>解決方法</h2>
現在、Laravel 5.5では　最新バージョンがインスールできないらしい。


ということで、

~~~:ターミナル
composer require laravel/socialite "3.0.*"
~~~

3.0をインストール。

~~~
Package manifest generated successfully.
~~~
完了しました！
Digdagでセッションの開始時刻が遅れた場合のslaの挙動

Digdagを使っているときに、ワークフローの実行時間が想定よりも長い場合に何かしらの処理をしたくなる時はよくあります。
そのようなときにはsla機能を使うと良いです。
https://docs.digdag.io/scheduling_workflow.html#setting-an-alert-if-a-workflow-doesn-t-finish-within-expected-time

例えば以下のように書いておけば、ワークフローが開始されてから10分経過時点でnotice.shが実行されます。

```yaml
sla:
  duration: 00:10:00
  +notice:
    sh>: notice.sh
```

しかし、ワークフローの定期実行を遅らせた場合には注意が必要です。

```yaml
schedule:
  daily>: 07:00:00

sla:
  duration: 00:10:00
  +notice:
    sh>: notice.sh
```

上記のワークフローを用意し、7時になる前に `digdag disable` コマンドでこのワークフローを定期実行を無効化します。
そして、07:05に `digdag endble` でワークフローを再度有効化します。
このとき `digdag enable` を実行した瞬間(07:05)にワークフローが動き出します。
そしてその5分後である 07:10にsla違反によってnotice.shが実行されます。
ワークフロー開始から10分後である07:15に実行される訳ではないです。

これはsla違反の判定に使用されるワークフローの開始時刻は、実際にワークフローが開始された時刻ではなく、本来ワークフローが開始される予定だった時刻であるためです。
そのため、backfillをするときにslaを有効にしたままにすると大量のsla違反が発生することになるので注意が必要です。
Command '変数名' not foundについて

シェルスクリプトを書いていると、タイトルのようなエラーがでることがあります。

```shell
timestamp = $(stat $item | grep Modify)
echo "$timestamp"

----------------------
#出力
Command 'timestamp' not found, but can be installed with:

sudo apt install irpas
```

原因は変数名の定義部分にスペースが入っていることです。

```shell
timestamp=$(stat $item | grep Modify)
```
としてあげましょう。
地図の四隅にボタンを追加する

Mapbox の地図の左上には、拡大・縮小ボタンなどが表示されています。
ここに任意のボタンを追加してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/f5a6f2e1-123c-8491-e6d5-0a35a80243ae.png)

```javascript
// Control implemented as ES6 class
class HelloWorldControl {
  onAdd(map) {
    this.map = map;
    
    const homeButton = document.createElement('button');
    homeButton.innerHTML = '<i class="fa fa-home" aria-hidden="true"></i>';
    homeButton.addEventListener('click', (e) => {
    	alert('Home button click');
    });
    
    this.container = document.createElement('div');
    this.container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group';
    this.container.appendChild(homeButton);

    return this.container;
  }

  onRemove() {
    this.container.parentNode.removeChild(this.container);
    this.map = undefined;
  }
}

const opt = {
  container: 'map',
  style: {
    version: 8,
    sources: {
      OSM: {
        type: "raster",
        tiles: [
          "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
        ],
        tileSize: 256,
        attribution:
        "OpenStreetMap",
      },
    },
    layers: [{
      id: "BASEMAP",
      type: "raster",
      source: "OSM",
      minzoom: 0,
      maxzoom: 18,
    }],
  },      
};

opt.center = [138.73072, 35.36286];
opt.zoom = 13;
const map = new mapboxgl.Map(opt);

map.addControl(new mapboxgl.NavigationControl({
  showCompass: false,
}));

map.addControl(new HelloWorldControl(), 'top-right');
```

- [Mapbox custom control buttons - JSFiddle - Code Playground](http://jsfiddle.net/amay077/bynhx3vt/89/)

``HelloworldControl`` というボタンを示すクラスを実装し、そのインスタンスを ``map.addControl()`` します。
HelloworldControl の実装での注意点は、コンテナとなる div 要素に ``mapboxgl-ctrl mapboxgl-ctrl-group`` のクラスを設定してあげることです。
これを行うと、ボタンの見た目が、拡大・縮小などと同じになります。
逆に見た目を完全に独自にしたい場合はこれを行わず、自分でデザインすることになります。

ボタンの表示位置は画面の四隅(top-right, bottom-right, bottom-left, top-left)が選べます。
位置の細かい指定はできず、地図隅からのマージンも指定できません。
恐らくですが、``HelloworldControl`` の実装方法次第では、隅からの余白を空けることはできるのだとおもいます。

「ボタンが押された」ことの検知も HelloworldControl の実装で行います。Mapbox がサポートできる事はないです。

## 参考

- https://codepen.io/roblabs/pen/zJjPzX
- [Build Mapbox GL JS plugins | by Mapbox | maps for developers](https://blog.mapbox.com/build-mapbox-gl-js-plugins-74c52f22db63)
【Swift】Swiftで使われる演算式には何がある？


#演算式とは
演算式とは、**演算子**の種類に応じた演算を行い、演算結果の値を返す式です。
演算子とは、少しかっこよく言ってますが、`+ , - , × , ÷` などです。
ちなみにプログラミングでは、`+ , - , * , /` で表します。

こちらが演算式の例になります。

```swift

var a = 12 + 20   // 32 (+演算子)
var b = 12 - 10   // 2  (-演算子)
var c = 12 * 2    // 24 (*演算子)
var d = 12 / 3    // 4  (/演算子)
```


##少しレベルの上がった演算式

先ほどまでの演算式は全て整数同士の演算式でした。
ここからは少しややこしい演算式をご紹介します。

演算式には、**それぞれの演算に対応した型**が定義されています。

例えば、乗算を行う＊演算子は、
整数を表すInt型や、浮動少数点数を表すDouble型に対応していますが、
文字列を表すString型には対応していません。

String型に対応してる演算子なんかないだろ！と思うかもしれませんが、
実は＋演算子はString型に対応しています。

```swift

var a = 2 + 2   // 4
var b = "こんに" + "ちは"   // こんにちは
```

なので、ただ整数のみを扱うのもという認識ではなく、
小数や文字列の計算もできると言うことを頭の隅に置いておいてください。

また、演算するもの同士の型にも注意が必要です。
基本的に計算するものは全て同じ型で計算する必要があります。

Int型 + Int型や、String型 + String型などは見てきました。
では、Int型 + String型や、Double型 + Int型はどうなのか見てみます。

```swift

var a = 10   // Int型
var b = "こんにちは"   //String型
var c = a + b   // 10こんにちは ではなく、コンパイルエラー

var d = 10.0   // Double型
var e = 1   // Int型
var f = d + e   // 11.0 ではなく、コンパイルエラー
```

このような結果になります。

そして実際に出るエラー内容が、
var c = a + b のコンパイルエラー内容
`Binary operator '+' cannot be applied to operands of type 'Int' and 'String'`
和訳：二項演算子「+」は、タイプ「整数」および「文字列」のオペランドには適用できません。

var f = d + e のコンパイルエラー内容
`Binary operator '+' cannot be applied to operands of type 'Double' and 'Int'`
和訳：二項演算子「+」は、タイプ「Double」および「Int」のオペランドには適用できません。

これを見て分かったかと思いますが、
**型が違うもの同士の計算は基本的に出来ません。**

ですが、Int + String はまだしも、
Int + Double の計算でしたら今後使う可能性は高いと思います。

こういう時の解決方法は、Int型をDouble型に変える方法です。

Double + Int がダメなら Double + Double にしてしまえ！ということですね。

```swift

var a = 10.0   // Double型
var b = 1   // Int型
var c = a + Double(b)   // 11.0  Double型
```
今回は、Int型ではなくDouble型同士の計算にしたいので、
Double型のイニシャライザを使いInt型をDouble型にしてます。
var c = a + **Double(b)** 

これを行うことによりDouble型同士の計算になりコンパイルエラーは発生しません。

では、String型もInt型やDouble型に変換してから計算すればいいのでは！？
と思う方もいるかもしれません。（私は単純なのでそう思いました・・・。）

```swift

var a = "こんにちは"    // String型
var b = Int(a)   // nil
```

予想通りといえば予想通りですが、Int型にはなりませんでした(笑)
ただ、コンパイルエラーにはなりませんでした。
おそらく変換できない値が入っていると nil になるように作られているのかもしれません。

そして、これはたまたま見つけたのですが、
下記のような場合だと自動でDouble型にしてくれるっぽいです。

```swift

var a = 10.0 + 1   //11.0  Double型
```

つまりどういうことかと言うと、
**一度宣言された変数を使う場合はイニシャライザで変換してから型を合わせないといけない。
変数を使わず数字同士の計算をする場合は自動的に変換してくれる。**
ということかと思います。

もし間違っていたらすみません。


次に最後なのですが、**否定演算子**というものも存在します。

否定演算子とは"！"のことで真理値を反転する演算子になります。
真理値とは、真偽を表すtrue や false のことで、型はBool型になります。

！演算子を使うとこの結果を逆にできます。
true は false に
false は true になります。

```swift

var a = true   // Bool型
var b = !a   //false   
```

！なんか使う機会あるの？と思うかもしれませんが、
if文という記法で結構使用するらしいので覚えておいて損はないと思います。

以上、長くなってしまいましたが最後まで読んでくださりありがとうございました。
Pythonのファイル・フォルダのループ処理

`glob`を使えばよいだけなのですが、実用的には絶対パス化、ディレクトリ・ファイルのフィルタリング、ソートも行った方がよいため、コードもいくらか長くなります。

### トップディレクトリ以下の全サブディレクトリを処理する

```python
import os
import glob

# トップディレクトリをセットする sys.argv[1]など
top_level_dir_path = '/home/user'
# サブディレクトリ、ファイルの絶対パスのリストを得る
# 再帰処理（サブディレクトリ以下のディレクトリも処理する）したい場合は recursive=True にする
# recursive=False の場合はワイルドカードは '/*' でよいが recursive=Trueの場合は '/**' とする
# Windows, Linuxどちらもパスの区切り文字は/でOK
paths = map(os.path.abspath, glob.glob(top_level_dir_path + '/**', recursive=False))
# ディレクトリのみフィルタリングする
paths = filter(os.path.isdir, paths)
# ソートされていないので昇順にソートする
paths = sorted(paths)
# 1つずつ処理する
for i, p in enumerate(paths):
    print('[%s / %s] Start processing %s' % (i + 1, len(paths), p))
```


### トップディレクトリ以下のファイル名パターンにマッチしたファイルを処理する

```python
import os
import glob

# トップディレクトリをセットする sys.argv[1]など
top_level_dir_path = '/home/user'
# サブディレクトリ、ファイルの絶対パスのリストを得る
# この例ではjpgファイルを取得している
# ファイル名に特定の文字列が含まれる場合は *tokutei*.jpg とする
# /**はrecurseive=Trueと合わせて再帰的にディレクトリを全列挙するという意味
paths = map(os.path.abspath, glob.glob(top_level_dir_path + '/**/*.jpg', recursive=True))
# ファイルのみフィルタリングする
paths = filter(os.path.isfile, paths)
# ソートされていないので昇順にソートする
paths = sorted(paths)
# 1つずつ処理する
for i, p in enumerate(paths):
    print('[%s / %s] Start processing %s' % (i + 1, len(paths), p))
```


### 参考

ファイル・ディレクトリの名前のパターンマッチングについては公式ドキュメントを参照してください。

- [glob \-\-\- Unix 形式のパス名のパターン展開 — Python 3\.9\.1rc1 ドキュメント](https://docs.python.org/ja/3/library/glob.html)
Spring BootでCSV出力

Java+SpringBootで検索結果をCSVダウンロードできる機能を実装できたので備忘録として残しておきます。

まず、こんな感じで検索結果があったとします。
![スクリーンショット 2020-11-29 16.06.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637360/b24399b7-347d-064e-9c2a-6cfe2eccb544.png)

で、このCSVボタンを押すと、検索結果全てがCSVファイルとしてダウンロードされるようにします


1 - まずこんな感じで検索ボタンが押された後のレコード情報をhiddenの\<form>に入れて保持しておきます。

```HTML
        <form id="csvform" method="post" th:action="@{/asset/csv}" th:object="${csvForm}">
          <div th:each="asset:${assets}">
            <input type="hidden" name="id" th:value="${asset.id}" />
            <input type="hidden" name="categoryId" th:value="${asset.categoryId}" />
            <input type="hidden" name="adminName" th:value="${asset.adminName}" />
            <input type="hidden" name="assetName" th:value="${asset.assetName}" />
            <input type="hidden" name="remarks" th:value="${asset.remarks}" />
          </div>
          <button type="submit">CSV</button>
        </form>
```
2 -  で、CSボタンが押されたらこの情報全てをポスト送信し、コントローラー側で受け取ってCSV形式に整形し、ダウンロードしてくれます。この際に、`CSV records`ではなく、`List<CSV> records`とすると、エラーが起きてしまうのはなぜだか分からずじまい... とりあえずList\<Object>を受け取るにはList\<Object>をフィールドにもっとクラスを用意しておかないといけないみたい。

```Java
  @PostMapping(value = "/csv", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
      + "; charset=UTF-8; Content-Disposition: attachment")
  @ResponseBody
  public Object csvDownload(@ModelAttribute("csvForm") CSV records) throws JsonProcessingException {
    List<CsvColumn> csvList = new ArrayList<>();
    for (int i = 0; i < records.getId().size(); i++) { // レコードの数ぶんだけループ回して
      csvList.add(new CsvColumn(records.getId().get(i), records.getCategoryId().get(i), records.getAdminName().get(i), records.getAssetName().get(i), records.getRemarks().get(i)));
    }
    CsvMapper mapper = new CsvMapper();
    CsvSchema schema = mapper.schemaFor(CsvColumn.class).withHeader();
    return mapper.writer(schema).writeValueAsString(csvList);
  }
```
```Java
@Data
public class CSV {

  List<Integer> id;

  List<Integer> categoryId;

  List<String> adminName;

  List<String> assetName;

  List<String> remarks;

}
```
```Java
@JsonPropertyOrder({"資産ID", "資産種別", "管理者名", "資産名", "備考"})
@Data
public class CsvColumn {

  @JsonProperty("資産ID")
  private Integer id;

  @JsonProperty("資産種別")
  private Integer categoryId;

  @JsonProperty("管理者名")
  private String adminName;

  @JsonProperty("資産名")
  private String assetName;

  @JsonProperty("備考")
  private String remarks;

  public CsvColumn () {}

  public CsvColumn (Integer id, Integer categoryId, String adminName, String assetName, String remarks) {
    this.id = id;
    this.categoryId = categoryId;
    this.adminName = adminName;
    this.assetName = assetName;
    this.remarks = remarks;
  }
```
turfjs の import 戦略について考える

turf.js を使うには、

- 全てのモジュールが含まれた @turf/turf を import する
- 個々の機能の @turf/xxxx を複数選んで import する

の方法があります。


## 全てのモジュールが含まれた @turf/turf を import する方法

```console
npm i --save @turf/turf
```

```javascript
// index.js
import { buffer, lineString, LineString, Feature } from '@turf/turf';

const feature: Feature<LineString> = lineString([[130, 34],[131, 35]]);
const buffered = buffer(feature, 5, { units: 'meters' });
```

## 個々の機能の @turf/xxxx を複数選んで import する方法

```console
npm i --save @turf/helpers @turf/buffer
```

```javascript
// index.js
import { lineString, LineString, Feature } from '@turf/helpers';
import buffer from '@turf/buffer';

const feature: Feature<LineString> = lineString([[130, 34],[131, 35]]);
const buffered = buffer(feature, 5, { units: 'meters' });
```

個々の機能を選んで import する場合、基本となるデータ型やそれに係わる関数は @turf/helpers に入っているので、実質 @turf/helpers は必須になるでしょう。
この例では Buffer 関数を使うため、 @turf/buffer をインストールし、import します。
@turf/turf を使う場合とでは import 文が変わるだけで、実装コードは変わりません。

## @turf/turf の import では接頭辞を付けられるのがよいのだけど…

私はこれまで @turf/turf を好んで使っていました。
しかも、import で ``* as turf`` とすることで、すべての型や関数を ``turf.xxxx`` と書くようにしていました。

```javascript
// index.js
import * as turf from '@turf/turf';

const feature: turf.Feature<turf.LineString> = turf.lineString([[130, 34],[131, 35]]);
const buffered = turf.buffer(feature, 5, { units: 'meters' });
```

どこで turf.js を使っているかが分かりやすいためで、他のライブラリでもこのような書き方をする場合もあります。
特に ``Feature``, ``Geometry`` などの GeoJSON 由来の型は、併用することが多い [Mapbox GL JS のライブラリ](https://docs.mapbox.com/help/glossary/features/) にも存在しており、よく混同します。
なので、turfjs 用の Feature は ``turf.Feature`` と明示的に記述して差別化しています。

機能を個々に選択する場合では、以下のように別名をそれぞれ付与することができますが、、、。

```javascript
// index.js
import { 
  lineString as turf_lineString, 
  LineString as turf_LineString, 
  Feature as turf_Feature
} from '@turf/helpers';
import { default as turf_buffer } from '@turf/buffer';

const feature: turf_Feature<turf_LineString> = turf_lineString([[130, 34],[131, 35]]);
const buffered = turf_buffer(feature, 5, { units: 'meters' });
```

指定方法がめんどいのと、できれば ``turf_`` ではなく、 ``turf.`` と書きたいところです。

## んで一括と個別、どちらがよいの？

一括でダウンロードされる? ``turf.min.js`` はファイルサイズが 550KB 程度あり小さくはありません。
しかし、webpack などのモジュールバンドラーを使うなら、使わない関数などを刈り取って必要な機能だけに圧縮できそうです。

私の場合、Angular や node.js の Web Server で使うことが多いため、Webpack での shrink に期待して、一括版の ``@turf/turf`` を使っておけばよいかなあ、という判断を現在はしています。

「個別版は○○という用途で使うのだ」という知見があれば教えてください。
いかに品質とスピードをあげるか…（　＾ω＾）

はじめまして、Koji_Mです。

初投稿となりますが、自身は社会人1年目の頃から常にこれを考えながら業務を遂行しています。

最初の配属先のリーダーから言われたことが胸に響いたからです。

「SEは常に品質とスピードを上げるかがポイントだよ、
品質はある程度の自己チェックなどの手間かけることで向上できるが、
スピードはその分落ちる。かといってチェックをしないこととなると品質が下がってしまう。
この両立をいかに両立させるかがコツだよ」

と教わりました。

みんなから尊敬されている先輩が言うのなら間違いない！と思いその日から心がけるようになりました。

そこで自身は考えた結果、「VBA」を積極的に学びました。

なぜなら。エクセルでの作業が多い中、VBAをいかに使いこなすかでスピード＆品質が変わってくると考えました。

手で一つ一つ作業するよりもVBAでボタンをポチっと押すことですぐに完了しミスもありません。

もちろん、早く作業が終わった場合は最後の自己レビューにしっかり時間を割いて完璧な資料を上長または客先へ提出します。

ここで他者の報告よりも早く、ミスなく、美しい成果物を作成することにより、

信頼、評価が得られ。結果１年目から設計、開発、現在のサブリーダー経験までさせてもらいました。

このVBAで鍛えられたコーディング力はJavaやShellでの開発でも生かされました。

VBAもやたらCallでいろんなモジュール呼び出さねばならないこともあったりしますからね。

SIerでの勤務であれば最初に学ぶべきはVBAを勧めます！

作業の効率化は上記のもちろんですが、どの配属先でもエクセルがない現場はありませんからね。

今後VBAのいろんな例を挙げていきたいと思います。

Web系ではあまりエクセルをやたら使うイメージはありませんが。。。

いかに効率化させるかという観点での思考は必須だと思われます。

では！
SEのための論理的文書作成のヒント

# はじめに
プログラマ時代は同僚のプログラマやSEなど、システムに精通している人とのやり取りがほとんどだった。そのため、システムの知識などの前提条件をお互い共有しているため、意思疎通が図りやすい。しかし、SEになりエンドユーザやお偉いさんと会話したりプレゼンすることが多くなると、彼らは基本的にシステムに詳しくないのでとたんに意思疎通が難しくなる。なぜなら、こちらは前提条件をベースに話を組み立てているが、彼らはその前提条件を知らないからである。その結果、彼らからしてみればこちらが話している内容は論理の飛躍に受け止められる。彼らに納得してもらうためには、ひとつひとつの当たり前の論理を丁寧に積み重ねて、誰が読んでもわかりやすい文書を作る必要があると考えた。

ということで、この記事では論理的な文書の書き方のヒントを自分用の覚書としてまとめてみました。

# 参考サイト
NHK高校講座 ロンリのちから
https://www.nhk.or.jp/kokokoza/tv/ronri/

接続詞について （一覧と解説）
https://pothos.blue/setuzokusi.htm

クリティカル・シンキングで始める論文読解
https://jrecin.jst.go.jp/seek/html/e-learning/900/index.html

# 推論
推論とは前提から結論を導くこと。推論の仕方には、**演繹法**、**帰納法**、**アブダクション** の3種類がある。
#### 演繹法
2つの前提から結論を導く推論の仕方。
例）
　前提1：人間はいつか死ぬ
　前提2：ソクラテスは人間である
　結論 ：ゆえにソクラテスはいつか死ぬ

つまり、2つの前提が与えられてそれらの前提が正しければ、そこから導かれる結論も必ず正しくなる、という事。上記の例も書いてみると一見当たり前に見えるが、**当たり前だからこそ説得力がある。** 

#### 帰納法
複数の個別的な前提から、より一般的な規則を導くこと。
例）
　前提1：フランスの戦車がポーランドの国境にいる。
　前提2：ドイツの戦車がポーランドの国境にいる。
　前提3：ロシアの戦車がポーランドの国境にいる。
　結論 ：ポーランドが戦車によって侵略されようとしている。

演繹法では前提が正しければ、そこから導かれた結論も必ず正しい。しかし、**帰納法の場合は必ずしも結論が正しいとは限らない。**（例えば上記の例だと、ポーランド、フランス、ドイツ、ロシア連合によってヨーロッパ全土を侵略しようとしている、という結論も考えられる。）それゆえに、結論の導き方に飛躍がないか、より注意をする必要がある。

#### アブダクション（仮説的推論）
結果や結論を最も良く説明する仮説を導くこと。
例）
　結果：赤ちゃんが泣いている。
　仮説 ：赤ちゃんはお腹が減っているのだろう。

アブダクションでは、複数の個別的な事象（結果）からもっとも妥当だと考えられる仮説を導き出す。しかし、これも帰納法と同じで仮説が必ず正しいとは限らないので注意が必要。（上記の例だと、もしかしたらおしめが汚れて泣いているのかもしれない。）

# 暗黙の前提
お互いに分かっていると思って省略する前提。しかし、その前提が分からない人にとっては論理の飛躍となる。例えば、「失敗は成功のもと」という言葉も実は論理の飛躍が発生している。「失敗は成功のもと」の暗黙の前提部分を明示的に書き出すと例えば以下の様になる。

失敗する
↓
（失敗の原因を見つける）
↓
（対策を実施する）
↓
その結果、成功する

人に読んでもらう文書を書くときは、相手にとってもその暗黙の前提が通じるのかを意識することが大切である。もし、通じるのであれば、冗長さを避けるためにも省略してしまって構わないと思うが、そうでないならひとつひとつ丁寧に書き出す必要がある。

また、**暗黙の前提自体が間違っていること**が往々にしてあるので注意が必要。

# 接続表現
言葉と言葉を繋ぐ役割。例えば、以下のようなものがある。

　・AそしてB → AにBを付け加える。
　・AしかもB → AにBを付け加える。さらに強調の効果がある。
　・AしかしB → Aとは対立するBを述べる（強調したいのはB)
　・AただしB → AにBを補足する。（強調したいのはA)
　・AなぜならB → Aの理由Bを述べる。
　・AだからB → Aの結果Bとなる。
　・AつまりB → Aの本質Bを述べる。

参考サイト「接続詞について （一覧と解説）」が良くまとまっていて参考になる。

# 否定について
ある事柄の否定は、**そのこと以外の全ての可能性**を表している。例えば、「インターネットの情報は全て正しい。」の否定は「インターネットの情報は全て間違っている」ではなくて、「インターネットの情報には間違っているものもある。」となる。

# 水かけ論・合意形成
意見を主張する際には、その根拠となる理由を明示する必要がある。なぜなら、互いに自分の意見を主張するだけでは、平行線のまま結論にたどり着かない（＝水掛け論）からである。根拠を示すことで検討が可能になり、議論が深まる。

また、議論の際には意見の対立を人と人との対立にしないこと。つまり、意見そのものを検討すること。その際に、相手の意見から取り入れることができる要素がないかを考えること（歩み寄り）で合意形成に近づく。
中学3年生200人の前で「IT業界で働くということ」という講演をしてきた

知り合いの先生から声を掛けてもらって、総合学習の一環としてIT業界で働くサラリーマン代表？みたいな形でお話してきました。

その時の発表資料（公開版）  [IT業界で働くということ](https://www2.slideshare.net/YokotaEiji/lecture-for-middlestudents-239575463)

今回は、その振り返りとして、

* そのきっかけから当日話すまでにやったこと
* 当日やってみての感想
* 後日、全員の感想文をもらった結果

を書いてみます。

## きっかけ

本当に突然だったのですが、たまたま中学校で学年主任をやってる方から声を掛けてもらって、講演のような依頼を頂きました。

最初は、こんなごく普通（普通とは？ｗ）の単なる1サラリーマンの話で興味持ってもらえるの？という不安はありましたが、せっかくもらった中々ないチャンスなので、引き受けることに。

まずは、何を話すかの大体の内容を事前の打ち合わせで相談で固めました。

## 内容の整理

大枠は、ITのこと、社会人のこと、というのは決まったものの、どんな展開・構成で話していくかを考えました。

まず考えたのが、当然呼んでもらったので先生の期待には答えたい。かといって、本来のターゲットは中学3年生。そこを間違えてはいけないこと。

なので、中学3年生に向けた内容でありながら先生達にも響く内容、という方針で進めることにしました。あとは、それをどうやって詳細化するか、を考えた時にマインドマップツールを使うことにしました。

## マインドマップ

[xmind](https://jp.xmind.net/)ってツールで無料期間内で使ってみました。

### まずは拡散

方針で決めたテーマを軸にしつつ、自分が中学3年生だったらどんなことを聞きたいか、今どきの中学3年生は、どんなことに興味があるのか、想像しながら、どんどん拡散させていきました。

![話したい事_blog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1533/53f3439a-3aeb-658c-5edb-56a7743fa516.png)


書いてみると、分かったのが、自分が喋りたいことが多いw

俯瞰してみると、軸から外れた内容も盛り上がっていることにも気づきます。そこはあくまで、総合学習の一環で中学3年生の為になる物を作らないと、と冷静になりました。

## 資料作り

話したいことの拡散・詳細な洗い出しができたので、そこからストーリーをつくるイメージで、資料を作っていきました。

今回やりたかったのは、

* スプリントを回すことで精度を上げる作る方をすること
* イメージ優先で、文字を少なくすること
* できるだけ"自分の言葉"で話すこと。よく見るありきたりのいいお言葉を並べないようにすること

### スプリントを回す

資料作りあるあるで、作り出すと時間は無限にかかります。なので、単にページ内で箇条書きだけしたような最低限のものを一度つくり（スプリント１）、以降時間の許す限りスプリントを回して資料の精度を上げていきました。

結局当日まで4スプリント回すことができました。

### 文字を少なくイメージ優先

いろんなプレゼンを見て思うのは、見終わったあとに記憶に残るのは、イメージ（絵や図）だったり、強調されたキーワード、だったりします。

とすると、結局、文字を多く書くのは発表する側の都合あって、聞く側のためになってないな、と感じます。なので、できるだけ言葉を少なく、イメージを多く使うようにしました。

### 自分の言葉で

まだ社会を経験していない学生に話そうとした時に、ありきたりなありがたい言葉は、いくつも思いつきます。

それらを並べてしまいそうになるのですが、経験が伴ってないと伝わらないと思ったので、できるだけ自身が感じた"自身の言葉"で話すように気をつけました。

## 発表当日

講堂の壇上に座って、
全員が講堂に入り着席するのを待ち、
いざ、始まりました。

![lecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1533/73648c56-b879-fa28-d3f5-8250e0a5ab5a.png)


多少緊張はしたものの、普段、技術系のコミュニティで話すのと変わらないぐらいだったので、どちらかというと想像よりリラックスしてできたと思います。

話しながらも時々生徒たちに質問してみました。
面白かったのは、一番反応が良かった質問が「Vtuberって知ってる人？」だったことかなｗ

以下ざっと当日の振り返り

### 良かった点

* お茶持参してよかった。途中途中で、一呼吸おいて喉を潤せた。
* 映像入れてよかった、資料に動きをつけれた。
* 途中ぷち休憩入れれて、よかった。自分も生徒も集中力が持ったと思う。

### 反省点

* １時間いらない。もっと要点まとめれた。
* もっと一人ひとりの目を見て話せた
* もっと動く（歩く）べき。飽きさせない。
* 後半ページごとの話が長い
    * ここはまだスプリント回して精度上げたかったのですが、きりがないのでしょうがない部分ではある

## 後日感想文をもらって

後日全員分の感想をもらえたので、それを実は今、一気に読み終えたところです。このブログを書き進める元気ももらえました。

ざっと内容をまとめると以下のような感じ。

* IT業界がどんなものか知ることができた
* "考えること"、"当たり前を当たり前と思わないこと"の重要さが分かった
    * 高校進学の意味を考えるきっかけになった
* 疑問に思う→調べる→身につける→アウトプットする、を意識していくこと
* チャレンジや失敗を恐れないでいいこと

・・・・伝えたいことがちゃんと響いてて良かった！
本当に、今後の人生で、頭の片隅にでも残ってくれたら嬉しいもんです。

## まとめ

* 中３の前で話すきっかけをもらえて
* 自身の人生の振り返りができて
* いいアウトプット（発表）ができた
* （多分それなりに）心に残せたようで嬉しい
数独オリジナル問題を作成する　４ｘ４から１００ｘ１００まで

#はじめに
スケジュールナースは、組み込みPythonを使って制約する機能があります。この機能を使って、数独オリジナル問題を作成してみます。

#pythonコードの解説
pythonコードは、数独サイズ(4-100)間で、同じコードです。GUIの集合情報を組み込んでいるので、短くかつシンプルにpythonコードを書けます。

##Sanity チェック
GUIで設定している
１．全日
２．全スタッフ
３．sfiftdef

は、例えば、9x9数独の場合、全て9である必要があります。
また、その平方根は2から、10までがサポート範囲です。

```
if len(全日) !=len(shiftdef) or len(全日) !=len(全スタッフ):
    raise ValueError("全日 and shiftdef are inconsistent")
blocks=int(math.sqrt(len(全日)))
if math.sqrt(len(全日)) not in range(2,11,1):
    raise ValueError("Illegal sudoku dimmension. Supported sizes are 4x4,9x9...100x100.")
```
##行制約
sc3.xxがスケジュールナース特有の組み込み制約関数です。詳細は、下記マニュアルをご覧ください。
https://www.nurse-scheduling-software.com/tutorial/schedule_nurse3/python_manual/python3_constraint_programming_manual.html

```
for day in 全日: #column
	for shift in shiftdef.keys():
		V=[]
		for person in 全スタッフ:
			V.append(sc3.GetShiftVar(person,day,shift))
		sc3.AddHard(sc3.SeqLE(1,1,V),'')
```
##列制約
```
for person in 全スタッフ: #row
	for  shift in shiftdef.keys():
		V=[]
		for  day in 全日:
			V.append(sc3.GetShiftVar(person,day,shift))
		sc3.AddHard(sc3.SeqLE(1,1,V),'')
```

##ブロック制約
```
for person in range(0,len(全スタッフ),blocks): #block
	for shift in shiftdef.keys():
		for  day in range(0,len(全日),blocks):
			V=[]
			for  i  in range(blocks):
				for  j in range(blocks):
					V.append(sc3.GetShiftVar(person+i,day+j,shift))
			sc3.AddHard(sc3.SeqLE(1,1,V),'')

```
と、コンパクトに書けます。
#数独問題作成
##ブランク数独解を求める
ブランク数独とは、私が勝手に名づけたものです。例えば、4x4数独で、予定をブランクにして2解を求めています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916040/a2050463-ef8c-0029-5499-12a087fd5713.png)
##唯一解であることの証明
この解を予定に送り、再度求解すると、当然のことながら解は、入力した予定そのものしかありません。このとき、2解目は存在しないとのメッセージが出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916040/3366f08f-138c-ec1c-4874-c6ce90e31854.png)

言い換えれば、1解しか存在しないということを表しています。つまりこのメッセージが出ているうちは、唯一の解である証明になります。

##ブランク部を増やしていく
解が1解しかない間は、ブランクを増やしていけます。
しかし、そのうち、次のように、2解が見つかってしまう場合が出てきますが、これは、数独問題としては、不適です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916040/c4693382-4932-5193-f987-67bdb602d6b0.png)
少し前に戻って、入力をやり直してもよいですし、諦めて、直前の解を問題として終了でもよいです。
次は、唯一の解しかないので、数独問題として適しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916040/1ed7a8fe-4262-9b29-4bf0-7adcc95d3a69.png)

##サイズを増やす
100x100までやってみました。1時間程度ブランク数独解の生成にかかりました。この辺が限界のようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/916040/a1095b23-406b-a406-cfc4-fc050a063afa.png)

##動画
英語版になりますが、動画は以下です。
https://youtu.be/ofCaurpkyJM
多次元の分布関数

多次元の分布関数．
1次元の分布関数はよく見かけるが，多次元の分布関数の情報があまりなさそうなのでまとめ．1次元の分布関数といえば単調増加だが，多次元の場合，単調増加だけでは不十分であったりする．単調増加でも分布関数でないものが存在する．

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">多次元の分布関数 <a href="https://twitter.com/hashtag/%E6%95%B0%E5%AD%A6?src=hash&amp;ref_src=twsrc%5Etfw">#数学</a> <a href="https://twitter.com/hashtag/%E7%B5%B1%E8%A8%88?src=hash&amp;ref_src=twsrc%5Etfw">#統計</a> <a href="https://twitter.com/hashtag/%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%96%E3%83%AD%E3%82%B0?src=hash&amp;ref_src=twsrc%5Etfw">#はてなブログ</a><br>多次元の分布関数 - koichi yamagata のブログ<a href="https://t.co/rdzZdqvj4r">https://t.co/rdzZdqvj4r</a></p>&mdash; Yamagata (@YamagataTwit) <a href="https://twitter.com/YamagataTwit/status/1332651024599846920?ref_src=twsrc%5Etfw">November 28, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
【Panoptic Segmentation】入門編

<h1>Panoptic Segmentation</h1>
Semantic Segmentation => すべてのpixelのclassを推定するタスク
Instance Segmentation => 物体として認識されたpixelの物体番号(id)を推定するタスク
Panoptic Segmentation => pixel毎にclassと物体番号(id)を推定するタスク

数えられるクラス（車や人）を Thing クラス
数えられないクラス（空や道）を Stuff クラスとする

<h2>評価指標</h2>

<h2>Segment Matching</h2>

Groung TruthとpredictionのIoUが0.5以上だとMatchingしたとみなされる

証明そんなに難しく無いので良かったら読んでみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/5ab3ae87-7599-305f-2a8f-fe43d044b879.png)


<h2> PQ Computation</h2>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/0409bb84-de5c-35ee-4052-d475d1dc43b5.png)
matchしたsegmentのIoUの平均値


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/6b03d7f0-dde5-0c40-96ca-dc38d0ccb6f1.png)
間違いをペナルティーとして分母に加える=>scoreが小さくなる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/9e309ac9-97fe-fac8-4795-afb6876ef890.png)
合体させたものをPanoptic Qualityとする
とりあえずこの公式だけ覚えておけば完璧!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/dd648f27-8803-80f5-2e45-d47ff3737987.png)
変形するとなんとなんと、Segmentation　QualityとRecognition Qualityになるじゃないか！


*クラスに振り分けられていないpixel,曖昧なpixelは計算から外す

<h2>結論</h2>
物体番号とclassを推定するタスクなのね

やってみないとイメージ沸かないな
実装したらコード乗っけます!

<h1>参考文献</h1>
Panoptic Segmentation
https://arxiv.org/pdf/1801.00868.pdf
コンピュータビジョンの最新論文調査 Segmentation 編
https://engineer.dena.com/posts/2019.08/cv-papers-19-segmentation/
Steinで複数条件（AND）でデータをとれない。。。⇒取れた　メモ



```javascript

let userNameList = await store.read(tranSheet, { search: { group_no: groupNo.toString,date: today } }).then(data => {
                        return data
                    }).catch(e => console.log(e))

```

結果のデータを見ると、2つ項目を指定するのはできるのだが、
最初の条件しかきいてないように見える。

確かに、この書き方だとANDかORかわからないもんね。

Steinのページには、Columnを複数指定できそうな記載があるのだが、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737206/ed1870a9-3bc9-f2cb-b63a-c4a141c7e7cb.png)


何を変えたわけではないが、なぜか取れるようになった。

```javascript

let userNameList = await store.read(tranSheet, { search: { date: today, group_no: groupNo.toString() } }).then(data => {
                        return data
                    }).catch(e => console.log(e))
```

何、この現象。。。


見えた！

STEP1　group_noの型が数字でとろうとしていたから取れなかった。
STEP2　group_noの文字列に変換がうまくいってなかった。
STEP3　うまく変換できて取れた！toString()になっていなかったからか！
【初心者でもわかる】php等を使わなくてもCSSで文末文字省略（…）する方法（複数行対応）

#どうも7noteです。文字を行で省略して最後の文字を「…」にする方法。

IE非対応ですが、CSSだけで文字省略する方法が神がかり的に便利だったので使い方をまとめます。
YouTubeでもこの方法を使って動画タイトルを省略しているようです。

**見本**
![sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/0449654d-7c19-0a93-8262-efa71b9426bd.gif)


## ソース

``` style.css
.text-ellipsis {
  display: -webkit-box;          /* 魔法の呪文 */
  -webkit-box-orient: vertical;  /* 魔法の呪文 */
  -webkit-line-clamp: 2;         /* 表示行数を指定。0以上の整数。 */
  overflow: hidden;              /* はみ出た部分を非表示 */
}
```

たったこの4行だけで、文章が長くなってもスッキリ見せることができます。
表示させる行数が指定できるのはとても便利ですね。

レスポンシブ等で無効化にする時は以下のようにすることで打ち消しが可能です。

```css
/* スマホだけ打ち消したい時は以下のように指定 */
@media screen and (max-width:768px) {
  .text-ellipsis {
    display: block;
    overflow: visible;
  }
}
```


### 注意点

- CSSで消しているので、**ソース上ではすべての文書が記載されている**。（ブラウザでソースを表示すると分かる）
- :hover時の下線（`text-decorarion: underline;`）等は**効かない**。透明度（`opacity`）も**効かない**。
- 「…」以外の記号や文字にしたり、「…」の文字色や大きさなどの**装飾ができない**。
- `box-orient`は非推奨のプロパティのため、のちのち動作しない可能性がある。

## まとめ

phpだとUTF-8でバイト数が3になってしまったり、文字数で制限してもそもそもフォントによって文字幅が違うから綺麗に文末で省略（…）させるのが難しかったのですが、
これならCSSだけで簡単に装飾できるため、文字省略の決定版といっても過言ではない。

IEが非対応だが、phpでもある程度の省略をかましながら、両方使うことでそこそこ綺麗に見せることは可能だと思います。

参考：https://www.white-space.work/text-ellipsis-php-and-css/

## 1行だけならIEにも対応可能な書き方

```style.css
.text-ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
```



#### おそまつ！
~ Qiitaで毎日投稿中!! ~
[【初心者向け】HTML・CSSのちょいテク詰め合わせ](https://qiita.com/7note)
Vue.js Props　$emit  個人チート

##　propsについて

1. 親子間でのデータの受け渡しを行う為に使用する。
 - 親では任意の属性名でを子コンポーネントタグで作成して、渡したいデータの定義を行う
 
```
親コンポーネント
<template>
  <div id="app">
    <Nav :data="message" />
    <router-view />
    <input type="text" v-model="message" />
    <Footer />
  </div>
</template>

<script>
import Nav from './component/Nav.vue';

export default {
  conponents: {
    Nav,
  },
  data () => {
    message: 'this is props'
  }
}
</script>
```
受け取るには子の方でpropsとして配列で受け取る。
親は属性名にデータが格納された変数と考えよう。なのでpropsに登録するのは属性名。

```
<template>
  <div>
    <p>{{ message }}</p>
    <p>Navです</p>
  </div>
</template>

<script>
export default {
  props: ["message"],
};
</script>
  required: true,

```

ちなにオブジェクトとして受けて、型のチェックもできる。
詳しい内容は公式でキュメンとで: [Vue.js,props](https://jp.vuejs.org/v2/guide/components-props.html "Vue")

```
<template>
  <div>
    <p>{{ message }}</p>
    <p>Navです</p>
  </div>
</template>

<script>
export default {
  props: {
    message: {
      type: String,
      required: true,
    },
  },
};
</script>

```
reactと比較すると

```
//親
import React from "react";
import { Nav } from "./Nav";

export default function App() {
  const message = "this is props";
  return (
    <>
      <p>親</p>
      <Nav message={message} />
    </>
  );
}

//子コンポーネント
import React from "react";

export const Nav = (props) => {
  const { message } = props;
  return <>{message}</>;
};

```

ここで忘れてはいけないのは親、子、ひ孫というコンポーネント管理があったとしたら、親→ひ孫へのデータ渡しはできない。
なので上記の方法と一緒でpropsを経由して一つ一つデータを渡して行かないといけない。
流石にこれではprops地獄になるので大規模になればデータの一元管理はVuexが望ましい。

## $emit
1. 子から親へデータを渡す際に活用する。
 - 子コンポーネントでmethodsとして$emitを活用する。
 - $emit（）第１引数はカスタムイベント名、第２引数は渡したいデータ。親はこの第１引数のカスタムイベント名を使用する。登録はケバブ出ないとダメ
　

```
<template>
  <div>
    <p>{{ message }}</p>
    <p>Navです</p>
    <button @click="send">親にデータ送ります</button>
  </div>
</template>

<script>
export default {
  props: {
    message: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      emitData: "this is emit",
    };
  },
  methods: {
    send() {
      this.$emit("send-emit-data", this.emitData);
    },
  },
};
</script>
```

2. 親でデータを受け取る。
 - データを送ってきた子コンポーネントタグで$emitで送られたエベント名を使用する。
 - 今回は引数にメソッドを定義している。
 - メソッドの引数は**value**として受け取る。これが$emitで送信したデータ。後はこれをデータプロパティに代入するだけ。
 
```
<template>
  <div id="app">
    <Nav :message="message" @send-emit-data="receive" />
  </div>
</template>

<script>
import Nav from "./components/Nav";

export default {
  components: {
    Nav,
  },
  data() {
    return {
      emitData: "",
    };
  },
  methods: {
    receive(value) {
      this.emitData = value;
      console.log(this.emitData);
    },
  },
};
</script>
```

## 終わりに
実践的チートシートにするべく再度編集予定
BigQueryのDATETIME_DIFF関数の罠

BigQueryでDATETIME型の差を求めるためにはDATETIME_DIFF関数を使います。
しかし、この関数は直感に反する動きをすることがあるため注意が必要です。

以下のSQLを実行すると、結果は1になります。2つのDATETIMEの間の差は2分だけなのに、不思議ですね。

```sql
select datetime_diff(datetime '2020-12-01 01:01:00' , datetime '2020-12-01 00:59:00', hour)
```

一方で上記と同じく2分の差である以下のDATETIMEを渡しても、結果が0になることもあります。

```sql
select datetime_diff(datetime '2020-12-01 01:05:00' , datetime '2020-12-01 01:03:00', hour)
```

差を出したあとの端数の処理(四捨五入・切り上げ・切り捨て)などが関係しているような気がしますが、ちょっと謎です。

# 答え
この関数は2つの日付の差ではなく、「差に近しいもの」を返す関数だった。

公式ドキュメントには以下のように書かれています。

> DATETIME_DIFF counts the number of part boundaries in this range of DATETIMEs.

https://cloud.google.com/bigquery/docs/reference/standard-sql/datetime_functions#datetime_diff

the number of part boundariesという部分がポイントです。
これを雑に意訳するならば、「指定された期間的に切りの良い時点の数」です。
これでもまだ良くわからない人が多いと思うので、具体例を挙げて説明します。

今回の2つの例では、partは第3引数で指定されているhourです。
なので、この場合のpart boundariesは以下の時刻です。

```
...
2020-11-30 23:00:00
2020-12-01 00:00:00
2020-12-01 01:00:00
2020-12-01 02:00:00
2020-12-01 03:00:00
2020-12-01 04:00:00
...
```

1つの目の例では2つのDATETIMEの間に上記の時刻は1つ(2020-12-01 01:00:00)含まれるため、結果は1になりました。
2つめの例では2つのDATETIMEの間に上記の時刻は1つも含まれていないので結果は0です。

このようにDATETIME_DIFFは差を計算するのではなく、「差に近い何か」を計算するため注意が必要です。
この挙動を避けるためには第３引数のparをsecondなどの細かい粒度の単位にして、DATETIME_DIFFの外側に丸め処理を書くのが良いです。

```sql
select round(datetime_diff(datetime '2020-12-01 01:05:00' , datetime '2020-12-01 01:03:00', second) / 60 / 60)
```

# ちなみに
TIMESTAMP型の差を求めるための関数である TIMESTAMP_DIFF関数にはこのような罠は存在せず、以下の2つはどちらとも0を返します。

```sql
select timestamp_diff(timestamp '2020-12-01 01:01:00' , timestamp '2020-12-01 00:59:00', hour)
```

```sql
select timestamp_diff(timestamp '2020-12-01 01:03:00' , timestamp '2020-12-01 01:01:00', hour)
```