1つめのフォームに、DateTimePickerを2つ貼り付けて、ラジオボタンで
数を選択して、2つ目のフォームに結果の日数を表示するというのを
作っています。
DateTimePickerは、カレンダー形式になるから使いやかと思い、
使ってみたいので、うまくいきません。
DateTimePickerというのは、計算するのには不向きなのでしょうか??
カレンダー機能を使って、選択された日から○○日前、○○日後を
○○○○年○○日○○月と表示させるのと、その期間は○○日です
と表示させたいのですが、表示できません。
.Netをはじめてまだそんなにたっていませんが、
どうしても解からないのです。
わかるかた教えてください。難しい説明はしないでね。
> 1つめのフォームに、DateTimePickerを2つ貼り付けて、ラジオボタンで
> 数を選択して、2つ目のフォームに結果の日数を表示するというのを
> 作っています。
すみません。それぞれのコントロールの意味がわかりません。どういう事でしょうか?
1つめのフォーム(Form1)にあるラジオボタンで選択させる「数」というのは、
何を表す数なのでしょうか? 日数ですか?
> DateTimePickerというのは、計算するのには不向きなのでしょうか??
向き不向きというか……日付計算は、コントロールではなく、
自前で行うべきことですから、計算という用途に対して使えるかどうか、
というのは、あまり関係ないかと思いますよ。
.NETには、日時を表現するための2種類のデータ型…DateTimeとTimeSpanがあります。
VBでいうところのDate型は、.NETのDateTime型に相当します。
現在の日付は「時刻」を表すSystem.DateTime構造体にて管理できます。
○○日後は、「時間」を表すSystem.TimeSpan構造体にて管理できます。
そして、DateTimeのAdd系メソッドや減算演算子を使えば、
「時刻」と「時間」、あるいは「2つの時刻値」から、
計算結果となる時刻や時間を算出する事ができます。
あるいは、VBの「DateAdd関数」を使って計算する事もできるでしょう。
> カレンダー機能を使って、選択された日から○○日前、○○日後を
> ○○○○年○○日○○月と表示させるのと、その期間は○○日です
> と表示させたいのですが、表示できません。
DateTimePickerから、選択された日付を受け取ったり、逆に、
DateTimePickerに、指定した日付を表示させたりするには、
Valueプロパティを利用してください。(Valueのデータ型はDateTime型です)
Form1
・DTPicker1(予定日),DTPicker2(実行日)を、
ラジオボタン(人数)を選択後、計算ボタンを押す。
Form2(ラベルで表示させる)
・予定日、予定日の○○日前の表示、実行日、実行日の○○日前の表示、
○○後の表示→すべて「○○○○年○○月○○日」と表示させる
・予定日の○○日前〜実行日の○○日後までの間の期間を求める
→「○○日間」
昔、VBを学生時に少しやったのですが、VBと.Netでは違うような
気がするのですが・・・。
ちなみにこんなふうに作りました。
Private Sub Btn日数計算_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Btn日数計算.Click
'
Dim Fr日数結果 As New Form日数結果
Dim Fr日数計算 As New Form日数計算
Fr日数計算 = Me
Me.Visible = False
'Form結果をモーダルで表示
Fr日数結果.Show()
'変数を宣言
Dim 出産日 As String
Dim 出産予定日 As String
Dim 出生児 As String
Dim wk予定産前 As String
Dim wk出産産前 As String
Dim wk出産翌日 As String
Dim wk産後 As String
Dim RBtn単胎, 双胎 As CheckState
出産予定日 = DTPicker1.Value
出産日 = DTPicker2.Value
'出生児の数により判定
If RBtn双胎.Checked Then
wk予定産前 = CStr(Val(出産予定日) - 98 + 1)
wk出産産前 = CStr(Val(出産日) - 98 + 1)
Else
wk予定産前 = CStr(Val(出産予定日) - 42 + 1)
wk出産産前 = CStr(Val(出産日) - 42 + 1)
End If
wk出産翌日 = CStr(Val(出産日) + 1)
wk産後 = CStr(Val(出産日) + 56)
'結果を表示する
Fr日数結果.Lb出産日産前.Text = Format(wk出産産前, "yyyy/MM/dd")
Fr日数結果.Lb出産日翌日.Text = Format(wk出産翌日, "yyyy/MM/dd")
Fr日数結果.Lb出産日産後.Text = Format(wk産後, "yyyy/MM/dd")
Fr日数結果.Lb出産日.Text = DTPicker2.Value
Fr日数結果.Lb予定日産前.Text = Format(wk予定産前, "yyyy/MM/dd")
If DTPicker1.Value >= DTPicker2.Value Then
Fr日数結果.Lb日数.Text = wk産後 - wk出産産前 + 1
Else
Fr日数結果.Lb日数.Text = wk産後 - wk予定産前 + 1
End If
Fr日数結果.Lb出産予定日.Text = DTPicker1.Value
End Sub
これで実行してみると、DTPickerを使ったところは、日付が表示されるけど
なぜか時間まで出てきてしまうこと。
そして、そのほかのところは、FORMATの中に書いた「yyyy/MM/dd」という
表示になってしまう。FORMATの使い方が違うのでしょうか??
> これで実行してみると、DTPickerを使ったところは、日付が表示されるけど
> なぜか時間まで出てきてしまうこと。
DTPickerというのは、DateTimePickerの事ですよね。であれば、Valueからは時間も返されますよ。
そもそも、DateTime型(VBでいう所のDate型)は、日付と時刻、両方の情報を保持していますから。
もし、この時刻情報が不要なのであれば、日付部だけを用いるようにしてみて下さい。
DateTime型のDateプロパティを使えば、日付部だけの情報(正確には、時刻値が
午前12時(00:00:00)に変更されたDateTime型の値ですが)を得る事をできます。
> そして、そのほかのところは、FORMATの中に書いた「yyyy/MM/dd」という
> 表示になってしまう。FORMATの使い方が違うのでしょうか??
FORMATではなく、Formatですよね。
Formatにおいて、「yyyy/MM/dd」は日付を表す書式ですが、第1引数に指定した値が、
日付として処理できない値だったため、期待する結果にならなかったのでしょう。
という事は、Formatの使い方が間違っているというよりは、Formatの第1引数に指定した値、
ひいては、その値を算出するための計算手順に問題がある、という事になるかと思います。
……というわけで、提示されたコードを見てみたのですが……
コード中の「wk予定産前」「wk出産産前」の算出に使われている
「- 98 + 1」や「- 42 + 1」という式の意味が私には分かりませんでした。
という事で、ご質問の処理内容について、もう少し教えてください。
> Dim 出産日 As String
> Dim wk出産産前 As String
> 出産日 = DTPicker2.Value
> If RBtn双胎.Checked Then
> wk出産産前 = CStr(Val(出産日) - 98 + 1)
> Else
> wk出産産前 = CStr(Val(出産日) - 42 + 1)
> End If
> Fr日数結果.Lb出産日産前.Text = Format(wk出産産前, "yyyy/MM/dd")
上記は、ラベル『Lb出産日産前』の表示に関連すると思われる部分だけを
抜粋したコードです。これについて、以下の事を教えてください。
例えば、RBtn双胎.Checked が「True」だったと仮定して、かつ、
DTPicker2.Value が「2003年11月24日 23時10分10秒」という値を返す時に……
問1) String変数 [出産日] には、どのような文字列が渡される事を期待していますか?
問2) その場合、計算式「Val(出産日) - 98 + 1」が、どのような値になると思いますか?
問3) その結果、String変数 [wk出産産前] は、どのような値になるでしょうか?
作っている内容というのが、
1つ目のフォームに、
出産予定日(○○○○年○○月○○日 ← DTPicker1)と
出産日(○○○○年○○月○○日 ← DTPicker2)を
入力し、出生児の数(単胎→RBtn単胎 or 双胎→RBtn双胎 ← ラジオボタン)を選択して
検索する。
2つ目のフォームに、
①出産日(Lb出産日)と出産予定日(Lb出産予定日)両方の
産前42日(単胎の場合)or
産前98日(双胎の場合)
を表示する(ラベル)
②出産日の翌日(Lb出産日翌日)と
出産日の産後56日(Lb出産日産後)で表示する
条件1 出生児の数によって産前の計算が違う
単胎なら42日、双胎なら98日
条件2 出産予定日より早く生まれた場合
出産日の産前、出産日の翌日、
出産日の産後56日と表示させる。
それ以外は、表示させない
条件3 出産予定日より遅く生まれた場合
出産日の産前、出産予定日の産前、出産日の翌日、
出産日の産後56日と表示させる
という感じで依頼をうけたのですが・・・。
>もし、この時刻情報が不要なのであれば、日付部だけを用いるようにしてみ>て下さい。
>DateTime型のDateプロパティを使えば、日付部だけの情報(正確には、時刻>値が午前12時(00:00:00)に変更されたDateTime型の値ですが)を得る事をで>きます。
Valueの値を変更してみたら、時刻が表示されずにできました。
>問1) String変数 [出産日] には、どのような文字列が渡される事を期待
>していますか?
型をStringにしたらエラーが消えたから、Dateにすると計算ができない
みたいだったから
>問2) その場合、計算式「Val(出産日) - 98 + 1」が、どのような値になる
>と思いますか?
日数を計算するときは、プラス1をしなければいけないと聞いてたので。
結果は、VBAみたいにいくかと思ってたから
>問3) その結果、String変数 [wk出産産前] は、どのような値になるで
>しょうか?
わからない
VBAとVBって、関数ひとつでもぜんぜん違うんですね。
> 作っている内容というのが、
(中略)
> という感じで依頼をうけたのですが・・・。
あぁ……それで何となくイメージがわきました。
多分、産前産後の休業制度に関する計算式でしょうね。
>> 問1) String変数 [出産日] には、どのような文字列が渡される事を期待
>> していますか?
> 型をStringにしたらエラーが消えたから、
でもプログラミングというのは、エラーが消えれば良い……というものでは無いですよね? (^^;
エラーにならなくなったとしたら、その後で、それぞれの変数に
「どのような値が渡されるか」を調べるようにしてみてください。
(VBのステップ実行機能や、ウォッチウィンドウなどが役に立つでしょう)
プログラムの間違い(いわゆる「バグ」)で一番多いのが、ロジック上のエラー
すなわち、「エラーは出ないけれど、期待動作しない」という物ですから、
それぞれの各行の意味を、きちんと把握しておかないと、望むべき結果は得られないかと。
また、「どういう結果になるのが正しいのか」を把握しておく事も重要ですね。
例えば……予定日に何年何月何日を指定したら、ラベルにこういう結果が
表示されるのが正しいのだ……という例を、ある程度ご自身で把握しておかないと、
折角作成したコードが、正しいかどうかの検証すらできませんから。
> Dateにすると計算ができないみたいだったから
Date型で計算できますよ。(というよりも、Date型を使った方が計算が楽かと思います)
# 最初の回答でも、『DateTimeのAdd系メソッドや減算演算子を使えば』と答えましたよね。(^^;
例えば、こんな感じで計算できますよ。
Dim 出産日 As Date 'これらの As Date の部分は、
Dim 出産日産後 As Date ' As DateTime と書いても同じ意味です。
出産日 = Me.DTPicker2.Value '選択された日付を取得
出産産前 = 出産日.AddDays(-42.0) '42日前の日付を取得
Fr日数結果.Lb出産日産前.Text = 出産産前.ToString("yyyy\/MM\/dd") '書式を整えて表示
元のコードのように、Date型の値を、そのままString型で受ける、というのは、
あまり良いコードとは言えません。日付型から文字列型への暗黙の変換は、
実行環境(コントロールパネルの設定)によって変換結果が異なることがあるため、
VB.NETはもちろんの事、VB6やVBAであっても、推奨されていないコーディングです。
異なるデータ型へ変換する時は、型変換処理を適切に行うように心がけた方が良いですよ。
実行環境によっては、期待する結果とは異なる変換が行われてしまう事がありえますから。。。
できれば、暗黙の型変換を防ぐため、モジュールの一番先頭の行に、
Option Explicit On
Option Strict On
の2行を入れておく事を強くお奨めします。
(あるいは、Visual Studio.NETの設定で、Option Strict Onを強制させる事もできます)
Option Strictを有効にしておくと、暗黙の型変換に頼ったコードが使われていた時に
コンパイルが通らないようになりますので、プログラムの問題を見つけやすくなります。
> VBAとVBって、関数ひとつでもぜんぜん違うんですね。
VB.NETとVB/VBAは、かなり違っていますね。(一時期は、VB.not と皮肉られていた程です)
でもVBAとVBに関していえば、関数などを見ても、一緒の部分がとても多いと思いますよ。
大きな違いと言っても、画面(Form)の扱いが違う程度だと思います。
VB(VBA)用の共通の基本関数が幾つかあって、それに、VB固有のオブジェクトが追加されているか、
あるいは、VBA固有の命令…すなわち、各製品(Excel, Outlook など)に固有のオブジェクト…が
追加されているか、という程度のものでしょう。
なお、細かい突っ込みですが…
> 'Form結果をモーダルで表示
> Fr日数結果.Show()
これはモーダルではなく、モードレスです。
> VBAとVBって、関数ひとつでもぜんぜん違うんですね。
でも日付型の概念は VB も .NET もエクセルとかも、Cでも同じ
だったと思いますよ。
日付型の本質は Long 型(…多分(汗))の数値演算です。これがよく
分かるのがエクセルのセル書式設定で、セルに0を入力して書式を
日付型にすると1900/1/1(0かも?)とかになっちゃいます。逆に
システム時刻を数値で表示させると37941.29167とかいう人間には
すぐに理解できない数字となります。セルに入っている数値は
変更せずに時刻だけ表示も月だけ表示もできますよね?。
つまり日付型は、年月日時分をまとめて一つの数値で覚えていると
理解してください。Date 型といった時点で年月日時分を全部
持ってます。このうち月日だけ…とか年月日だけ…時刻だけ…と
表面に出してるのが DateTimePicker であり Format 関数であり
その他の日付関係のコントロールなのです。表面の表示だけ変えてる
…まさにエクセルの書式設定です。
で、日付型でプログラムに計算してもらうには、うらで
37941.29167 - 37441.29167 …とかいう計算をしてもらわないと
いけませんから、普通に98とか42を引いても答えは出てきません。
日付型には日付型特有の関数を使うと覚えておきましょう。
日の計算なら魔界の仮面弁士さんが使われた AddDays
月なら AddMonths 年なら AddYears …
日付の差なら DateDiff …だったかな?。いずれもヘルプで
『日付型』を調べれば出てくると思いますよ。
> Dateにすると計算ができないみたいだったから
…それだと日付型の意味が無いじゃないですか(^^;)
自分のマズいコーディングを棚に上げる前にヘルプ読んで下さい。
お願いしますからm(;_;)m
いろいろとありがとうございます。
>出産産前 = 出産日.AddDays(-42.0) '42日前の日付を取得
産前、産後の表示はうまくいくようになりました。
でも、日数が表示しなくなりました。
出産産前みたいに、日数計算のところでもAddDaysを使ってみたら
エラーになってしまいました。
こんな使い方では、いけないのでしょうか??
If Me.DTPicker1.Value >= Me.DTPicker2.Value Then
Fr日数結果.Lb日数.Text = (wk産後.AddDays(-wk出産産前, 1))
Else
Fr日数結果.Lb日数.Text = (wk産後.AddDays(-wk予定産前, 0))
End If
いまいち、AddDaysの使い方がわかりません。
さしつかえなければ、教えてください。
> Fr日数結果.Lb日数.Text = (wk産後.AddDays(-wk出産産前, 1))
エラーが出ているのであれば、[wk産後]や[wk出産産前]のデータ型も書いてください。
あるいはせめて、エラーメッセージを記述してください。m(_ _)m
データ型によって、サポートしているメソッドが異なりますので、
これだけの情報だと、メソッドの使い方でエラーになっているのか、
それとも、変数の定義にも問題があるのか、といった判断ができません。
とりあえず今回は、[wk産後]がDate型、[wk出産産前]がDouble型だと仮定しますね。
> いまいち、AddDaysの使い方がわかりません。
分からなければ、ヘルプを読みましょう。(^^;)
(ヘルプの記述は難解かも知れませんが、これが全ての基本になるので)
——指しあたって、問題点は2つあります。
1) AddDaysメソッドは、引数を1つだけ受け取る仕様なのです。
しかし、提示されたコードでは、引数を2つ渡していますよね。
2) AddDaysメソッドの引数は、Date型(System.DateTime構造体)です。
一方、LabelのTextプロパティは、String型です。先にも書きましたように、
Date型をそのままString型に渡さないように直してみて下さい。
(ToStringメソッドを使って書式化するサンプルを、先に回答しましたよね)
≫ 特攻隊長まるるうさん
> 日付の差なら DateDiff …だったかな?。
DateTime型のop_Subtractionメソッドも使えますよ。
おっと、一箇所訂正。m(_ _)m
> 2) AddDaysメソッドの引数は、Date型(System.DateTime構造体)です。
『AddDaysメソッドの戻り値は、』の間違いです。失礼しました。
>> でも、日数が表示しなくなりました。
日数の計算方法がわかりませんが、○日前(あるいは△日後)の
日付を求めるのであれば、AddDaysなどのAdd系メソッドで算出できます。
2つの日付値の差を求めるなら、日付型の「op_Subtractionメソッド」か、
あるいは「DateDiff関数」で行えます。ただしこの場合、
前者の戻り値はTimeSpan型、後者はLong型ですので、
ラベルに表示する際には、(CStr関数かToStringメソッドなどで)
文字列に変換してから表示させる必要がありますね。
時間がたってしまいましたが、
いろいろやってみましたが、やはり日数計算ができません。
日数計算というのは、
出産予定日>=出産日
真 産後-出産産前+1 (+1 は日付の誤差を計算するため)
偽 産後-予定日産前+1
例1 出産予定日→6/27 出産日→6/9 の場合
出産予定日産前→5/17 出産日産前→4/29
出産日翌日→6/10
出産日産後→8/4
計98日間(真なので)
例2 出産予定日→6/9 出産日→6/27 の場合
出産予定日産前→4/29 出産日産前→5/17
出産日翌日→6/28
出産日産後→8/22
計116日間(偽なので)
Dim wk予定産前 As DateTime
Dim wk出産産前 As DateTime
Dim wk出産翌日 As DateTime
Dim wk産後 As DateTime
Dim 日数 As DateTime
出産予定日 = Me.DTPicker1.Value
出産日 = Me.DTPicker2.Value
'出生児の数により判定
If RBtn双胎.Checked Then
wk予定産前 = 出産予定日.AddDays(-98 + 1.0)
wk出産産前 = 出産日.AddDays(-98 + 1.0)
Else
wk予定産前 = 出産予定日.AddDays(-42 + 1.0)
wk出産産前 = 出産日.AddDays(-42 + 1.0)
End If
wk出産翌日 = 出産日.AddDays(+1.0)
wk産後 = 出産日.AddDays(+56.0)
'日数計算の判定(出産予定日>=出産日)
If Me.DTPicker1.Value >= Me.DTPicker2.Value Then
日数 = wk産後.AddDays(-Val(wk出産産前) + 1.0)
Else
日数 = wk産後.AddDays(-Val(wk予定産前) + 1.0)
End If
Fr日数結果.Lb日数.Text() = 日数
Fr日数結果.Lb出産予定日.Text = Me.DTPicker1.Value
Fr日数結果.Lb出産日.Text = Me.DTPicker2.Value
って感じでやってみたら、
例1の結果が 199日 になってしまいます。
例2の結果が 199日 と同じ結果になってしまいます。
どうしたら、よいのかアドバイスください。
『時刻』と『時間』の違いに注意してみてください。
「○月○日」「○月○日○時○分」などの『日付』や『時刻』は
DateTime型で表されます。
しかし、「○日間」「○分間」などという『期間』や『時間間隔』は
TimeSpan型(など)で表されることになります。
時間間隔を求める場合は、2つのDateTime型の差を得れば良いでしょう。
その『2つの日付値の差を求める』方法については、先に
私や特攻隊長まるるうさんからの回答が付いていますよね。
もうひとつ教えてください。
上記のやり方でやって、あと少しというところになったのですが、
上の例でいうのであれば、例1で98日にのろころが97日、
例2で116日が115日となってしまいます。
日数 = DateTime.op_Subtraction(wk産後, wk出産産前)
にプラス1をしたいのですが、他の関数と組み合わせることは
可能なのでしょうか??
Dim 日数 As TimeSpan
If Me.DTPicker1.Value >= Me.DTPicker2.Value Then
日数 = DateTime.op_Subtraction(wk産後, wk出産産前)
Else
日数 = DateTime.op_Subtraction(wk産後, wk予定産前)
End If
Fr日数結果.Lb日数.Text() = 日数.ToString()
いろいろありましたが、なんとなく完成することが
できました。いろいろなアドバイスありがとうございます。
今回のプログラムにいろいろ追加して、勉強してみるつもりです。
ありがとうございました。
# う、送信ボタンを押したつもりだったけど、送信されていない…。
# 解決済みのようですが、折角書いたので、投稿させてください。(^^;
> 例2で116日が115日となってしまいます。
例えば、「11月29日〜30日」という期間を考えてみてください。
この期間は2日でしょうか。それとも1日でしょうか?
ある意味では、『29日を1日目、30日を2日目と見なし、2日間』と見なす考え方もありますし、
一方では、『29日から30日までの経過時間は24時間なので、1日間』と見なす考え方もあります。
———ですから、日付処理をプログラミングする場合は、求められている結果が、
前者の方法(初日を1日目と見る仕様)になっているのか、それとも、
後者の方法(初日を0日目と見る仕様)になっているのかを、事前に
よく確認しておく必要がありますね。
> = DateTime.op_Subtraction(wk産後, wk出産産前)
この場合は、「〜 = wk産後.Subtract(wk出産産前)」と書く事もできます。(どちらでもOK)
op_SubtractionやSubtractで得た値は、期間を表すデータ「TimeSpan型」なので、
そこから、日数に換算した値を得るために、「Daysプロパティ」か
「TotalDaysプロパティ」を使ってください。
これらのプロパティの戻り値は、DaysがInteger型、TotalDaysがDouble型です。
どちらも、単純な数値型なので、四則演算がそのまま使えます。
あるいは、DateDiff関数を使うという手もあります。こちらを使った場合は、
TimeSpan型を経由せずに、直接、日数換算の数値を算出する事ができます。
ツイート | ![]() |