C#にはDateTime構造体とTimeSpan構造体があり、両者を利用することで現在時刻を取得したり、ある時刻Aからある時刻Bまでの時間を調べることができます。○時○分○秒の1000秒後は何時何分何秒なのか、○年○月○日の1000日後は何年何月何日なのかとか、○年○月○日と×年×月×日のあいだは何日離れているのかなどがわかります。
では最初に現在時刻を取得するコンソールアプリケーションをつくってみましょう。最後にこれらを組み合わせて実用的なWindowsアプリケーションやWebアプリをつくることにします。
現在時刻を取得して表示させる
現在時刻を取得して表示させるだけならこれでOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System; class Program { static void Main() { DateTime now = DateTime.Now; Console.WriteLine(now); // 2022/08/25 11:25:05 のように表示される Console.WriteLine(now.ToString("yyyy年MM月dd日のHH時mm分ss秒")); // 2022年08月25日の11時25分05秒 と表示される つねに月日時分秒が1桁でもつねに2桁で表示される Console.WriteLine(now.ToString("yyyy年M月d日のH時m分s秒")); // 2022年8月25日の11時25分5秒 と表示される つねに月日時分秒が1桁の場合は1桁で表示される // 曜日も取得できる 上は 「木曜日」 下は「木」とだけ出力される Console.WriteLine(now.ToString("dddd")); Console.WriteLine(now.ToString("ddd")); // 今日は2022年08月25日(木) 時刻は11時25分05秒です と表示される Console.WriteLine(now.ToString("今日はyyyy年MM月dd日(ddd) 時刻はHH時mm分ss秒です")); } } |
DateTime.ToString(“~”)の引数のなかで使えるものは他にもあります。各自で調べてみてください。
時刻と時刻のあいだの時間
時刻と時刻のあいだの時間を表示させます。この場合はTimeSpan構造体を使います。
ここでは現在時刻は2022年1月1日0時0分0秒からどれだけ経過しているのかを表示させてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; class Program { static void Main() { DateTime now = DateTime.Now; Console.WriteLine(now); // 2022/08/25 11:50:58 DateTime start = new DateTime(2022, 1, 1, 0, 0, 0); // 2022年1月1日0時0分0秒 TimeSpan span = now - start; // 差を求める 結果はTimeSpan型で返される Console.WriteLine(span); // 236.11:50:58.1680748 と出力される 235日と11時間50分58.1680748秒経過していることがわかる Console.WriteLine(span.TotalSeconds); // 20433058.1680748 秒数に換算すると20433058.1680748秒 } } |
指定された時間が経過したあとの時刻
指定された時間が経過したあと、または以前の時刻は以下のように取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Program { static void Main() { DateTime start = new DateTime(2022, 8, 25, 9, 15, 40); // 2022年8月25日 9時15分40秒 TimeSpan span = new TimeSpan(20, 10, 15, 30); // 20日10時間15分30秒 DateTime end1 = start + span; // 20日10時間15分30秒 経過の時刻を取得 DateTime end2 = start - span; // 20日10時間15分30秒 以前の時刻を取得 Console.WriteLine(end1.ToString("yyyy年MM月dd日(ddd) HH時mm分ss秒")); // 2022年09月14日(水) 19時31分10秒 と出力される Console.WriteLine(end2.ToString("yyyy年MM月dd日(ddd) HH時mm分ss秒")); // 2022年08月04日(木) 23時00分10秒 と出力される } } |
時間の四則計算
100秒は1分40秒です。では12345秒なら何時間何分何秒でしょうか? 小学校の算数ででてきそうな問題ですが、C#が使えるならコンピュータに計算させましょう。
1 2 3 4 5 6 7 8 9 10 |
using System; class Program { static void Main() { TimeSpan span = new TimeSpan(0,0,12345); // 0時間0分12345秒 Console.WriteLine(span); // 03:25:45 と出力される したがって 12345秒 == 3時間25分45秒 } } |
一人でやろうとすると50日かかる作業を7人で手分けしておこなう場合、何日必要だろうか?
また一人でやろうとすると50日かかる作業を数人で手分けして15日で終わらせたい。この場合、何人必要だろうか?
計算上は以下のようになります。ただこれは机上の空論であって実際にそうなるとはいえません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System; class Program { static void Main() { TimeSpan span1 = new TimeSpan(50, 0, 0, 0); // 50日 TimeSpan span2 = span1 / 7; // 50日を7で割る Console.WriteLine(span2); // 7.03:25:42.8571429 と出力される したがって 7日強必要 TimeSpan span3 = new TimeSpan(15, 0, 0, 0); double ret = Math.Ceiling(span1 / span3); Console.WriteLine(ret); // 4 と出力される したがって最低でも4人でやらないと納期に間に合わない } } |
以下のようにTimeSpan構造体を使うと、時間の四則演算も簡単にできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using System; class Program { static void Main() { TimeSpan span = new TimeSpan(1, 30, 0); TimeSpan span1a = new TimeSpan(0, 50, 0); // 50分 TimeSpan span1b = new TimeSpan(0, 0, 3000); // 3000秒(すなわち50分) Console.WriteLine(span + span1a); // 02:20:00 1時間30分に50分を足すと2時間20分 Console.WriteLine(span + span1b); // 02:20:00 1時間30分に3000秒を足すと当然のことながら上と同じ Console.WriteLine(span * 4); // 06:00:00 1時間30分の4倍は 6時間 Console.WriteLine(span / 4); // 06:00:00 1時間30分の4分の1は 22分30秒 } } |
Windows フォームアプリを作ってみる
それではWindows フォームアプリで同じようなものをつくってみましょう。
指定された2つの時刻のあいだの時間、指定された時刻から指定された時間の前後の時刻を求める機能をひとつのアプリにまとめます。
デザイナで以下のようなものをつくります。
NumericUpDownを操作してボタンをクリックすると年、月、日の値を設定できるようにしています。設定後に[計算][加算][減算]のボタンをクリックすると開始時として設定されている時刻と終了時として設定されている時刻との時間や、開始時として設定されている時刻にある時間を加算もしくは減算した時刻を知ることができます。
フィールド変数とコンストラクタは以下のようになっています。アプリケーションが起動したらSetStartNowメソッドとSetEndNowメソッドを呼び出して、フィールド変数に現在の年月日、時刻を格納します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); radioButton1.Checked = true; SetStartNow(); SetEndNow(); } int _startYear = 0; int _startMonth = 0; int _startDay = 0; int _startHour = 0; int _startMinute = 0; int _startSecond = 0; int _endYear = 0; int _endMonth = 0; int _endDay = 0; int _endHour = 0; int _endMinute = 0; int _endSecond = 0; int _addYear = 0; int _addMonth = 0; int _addDay = 0; int _addHour = 0; int _addMinute = 0; int _addSecond = 0; } |
SetStartNowメソッドとSetEndNowメソッドは以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public partial class Form1 : Form { void SetStartNow() { DateTime today = DateTime.Now; _startYear = today.Year; _startMonth = today.Month; _startDay = today.Day; _startHour = today.Hour; _startMinute = today.Minute; _startSecond = today.Second; SetLabelText(); } void SetEndNow() { DateTime today = DateTime.Now; _endYear = today.Year; _endMonth = today.Month; _endDay = today.Day; _endHour = today.Hour; _endMinute = today.Minute; _endSecond = today.Second; SetLabelText(); } } |
SetLabelTextメソッドはフォーム上のLabelにこれから調べようとしている開始時刻と終了時刻、加算減算しようとしている時刻の情報を表示させるためのものです。
RadioButtonがふたつあります。上のRadioButtonが選択されているときは二つの時刻(開始時刻と終了時刻の時間の差)を時間を計算します。下のRadioButtonが選択されている場合は開始時刻にある時間を加算または減算することで得られる時刻を計算します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class Form1 : Form { void SetLabelText() { string start = $"{_startYear}年{_startMonth}月{_startDay}日{_startHour}時{_startMinute}分{_startSecond}秒"; if (radioButton1.Checked) { string end = $"{_endYear}年{_endMonth}月{_endDay}日{_endHour}時{_endMinute}分{_endSecond}秒"; label1.Text = $"{start} ~ {end}"; } else { string add = $"{_addYear}年{_addMonth}ヶ月{_addDay}日{_addHour}時間{_addMinute}分{_addSecond}秒"; label1.Text = $"{start} + {add}"; } } } |
ボタンがクリックされたらNumericUpDownの値から開始時刻を設定するための値を設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public partial class Form1 : Form { private void ButtonSetStartYear_Click(object sender, EventArgs e) { _startYear = (int)numericUpDown1.Value; SetLabelText(); } private void ButtonSetStartMonth_Click(object sender, EventArgs e) { _startMonth = (int)numericUpDown1.Value; SetLabelText(); } private void ButtonSetStartDay_Click(object sender, EventArgs e) { _startDay = (int)numericUpDown1.Value; SetLabelText(); } private void ButtonSetStartHour_Click(object sender, EventArgs e) { _startHour = (int)numericUpDown1.Value; SetLabelText(); } private void ButtonSetStartMinute_Click(object sender, EventArgs e) { _startMinute = (int)numericUpDown1.Value; SetLabelText(); } private void ButtonSetStartSecond_Click(object sender, EventArgs e) { _startSecond = (int)numericUpDown1.Value; SetLabelText(); } } |
現在時刻を計算の起点にしたい場合が多いと思うので、[開始年月日に今日の日付をセット]、[開始年月日に現在時刻をセット]のボタンをクリックしたらフィールド変数に一気に値を設定できるようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { private void ButtonSetStartToday_Click(object sender, EventArgs e) { DateTime today = DateTime.Now; _startYear = today.Year; _startMonth = today.Month; _startDay = today.Day; SetLabelText(); } private void ButtonSetStartNow_Click(object sender, EventArgs e) { SetStartNow(); } } |
ふたつの時刻の差を計算するときの終了時刻を設定するボタンをクリックしたときの処理を示します。これもやっていることは先に示したものと同じです。値を格納するフィールド変数が違うだけでやっていることは一緒です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
public partial class Form1 : Form { private void ButtonSetEndYear_Click(object sender, EventArgs e) { _endYear = (int)numericUpDown2.Value; SetLabelText(); } private void ButtonSetEndMonth_Click(object sender, EventArgs e) { _endMonth = (int)numericUpDown2.Value; SetLabelText(); } private void ButtonSetEndDay_Click(object sender, EventArgs e) { _endDay = (int)numericUpDown2.Value; SetLabelText(); } private void ButtonSetEndHour_Click(object sender, EventArgs e) { _endHour = (int)numericUpDown2.Value; SetLabelText(); } private void ButtonSetEndMinute_Click(object sender, EventArgs e) { _endMinute = (int)numericUpDown2.Value; SetLabelText(); } private void ButtonSetEndSecond_Click(object sender, EventArgs e) { _endSecond = (int)numericUpDown2.Value; SetLabelText(); } private void ButtonSetEndToday_Click(object sender, EventArgs e) { DateTime today = DateTime.Now; _endYear = today.Year; _endMonth = today.Month; _endDay = today.Day; SetLabelText(); } private void ButtonSetEndNow_Click(object sender, EventArgs e) { SetEndNow(); } } |
現在時刻に加算または減算する時間を設定するボタンをクリックしたときの処理を示します。これもやっていることはほとんどかわりません。どのボタンをクリックしたのか、その場合、どのフィールド変数に値を格納するのかが違うだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public partial class Form1 : Form { private void ButtonSetAddYear_Click(object sender, EventArgs e) { _addYear = (int)numericUpDown3.Value; SetLabelText(); } private void ButtonSetAddMonth_Click(object sender, EventArgs e) { _addMonth = (int)numericUpDown3.Value; SetLabelText(); } private void ButtonSetAddDay_Click(object sender, EventArgs e) { _addDay = (int)numericUpDown3.Value; SetLabelText(); } private void ButtonSetAddHour_Click(object sender, EventArgs e) { _addHour = (int)numericUpDown3.Value; SetLabelText(); } private void ButtonSetAddMinute_Click(object sender, EventArgs e) { _addMinute = (int)numericUpDown3.Value; SetLabelText(); } private void ButtonSetAddSecond_Click(object sender, EventArgs e) { _addSecond = (int)numericUpDown3.Value; SetLabelText(); } } |
開始時刻と終了時刻の差の時間を計算する処理を示します。DateTime構造体のインスタンスを生成して引き算をすればTimeSpan構造体のインスタンスが返されます。これを文字列に変換して結果をLabelに表示します。このとき不適切な値が設定されていると例外が発生するので、例外処理をおこなっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
public partial class Form1 : Form { private void ButtonGetTimeSpan_Click(object sender, EventArgs e) { try { DateTime start = new DateTime(_startYear, _startMonth, _startDay, _startHour, _startMinute, _startSecond); DateTime end = new DateTime(_endYear, _endMonth, _endDay, _endHour, _endMinute, _endSecond); TimeSpan ts = end - start; string text; if (ts.Days != 0) { text = $"{Math.Abs(ts.Days)}日{Math.Abs(ts.Hours)}時間{Math.Abs(ts.Minutes)}分{Math.Abs(ts.Seconds)}秒"; int month = 0; int year = 0; year = Math.Abs(ts.Days) / 365; month = (Math.Abs(ts.Days) / 30) % 12; string text2 = ""; if (year > 0) text2 += $"{year}年"; if (month > 0) text2 += $"{month}ヵ月"; if (text2 != "") text2 = $"(約{text2})"; text += text2; } else if (ts.Hours != 0) text = $"{Math.Abs(ts.Hours)}時間{Math.Abs(ts.Minutes)}分{Math.Abs(ts.Seconds)}秒"; else if (ts.Minutes != 0) text = $"{Math.Abs(ts.Minutes)}分{Math.Abs(ts.Seconds)}秒"; else text = $"{Math.Abs(ts.Seconds)}秒"; if (ts.TotalSeconds > 0) label2.Text = "計算結果:" + text; else label2.Text = "計算結果:- " + text; } catch { label2.Text = $"設定された日時が不正です"; } } } |
開始時刻に時間を加算または減算したときの処理を示します。DateTime構造体のインスタンスを生成してここにTimeSpan構造体のインスタンスを加算、または減算するとDateTime構造体のインスタンスが返されます。これを文字列に変換して結果をLabelに表示します。この場合も開始時刻に不適切な値が設定されていると例外が発生するので、例外処理をおこなっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public partial class Form1 : Form { private void ButtonAddTimeSpan_Click(object sender, EventArgs e) { try { DateTime start = new DateTime(_startYear, _startMonth, _startDay, _startHour, _startMinute, _startSecond); TimeSpan ts = new TimeSpan(_addYear * 365 + _addMonth * 30 + _addDay, _addHour, _addMinute, _addSecond); DateTime end = start + ts; string text = $"{end.Year}年{end.Month}月{end.Day}日{end.Hour}時{end.Minute}分{end.Second}秒"; ; label2.Text = "計算結果:" + text; } catch { label2.Text = $"設定された日時が不正です"; } } private void ButtonSubtractTimeSpan_Click(object sender, EventArgs e) { try { DateTime start = new DateTime(_startYear, _startMonth, _startDay, _startHour, _startMinute, _startSecond); TimeSpan ts = new TimeSpan(_addYear * 365 + _addMonth * 30 + _addDay, _addHour, _addMinute, _addSecond); DateTime end = start - ts; string text = $"{end.Year}年{end.Month}月{end.Day}日{end.Hour}時{end.Minute}分{end.Second}秒"; ; label2.Text = "計算結果:" + text; } catch { label2.Text = $"設定された日時が不正です"; } } } |
どのRadioButtonが選択かでボタンをクリックしても処理がおこなわれない場合があります。その場合は最初からボタンをクリックできないようにしたほうがユーザーに対しては新設設計といえます。どのRadioButtonが選択されているかでボタンやNumericUpDownコントロールを操作不能にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public partial class Form1 : Form { private void radioButton1_CheckedChanged(object sender, EventArgs e) { numericUpDown2.Enabled = radioButton1.Checked; ButtonSetEndYear.Enabled = radioButton1.Checked; ButtonSetEndMonth.Enabled = radioButton1.Checked; ButtonSetEndDay.Enabled = radioButton1.Checked; ButtonSetEndHour.Enabled = radioButton1.Checked; ButtonSetEndMinute.Enabled = radioButton1.Checked; ButtonSetEndSecond.Enabled = radioButton1.Checked; ButtonSetEndToday.Enabled = radioButton1.Checked; ButtonSetEndNow.Enabled = radioButton1.Checked; ButtonGetTimeSpan.Enabled = radioButton1.Checked; numericUpDown3.Enabled = radioButton2.Checked; ButtonSetAddYear.Enabled = radioButton2.Checked; ButtonSetAddMonth.Enabled = radioButton2.Checked; ButtonSetAddDay.Enabled = radioButton2.Checked; ButtonSetAddHour.Enabled = radioButton2.Checked; ButtonSetAddMinute.Enabled = radioButton2.Checked; ButtonSetAddSecond.Enabled = radioButton2.Checked; ButtonAddTimeSpan.Enabled = radioButton2.Checked; ButtonSubtractTimeSpan.Enabled = radioButton2.Checked; } } |