LoginSignup
0

posted at

updated at

【logic】時刻のみのtime_tを作ってみた

はじめに

過去記事は「auカブコム証券のkabuステーションREST APIに関する記事一覧」。

x分足の計算をする際に、モヤモヤしていたものを改善してみた。

いきさつ

チャートデータをx分足を計算する際に、例えば、現物午前ならば、9:00-11:30の範囲の時間に5分足なら、9:00:00-9:04:59が9:00:00にまとめることになります。
ここで、x分足が、1分足ならば、HH: mm:ssのss秒を00秒にするだけでまとめられます。
これが、xが1, 3, 5, 10, 15, 20, 30, 60と作っていくと、いろいろと面倒なことが起きます。

当初の時刻文字列テーブル

現物だけならば、09:00-11:30と12:30-15:00なので、それほど大きな問題はないですが、先物の場合、08:45-15:15と16:30-翌06:00のため、いろいろ問題があります。
特に、日付をまたぐところが、x=1, 3, 5, 10, 15, 30は、ちょうど24:00となりますが、x=20の場合は23:50から24:10、x=60の場合は23:30から24:30と24:00をまたぐため、前半が当日のみ、後半の翌日が当日分に割り当てられます。

正直、よく分からないのと、終了時刻の境界値もまたぐ場合があり、文字列でテーブルを作りました。

	/**
	 * 先物日中(08:45-15:20)の時刻一覧。
	 */
	private static String[] time1 = new String[79 + 1]; // 79 = 12 * 6.5 + 1
	/**
	 * 先物夜間(16:30-24:00)の時刻一覧。
	 */
	private static String[] time2 = new String[90 + 1]; // 90 = 12 * 7.5
	/**
	 * 先物夜間(00:00-06:05)の時刻一覧。
	 */
	private static String[] time3 = new String[73 + 1]; // 73 = 12 * 6 + 1
	/**
	 * 現物午前(09:00-11:35)の時刻一覧。
	 */
	private static String[] time4 = new String[31 + 1]; // 31 = 12 * 2.5 + 1
	/**
	 * 現物午後(12:30-15:05)の時刻一覧。
	 */
	private static String[] time5 = new String[31 + 1]; // 31 = 12 * 2.5 + 1

	/**
	 * 先物日中(08:45-15:20)の時刻一覧の初期化。
	 */
	private static void init1() {
		int hour = 8;
		int min = 45;
		for (int i = 0; i < time1.length; i++) {
			String time = String.format("%02d:%02d:00", hour, min);
			time1[i] = time;
			min += 5;
			if (min >= 60) {
				hour++;
				min -= 60;
			}
		}
	}

	/**
	 * 先物夜間(16:30-24:00)の時刻一覧。
	 */
	private static void init2() {
		int hour = 16;
		int min = 30;
		for (int i = 0; i < time2.length; i++) {
			String time = String.format("%02d:%02d:00", hour, min);
			time2[i] = time;
			min += 5;
			if (min >= 60) {
				hour++;
				min -= 60;
			}
		}
	}

	/**
	 * 先物夜間(00:00-06:05)の時刻一覧。
	 */
	private static void init3() {
		int hour = 0;
		int min = 0;
		for (int i = 0; i < time3.length; i++) {
			String time = String.format("%02d:%02d:00", hour, min);
			time3[i] = time;
			min += 5;
			if (min >= 60) {
				hour++;
				min -= 60;
			}
		}
	}

	/**
	 * 現物午前(09:00-11:35)の時刻一覧。
	 */
	private static void init4() {
		int hour = 9;
		int min = 0;
		for (int i = 0; i < time4.length; i++) {
			String time = String.format("%02d:%02d:00", hour, min);
			time4[i] = time;
			min += 5;
			if (min >= 60) {
				hour++;
				min -= 60;
			}
		}
	}

	/**
	 * 現物午後(12:30-15:05)の時刻一覧。
	 */
	private static void init5() {
		int hour = 12;
		int min = 30;
		for (int i = 0; i < time5.length; i++) {
			String time = String.format("%02d:%02d:00", hour, min);
			time5[i] = time;
			min += 5;
			if (min >= 60) {
				hour++;
				min -= 60;
			}
		}
	}

time_valの置き換え

ここで、hour, minの2つのint変数(秒足ならば、さらにsec)でループする際に、C/C++のswap(&a, &b)のようなポインタ渡しや、C++の参照渡しができないと、minが60を超えたらhour++するために、クラスを用意したり、int[]で渡すのは・・・ということで、考えたらが、時刻のみのtime_tで、"00:00:00"からの経過時間(秒)を管理するint値でループすれば、60秒で1分繰り上がりや、60分で1時間を考えることなく加算していけるかなと。

	/**
	 * 00:00:00を基準とした経過時間(秒)を計算する。
	 * 
	 * @param hour 時。
	 * @param min  分。
	 * @param sec  秒。
	 * @return 経過時間。
	 */
	public static int time_val(int hour, int min, int sec) {
		return hour * 60 * 60 + min * 60 + sec;
	}

	/**
	 * 00:00:00を基準とした経過時間(秒)を計算する。
	 * 
	 * @param s 時刻文字列(HH:mm:ss)。
	 * @return 経過時間。
	 */
	public static int time_val(String s) {
		if (s == null || s.length() < 8 || s.charAt(2) != ':' || s.charAt(5) != ':') {
			return 0;
		}
		if (s.length() > 8) {
			s = s.substring(0, 8);
		}
		int hour = StringUtil.parseInt(s.substring(0, 2));
		int min = StringUtil.parseInt(s.substring(3, 5));
		int sec = StringUtil.parseInt(s.substring(6, 8));
		return time_val(hour, min, sec);
	}

	/**
	 * 00:00:00を基準とした経過時間(秒)を文字列で取得する。
	 * 
	 * @param time_val 経過時間。
	 * @return 時刻文字列(00:00:00-23:59:59)。
	 */
	public static String toString(int time_val) {
		while (time_val < 0) {
			time_val += 24 * 60 * 60;
		}
		int hour = time_val / 3600 % 24;
		int min = time_val / 60 % 60;
		int sec = time_val % 60;
		String time = String.format("%02d:%02d:%02d", hour, min, sec);
		return time;
	}

	/**
	 * 00:00:00を基準とした経過時間(秒)を文字列で取得する。
	 * 
	 * @param time_val 経過時間。
	 * @param b99h     true:99時まで、false:23時まで。
	 * @return 時刻文字列(00:00:00-23:59:59または99:59:59)。
	 */
	public static String toString(int time_val, boolean b99h) {
		while (time_val < 0) {
			time_val += 24 * 60 * 60;
		}
		int hour = time_val / 3600;
		if (b99h) {
			while (hour > 99) {
				hour -= 24;
			}
		} else {
			hour %= 24;
		}
		int min = time_val / 60 % 60;
		int sec = time_val % 60;
		String time = String.format("%02d:%02d:%02d", hour, min, sec);
		return time;
	}

このstaticメソッドを用意しただけで、init1()からinit5()が1つにまとめられました。
time2のときだけtrueを渡しているのは、20分足のとき、24時を超えた00:10では大小関係が23:50 <= x < 00:10となってしまうため、23:50 <= x < 24:10とするためです。

		int st;
		int ed;
		int dt = DELTA5;
		{
			st = TimeUtil.time_val(8, 45, 0);
			ed = TimeUtil.time_val(15, 15, 0);
			ChartTimeLogic.init(time1, st, ed, dt, false);
		}
		{
			st = TimeUtil.time_val(16, 30, 0);
			ed = TimeUtil.time_val(23, 59, 59);
			ChartTimeLogic.init(time2, st, ed, dt, true);
		}
		{
			st = TimeUtil.time_val(0, 0, 0);
			ed = TimeUtil.time_val(6, 0, 0);
			ChartTimeLogic.init(time3, st, ed, dt, false);
		}
		{
			st = TimeUtil.time_val(9, 0, 0);
			ed = TimeUtil.time_val(11, 30, 0);
			ChartTimeLogic.init(time4, st, ed, dt, false);
		}
		{
			st = TimeUtil.time_val(12, 30, 0);
			ed = TimeUtil.time_val(15, 00, 0);
			ChartTimeLogic.init(time5, st, ed, dt, false);
		}

	/**
	 * 時刻一覧の初期化。
	 * 
	 * @param times 初期化する時刻一覧。
	 * @param st    開始時刻。
	 * @param ed    終了時刻。
	 * @param dt    間隔。
	 * @param b99h  true:99時まで、false:23時まで。
	 */
	public static void init(String[] times, int st, int ed, int dt, boolean b99h) {
		int tim = st;
		int cnt = (ed - tim) / dt + 1;
		if (times.length != cnt + 1) {
			throw new RuntimeException("times.length=" + times.length + ", cnt+1=" + (cnt + 1));
		}
		for (int i = 0; i < cnt + 1; i++) {
			String time = TimeUtil.toString(tim, b99h);
			times[i] = time;
			tim += dt;
		}
	}

もう文字列を止める

CSVファイルから読んだ文字列から処理していたので、文字列のテーブルでグループ化していましたが、もう時刻の文字列からtime_valに変換して、数値演算だけでグループ化します。

すると、

	public static final int 日付変更線 = TimeUtil.time_val(8, 0, 0);
	public static final int 先物日中開始時刻 = TimeUtil.time_val(8, 45, 0);
	public static final int 先物日中終了時刻 = TimeUtil.time_val(15, 15, 0);
	public static final int 先物夜間開始時刻 = TimeUtil.time_val(16, 30, 0);
	public static final int 先物夜間終了時刻 = TimeUtil.time_val(30, 0, 0);
	public static final int 現物午前開始時刻 = TimeUtil.time_val(9, 0, 0);
	public static final int 現物午前終了時刻 = TimeUtil.time_val(11, 30, 0);
	public static final int 現物午後開始時刻 = TimeUtil.time_val(12, 30, 0);
	public static final int 現物午後終了時刻 = TimeUtil.time_val(15, 0, 0);

	/**
	 * 指定した時刻が所属する時刻を検索する。
	 * 
	 * @param dt      間隔。
	 * @param bFuture true:先物/OP、false:現物/指数
	 * @param time    現値の時刻。
	 * @return 所属する時刻。範囲外の場合はnull。
	 */
	public static String search(int dt, boolean bFuture, String time) {
		if (time == null) {
			return null;
		}
		int off = 0;
		int ed = -1;
		int tim = TimeUtil.time_val(time);
		if (bFuture) {
			if (tim < ChartTimeLogic.日付変更線) {
				tim += ChartTimeLogic.DELTA1d;
			}
			if (tim >= ChartTimeLogic.先物夜間開始時刻) {
				off = ChartTimeLogic.先物夜間開始時刻;
				ed = ChartTimeLogic.先物夜間終了時刻;
			} else if (tim >= ChartTimeLogic.先物日中開始時刻) {
				off = ChartTimeLogic.先物日中開始時刻;
				ed = ChartTimeLogic.先物日中終了時刻;
			}
		} else {
			if (tim >= ChartTimeLogic.現物午後開始時刻) {
				off = ChartTimeLogic.現物午後開始時刻;
				ed = ChartTimeLogic.現物午後終了時刻;
			} else if (tim >= ChartTimeLogic.現物午前開始時刻) {
				off = ChartTimeLogic.現物午前開始時刻;
				ed = ChartTimeLogic.現物午前終了時刻;
			}
		}
		tim -= off;
		tim = tim / dt * dt;
		tim += off;
		if (tim > ed) {
			return null;
		}
		time = TimeUtil.toString(tim);
		return time;
	}

すべて開始時刻(off)を引いて、ここを基準に、tim / dt * dtでdt幅で丸めて、offを戻して、終了時刻以内ならば、文字列に変換する。
ファイル上では日付が変わりますが、日付変更線を08:00:00として、00:00:00-07:59:59の時刻は24:00:00-31:59:59として扱うことで、16:30-30:00まで連続した時刻として計算できます。

自動テスト

自動テストは、初期のときから作成し、24時間×60分×60秒のすべてをチェックしてましたが、デグレードチェックに役に立ちました。
5分足ならば、常にminを5で丸めるだけですが、他のx分足だとexpected()が複数パターン作ることになります。

	@Test
	public void searchFalseTest() {
		boolean bFuture = false;
		int i = 0;
		for (int h = 0; h <= 8; h++) {
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					assertNull(time, a1);
					i++;
				}
			}
		}
		for (int h = 9; h <= 10; h++) {
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					String e1 = expected(h, m);
					assertEquals(time, e1, a1);
					i++;
				}
			}
		}
		{
			int h = 11;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					if (m <= 34) {
						String e1 = expected(h, m);
						assertEquals(time, e1, a1);
					} else {
						assertNull(time, a1);
					}
					i++;
				}
			}
		}
		{
			int h = 12;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					if (m <= 29) {
						assertNull(time, a1);
					} else {
						String e1 = expected(h, m);
						assertEquals(time, e1, a1);
					}
					i++;
				}
			}
		}
		for (int h = 13; h <= 14; h++) {
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					String e1 = expected(h, m);
					assertEquals(time, e1, a1);
					i++;
				}
			}
		}
		{
			int h = 15;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					if (m <= 4) {
						String e1 = expected(h, m);
						assertEquals(time, e1, a1);
					} else {
						assertNull(time, a1);
					}
					i++;
				}
			}
		}
		for (int h = 16; h <= 23; h++) {
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					assertNull(time, a1);
					i++;
				}
			}
		}
		assertEquals(24 * 60 * 60, i); // テスト件数
	}

	@Test
	public void searchTrueTest() {
		boolean bFuture = true;
		int i = 0;
		for (int h = 0; h <= 5; h++) {
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					String e1 = expected(h, m);
					assertEquals(time, e1, a1);
					i++;
				}
			}
		}
		{
			int h = 6;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					if (m <= 4) {
						String e1 = expected(h, m);
						assertEquals(time, e1, a1);
					} else {
						assertNull(time, a1);
					}
					i++;
				}
			}
		}
		{
			int h = 7;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					assertNull(time, a1);
					i++;
				}
			}
		}
		{
			int h = 8;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					if (m <= 44) {
						assertNull(time, a1);
					} else {
						String e1 = expected(h, m);
						assertEquals(time, e1, a1);
					}
					i++;
				}
			}
		}
		for (int h = 9; h <= 14; h++) {
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					String e1 = expected(h, m);
					assertEquals(time, e1, a1);
					i++;
				}
			}
		}
		{
			int h = 15;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					if (m <= 19) {
						String e1 = expected(h, m);
						assertEquals(time, e1, a1);
					} else {
						assertNull(time, a1);
					}
					i++;
				}
			}
		}
		{
			int h = 16;
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					if (m <= 29) {
						assertNull(time, a1);
					} else {
						String e1 = expected(h, m);
						assertEquals(time, e1, a1);
					}
					i++;
				}
			}
		}
		for (int h = 17; h <= 23; h++) {
			for (int m = 0; m <= 59; m++) {
				for (int s = 0; s <= 59; s++) {
					String time = testdata(h, m, s);
					String a1 = search(bFuture, time);
					String e1 = expected(h, m);
					assertEquals(time, e1, a1);
					i++;
				}
			}
		}
		assertEquals(24 * 60 * 60, i); // テスト件数
	}

	private String testdata(int h, int m, int s) {
		String time = String.format("%02d:%02d:%02d", h, m, s);
		return time;
	}

	private String search(boolean bFuture, String time) {
		String a1 = ChartTime5mLogic.search(bFuture, time);
		return a1;
	}

	private String expected(int h, int m) {
		int m1 = m / 5 * 5;
		String e1 = String.format("%02d:%02d:00", h, m1);
		return e1;
	}

追記:むかしのソースをr1で残す

後から参照するため、int hour, min, sec;のソースをr1で戻す。
ただし、現物対応する前のため、引数が異なる。
r1: String search(String time)
none: String search(boolean bFuture, String time)

githubソース

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
What you can do with signing up
0