LoginSignup
2
1

More than 5 years have passed since last update.

rubyzipを使ってファイル圧縮する際に任意の変更日付をいれてテストしてみたら展開ファイルが1秒ズレてハマったという話。

Posted at

Ubiregi Advent Calendar 2018の14日目です。

言いたいことはタイトル通りです。

やりたいこと

ユビレジでは売上データのcsvをダウンロードする機能があります。
その時に、アカウントのタイムゾーンによって、zipに圧縮するファイルの変更日時を変えたいという時があると思います。
なので、そんな感じでサンプルコードをやっていきます。

実装

require 'zip'
require 'time'

def compress(path, time)
  buffer = Zip::OutputStream.open('data.zip') do |out|
    zip_entry = Zip::Entry.new(out, "sample.csv", nil, nil, nil, nil, nil, nil, Zip::DOSTime.parse(time))
    out.put_next_entry zip_entry
    buffer = File.read(path)
    out.write(buffer)
  end
end

Zip::Entry.new のドキュメントを見ると、色々引数が指定できるみたいですね。最後の引数にZip::DOSTimeのインスタンスを渡してやれば良さそうなのでそうします。

テストコードをかく

テストコードも書いていきましょう。
あくまでもサンプルで、これだとローカルのファイルを用意しなきゃいけなかったり、ローカルに圧縮ファイルが作成されたりしちゃうので、実際にやる時はいい感じにやってください。

require 'minitest/autorun'

class CompressTest < Minitest::Test
  def test_compress
    compress('/Users/kitaindia/Documents/today.csv', '2018-12-24 15:15:15')
    Zip::InputStream.open('data.zip') do |io|
      assert_equal '2018-12-24 15:15:15 +0900', io.get_next_entry.time
    end
  end
end

実行してみます。

F

Failure:
CompressTest#test_compress [sample.rb:19]:
Expected: "2018-12-24 15:15:15 +0900"
  Actual: 2018-12-24 15:15:14 +0900

はい。1秒ずれてしまいました。

解決

# Bits 0-4  2 second increments (0-29)

2秒ごとにインクリメントするのだそうです。
秒数は5bit(32通り)で表してるんだそうで・・・。

wikipedia によると

DOSのFATファイルシステムは2秒単位でタイムスタンプを保持し、ZIPファイルレコードはこれを模倣している。結果として、ZIPアーカイブ内にあるファイルのタイムスタンプも2秒単位で丸められる。

なるほど・・・。しかし、テストで偶然2分の1を踏んでしまうなんて、ついてないですね・・・。

まとめ

実際の慌て具合の様子。

スクリーンショット 2018-12-15 0.36.08.png

解決の様子。

スクリーンショット 2018-12-15 0.40.47.png

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1