site_bc\strftime

composer にあった site_bc\strftime がイマイチだったのでかなり前に自作したんだけど、公開してなかったので供養。

最近だと生成ツールに「strftime() と互換性のある関数作って」と言えば作ってくれそうだけど。
(ちょっと前に試したらイマイチだったけど、近年ならマシになってるかも)

<?php
namespace site_bc;

use Exception;

function strftime($format, $ut=null) {
	if (STRFTIME::$FUNC) {
		return STRFTIME::conv_func($format, $ut);
	}

	$date_format = STRFTIME::conv_format($format);
	return date($date_format, $ut);
}

class STRFTIME {
	static $FUNC = true;
	static $FULL = true;
	const FORMAT = [
		'a' => 'D',
		'b' => 'M',
		'c' => ['D M j H:i:s Y', 'conv_c1'],
		'd' => 'd',
		'e' => ['j', 'conv_e1'],
		'f' => '\f',
		'g' => ['o', 'conv_g1'],
		'h' => 'M',
		'i' => '\i',
		'j' => ['z', 'conv_j1'],
		'k' => ['G', 'conv_k1'],
		'l' => ['g', 'conv_l1'],
		'm' => 'm',
		'n' => "\n",
		'o' => '\o',
		'p' => 'A',
		'q' => '\q',
		'r' => 'h:i:s A',
		's' => 'U',
		't' => "\t",
		'u' => 'N',
		'v' => ['j-M-Y', 'conv_v1'],
		'w' => 'w',
		'x' => 'm/d/y',
		'y' => 'y',
		'z' => 'O',

		'A' => 'l',
		'B' => 'F',
		'C' => ['', 'conv_c2'],
		'D' => 'm/d/y',
		'E' => '',		# 特殊ケースあり
		'F' => 'Y-m-d',
		'G' => 'o',
		'H' => 'H',
		'I' => 'h',
		'J' => '\J',
		'K' => '\K',
		'L' => '\L',
		'M' => 'i',
		'N' => '\N',
		'O' => '',		# 特殊ケースあり
		'P' => '\P',
		'Q' => '\Q',
		'R' => 'H:i',
		'S' => 's',
		'T' => 'H:i:s',
		'U' => ['W', 'conv_u2'],
		'V' => 'W',
		'W' => ['W', 'conv_w2'],
		'X' => 'H:i:s',
		'Y' => 'Y',
		'Z' => 'T',

		'%' => '%',
	];

	static $search;
	static $replace;
	static $invalid;
	static function _mk_replace() {
		if (! isset(static::$search)) {
			static::$search = [];
			static::$replace = [];
			static::$invalid = '';

			foreach (static::FORMAT as $f => $t) {
				static::$search[] = "%$f";
				if (is_array($t)) {
					static::$replace[] = $t[0];
					static::$invalid .= $f;
				}
				else {
					static::$replace[] = $t;
				}
			}
		}

		return [ static::$search, static::$replace, static::$invalid ];
	}
	static function conv_format($format, $full=null) {
		if ($full === null) $full = static::$FULL;

		$format = preg_replace('/(?<![%])([a-zA-Z])/', '\\\\$1', $format);;

		$sr = static::_mk_replace();
		if ($full) {
			if (preg_match("/(%[{$sr[2]}])/", $format, $m)) {
				throw new Exception("can't convert date format '{$m[1]}'");
			}
		}

		return str_replace($sr[0], $sr[1], $format);
	}

	static function conv_func($format, $ut=null) {
		$ut ??= time();

		$date_str = preg_replace_callback(
			'/[%]([a-zA-Z%])/',
			function ($m) use ($ut) {
				$conv = static::FORMAT[$m[1]];

				if (is_array($conv)) {
					return static::{$conv[1]}($ut);
				}

				return date($conv, $ut);
			},
			$format
		);

		return $date_str;
	}

	static function conv_e1($ut) {
		return str_pad(idate('d', $ut), 2, ' ', STR_PAD_LEFT);
	}
	static function conv_g1($ut) {
		return substr( date('o', $ut), -2);
	}
	static function conv_c2($ut) {
		return (int)(idate('Y', $ut) / 100);
	}
	static function conv_k1($ut) {
		return str_pad(idate('H', $ut), 2, ' ', STR_PAD_LEFT);
	}
	static function conv_l1($ut) {
		return str_pad(idate('h', $ut), 2, ' ', STR_PAD_LEFT);
	}
	static function conv_j1($ut) {
		return str_pad((date('z', $ut) + 1), 3, '0', STR_PAD_LEFT);
	}
	static function conv_c1($ut) {
		$day = static::conv_e1($ut);
		return date("D M $day H:i:s Y", $ut);
	}
	static function conv_v1($ut) {
		$day = static::conv_e1($ut);
		return date("$day-M-Y", $ut);
	}

	static function calc_weeknum($ut, $target) {
		$year = idate('Y', $ut);
		$ut_d1 = mktime(0, 0, 0, 1, 1, $year);
		$d1_week = idate('w', $ut_d1);
		$next = (7 - $d1_week + $target) % 7;
		$ut_ds = mktime(0, 0, 0, 1, $next + 1, $year);
		$dd = date('z', $ut) - date('z', $ut_ds);
		$weeknum = floor($dd / 7) + 1;
		return str_pad($weeknum, 2, '0', STR_PAD_LEFT);
	}
	static function conv_u2($ut) {
		return static::calc_weeknum($ut, 0);
	}
	static function conv_w2($ut) {
		return static::calc_weeknum($ut, 1);
	}

}


新しく買ったデュアルモバイルモニタ(14インチ、WUXGA×2)は凄くいい

 2021年にリモートワーク用にモバイルモニタを買って便利に使っていたのですが。
asakura-t.hatenablog.com

 2023年頃にノートPCの左右にモニタを追加するモデルが出てきたのに興味はありつつ、自分の使い方だと左右に追加するのはイマイチだなぁとスルーしていたところ。
pc.watch.impress.co.jp

 2024年に入って「折りたたみ式デュアルモニタ」というのが出始めて。
pc.watch.impress.co.jp

 縦置き可能な点には凄く心を引かれたけれど、最初に見かけたのはクラファンなのでスルーしていたところ、サンコーからクラファンより安価なものが出たので速攻で購入。
pc.watch.impress.co.jp

www.thanko.jp

 デュアルで縦置き可能なのは本当にいい感じ。……なのだけれど、使っていると

  • 幅1080pxは物足りない。やはり1200pxは欲しい
    • なお横置きで縦1080pxなのは全然足りない(縦pxが欲しいから縦置きしたいのだし)
  • 15.6インチはちょっと大きい

――みたいな不満が出てきたりもする。
 その後に見かける類似の折りたたみモニタはどれこもこれも「フルHD」「15インチクラス」で代わり映えしない。最近はノートPCもWUXGAが増えてきてるのに……とか思ってたときにサンコーから新モデルの発表が!
pc.watch.impress.co.jp
www.thanko.jp

 知った瞬間に即注文。
 実際に使ってみると、不満に思っていた部分は全て解消!
 また、以前のものと違って2画面を1つの画面とするモードもあるので、場合によってはそっちのほうが便利かもしれない。

 いやほんと、買って良かった。

telnetな電子公告をPowerShellで読み書きする

 一部で話題のtelnet電子公告に関するコメントを眺めてたら

あれはtelnetではなくただのsocket通信では?

netcatで読むのが便利(ncat ならssl通信もできるし)

的な話があった。確かにそうだなと思い、socket通信ならWindowsにわざわざtelnetを追加しなくてもPowerShellでいいのでは?とちょこっと調べて書いてみた:

$hostname = "koukoku.shadan.open.ad.jp"

$port = 23
$enc_name = 'shift_jis'

$start_cmd = "notalk"

$encoding = [System.Text.Encoding]::GetEncoding($enc_name)

$socket = New-Object System.Net.Sockets.TcpClient($hostname, $port)
$stream = $socket.GetStream()

$writer = New-Object System.IO.StreamWriter($stream, $encoding)
$writer.WriteLine($start_cmd);
$writer.Flush()

$reader = New-Object System.IO.StreamReader($stream, $encoding)
while (($line = $reader.ReadLine()) -ne $null) {
    Write-Output $line
}

$socket.Close()

 いちおうこんな感じで動くっぽい。

 もっといい感じの書き方がありそうな気がするけど、PowerShellは全然触ったことないからモダンな書き方が分からない。

 SSL版だとこんな感じ:

$hostname = "koukoku.shadan.open.ad.jp"

$port = 992
$enc_name = 'utf-8'

$start_cmd = "notalk"

$encoding = [System.Text.Encoding]::GetEncoding($enc_name)

$socket = New-Object System.Net.Sockets.TcpClient($hostname, $port)
$stream = $socket.GetStream()

$sslStream = New-Object System.Net.Security.SslStream($stream, $false)
$sslStream.AuthenticateAsClient($hostname)

$writer = New-Object System.IO.StreamWriter($sslStream, $encoding)
$writer.WriteLine($start_cmd);
$writer.Flush()

$reader = New-Object System.IO.StreamReader($sslStream, $encoding)
while (($line = $reader.ReadLine()) -ne $null) {
    Write-Output $line
}

$socket.Close()

 個人的には"notalk"の部分を"nobody"にしてチャットだけ見てたり、$writer.WriteLine() に$argsを書いてチャット投稿するコマンドを作ったりしています。

(2画面開いて使ってる)

(おそらく現状では古くなってる)kvmでFreeBSDのイメージを使った時のメモ

virt-installでのインストール

kvm# virt-install \
    --name=<DOMNAME> \
    --ram=1024 \
    --vcpus=1 \
    --hvm \
    --disk=<ディスクイメージファイル>,format=qcow2,bus=virtio \
    --cdrom=<CDROMイメージファイル> \
    --network=bridge=br0,model=virtio \
    --video=vga \
    --graphics none
  • ブート画面が出たら ESC で一旦抜ける
  • オプションを指定し、起動する
OK set console=comconsole
OK set hw.broken_txfifo=1
OK boot
  • インストール作業(略)
  • インストール終了後、起動時のシリアルコンソールを有効にする
freebsd# tee -a /boot/loader.conf <<EOF
console="comconsole"
hw.broken_txfifo=1
EOF

仮想イメージの利用

kvm# virt-install \
    --name=<DOMNAME> \
    --ram=1024 \
    --vcpus=1 \
    --hvm \
    --disk=<仮想イメージファイル>,format=qcow2,bus=virtio \
    --network=bridge=br0,model=virtio \
    --video=vga \
    --graphics=none \
    --boot=hd
  • ※FreeBSD13以降で配布されている仮想イメージファイルはシリアルコンソールでの作業ができないので、diskを仮想イメージ、cdromをFreeBSD12等のブートイメージを指定したうえでcdromから起動し、/boot/loader.conf にシリアルコンソールを使う設定を追加する、とかが必要っぽい
  • シリアルコンソールの指定等は通常のインストールと同様にする

設定の追加、変更

  • bsdconfig で以下の設定を変えといたほうがよさそう
7 Console
        3 Keymap をJapanese 106にしないとVNC接続の時に困る
8 Timezone
        Asia/Japan を選択
A Network Management
        1 Hostname/Domain
        2 Network Interfaces
            ipaddr や netmask を設定
        4 Default Router/Gateway
        5 DNS nameservers

ストレージの拡張

  • イメージを増やす
host> qemu-img resize foo.qcow2 +10G
  • FreeBSD側で認識させる
    • シングルユーザーで起動
# gpart recover vtbd0
# gpart show -l
# gpart resize -i 4 vtbd0
# gpart show -l
# growfs /dev/gpt/rootfs
# df -h

モデルナ製ワクチンの都道府県別配分状況(および配分傾向で気になること)

 モデルナの配分計画について公表されているものは見つけられなかったのだけど、首相官邸のワクチンページの「都道府県別の実績」にワクチン種別による接種回数と供給量が記載されているのが分かった。
 こちらはエクセルでも公表されてるので便利。
(供給がいつだったのかは分からないので、このファイルをクロールしてるメディア等がいるだろうから、そのうちどこかで公開して欲しい)

 とりあえず2021-10-04時点のデータをコピペし、モデルナ分を加算してみた。


 モデルナ分を加算した感想は以下の通り:

  • 現在の予定では12歳以上人口の約86%分の配分が実施される模様
    • モデルナの未配分が850万人分程度あるので、今後は主にそれが使われるのだろう(当初の計画よりモデルナが使われていないのは、輸入が遅れた混乱によるものかな?)
  • ファイザーの配分が8割を超えていた秋田、和歌山、宮崎はモデルナの利用が少ないため合計確保量としては84%未満と低めになっている
    • なお84%未満の自治体はかなりあるので、今後はこれらの自治体でモデルナによる接種が始まる/増える可能性はありそう
  • ファイザーもモデルナも多いのは山梨で、9割以上になっている(なぜ多かったのかは気になるところ)。また茨城、新潟はモデルナを足すと9割前後になっている
  • ファイザーが少なめだった宮城、群馬、愛知はモデルナを含めると88%以上を確保していた(これは予想通り)。
    • 三重がやや少なめなのは愛知で接種しているからなのかは気になるところ
  • 東京は100%を超える量を確保している。ただし隣接する埼玉、千葉、神奈川が非常に少なくなっている
    • これはおそらく「東京で接種しているから」ということなのだとは思うけれど。裏を返すと「東京に出てこい」ということなので、これの功罪については検証すべきだろう(少なくとも東京はかなり優遇されていたと言えよう)
  • 大阪もモデルナを加えると多くなるが9割に満たないため、東京ほどの集中ではない。隣接する京都、兵庫がやや少なめなのが気になるところ(大阪で接種している前提なのかどうか)
  • 島根はモデルナを足しても約81%で、かなり少ない配分になっている。隣接する県で接種している感じでもなさそうなので、気になるところ。
    • 現状の接種率そのものは低いわけではないので(消化率が高い)、今後モデルナの配分が増えるのかもしれない


 ついでに、64歳以下への配分がどの程度なのかも推計してみた。
 65歳以上の接種率が9割程度とのことなので、総数から65歳以上人口の9割を引いて、それを64歳以下の接種対象人口と比較したものである。
(実際は都道府県毎に65歳以上の接種率は違っているためそれを考慮すべきだろうけど、今回はそこまではしない)


 64歳以下の配分推計についての感想は以下の通り:

  • 64歳以下に対しても85%程度のワクチン配分計画になっている
  • ただし、自治体(都道府県)による差が非常に大きく、今後問題になるかもしれない
  • 64歳以下のワクチン配分量が多い(9割前後)のは 宮城、茨城、群馬、新潟、山梨
  • 64歳以下のワクチン配分量が少ない(8割未満)なのは 秋田、福島、島根、広島、徳島、高知、福岡、大分、宮崎、鹿児島
    • これらの県では今後モデルナの接種を進めないと問題が起こりそう
  • 東京は109%なので非常に多いが、隣接する埼玉、千葉、神奈川が非常に少ない(千葉、神奈川は7割前半)
    • 南関東4都県を合計すると配分量は約88%なので「やや多い」くらいになる
    • 要するに「東京で接種しろ」ということなのだろう。それでよかったのかは今後検証すべき。
    • このように東京は非常に優遇されているのに「足りない」という話が出るのは、東京都の各自治体への配分に問題があった可能性が高そうなので、今後検証すべき
  • 三重は8割未満で少ないが、愛知で接種しているためなのかは気になるところ(合計すれば標準的な量になる)
  • 大阪が9割弱の配分でやや多いが、これも京都、兵庫の分を含んでいるのだろう(合計すると「やや少ない」程度になる)
  • 上記の以外でも配分が少なめに見える県はかなりあり、今後は地方から「ワクチンが足りない」という話が数多く出てくる可能性がある
    • 元々モデルナの利用が少ないところは職域でまとめることが難しいのだろうから、県庁あたりが主導して集団接種会場を作るしかなさそう

ファイザー製ワクチンの都道府県別配分状況

 厚労省のサイトにPDFでクール毎の配分予定はあったけど、結局トータルでどうなってるのかが分かりにくかったので引き写してみた。

 とりあえずOneDriveに置いたけど、どこに置くのがいいんだろうか。


 上記を作成するために参照したURLは以下の通り:

 上記にあった都道府県別の箱数を表にまとめた。総箱数等は「供給の見通し」にあった数と合ってるのでたぶん入力ミスはないと思うが、間違いがあったらご指摘ください。

 都道府県別の人口については統計局のデータを利用。
 こちらはエクセルのファイルで公開されていたのでコピペしただけで済んでよかった。



 この配分計画を見ると人によって色々思うことはあるだろうけれど。
 僕の感想は以下の通り:

  • ファイザー製ワクチンは接種対象者の約7割分になったらしい
    • モデルナが0.25億人分のはずなので(ちゃんと入荷していれば)約2割分はありそう
  • 最終的にファイザーが12歳以上人口の8割配分があったのは 秋田、山形、山梨、和歌山、宮崎 になる模様(その分モデルナは少なそう)
  • 逆に7割以下になるのは 宮城、群馬、東京、神奈川、愛知、京都、大阪 になる模様
    • 東京、神奈川、京都、大阪は大規模接種や職域接種でモデルナが大量に使われている感じがあるので納得ではある
    • 宮城、群馬、愛知もモデルナの利用が多いのかは気になるところ
  • 神奈川は12歳以上人口に対する配分数が9クールまで非常に少なかった。そりゃ8月に入るまで「64歳以下のワクチン接種できません」と言うしかないよな。
  • 第7クール(6月前半)分で65歳以上の8割分は分配していたらしい
    • 南関東では東京と千葉・神奈川の差が大きく「五輪で東京を優先していたのでは?」という疑念がある(ただし、東京より多い比率の県もあるし、12歳以上人口比率からすると多いとは言えないかもしれない)
    • 沖縄も65歳以上に対する配分はそれなりにあったが、12歳以上に対する比率としては少ないので64歳以下の接種が始まってから苦労していたのかもしれない
  • 第8クール(6月後半)分で65歳以上人口を超過していたらしい
    • 7月頃から64歳以下のワクチン接種が始まっていそうなのに始まらなかったのは基礎疾患を持つ人を優先していたためだろうか(このあたりがよく分からない)
  • 第8クール以降の分を64歳以下向けと想定すると、東京・群馬の配分が非常に少ない
    • ただし東京は第7クールまでの配分が多かったし、大規模接種や職域接種でモデルナを大量に利用できていたので総合的には配分量が多くなっていたと思われる
      • 東京の各自治体に対する配分がおかしかったのでは?ということは気になってる(どうも23区を優先していた雰囲気があるため)
    • 群馬の接種率は高いほうであるため、モデルナの配分が多かったのかもしれない
  • 京都・大阪の64歳以下向け配分が少なめなのも大規模接種があったからかもしれない(会場は大阪なので京都が不利益だった気もする)
  • 山口も64歳以下向け配分が少ないが、それ以前の65歳以上での配分が多かったので、その調整のためだろう
    • 65歳以上向けが著しく多かったのは気になるところ(総理の地元だからでは?以外の理由はあるのだろうか)

新型コロナ感染拡大対策、の確認

 どこかに整理している記事もありそうだけど、自分の理解もかねて。

 とりあえずモデルとして感染者1人が1.5人に感染させる、という前提で考える。
(感染者120人がいると180人に感染させる)

人流を減らす

 感染者の識別は難しいから全体を減らそう、というもの。
 感染者120人のうち80人しか出回らない(1/3を抑制)なら次の感染は120人に抑えられるし、60人(1/2を抑制)なら90人に抑えられる、みたいな計算のはず。
(実際には接触の機会が減るのでもっと減るはず、みたいな複雑な話は今回は捨てる)

保健所による追跡調査

 感染者を見つけて次の感染を起こす前に隔離しよう、というもの。
 感染者120人のうち次の感染を出す前に40人(1/3)見つけて隔離すれば、次の感染は80人の感染者が引き起こす120人に抑えられるはず。
 実際に次の感染を出す前にどれくらい見つけられているのかは分からないけど、感染者が少ない状況のときは相応に効果を上げているように見える(前述の1/3より多く見つけていそう)。
 逆に感染者が増えすぎると調査が追いつかず感染を出す前に見つけられていないように思う。
 「PCR検査を増やそう」というのは、ここでの感染者の見落としを減らしたいためだろう。

スプレッダー(クラスター)対策

 感染者全員が次の感染を引き起こしているのではなく、一部の感染者(スプレッダー)が多くの感染を引き起こしているらしい。
 例えば、感染者120人のうち他者に感染させる人が30人しかいないとして、それぞれ6人に感染させているとトータルで180人に感染させることになる(感染者120人が180人に感染させるのと同等になる)。
 であれば、この30人を見つけて隔離すれば広がらないはず、というもの。
 例えばスプレッダーの条件および比率が

  • 生活を共にする(10人)
  • 飲酒を伴う外食(5人)
  • 3密(5人)
  • その他(10人)

――であれば、「3密」「飲酒を伴う外食」をなくすことで次の感染者を減らせる(20人が120人に感染させる)ことになる。
 実際にはスプレッダーの条件・比率が明確でないため、その線引きに揉める要因があるのだろう。
(上記なら「3密」は条件としてはあまりよくなく、「会食」のほうがいいかもしれないし)

 先日「高リスク者が全員感染し隔離されると新規感染者が減るのでは?」という与太話があったけど、スプレッダーが減るから減るはずなんだよね。前述の「その他」が全て「高リスク者」なのだとすれば、それだけで感染拡大者の1/3が減るわけだし。

対策の組み合わせ

 スプレッダー対策と追跡調査で感染拡大者をそれぞれ2/3にできる(1/3を減らせる)のであれば、感染拡大者を4/9に減らすことになるはず(120人のうち53人が感染拡大者として80人に感染させる)。
 実際にはそこまで減っていないのは、前提の「感染者1人が1.5人に感染させる」(再生産数=1.5)より多いからだろう。
 再生産数を変えた場合、

  • 再生産数が3の時、感染者120人のうち拡大者を4/9に減らしているなら、次の感染者は160人
  • 再生産数が6の時、感染者120人のうち拡大者を4/9に減らしているなら、次の感染者は320人

――程度になるはずだけど、そこまで多いようには見えない(人流で減っている分があったとしても)。
 であれば、スプレッダー対策と追跡調査はもっと効率がよいのかもしれず、例えば拡大者をそれぞれ1/2に減らしているのであれば、

  • 再生産数が3の時、感染者120人のうち拡大者を1/4に減らしているなら、次の感染者は90人
  • 再生産数が6の時、感染者120人のうち拡大者を1/4に減らしているなら、次の感染者は180人

――になり、再生産数が3程度であれば人流を減らさなくても大幅に減るし、再生産数6程度なら人流を2/3程度にする必要がありそうに見える。


 ちなみに与太話で書いた「高リスク者感染隔離」が本当に1/3程度になるなら、それだけで次の感染者は120人程度になり、人流が少し減れば次の感染者は減っていきそうである。
 また「飲酒を伴う外食」がスプレッダーとして1/6程度寄与しているのなら、それが可能になった時にそこそこ感染者が増える傾向になりそうな気もする。
(このあたりスプレッダー要素の寄与度は全然分からないからねぇ)