ScalaMatsuri 2018トレーニングデイの感想。またはチュートリアルがひどかった件
ScalaMatsuri2018というScala言語のカンファレンスに参加してきました。といっても3日間のうちの1日目(というか0日目みたいな位置づけ)のトレーニングデイというものだけですが。 最近Scalaを使っているし、Scalaユーザーにいくつか聞けたらなぁーとかちょっと勉強したいなーって感じで。
運営の方はおそらく大変だったと思いますがいろいろ対応してくださって助かりました。 ただ、自分が参加したチュートリアルセッションはひどかったです。自分もハンズオンで教えることがあり、失敗を幾度かしてフィードバックをもらっては改善してきました。ので、今回は私がフィードバックをする番だと思いましたので、このエントリにさせていただきます。
- トラックとかモチベーションについて
- Scala入門ハンズオンのタイムライン
- Implicit入門
- CTO座談会
- まとめ
Java/Groovyでうるう秒を扱う方法
Java言語でうるう秒を扱う方法を調べたのでまとめておきます。Twitterで質問したらたくさんのリプライ、エアリプがありそれらをもとに調べることで達成できました。ありがとうございました。
概要
- Java標準APIのOracle実装ではうるう秒を扱えない。Calendar, Dateなどの旧型のAPI、ZonedDateTimeなどのDate and Time APIどちらも。
- ThreeTen-Extra というライブラリを使うとうるう秒を扱える。
うるう秒を扱えるということに対して期待していること
4年に1度だけ2/29があるうるう年のように、2年から5年に1度くらいの間隔で世界中の時計が1秒だけ増えることがあります。これをうるう秒と言っています。最近だと日本時間で2017/1/1 08:59:59 のつぎが 2017/1/1 08:59:60 となりました。普段はない60秒があり、1分間が61秒になりました。 これはいつ挿入されるかは規則はいまのところありません。(どれくらい前に告知されるのかは決まっていないのかな?)
で、システムにおいてうるう秒においても 08:59:60 のように時刻を正確に記録できるのかが気になりました。
というのも例えば、UNIXTIMEはうるう秒を丸めるという仕様になっています。
具体的には次のような形になります。
- 東京の時刻
- UNIXTIME
- 2012-07-01T08:59:59+09:00[Asia/Tokyo]
- 1341100799
- 2012-07-01T08:59:60+09:00[Asia/Tokyo] <= うるう秒挿入
- 1341100799 <= 1秒前と同じ
- 2012-07-01T09:00:00+09:00[Asia/Tokyo]
- 1341100800
ということで、UNIXTIMEを使うとうるう秒時点の扱いを気をつけなければいけません。(保存にしろ、描画にしろ)
ので、流れとして
- 「2012-07-01T08:59:60+09:00[Asia/Tokyo]」という時刻を表現できるようにするにはどうしたらいいのだろうか?という視点で調べました。
- このシステムはうるう秒が来ても正確に動作するシステムであるといえるようにしたい。
- となると、うるう秒を発生させるテストを書かなければいけない。
- (例えば)Javaでうるう秒を任意に起こすことはできるのか?
という形です。
そこで、「2012-07-01T08:59:59+09:00[Asia/Tokyo]」のオブジェクトを生成し、1秒すすめたときに「2012-07-01T08:59:60+09:00[Asia/Tokyo]」が生成できるのか?ということを調べました。
Java標準のAPIだとどうなるのか
Java標準の時間を扱うクラスというとJava7まではjava.util.Calendar、java.util.Dateを使い、Java8からはDate and Time APIである java.time.ZonedDateTime などを使います。
JavaDocにはOS依存だよーとか、Java実装依存だよーとか書かれていたので、次のような環境で試しました。
ですがいずれの場合でも、「2012-07-01T08:59:59+09:00[Asia/Tokyo]」のオブジェクトを生成し、1秒すすめたときには 60秒にならず09:00:00になってしまいました。
Calendarの場合
def c = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo")) // 59秒を生成 c.set(1900 + 112, 6, 1, 8, 59, 59) // うるう秒にすすめる c.add(Calendar.SECOND, 1) // fail assert c.getTime().toString() == "Sun Jul 01 08:59:60 JST 2012" // うるう秒で生成 def d = new Date(112, 6, 1, 8, 59, 60) // fail assert d.toString() == "Sun Jul 01 08:59:60 JST 2012"
ZonedDateTimeの場合
// 59秒を生成 def zdt59 = ZonedDateTime.parse("2012-07-01T08:59:59+09:00[Asia/Tokyo]") // うるう秒にすすめる // fail assert zdt59.plusSeconds(1).toString() == "2012-07-01T08:59:60+09:00[Asia/Tokyo]"
ちなみにZonedDateTimeのparseメソッドはそもそも60秒をパースできませんし、Instantのparseメソッドは60秒をパースしても0秒に丸めてしまいます。
ThreeTen-Extraを使うとどうなるのか
ThreeTen-Extraというライブラリを使うとこれが実は出来ます!(なんだってー!なんのためのJSR310だっt
- Stephen Colebourneが「Joda-Time」をつくる
- JSR-310としてJoda-Timeのように便利にしようとDate and Time APIが取り込まれる
- Stephen ColebourneがJSR-310の拡張ライブラリ「ThreeTen-Extra」をつくる
もう、Stephen Colebourneに足向けて寝られないですね。
UtcInstantというクラスを使うとうるう秒を保持した計算ができるようになっています。なのでタイムゾーンなしの文字である「2012-06-30T23:59:59.000Z」を渡して1秒足してみて60秒になれば成功です。
import java.time.Duration import org.threeten.extra.scale.UtcInstant def ui = UtcInstant.parse("2012-06-30T23:59:59.000Z") def ui2 = ui.plus(Duration.ofSeconds(1) // success assert ui2.toString() == "2012-06-30T23:59:60Z"
ということでうるう秒を鑑みて時刻を考えるなら、UtcInstantとして常に計算し、日付、タイムゾーンは別で管理しておくという戦略をとればよさそうです。
ちなみにUtcInstantでは日付はMJD(修正ユリウス日)が使われています。のでだいたい5桁くらいで収まる範囲です。
ただしこれがバグっぽいAPIがある
UtcInstantにはisLeapSecond()というメソッドがあるのですが、これがうるう秒ちょうどになった瞬間はまだうるう秒じゃないという判定をしてしまっているように見えます。
- 08:59:59.999
- 08:59:60.000 <= うるう秒 <= ここでisLeapSecondがfalseになってしまう。
- 08:59:60.001 <= うるう秒 <= ここはtrueを返してくれる
- ...
- 09:00:00.000
まだThreeTen-Extraの仕様を読み切ってないのですが、仕様が僕のおもっているとおりならこれはtrueを返すべきところなので、まずかったらIssueをあげようかなーと。
そのほかの話
ここまでで、Javaでうるう秒を扱うという方法がわかったわけですが、システム全体で考えるといろいろと面倒です。 まず、Windowsはうるう秒をサポートしていません。
次に、Linuxではうるう秒については60秒になった瞬間に59秒をやり直します。(60秒から0秒のあいだまでもう一度59秒になります)
そして、NTPサーバがどのように返答するか次第でOSの挙動は変わります。
ちなみに、GoogleなんかではLeap Smearingといって、うるう秒の1秒を(うるう秒前後の)20時間かけてちょっとずつ時計の進みを遅くすることで60秒という1秒間を分散させています。こうすることで、NTPサーバに問い合わせても60秒という時間が指定されません。つまり、ある20時間だけほんの少しずつだけ時計が遅く進んでいるのです。
クラウドを使っている場合にはインフラのNTPまでを統一するのは難しいので、要件に応じてどこを何に統一するのか考える必要があります。
最後に
情報収集につきあってくださった id:megascus さん、エアリプしてくれた khasunuma さんありがとうございました。めっちゃ勉強になりました。 そしてコードを簡単に共有できるWandbox最高でした。Groovyの最新版うごかしてくれてありがとう。(もう少し言えば、@Grabが動くようになっているともっともっと嬉しいんだなー。)
参考
13 章 : Date and Time API · Java Study
- ここが正しかった。やはりJCP会員つよし。というか、このサイトかなりすごい。khasunumaさんすごい。
Java本格入門 ~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで
- 作者: 谷本心,阪本雄一郎,岡田拓也,秋葉誠,村田賢一郎,アクロクエストテクノロジー株式会社
- 出版社/メーカー: 技術評論社
- 発売日: 2017/04/18
- メディア: 大型本
- この商品を含むブログを見る
- 作者: Joshua Bloch
- 出版社/メーカー: Addison-Wesley Professional
- 発売日: 2018/01/06
- メディア: ペーパーバック
- この商品を含むブログ (2件) を見る
Scrumのカンファレンスに参加してきた #RSGT2018
ソフトウェアコードのはじめかた
- プロジェクト名や名前空間の雑さはIDEなどの強力さを考慮して決めること
- プロジェクトをつくるときは次の手順でやること
- てきとうにスキャッフォルドでつくる。gradle initとか。
- gitignoreを追加する gitignore.io - Create Useful .gitignore Files For Your Project
- git init
- git add
- git commit で↑までにやったコマンドやWebサービスでの指定方法などをコメントにのこす
- 要件を確認してアーキテクチャと単語の整理
- noteという名前空間をつくり、そこにメモ書き用テストファイルをつくる。
- 要件をテストコードにはりつける
- 要件の一部をテストコードに翻訳する。簡単なケースだけでいい。 : RED
- プロダクトコードを仮実装する。 : GREEN
- 他の入出力のケースをテストに追加する。三角測量。 : RED
- プロダクトコードを修正する。 : GREEN
- テストコードをリファクタリングする。 : GREEN
- 何をクラスとして切り出すかは、何をつくりたいのかを定義している段階できまるので、コードをかきながら決めないほうがいい。
- テストコードはできるだけ長く書きはじめていく。テストコードは動的構造を記述する部分であり、プロトコル定義ツールとして使う。
- 小さいテストコードはあくまでデバッグのサポート、型システムのサポート程度に考えて記述する。
- 静的構造はプロダクトコード側で記述できる内容なので、プロダクトコードのリファクタリングや大枠の設計でおこなうようにする。
- テストコードとプロダクトコードの設計がかたまったら、noteにあるテストコードを適切な名前空間にきりだしてあげる。
Scrumが難しいのは幻想-情熱の再定義- を講演してきました。 #RSGT2018
私が所属しているチームは2017年にいろんなプラクティスを実践してきました。その内容をRegional Scrum Gathering Tokyo 2018で発表しました。
発表内容の概要
私達のチームは2016年までメトリクスの活用、スプリント期間の短縮、くじ引きで決めるPOやSM、などのプラクティスを通して改善を繰り返してきました。スクラムガイドもどんどん破りました。
このチームはScrumが難しいなんて思っていませんし、誰でも出来ると信じています。
チームが開発する製品は大きく変わりましたがScrumが難しいなんてことはありませんでしたし、
なによりこのチームのエッセンスを大学生40名に導入したところなんと1週間で1日スプリントをモノにしました。Scrumが難しいのは幻想だったのかもしれません。
我々のチームはこういったことを通して2017年にいくつかのプラクティスを確立しました。スプリント期間は1時間へ、チーム内ボトルネックへの対応時間は25分以内を保証、人的リソース活用の損益分岐点を常に意識できる開発プロセスです。
結果、1週間でレビューを35回以上、振り返りを30回以上行っています。1週間で改善した項目は最大で20アイテムにおよび、それらのムダ取りによって6ヶ月間で最大2倍の成果を生み出しています。
チームのパフォーマンスを最大化するために私達の計画的な学び方、偶発的事象からの学び方などをScrumの文脈でご紹介します。
スライド
2017年の資料
Scrumありがとう、 そしてさようなら -Scrum 破- #rsgt2017 // Speaker Deck
2016年の資料
Scrum,Test,Metrics #sgt2016
Spockのレポート生成はHTML, Markdown, Asciidocできるし、カスタムも出来るんだぜ
GroovyのテスティングフレームワークであるSpockは標準ではレポート生成機能はありません。 多くはGradleでビルドしたときのxmlやhtmlを利用していると思います。
Spockにはspock-reportという拡張ライブラリがあり、これを依存関係に追加するだけでテスト結果のレポートを独自に追加で生成できます。 しかもなんと、HTMLだけではなくMarkdownやAsciidocとして生成することもできます。 また、最新のspock-reportではテストコードからレポートに文章を追加することも出来るようになりました。
今回はそんなspock-reportの便利機能をいくつか紹介します。
本ブログで説明しているサンプルコード全部入りのspockおよびspock-reportのプロジェクトテンプレートはこちら。
本記事はG* Advent Calendar 2017の16日目になります。
G* Advent Calendar 2017 - Qiita
- 最低限の使い方
- レポートをMarkdown/Asciidocとして生成する
- テストコードから説明を追加する
Spock1.1の新機能紹介
SpockというGroovy言語で記述するテスティングフレームワークがあります。 今回は2017/05/01にリリースされたバージョン1.1の新機能のうち3つを紹介します。
本記事はG* Advent Calendar 2017の14日目です。
- verifyAll メソッドによる(Soft Assert)
- where句のデータテーブルで左側の値を参照できる
- @PendingFeatureで未実装機能マーカーでテストを実行しない
- その他