0
0

More than 1 year has passed since last update.

Rubyで文字列の時間表記を秒数に変換

Last updated at Posted at 2022-03-09

やりたいこと

06:23.19みたいなものを383.19秒のようにする。
2つの地点の通過タイムから区間タイムを計算する。
丸め誤差がでないように浮動小数点はできるだけ使わないようにする。

環境

macOS 10.15.7
Ruby 3.1.0

使用したメソッド

[分] : [秒] . [コンマ秒]形式の文字列を秒に変換してみる

  • "06:23.19"を秒に変換する
  • 丸め誤差対策
    • 文字列の段階で小数点を削除する
    • ケタを戻すときは有理数を使う

code

str = "06:23.19"
strs = str.partition(":")
#=> ["06", ":", "23.19"]

#分のケタを秒にする
#整数にするため、100倍して2ケタ上げておく
min = strs[0].to_i * 60 * 100
#=> 36000

#秒の部分から小数点を削除して整数にする
sec = strs[2].delete('.').to_i
#=> 2319

sum_sec = min + sec
#=> 38319

#有理数を使って2ケタ戻す
puts "#{str}#{(sum_sec * 0.01r).to_f}秒"
#=> 06:23.19は383.19秒

区間タイムを計算するメソッドをつくる

A地点の通過タイムが01:30.24。
B地点の通過タイムが03:12.14。
A-B間の区間タイムを計算する

メソッド

def to_interval(here, pre)
	#分の単位を変換
	min_here = here.partition(":")[0].to_i * 60 * 100
	min_pre  =  pre.partition(":")[0].to_i * 60 * 100

	#秒の単位を変換
	sec_here = here.partition(":")[2].delete('.').to_i
	sec_pre  =  pre.partition(":")[2].delete('.').to_i

	#差を求める
	interval_sec = (min_here + sec_here) - (min_pre + sec_pre)

	#計算結果を[分]:[秒].[コンマ秒]形式に戻す
	#秒を分に戻す
	min = interval_sec / 6000
	#秒の単位の桁を戻す
	sec = interval_sec.modulo(6000) * 0.01r
	
	interval_str = "#{sprintf("%02d", min)}:#{sprintf("%2.2f", sec)}"
	return interval_str
end

つくったメソッドを実行してみる

run_to_interval.rb
here = "03:12.14"
pre  = "01:30.24"
puts "A-B間のタイムは#{to_interval(here, pre)}です。"

ターミナル

$ ruby run_to_interval.rb
#=> A-B間のタイムは01:41.90です。

意図通りに動いた。

追記 [2022-03-10]

コメントで@scivolaさんからアドバイスをいただいたのでメソッドを書き換えました。

主な変更点

  • to_rを使って文字列から有理数に変換
  • map(&:[メソッド])を使って配列の要素を一気に変換
  • 番号指定パラメータ_1, _2を使ってブロック内で要素を各々処理

[2022-03-11]
@scivolaさんからご指摘いただき、変更を更に修正しました。
詳しい内容はコメント欄参照。

修正点

  • /60, modulo(60)divmod(60)
  • sprintfString#%

メソッド(改)と実行の結果

run_to_interval.rb
def to_interval(here_str, pre_str)
	here_sec_r = here_str.split(":").map(&:to_r).then{ _1 * 60 + _2 }
	pre_sec_r   =  pre_str.split(":").map(&:to_r).then{ _1 * 60 + _2 }
	#差を求める
	interval_sec_r = here_sec_r - pre_sec_r

	#秒から分の単位を取得
#	min = interval_sec_r / 60 (下記に修正)
	#余りの秒を取得
#	sec = interval_sec_r.modulo(60) (下記に修正)

#	interval_str = "#{sprintf("%02d", min)}:#{sprintf("%2.2f", sec)}" (下記に修正)

# =====文字列に戻す部分の修正========
	# Rationalから整商と剰余を配列で返すNumeric#divmodを使用
	min, sec = interval_sec_r.divmod(60)
	# String#%を使って成形
	interval_str = "#{"%02d" % min}:#{"%05.2f" % sec}"
# =====//文字列に戻す部分の修正========

	return interval_str
end

interval_str = to_interval("03:12.14", "01:30.24")
puts "A-B間のタイムは#{interval_str}です。"
#=> A-B間のタイムは01:41.90です。

秒をもとめるところが随分とスッキリしました。

0
0
5

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
0
0