質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

4回答

6177閲覧

SimpleDateFormatで日時形式判定

Hyonta

総合スコア36

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2016/04/30 07:42

編集2016/05/03 02:37

###実現したいこと
Javaの初心者です。
ログ解析用のツールを作成しているのですが、
標準入力から入力された日時が以下のいづれかの形式でなければエラーとなり、もう一度標準入力を求めるメッセージを出力させたいです。
yyyy/MM/dd
yyyy/MM/dd-hh
yyyy/MM/dd-hh:mm
yyyy/MM/dd-hh:mm:ss

SimpleDateFormatを使用して判定する方法を教えていただきたいです。
また、存在しない日時のとき、開始日時が終了日時より大きい時もエラーとしたいです。
現在作成中ですが、日時フォーマットを、例えば「yyyy/MM/dd-hh」で入力した場合もエラーとなってしまいます。
修正方法を教えていただきたいです。また、ソースコードにおかしいところがあれば教えていただきたいです。

##ソースコード
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Scanner;

public class Execute {

private static final int OK = 0; private static final int ERROR_DATE_FORMAT = 1; private static final int ERROR_DATE_NO_EXIST = 2; private static final int ERROR_DATE_START_TO_END = 3; public static void main(String[] args) { System.out.println("開始日時を入力してください。"); Scanner scan = new Scanner(System.in); String startTime = scan.next(); if (CheckDate(startTime) == ERROR_DATE_FORMAT) { System.out.println("日時の形式が正しくありません。もう一度入力してください。"); } if (CheckDate(startTime) == ERROR_DATE_NO_EXIST) { System.out.println("存在しない日時が入力されています。"); } if (CheckDate(startTime) == OK) { System.out.println("終了日時を入力してください。"); //終了日時を入力する処理を記載する。 } scan.close(); } private static int CheckDate(String startTime) { final String FORMAT1 = "^[0-9][4]/[01]?[0-9]/[0123]?[0-9]$"; final String FORMAT2 = "^[0-9][4]/[01]?[0-9]/[0123]?[0-9]-([01]?[0-9]|[2]?[0-3])$"; final String FORMAT3 = "^[0-9]{4}/[01]?[0-9]/[0123]?[0-9]-([01]?[0-9]|[2]?[0-3]):([0-5]?[0-9])$"; final String FORMAT4 = "^[0-9]{4}/[01]?[0-9]/[0123]?[0-9]-([01]?[0-9]|[2]?[0-3]):([0-5]?[0-9]):([0-5]?[0-9])$"; long startTimes; //long endTime; //while (true) { SimpleDateFormat format = null; if (startTime.matches(FORMAT1)) { format = new SimpleDateFormat("yyyy/MM/dd"); } else if (startTime.matches(FORMAT2)) { format = new SimpleDateFormat("yyyy/MM/dd-hh"); } else if (startTime.matches(FORMAT3)) { format = new SimpleDateFormat("yyyy/MM/dd-hh:mm"); } else if (startTime.matches(FORMAT4)) { format = new SimpleDateFormat("yyyy/MM/dd-hh:mm:ss"); } else { return ERROR_DATE_FORMAT; // System.out.println("日時の形式が正しくありません。もう一度入力してください。"); // continue; } try { format.setLenient(false); startTimes = format.parse(startTime).getTime(); } catch (ParseException e) { return ERROR_DATE_NO_EXIST; // System.out.println("存在しない日時が入力されています。"); // continue; } // break; return OK; }

}

##現在の問題点

・yyyy/MM/dd
yyyy/MM/dd-hh のフォーマットの場合もエラーとなってしまう。

・15行目などの
System.out.println("日時の形式が正しくありません。もう一度入力してください。");
のあとにもう一度標準入力を再開する方法がわからない。

##エラー種類
・日時の形式が正しくありません。もう一度入力してください。
・存在しない日時が入力されています。もう一度入力してください。
・開始日時>終了日時となっています。もう一度入力してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

私自身思いついたことが実際できるのか気になり、実験してみました。
ParsePositionを使って解析位置を追跡し、先頭から段階的に解析しています。

java

1import java.text.DateFormat; 2import java.text.ParsePosition; 3import java.text.SimpleDateFormat; 4import java.util.Calendar; 5import java.util.Date; 6 7public class Q33781 { 8 public static void main(String[] args) { 9 10 // 出力用フォーマット 11 DateFormat forOut = new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss"); 12 13 // 段階的に解析するためのフォーマット 14 SimpleDateFormat format1 = new SimpleDateFormat("yyyy/MM/dd"); 15 SimpleDateFormat format2 = new SimpleDateFormat("-HH"); 16 SimpleDateFormat format3 = new SimpleDateFormat(":mm"); 17 SimpleDateFormat format4 = new SimpleDateFormat(":ss"); 18 SimpleDateFormat[] formats = { format1, format2, format3, format4 }; 19 20 // 解析する情報に応じたCalendarクラスのフィールド定数 21 int[] fields = { Calendar.DATE, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND }; 22 23 // 各種フォーマットを厳密モードにする 24 for (SimpleDateFormat format : formats) { 25 format.setLenient(false); 26 } 27 28 String[] times = { "2016/04/30", "2016/04/30-17", "2016/04/30-17:30", "2016/04/30-17:30:50", 29 "2016/04/32", "2016/04/30-25", "2016/04/30-17:60", "2016/04/30-17:30:60", "2016-04-30"}; 30 Text: for (String str : times) { 31 // 日時設定用Calendarインスタンス 32 Calendar cal = Calendar.getInstance(); 33 // 解析位置を保持するための、ParsePositionインスタンス 34 ParsePosition pos = new ParsePosition(0); 35 36 // 解析位置が対象文字列の最後に達するか、formatsのフォーマットの数ループするまでループ 37 for (int i = 0; pos.getIndex() < str.length() && i < formats.length; i++) { 38 Date date = formats[i].parse(str, pos); 39 // 解析失敗時 40 if (date == null) { 41 System.out.println(str + " の解析失敗 " + pos.getErrorIndex()); 42 continue Text; 43 } 44 // 最初の年月日はそのまま設定 45 if (i == 0) { 46 cal.setTime(date); 47 // 時分秒はDateをCalendarに変換し、対象のフィールドだけ抽出 48 } else { 49 Calendar temp = Calendar.getInstance(); 50 temp.setTime(date); 51 cal.set(fields[i], temp.get(fields[i])); 52 } 53 } 54 System.out.println(str + " の解析結果 " + forOut.format(cal.getTime())); 55 } 56 } 57 58}

出力結果

2016/04/30 の解析結果 2016/04/30-00:00:00
2016/04/30-17 の解析結果 2016/04/30-17:00:00
2016/04/30-17:30 の解析結果 2016/04/30-17:30:00
2016/04/30-17:30:50 の解析結果 2016/04/30-17:30:50
2016/04/32 の解析失敗 0
2016/04/30-25 の解析失敗 10
2016/04/30-17:60 の解析失敗 13
2016/04/30-17:30:60 の解析失敗 16


これで解析の可不可の判定、および解析した時のCalendarオブジェクトの取得が可能になります。
開始、終了の時間の前後の判定はCalendarのcompareメソッドを使えば可能です。
また、エラーの内容として「書式が合っていない」か「存在しない日時」かを分けたければ、SimpleDateFormatを厳密モード、非厳密モードの2パターンで解析すれば可能かと思います。
厳密で失敗し、非厳密で成功すれば「存在しない日時」、両方失敗すれば「書式が合っていない」とエラーが出せます。
ただし、このフォーマットだと"2016/4/30"の様に2桁であるべきところが1桁しかない場合も通すので、厳密に4桁/2桁/2桁などを判定したいのならやはり正規表現になるかもしれません。

投稿2016/05/03 07:04

編集2016/05/03 07:34
swordone

総合スコア20649

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hyonta

2016/05/03 12:33

回答ありがとうございました! 少し難しそうですが、こちらの方法も試してみます!
guest

0

ベストアンサー

こんな感じでしょうか。デバッグしていないので間違っていたらごめんなさい。

Java

1import java.text.ParseException; 2import java.text.SimpleDateFormat; 3import java.util.Scanner; 4 5public class Main { 6 7 private static final String FORMAT1 = "^[0-9]{4}/[01]?[0-9]/[0123]?[0-9]$"; 8 private static final String FORMAT2 = "^[0-9]{4}/[01]?[0-9]/[0123]?[0-9]-([01]?[0-9]|[2]?[0-3])$"; 9 private static final String FORMAT3 = "^[0-9]{4}/[01]?[0-9]/[0123]?[0-9]-([01]?[0-9]|[2]?[0-3]):([0-5]?[0-9])$"; 10 private static final String FORMAT4 = "^[0-9]{4}/[01]?[0-9]/[0123]?[0-9]-([01]?[0-9]|[2]?[0-3]):([0-5]?[0-9]):([0-5]?[0-9])$"; 11 12 public static void main(String[] args) { 13 14 long startDateTime; 15 long endDateTime; 16 System.out.println("開始日時を入力してください"); 17 Scanner scan = new Scanner(System.in); 18 19 while (true) { 20 21 String s = scan.next(); 22 23 SimpleDateFormat format = null; 24 if (s.matches(FORMAT1)) { 25 format = new SimpleDateFormat("yyyy/MM/dd"); 26 } else if (s.matches(FORMAT2)) { 27 format = new SimpleDateFormat("yyyy/MM/dd-hh"); 28 } else if (s.matches(FORMAT3)) { 29 format = new SimpleDateFormat("yyyy/MM/dd-hh:mm"); 30 } else if (s.matches(FORMAT4)) { 31 format = new SimpleDateFormat("yyyy/MM/dd-hh:mm:ss"); 32 } else { 33 System.out.println("日時の形式が正しくありません。もう一度入力してください。"); 34 continue; 35 } 36 37 try { 38 format.setLenient(false); 39 startDateTime = format.parse(s).getTime(); 40 } catch (ParseException e) { 41 System.out.println("存在しない日時が入力されています。もう一度入力してください。"); 42 continue; 43 } 44 45 break; 46 } 47 48 System.out.println("終了日時を入力してください"); 49 while (true) { 50 String s = scan.next(); 51 52 SimpleDateFormat format = null; 53 if (s.matches(FORMAT1)) { 54 format = new SimpleDateFormat("yyyy/MM/dd"); 55 } else if (s.matches(FORMAT2)) { 56 format = new SimpleDateFormat("yyyy/MM/dd-hh"); 57 } else if (s.matches(FORMAT3)) { 58 format = new SimpleDateFormat("yyyy/MM/dd-hh:mm"); 59 } else if (s.matches(FORMAT4)) { 60 format = new SimpleDateFormat("yyyy/MM/dd-hh:mm:ss"); 61 } else { 62 System.out.println("日時の形式が正しくありません。もう一度入力してください。"); 63 continue; 64 } 65 66 try { 67 format.setLenient(false); 68 endDateTime = format.parse(s).getTime(); 69 } catch (ParseException e) { 70 System.out.println("存在しない日時が入力されています。もう一度入力してください。"); 71 continue; 72 } 73 74 if (endDateTime - startDateTime < 0) { 75 System.out.println("開始日時>終了日時となっています。もう一度入力してください。"); 76 continue; 77 } 78 79 break; 80 } 81 82 scan.close(); 83 84 } 85 86} 87

メソッドに分けた方がいいとかあるでしょうけど、そこまでやろうとすると面倒なので手を抜いています。

投稿2016/05/01 03:55

kentei_syunrai

総合スコア946

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hyonta

2016/05/03 02:40 編集

回答ありがとうございます!本当にありがとうございます。 kenteiさんの回答内容をもとに作成しましたがわからない箇所がありますので、質問内容を編集しております。見ていただけると幸いです。
kentei_syunrai

2016/05/03 04:28

>・yyyy/MM/dd >yyyy/MM/dd-hh のフォーマットの場合もエラーとなってしまう。 正規表現FORMAT1 ,2が間違っているからです。 "^[0-9][4] ← ここは{}で囲んでください。4文字の意味です >15行目などの >System.out.println("日時の形式が正しくありません。もう一度入力してください。"); >のあとにもう一度標準入力を再開する方法がわからない。 while文で回してないと動かないです。 私のソースではそうしていると思いますが。 デバッグするなり何なりして、自分で調べることも重要ですよ。
Hyonta

2016/05/03 12:35

ありがとうございました。 やりたい処理を実装することができました。 自分でもデバッグなどを駆使しながら調べて、できるだけ成長できるように頑張ります。
swordone

2016/05/03 12:55

これだと"2016/20/01"のような文字列も「日時の形式が正しくない」になるのですがいいのでしょうか? シンプルに"^\\d{4}/\\d{2}/\\d{2}-\\d{2}:\\d{2}:\\d{2}$"かとも思うのですが…
guest

0

java parse SimpleDateFormat
で google 検索してみると良いです。

たとば、次のページなどを読んでみてください。

文字列を Data 型に変換できれば、あとは大小比較ができます。

投稿2016/04/30 11:00

katoy

総合スコア22322

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hyonta

2016/05/03 02:41

回答ありがとうございます。質問内容を編集しました。
guest

0

まずは自分なりに実装してみたらどうでしょうか。

投稿2016/04/30 09:23

yona

総合スコア18155

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hyonta

2016/05/03 02:41

回答ありがとうございます。質問内容を編集しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問